simvx.core.graphics.material¶
Material: pure data (no GPU dependencies).
Backends (SDL3, Vulkan) extend this to add texture/GPU management.
Module Contents¶
Classes¶
Pure material data for rendering. Backend-agnostic. |
Data¶
API¶
- simvx.core.graphics.material.log¶
‘getLogger(…)’
- class simvx.core.graphics.material.Material(colour: tuple[float, ...] | numpy.ndarray = (1.0, 1.0, 1.0, 1.0), metallic: float = 0.0, roughness: float = 0.5, blend: Literal[opaque, alpha, additive, cutoff] = 'opaque', alpha_cutoff: float = 0.5, wireframe: bool = False, double_sided: bool = False, unlit: bool = False, albedo_map: str | bytes | None = None, normal_map: str | bytes | None = None, metallic_roughness_map: str | bytes | None = None, emissive_map: str | bytes | None = None, ao_map: str | bytes | None = None, emissive_colour: tuple[float, ...] | None = None, emissive_strength: float | None = None, needs_scene_colour: bool = False, needs_scene_depth: bool = False, uv_offset: tuple[float, float] = (0.0, 0.0), uv_scale: tuple[float, float] = (1.0, 1.0), uv_rotation: float = 0.0, wetness_affected: bool = False, receives_decals: bool = True)[source]¶
Pure material data for rendering. Backend-agnostic.
Every map kwarg (
albedo_map,normal_map,metallic_roughness_map,emissive_map,ao_map) accepts three forms:str: filesystem path or asset URI; backend loads from disk viaTextureManager.bytes: raw image bytes (PNG / JPEG / etc.) decoded by the backend’s image loader.numpy.ndarray: an in-memory texture, shape(H, W, C)where C is 1, 3, or 4. Coerced touint8at construction time so the GPU always sees byte-per-channel data;float32arrays in[0, 1]are scaled, out-of-range floats log a WARNING and clip, and unsupported dtypes raiseTypeError. Used by Procedural Planets and Q1K3 to bake gradient ramps without shipping PNGs.
Example: mat = Material(colour=(1, 0, 0, 1)) # Red mat = Material(colour=(0, 1, 0), blend=”alpha”) # Translucent green mat = Material(albedo_map=”textures/brick.png”) # On-disk texture mat = Material(albedo_map=numpy_rgba_uint8) # Numpy texture
Initialization
Initialize material with colour and properties.
Args: colour: RGBA (or RGB auto-expanded to 1.0 alpha) in [0-1] metallic: [0-1] metallic factor roughness: [0-1] roughness factor blend: “opaque”, “alpha”, “additive”, or “cutoff” (alpha-tested cutout: fragments with albedo alpha below
alpha_cutoffare discarded; renders in the opaque pass, no sorting) alpha_cutoff: [0-1] alpha-test threshold forblend="cutoff"wireframe: Render as wireframe double_sided: Disable backface culling unlit: Disable lighting (flat colour) albedo_map: Path or embedded bytes for albedo/diffuse texture, or a SubViewport / RenderView node whose live offscreen feed to sample. A PlanarReflection3D node is sampled with a mirrored projective UV from the fragment’s clip position (planar reflection) instead of the mesh UV. normal_map: Path or embedded bytes for normal map texture (optional) metallic_roughness_map: Path or embedded bytes for metallic-roughness texture (optional) emissive_map: Path or embedded bytes for emissive texture (optional) ao_map: Path or embedded bytes for ambient occlusion texture (optional) emissive_colour:(R, G, B)or(R, G, B, intensity)packing. If a 4-tuple, the fourth component is the intensity multiplier (legacy/round-trip form). Prefer the 3-tuple form with the separateemissive_strengthkwarg. emissive_strength: Scalar multiplier applied to the emissive RGB. Stored as the 4th component ofemissive_colour. May be combined with a 3-tupleemissive_colouror used alone (an opaque-white default is supplied whenemissive_colourisNone). needs_scene_colour: The material samples the copied opaque scene colour (screen-space refraction). Schedules the desktop opaque/transparent pass split so the transparent draw can read the scene behind it. Only meaningful for a transparent (blend="alpha") material. needs_scene_depth: The material samples the copied opaque scene depth. Rides the same pass split asneeds_scene_colour. uv_offset: UV-space offset applied to every material texture sample (KHR_texture_transform). Applied after scale/rotation:uv' = rotate(uv * uv_scale, uv_rotation) + uv_offset. uv_scale: UV-space scale factor per axis.(1, 1)is identity. uv_rotation: UV rotation in radians, counter-clockwise about the UV origin. Identity transforms (all defaults) are free: the shader path is gated by a feature bit set only when any of the three deviates from identity. wetness_affected: The surface responds to global weather. Under rain (WorldEnvironmentwetness/rain_intensity> 0, delivered via FrameGlobals) the shader darkens the albedo, drops the roughness (wet surfaces are glossier), and overlays an animated ripple normal on near-horizontal faces. Free when dry or unset: the shader block is gated on both this feature bit andwetness > 0, so a dry frame is byte-identical to the dry path. receives_decals: WhetherDecal3Dprojectors composite onto this surface. True by default (every surface receives, as before); set False to exclude a surface (glass, water, skybox proxies). Combined with each decal’scull_mask, which selects receiving render layers.- __slots__¶
(’weakref’, ‘_uid’, ‘colour’, ‘metallic’, ‘roughness’, ‘blend’, ‘alpha_cutoff’, ‘wireframe’, ‘do…
- __deepcopy__(memo: dict) simvx.core.graphics.material.Material[source]¶
- __setstate__(state: Any) None[source]¶
Restore a pickled material, minting a fresh identity.
A
_uidis only meaningful in the process that minted it, so a restored material takes a new one rather than a number that may already belong to a live material here.
- property content_key: tuple[source]¶
Hashable key representing all rendering-relevant properties.
Two materials with the same content_key are visually identical and can share a single GPU material slot. A :class:
Textureis fingerprinted by its stable identity; an unhashable raw source (a numpy ndarray handed straight to the TextureManager) has no identity of its own and falls back toid(), which is exactly the gap wrapping it in aTexturecloses.The texture’s
versiondeliberately stays OUT of the key. It drives re-upload, not re-slotting: putting it in would mint a fresh material slot on every pixel update of an otherwise unchanged material.