material-designer-runtime 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,19 +13,94 @@ supported version alongside it.
13
13
 
14
14
  ## Usage
15
15
 
16
+ There are two ways to put a document's material on a mesh: let the runtime hand you a
17
+ **ready-made material** (all config loaded for you), or bake the **channel textures** and assign
18
+ them to a material you build yourself. Everything bakes on a `WebGPURenderer`, which must be
19
+ initialized (`await renderer.init()`) first.
20
+
21
+ ### Get a ready-made material
22
+
23
+ `getNodeMaterial()` and `getMeshMaterial()` both load the document's family and every setting
24
+ (metalness, roughness, the physical lobes, phong shininess, toon gradient, …) — you never copy
25
+ config by hand. Pick based on what you need:
26
+
27
+ - **`getNodeMaterial()`** — the TSL node material (`MeshStandardNodeMaterial`, `MeshPhysicalNodeMaterial`, …)
28
+ with full procedural fidelity: triplanar projection, parallax-occlusion, per-vertex AO, and
29
+ procedurally-driven lobes. WebGPU only.
30
+ - **`getMeshMaterial()`** — a plain Three.js material (`MeshStandardMaterial`, `MeshPhysicalMaterial`,
31
+ `MeshLambertMaterial`, `MeshToonMaterial`, `MeshPhongMaterial`, `MeshMatcapMaterial`) with the baked
32
+ maps in the standard slots. Drops the node-only features above.
33
+
16
34
  ```ts
17
35
  import { MaterialGraphRuntime } from "material-designer-runtime";
36
+ import { WebGPURenderer } from "three/webgpu";
37
+ import { Mesh, SphereGeometry } from "three";
38
+
39
+ const renderer = new WebGPURenderer();
40
+ await renderer.init();
18
41
 
19
42
  const runtime = new MaterialGraphRuntime()
20
43
  .setRenderer(renderer)
21
- .setBackend("offline")
22
44
  .fromDocument(document);
45
+ await runtime.refresh(); // bake the graph to channel textures
23
46
 
24
- await runtime.refresh();
25
- mesh.material = runtime.material;
47
+ const geometry = new SphereGeometry(1, 128, 64);
26
48
 
27
- runtime.setOutputResolution(1024);
49
+ // (a) TSL node material — full fidelity (triplanar, parallax, procedural lobes), WebGPU only:
50
+ const mesh = new Mesh(geometry, runtime.getNodeMaterial());
51
+ // The node material object can change on a re-bake (e.g. a family switch) — keep the mesh current:
52
+ runtime.surface.onRebuilt(() => { mesh.material = runtime.getNodeMaterial(); });
53
+
54
+ // (b) …or a plain Three.js material with the baked maps + settings loaded in (pick one path):
55
+ // geometry.setAttribute("uv1", geometry.getAttribute("uv")); // .aoMap samples the 2nd UV set
56
+ // const mesh = new Mesh(geometry, runtime.getMeshMaterial());
57
+
58
+ // Live edits re-bake implicitly — await whenIdle() before reading back:
28
59
  runtime.setNodeParam("noise", "scale", 18);
60
+ runtime.setOutputResolution(1024);
61
+ await runtime.whenIdle();
62
+
63
+ runtime.dispose(); // release GPU resources when done
29
64
  ```
30
65
 
