Skip to content

material

FreeBodyEngine.graphics.material #

BlendMode #

Bases: Enum

How a Material's draws combine with what's already in the framebuffer - drives both the draw queue's opaque/transparent split (see Renderer.submit/flush) and which shader pair a Material loads by default (an opaque material writes the G-buffer; a transparent/additive one is forward-shaded directly against 'lit' - see PBRPipeline).

ADDITIVE = auto() class-attribute instance-attribute #

OPAQUE = auto() class-attribute instance-attribute #

TRANSPARENT = auto() class-attribute instance-attribute #

Material(data, property_definitions, injector=Injector(), default_vert='engine://shader/default_shader.fbvert', default_frag='engine://shader/default_shader.fbfrag') #

A shader plus a set of named properties (colors or textures) that drive its uniforms - built from parsed .fbmat TOML data against a property_definitions schema (see e.g. PBRMaterial's albedo/normal/ roughness/... set). Property values are readable/writable both as plain attributes (material.albedo) and as dict items (material['albedo']), transparently redirected to self.properties via __getattribute__/ __setattr__/__getitem__/__setitem__ below.

Parses data against property_definitions into self.properties, and compiles this material's shader (from data['shader'], defaulting to default_vert/default_frag) via the renderer.

default_vert/default_frag exist so a GraphicsPipeline-specific Material subclass can pick its own defaults (e.g. PBRMaterial selecting a forward-lit shader pair for a non-opaque blend mode, since its deferred G-buffer can't hold a blended surface) without this generic base class knowing anything about that pipeline's rendering model - see PBRMaterial in graphics/pbr/material.py.

blend_mode = {'opaque': BlendMode.OPAQUE, 'transparent': BlendMode.TRANSPARENT, 'additive': BlendMode.ADDITIVE}.get(str(data.get('blend', 'opaque')).lower(), BlendMode.OPAQUE) instance-attribute #

data = data instance-attribute #

pixel_filter = str(data.get('filter', 'linear')).lower() == 'nearest' instance-attribute #

properties = self.parse_properties(property_definitions) instance-attribute #

property_definitions = property_definitions instance-attribute #

shader = get_service('renderer').load_shader(files.get_file(vert_source), files.get_file(frag_source), injector, geom_file) instance-attribute #

parse_properties(property_definitions) #

Builds {property_name: parsed_value} from self.data, keeping only the properties declared in property_definitions and ignoring anything else the .fbmat TOML might contain (e.g. shader/ filter, which are handled separately).

parse_property_val(val, property, property_definitions) #

Interprets one property's raw TOML value against its declared PropertyType - currently only implemented for COLOR_RGB/COLOR_RGBA: a Texture/Image is passed through as-is, a #-prefixed string is parsed as a hex Color, and a 2-4 length sequence of numbers is parsed as a Color too. Warns and returns None (silently, since no return follows the warning) for anything that doesn't match, or for any other PropertyType.

reload(data) #

Re-applies freshly loaded .fbmat TOML data to this SAME Material object in place - every Sprite/Model/etc. already holding a reference keeps working, no re-wiring needed. Used by dev-mode hot reload (see core/files/hot_reload.py). Only property data (colors/texture paths) is re-applied here; call reload_shader() separately if the shader source files changed instead.

reload_shader() #

Recompiles this material's shader in place (same Shader object, same GL program id) from whatever its vert/frag/geom source files currently contain - for when one of those files changed, not the .fbmat itself.

use() #

Uploads every property's current value to the shader as uniforms - a Color property sets {Prop}_Color and {Prop}_useTexture = False; a Texture/Image property applies this material's pixel_filter to it and sets {Prop}_Texture and {Prop}_useTexture = True - then activates the shader for drawing.

MaterialInjector(material) #

Bases: Injector

Lets a shader reference a material property (e.g. ALBEDO) as a bare identifier and have it transparently resolve to sample(prop_Texture, uv) or prop_Color depending on whether the material was given a texture or a plain color, without the shader author writing that branch by hand.

Stores the Material this injector rewrites shader references for. mat_properties is left empty here and filled lazily by _property_types().

mat_properties = {} instance-attribute #

material = material instance-attribute #

ast_inject(tree) #

Fragment-shader-only: rewrites every bare PROPERTY identifier (e.g. ALBEDO) into useTexture ? sample(Texture, uv) : Color, and injects the {Prop}_Texture/{Prop}_Color/{Prop}_useTexture uniforms that ternary references - letting a shader author write ALBEDO directly instead of hand-writing the texture-vs-color branch themselves.

get_builtins() #

Fragment-shader-only: declares each material property's capitalized name (e.g. ALBEDO) as a uniform-kind builtin so it type-checks during semantic analysis even if ast_inject() below somehow doesn't get to rewrite it first - belt-and-braces, since ast_inject() is what actually performs the real rewrite.

parse_property(property) #

Maps a COLOR_* PropertyType to the GLSL type its backing uniform is declared as (e.g. COLOR_RGB -> 'vec3').

PropertyType #

Bases: Enum

The kinds of value a Material property can hold - a plain scalar/ color, or a texture. Drives both how Material.parse_property_val interprets a .fbmat's raw data for a property and what GLSL type MaterialInjector.parse_property resolves a color property to for a shader referencing it as a bare identifier.

COLOR_R = auto() class-attribute instance-attribute #

COLOR_RG = auto() class-attribute instance-attribute #

COLOR_RGB = auto() class-attribute instance-attribute #

COLOR_RGBA = auto() class-attribute instance-attribute #

FLOAT = auto() class-attribute instance-attribute #

INT = auto() class-attribute instance-attribute #

TEXTURE = auto() class-attribute instance-attribute #