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).
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.