66
+ ### Bring your own material
67
+
68
+ Bake just the PBR channel maps with the shared `bakeService` (no material surface is created) and
69
+ assign them to your own material. The baked textures already carry the correct `.colorSpace`
70
+ (base color / emission are sRGB, data channels linear), so you assign them as-is:
71
+
72
+ ```ts
73
+ import { bakeService, MaterialGraphSession, defaultRegistry } from "material-designer-runtime";
74
+ import { WebGPURenderer } from "three/webgpu";
75
+ import { Mesh, BoxGeometry, MeshStandardMaterial } from "three";
76
+
77
+ const renderer = new WebGPURenderer();
78
+ await renderer.init();
79
+ bakeService.attachRenderer(renderer);
80
+
81
+ const session = new MaterialGraphSession(document, defaultRegistry);
82
+ const set = await bakeService.bake(session, { size: 1024 }); // set.present = the channels actually baked
83
+
84
+ const material = new MeshStandardMaterial();
85
+ material.map = set.texture("baseColor"); // THREE.Texture | null, keyed by channel
86
+ material.roughnessMap = set.texture("roughness");
87
+ material.metalnessMap = set.texture("metallic");
88
+ material.normalMap = set.texture("normal");
89
+ material.aoMap = set.texture("ambientOcclusion");
90
+ material.emissiveMap = set.texture("emission");
91
+ material.emissive.set(0xffffff);
92
+ material.roughness = 1;
93
+ material.metalness = 1; // the maps carry the values — keep the multipliers at 1
94
+
95
+ const geometry = new BoxGeometry(1, 1, 1);
96
+ geometry.setAttribute("uv1", geometry.getAttribute("uv")); // aoMap samples the 2nd UV set
97
+ const mesh = new Mesh(geometry, material);
98
+ // Keep `set` alive while the material is in use — set.dispose() frees the render targets (and the textures).
99
+ ```
100
+
101
+ `height` is baked separately (`set.heightTarget?.texture`). To read a channel back as CPU pixels
102
+ for PNG export, use `bakeService.readImage(session, channel, 1024)` → `ImageData` (size must be a
103
+ multiple of 64). If you keep a live `MaterialGraphRuntime`, the same baked textures are also
104
+ reachable via `runtime.surface.getChannelTexture(channel)` and `runtime.surface.getHeightTexture()`.
105
+
31
106
  The editor owns UI state, selection, storage, presets, and undo history. This package owns only graph document loading, compilation, baking, direct parameter updates, and the material surface.
@@ -1,6 +1,6 @@
1
1
  import { type NodeRegistry } from "./graph/registry";
2
2
  import { type GraphChange, type MaterialGraphDocument, type MaterialGraphSource } from "./graph/types";
3
- export declare const MATERIAL_DOCUMENT_VERSION = 2;
3
+ export declare const MATERIAL_DOCUMENT_VERSION = 4;
4
4
  export declare function cloneMaterialDocument(doc: MaterialGraphDocument): MaterialGraphDocument;
5
5
  export declare function createDefaultMaterialDocument(): MaterialGraphDocument;
6
6
  export declare function migrateMaterialDocument(doc: MaterialGraphDocument): MaterialGraphDocument;
@@ -1,6 +1,6 @@
1
1
  import * as THREE from "three";
2
2
  import { RenderTarget, type WebGPURenderer, type MeshBasicNodeMaterial } from "three/webgpu";
3
- import { type CacheEntry } from "./compiler";
3
+ import { type CacheEntry, type ParamUsage } from "./compiler";
4
4
  import type { MaterialGraphSource, MaterialValue, PbrSocket } from "./types";
5
5
  export declare const SURFACE_CHANNELS: PbrSocket[];
