# Materials `simvx.core.Material` is the backend-agnostic material data SimVX feeds to both Vulkan and the WebGPU runtime. It carries colour, PBR parameters, blending mode, flags (wireframe, double-sided, unlit), and up to five texture maps. ```python from simvx.core import Material red = Material(colour=(1, 0, 0, 1)) glass = Material(colour=(0.6, 0.9, 1.0, 0.4), blend="alpha") brick = Material(albedo_map="textures/brick.png", roughness=0.8) ``` ## Constructor | Argument | Type | Default | Notes | |---------------------------|-----------------------|--------------------|--------------------------------------| | `colour` | RGBA (or RGB) in 0-1 | `(1, 1, 1, 1)` | Multiplied by albedo at shade time | | `metallic` | `float` 0-1 | `0.0` | PBR metallic factor | | `roughness` | `float` 0-1 | `0.5` | PBR roughness factor | | `blend` | `"opaque" / "alpha" / "additive"` | `"opaque"` | Pipeline derivative selector | | `wireframe` | `bool` | `False` | Force `VK_POLYGON_MODE_LINE` | | `double_sided` | `bool` | `False` | Disable backface culling | | `unlit` | `bool` | `False` | Skip lighting; use raw albedo | | `albedo_map` | `str / bytes / ndarray`| `None` | Diffuse texture | | `normal_map` | `str / bytes / ndarray`| `None` | Tangent-space normal map | | `metallic_roughness_map` | `str / bytes / ndarray`| `None` | Packed B=metal, G=rough | | `emissive_map` | `str / bytes / ndarray`| `None` | RGB emissive | | `ao_map` | `str / bytes / ndarray`| `None` | Ambient occlusion | | `emissive_colour` | 3- or 4-tuple | `None` | RGB or RGB+intensity | | `emissive_strength` | `float` | `None` | Scalar multiplier; folds into slot 4 | ## Texture sources Every `*_map` kwarg accepts three forms: ### Filesystem / asset URI (`str`) ```python Material(albedo_map="assets/brick.png") Material(albedo_map="pkg://my_game.assets/brick.png") # importlib.resources URI ``` The backend's `TextureManager` resolves the path, decodes the image, and caches by `(source, filter)`. ### Embedded image bytes (`bytes`) ```python with open("brick.png", "rb") as f: Material(albedo_map=f.read()) ``` Useful when bundling a single-file game export: the bytes ship in the Python source. ### NumPy ndarray (in-memory texture) ```python import numpy as np # 256x256 procedural ramp, RGBA uint8 ramp = np.zeros((256, 256, 4), dtype=np.uint8) ramp[..., 0] = np.linspace(0, 255, 256, dtype=np.uint8)[None, :] ramp[..., 3] = 255 Material(albedo_map=ramp, unlit=True) # 2D height-tinted gradient, float32 in [0, 1] gradient = np.zeros((1, 256, 4), dtype=np.float32) gradient[..., 0] = np.linspace(0.1, 1.0, 256) # R rises with height gradient[..., 3] = 1.0 # opaque Material(albedo_map=gradient) ``` The constructor coerces `ndarray` sources to `uint8` at construction: - `uint8` → passed through unchanged. - `float32` / `float64` in `[0, 1]` → scaled by 255 to `uint8`. - `float` outside `[0, 1]` → WARNING logged, clipped, then scaled. - Other dtypes → `TypeError`. This guarantees the GPU sees byte-per-channel data on every backend. #### Used by Shipped ports relying on `Material(albedo_map=ndarray)`: - **Procedural Planets**: bakes an HSL height-and-latitude ramp into a 1D texture so the planet shader can sample without a custom `ShaderMaterial` (which is Vulkan-only: see the TODO entry). - **Q1K3**: bakes per-room palette ramps for the retro look. - **HexGL**: bakes a track-side speed-strip texture. Use this pattern when: - You need procedural / parameterised shading on the **web** target (`ShaderMaterial` has no WGSL emission path yet). - You don't want to ship binary asset files in a code-first port. - The texture is small enough that numpy generation is cheaper than disk I/O. ## Web export compatibility The `Material` data shape is identical across Vulkan and WebGPU. `albedo_map=ndarray` is fully supported on web: the numpy buffer ships through the resource channel as a `KIND_TEXTURE` upload. No additional configuration required. What is **not** yet web-compatible: - `ShaderMaterial`: Vulkan-only. The WGSL emission path is tracked in `TODO.md` (`## Tier 2 / Architectural`). ## Inspector visibility All `Material` constructor arguments are stored as plain Python attributes on the instance (no Property descriptors yet). The editor inspector renders the colour swatch and PBR sliders via a custom material widget; texture-map fields are read-only and show the source URI / "ndarray ({H}x{W}, dtype)" summary.