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.

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" / "cutoff"

"opaque"

Pipeline derivative selector

alpha_cutoff

float 0-1

0.5

Alpha-test threshold for blend="cutoff" (renders in the opaque pass, no sorting)

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

uv_offset

(u, v)

(0, 0)

Applied to every map: uv' = rotate(uv * uv_scale) + uv_offset

uv_scale

(u, v)

(1, 1)

Per-axis UV scale

uv_rotation

float radians

0.0

Counter-clockwise about the UV origin

Texture sources

Every *_map kwarg accepts three forms:

Filesystem / asset URI (str)

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)

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)

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.

  • 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 without a custom shader (or your ShaderMaterial uses separated textures / transparency, which fall back on web: see below).

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

ShaderMaterial is partly web-compatible: opaque, UBO-only shaders are transpiled GLSL→WGSL at export (build-time naga) and render through a per-material pipeline. Shaders using separated textures (group(2) binding 1+) or transparency warn and fall back to the underlying Material on web; export with --strict to fail instead of degrading.

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.