6
6
  export interface BakeOptions {
@@ -27,9 +27,11 @@ export declare class BakedTextureSet {
27
27
  private readonly cacheMats;
28
28
  cachePlan: CacheEntry[];
29
29
  uniforms: Map<string, Record<string, MaterialValue>>;
30
+ paramUsage: ParamUsage;
31
+ private readonly constArrays;
30
32
  heightTarget: RenderTarget | null;
31
33
  hasHeight: boolean;
32
- present: Set<"roughness" | "normal" | "baseColor" | "metallic" | "emission" | "ambientOcclusion">;
34
+ present: Set<"roughness" | "normal" | "baseColor" | "metallic" | "ambientOcclusion" | "emission">;
33
35
  signature: string;
34
36
  size: number;
35
37
  constructor(size: number, channels: PbrSocket[]);
@@ -39,9 +41,11 @@ export declare class BakedTextureSet {
39
41
  cacheTarget(cacheId: string, size?: number, mips?: boolean): RenderTarget;
40
42
  cacheMaterial(cacheId: string): MeshBasicNodeMaterial;
41
43
  cacheTexture(cacheId: string, size?: number, mips?: boolean): THREE.Texture;
44
+ pruneCaches(liveIds: ReadonlySet<string>): void;
42
45
  firstTarget(): RenderTarget | null;
43
46
  ensureHeightTarget(): RenderTarget;
44
47
  texture(ch: PbrSocket): THREE.Texture | null;
48
+ allocConstantArray(nodeId: string, key: string, data: ArrayLike<number>, elementType: "float" | "vec3"): MaterialValue;
45
49
  flushCaches(): void;
46
50
  dispose(): void;
47
51
  }
@@ -4,7 +4,7 @@ export declare const FIELD_CHANNELS: PbrSocket[];
4
4
  export declare const COLOR_CHANNELS: PbrSocket[];
5
5
  export declare function encodeChannel(node: MaterialValue, channel: PbrSocket): MaterialValue;
6
6
  export declare function makeChannelMaterial(): MeshBasicNodeMaterial;
7
- export declare function compileMaterialsAsync(renderer: WebGPURenderer, materials: MeshBasicNodeMaterial[]): Promise<void>;
7
+ export declare function compileMaterialsAsync(renderer: WebGPURenderer, materials: MeshBasicNodeMaterial[], destSize: number): Promise<void>;
8
8
  export declare function renderCacheToTarget(renderer: WebGPURenderer, material: MeshBasicNodeMaterial, rt: RenderTarget): void;
9
9
  export declare function renderMaterialToTarget(renderer: WebGPURenderer, material: MeshBasicNodeMaterial, rt: RenderTarget, isNormal?: boolean): void;
10
10
  export declare function renderColorNodeToTarget(renderer: WebGPURenderer, colorNode: MaterialValue, rt: RenderTarget, isNormal?: boolean): void;
@@ -1,9 +1,11 @@
1
1
  import * as THREE from "three";
2
- import { MeshStandardNodeMaterial, MeshPhysicalNodeMaterial } from "three/webgpu";
3
- import { type MaterialBackend, type MaterialBundle, type MaterialGraphDocument, type MaterialValue, type PortKind } from "./types";
2
+ import { type NodeMaterial } from "three/webgpu";
3
+ import { type MaterialBackend, type MaterialBundle, type MaterialGraphDocument, type MaterialType, type MaterialTypeSettings, type MaterialValue, type PortKind } from "./types";
4
4
  import { type NodeRegistry } from "./registry";
5
+ import { gradientMapFor, matcapFor } from "./material-textures";
6
+ export { gradientMapFor, matcapFor };
5
7
  export interface CompiledMaterial {
6
- material: MeshStandardNodeMaterial;
8
+ material: NodeMaterial;
7
9
  uniforms: Map<string, Record<string, MaterialValue>>;
8
10
  }
9
11
  export interface CacheSizing {
@@ -11,6 +13,8 @@ export interface CacheSizing {
11
13
  size?: number;
12
14
  }
13
15
  export type CacheAlloc = (cacheId: string, kind: PortKind, sizing?: CacheSizing) => THREE.Texture;
16
+ export type ParamUsage = Map<string, Record<string, "live" | "constant">>;
17
+ export type ConstantArrayAlloc = (nodeId: string, key: string, data: ArrayLike<number>, elementType: "float" | "vec3") => MaterialValue;
14
18
  export interface CacheEntry {
15
19
  cacheId: string;
16
20
  kind: PortKind;
@@ -21,16 +25,42 @@ export interface CompileOptions {
21
25
  backend: MaterialBackend;
22
26
  soloNodeId?: string;
23
27
  allocCache?: CacheAlloc;
28
+ allocConstantArray?: ConstantArrayAlloc;
24
29
  outputResolution?: number;
25
30
  }
26
31
  export interface CompiledSockets {
27
32
  bundle: MaterialBundle;
28
33
  uniforms: Map<string, Record<string, MaterialValue>>;
29
34
  cachePlan: CacheEntry[];
35
+ paramUsage: ParamUsage;
30
36
  }
31
37
  export declare function readOutputResolution(doc: MaterialGraphDocument): number;
38
+ export declare function readMaterialSurface(doc: MaterialGraphDocument): {
39
+ type: MaterialType;
40
+ settings: MaterialTypeSettings;
41
+ };
42
+ export interface MaterialConfig {
43
+ type: MaterialType;
44
+ baseColor: string;
45
+ metallic: number;
46
+ roughness: number;
47
+ ior: number;
48
+ alpha: number;
49
+ coat: number;
50
+ coatRoughness: number;
51
+ sheen: number;
52
+ sheenRoughness: number;
53
+ transmission: number;
54
+ emission: string;
55
+ emissionStrength: number;
56
+ shininess: number;
57
+ specular: string;
58
+ gradientSteps: number;
59
+ matcap: string;
60
+ }
61
+ export declare function readMaterialConfig(doc: MaterialGraphDocument): MaterialConfig;
32
62
  export declare function countGraphNodes(doc: MaterialGraphDocument): number;
33
63
  export declare function compileSockets(doc: MaterialGraphDocument, registry: NodeRegistry, optsIn: CompileOptions): CompiledSockets;
34
- export declare function newSurfaceMaterial(): MeshPhysicalNodeMaterial;
35
- export declare function applyBundle(material: MeshPhysicalNodeMaterial, bundle: MaterialBundle): void;
64
+ export declare function newSurfaceMaterial(type?: MaterialType, settings?: MaterialTypeSettings): NodeMaterial;
65
+ export declare function applyBundle(material: NodeMaterial, bundle: MaterialBundle, type: MaterialType): void;
36
66
  export declare function compileGraph(doc: MaterialGraphDocument, registry: NodeRegistry, opts: CompileOptions): CompiledMaterial;
@@ -0,0 +1,3 @@
1
+ import * as THREE from "three";
2
+ export declare function gradientMapFor(steps: number | undefined): THREE.DataTexture;
3
+ export declare function matcapFor(_id: string | undefined): THREE.Texture | null;
@@ -0,0 +1,6 @@
1
+ import * as THREE from "three";
2
+ import { type MaterialGraphDocument, type PbrSocket } from "./types";
3
+ export interface ChannelTextures {
4
+ get(channel: PbrSocket): THREE.Texture | null;
5
+ }
6
+ export declare function buildMeshMaterial(doc: MaterialGraphDocument, textures: ChannelTextures): THREE.Material;
@@ -0,0 +1,2 @@
1
+ import { type MaterialNodeDef } from "../../types";
2
+ export declare const shaderMaterialNode: MaterialNodeDef;
@@ -0,0 +1,2 @@
1
+ import type { MaterialNodeDef } from "../../types";
2
+ export declare const vectorRotateNode: MaterialNodeDef;
@@ -1,12 +1,14 @@
1
1
  import * as THREE from "three";
2
- import type { MeshStandardNodeMaterial } from "three/webgpu";
3
- import { type MaterialBackend, type MaterialGraphSource } from "./types";
2
+ import type { NodeMaterial } from "three/webgpu";
3
+ import { type MaterialBackend, type MaterialGraphSource, type PbrSocket } from "./types";
4
4
  import { type MaterialBakeService } from "./bake-service";
5
5
  export declare class TexturedSurface {
6
6
  private readonly graph;
7
7
  private readonly service;
8
8
  private readonly source?;
9
- private readonly offlineMat;
9
+ private offlineMat;
10
+ private matType;
11
+ private matSig;
10
12
  private liveUniforms;
11
13
  private material_;
12
14
  readonly scaleUniform: import("three/webgpu").UniformNode<"float", number>;
@@ -26,13 +28,25 @@ export declare class TexturedSurface {
26
28
  private pendingKind;
27
29
  private flushRequested;
28
30
  private readonly listeners;
31
+ private readonly textureListeners;
29
32
  private lastError_;
33
+ private processingDepth;
34
+ private idleWaiters;
30
35
  constructor(graph: MaterialGraphSource, service: MaterialBakeService, source?: string | undefined);
31
- get material(): MeshStandardNodeMaterial;
36
+ get material(): NodeMaterial;
32
37
  get lastError(): string | null;
38
+ get busy(): boolean;
39
+ whenIdle(): Promise<void>;
40
+ private enterProcessing;
41
+ private exitProcessing;
33
42
  getLastBakeMs(): number;
34
43
  onRebuilt(fn: () => void): () => void;
35
44
  private notify;
45
+ onTexturesUpdated(fn: () => void): () => void;
46
+ private notifyTexturesUpdated;
47
+ getChannelTexture(ch: PbrSocket): THREE.Texture | null;
48
+ getHeightTexture(): THREE.Texture | null;
49
+ presentChannels(): ReadonlySet<PbrSocket>;
36
50
  refresh(): Promise<void>;
37
51
  regenerate(): void;
38
52
  setScale(value: number): void;
@@ -31,8 +31,25 @@ export interface ParamDef {
31
31
  options?: string[];
32
32
  default: unknown;
33
33
  bakeStructural?: boolean;
34
+ structural?: boolean;
34
35
  }
35
36
  export type MaterialBackend = "live" | "offline";
37
+ export type MaterialType = "standard" | "physical" | "lambert" | "toon" | "phong" | "matcap";
38
+ export declare const MATERIAL_TYPES: MaterialType[];
39
+ export interface MaterialTypeSettings {
40
+ shininess?: number;
41
+ specular?: string;
42
+ gradientSteps?: number;
43
+ matcap?: string;
44
+ }
45
+ export interface MaterialTypeCaps {
46
+ roughMetal: boolean;
47
+ physicalLobes: boolean;
48
+ ao: boolean;
49
+ emissive: boolean;
50
+ }
51
+ export declare const MATERIAL_TYPE_CAPS: Record<MaterialType, MaterialTypeCaps>;
52
+ export declare function normalizeMaterialType(raw: unknown): MaterialType;
36
53
  export type GraphChange = {
37
54
  kind: "structural";
38
55
  } | {
@@ -47,8 +64,9 @@ export type GraphChange = {
47
64
  };
48
65
  export interface BuildCtx {
49
66
  inputs: Record<string, MaterialValue | undefined>;
50
- uniforms: Record<string, MaterialValue>;
51
- params: Record<string, unknown>;
67
+ live(key: string): MaterialValue;
68
+ constant(key: string): unknown;
69
+ constantArray(key: string, compute: () => ArrayLike<number>, elementType?: "float" | "vec3"): MaterialValue;
52
70
  coord: MaterialValue;
53
71
  backend: MaterialBackend;
54
72
  tileRepeat?: number;
@@ -130,6 +148,7 @@ export interface MaterialGraphSource {
130
148
  onChange(fn: (change: GraphChange) => void): () => void;
131
149
  }
132
150
  export declare const MATERIAL_OUTPUT_TYPE = "material-output";
151
+ export declare const SHADER_MATERIAL_TYPE = "shader-material";
133
152
  export declare const PBR_SOCKETS: readonly ["baseColor", "normal", "emission", "roughness", "metallic", "ambientOcclusion"];
134
153
  export type PbrSocket = (typeof PBR_SOCKETS)[number];
135
154
  export interface MaterialBundle {
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@ export { MaterialGraphRuntime, type MaterialGraphRuntimeOptions } from "./runtim
2
2
  export { MATERIAL_DOCUMENT_VERSION, MaterialGraphSession, cloneMaterialDocument, createDefaultMaterialDocument, migrateMaterialDocument, } from "./document";
3
3
  export { createMaterialTopologyKey } from "./topology";
4
4
  export { MaterialBakeService, BakedTextureSet, SURFACE_CHANNELS, bakeService, type BakeOptions, type BakeReport, } from "./graph/bake-service";
5
- export { compileGraph, compileSockets, countGraphNodes, newSurfaceMaterial, readOutputResolution, type CompileOptions, type CompiledSockets, } from "./graph/compiler";
5
+ export { compileGraph, compileSockets, countGraphNodes, newSurfaceMaterial, readMaterialSurface, readMaterialConfig, readOutputResolution, type CompileOptions, type CompiledSockets, type MaterialConfig, } from "./graph/compiler";
6
+ export { buildMeshMaterial, type ChannelTextures } from "./graph/mesh-material";
6
7
  export { NodeRegistry, createDefaultRegistry, defaultRegistry, nodeParamDefs, nodePorts, } from "./graph/registry";
7
8
  export { TexturedSurface } from "./graph/textured-surface";
8
9
  export { runTilingTest } from "./graph/tiling-test";