Appearance
@carrot/engine-materials ​
ts
Schema-driven material system - typed property bags that reference shaders by key, with per-instance overrides and dirty tracking.
Installation ​
bash
npm install @carrot/engine-materialsArchitecture / How It Works ​
Material = schema + property values + shader key ​
A Material is an abstract base class. Subclasses declare their properties in the constructor using define() with typed MaterialPropertyDef<T> definitions. Each material references a shader by string key (e.g. core.sprite.default) - the renderer resolves this to a compiled GPU program at draw time.
The schema approach means:
- Properties are fully typed -
getProperty(def)returnsT | undefinedwithout casts. - Editors and serializers can introspect the schema to build UI automatically.
- Property definitions carry metadata: label, type, default value, min/max/step, enum options.
MaterialInstance = per-object copy ​
MaterialInstance wraps a Material and provides per-object property overrides. Unmodified properties fall through to the source material. The renderer checks isDirty to know when uniforms need re-uploading.
This is the classic shared-material pattern: one Material definition, many MaterialInstances with individual tweaks (different tint colours, different textures, etc.).
Built-in material library ​
Three ready-to-use materials ship in library/:
- Material2DSprite - textured + tint, shader
core.sprite.default. Defaultstransparent = truein constructor (sprites are inherently alpha-blended) - Material2DUnlitColor - solid colour, shader
core.unlit.color - Material2DUnlitTextured - textured + colour, shader
core.unlit.textured
MaterialType ​
Categorises materials for render pipeline sorting: Unlit, Simple, Pbr. The renderer can batch by type for optimal draw order.
Dependencies ​
| Package | Used For |
|---|---|
@carrot/colors | ColorRgbLike type for colour properties |
@carrot/engine-textures | Texture type for texture properties |
Build ​
bash
npm run build # runs tscUsage Guide ​
Schema-driven material system with typed properties, per-instance overrides, and dirty tracking.
Import ​
ts
import {
Material, MaterialInstance, MaterialSchema,
MaterialPropertyDef, defineProperty,
MaterialPropertyType, MaterialType,
Material2DSprite, Material2DUnlitColor, Material2DUnlitTextured,
} from '@carrot/engine-materials';Common Patterns ​
1. Using a built-in material ​
ts
const spriteMat = new Material2DSprite({
texture: myTexture,
tint: { r: 1, g: 0.5, b: 0, a: 1 },
});2. Flat colour material ​
ts
const redMat = new Material2DUnlitColor({ r: 1, g: 0, b: 0, a: 1 });3. Per-instance overrides ​
ts
const baseMat = new Material2DSprite({ texture: atlasTexture });
// Each enemy gets its own tint
const enemy1 = new MaterialInstance(baseMat);
enemy1.setProperty(baseMat.tintColor, { r: 1, g: 0, b: 0, a: 1 });
const enemy2 = new MaterialInstance(baseMat);
enemy2.setProperty(baseMat.tintColor, { r: 0, g: 0, b: 1, a: 1 });4. Checking dirty state (renderer) ​
ts
if (instance.isDirty) {
uploadUniforms(instance);
instance.clearDirty();
}5. Defining a custom material ​
ts
class TerrainMaterial extends Material {
readonly type = MaterialType.Simple;
readonly heightMap = this.define(
defineProperty<Texture>('u_HeightMap', 'Height Map', MaterialPropertyType.Texture),
);
readonly tileScale = this.define(
defineProperty<number>('u_TileScale', 'Tile Scale', MaterialPropertyType.Number, {
defaultValue: 1,
min: 0.1,
max: 100,
step: 0.1,
}),
);
readonly blendColor = this.define(
defineProperty<ColorRgbLike>('u_BlendColor', 'Blend Color', MaterialPropertyType.Color, {
defaultValue: { r: 1, g: 1, b: 1, a: 1 },
}),
);
constructor(shaderKey = 'game.terrain.default') {
super('Terrain', shaderKey);
}
}6. Reading properties by key (serialization, editors) ​
ts
const value = material.getPropertyByKey('u_Color');
material.setPropertyByKey('u_Color', { r: 0, g: 1, b: 0, a: 1 });
// Iterate all property definitions
for (const def of material.schema) {
console.log(def.key, def.label, def.propertyType);
}7. Instance with shader override ​
ts
const instance = new MaterialInstance(baseMat, 'custom.sprite.outlined');
// Uses a different shader program while keeping the same property schema8. Resetting overrides ​
ts
instance.clearProperty(baseMat.tintColor); // revert to base material value
instance.resetOverrides(); // revert all overrides9. Transparency ​
ts
const glassMat = new Material2DUnlitColor({ r: 1, g: 1, b: 1, a: 0.5 });
glassMat.transparent = true;
// Renderer sorts transparent materials to draw back-to-front
// Note: Material2DSprite defaults to transparent = true on construction,
// since sprites are inherently alpha-blended. No need to set it manually.