@combos-fun/plugin-renderer-3d-material 0.0.46

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 ADDED
@@ -0,0 +1,20 @@
1
+ # @combos-fun/plugin-renderer-3d-material
2
+
3
+ @combos-fun/plugin-renderer-3d-material — part of the Combos Fun engine monorepo.
4
+
5
+ Keywords: `3d`, `material`, `shader`, `pbr`.
6
+
7
+ ## Documentation
8
+
9
+ - Per-package agent / developer notes: [`agent-skill.md`](./agent-skill.md)
10
+ - Machine-readable manifest: [`combos-plugin.json`](./combos-plugin.json)
11
+
12
+ When consumed from npm:
13
+
14
+ ```ts
15
+ import manifest from '@combos-fun/plugin-renderer-3d-material/plugin-manifest';
16
+ ```
17
+
18
+ ## License
19
+
20
+ Internal workspace package, part of the Combos Fun engine monorepo.
package/agent-skill.md ADDED
@@ -0,0 +1,79 @@
1
+ # `@combos-fun/plugin-renderer-3d-material` — Agent notes
2
+
3
+ Replaces materials on meshes targeted within the same GameObject's 3D visual. Use one `Material3D` per GameObject for built-in Three materials or custom GLSL.
4
+
5
+ Prerequisite: follow the parent `@combos-fun/plugin-renderer-3d` skill for system order, named resources, hierarchy, opacity scope, and stale-async handling.
6
+
7
+ ## Public API
8
+
9
+ Runtime exports:
10
+
11
+ ```ts
12
+ import {
13
+ Material3D,
14
+ Material3DSystem,
15
+ collectTargetMeshes,
16
+ } from '@combos-fun/plugin-renderer-3d-material';
17
+ ```
18
+
19
+ Type-only exports:
20
+
21
+ ```ts
22
+ import type {
23
+ Material3DParams,
24
+ Material3DKind,
25
+ Material3DUniformValue,
26
+ } from '@combos-fun/plugin-renderer-3d-material';
27
+ ```
28
+
29
+ It is also valid to combine these in one import using the `type` modifiers. `Material3DKind` and `Material3DUniformValue` are types, not runtime values. `componentName = 'Material3D'`; `systemName = 'Material3DSystem'`.
30
+
31
+ ## Parameters
32
+
33
+ | Field | Type | Default |
34
+ |-------|------|---------|
35
+ | `type` | `'standard' \| 'basic' \| 'lambert' \| 'toon' \| 'physical' \| 'shader'` | `'standard'` |
36
+ | `resource` | `string` | `''`; SHADER name for `shader` |
37
+ | `node` | `string` | `''`; all target meshes |
38
+ | `uniforms` | `Record<string, number \| boolean \| number[]>` | `{}` |
39
+ | `color` | `number` | `0xffffff` |
40
+ | `opacity` | `number` | `1` |
41
+ | `roughness` / `metalness` | `number` | `1` / `0` |
42
+ | `emissive` | `number` | `0` |
43
+ | `wireframe` / `doubleSide` | `boolean` | `false` |
44
+ | `map` | `string` | `''`; keep the source material's map |
45
+
46
+ `node` matches a mesh whose own name or ancestor name equals the value. Instanced meshes are skipped. For non-model roots, traversal stops at nested `Transform3D` roots so a parent's material does not overwrite child GameObjects.
47
+
48
+ ## Runtime and lifecycle
49
+
50
+ - Built-in material replacements copy the source texture maps; a non-empty `map` asynchronously loads an IMAGE override.
51
+ - `shader` asynchronously loads a SHADER resource. A missing vertex shader uses the built-in UV vertex shader; a fragment shader is required.
52
+ - Concurrent changes/removal invalidate pending shader or map work. Stale map textures are disposed. Removal restores original materials and disposes created materials/textures.
53
+ - The system retries target discovery each frame, so a sibling `Model3D` can finish loading later.
54
+ - `time` and `uTime` entries in `uniforms` receive elapsed seconds each renderer update; other values are copied, with booleans converted to `0`/`1`.
55
+ - Built-in material classes retain normal skinned-mesh support. The default custom shader has no skinning logic.
56
+
57
+ `Material3D.opacity` is the replacement material's value. A sibling `Render3D` subsequently overwrites every mesh material below that GameObject root each frame; use `Render3D.opacity` for root-wide live fades.
58
+
59
+ ## Package-specific pitfalls
60
+
61
+ - Put `Material3DSystem` after the visual system so synchronous visuals exist first.
62
+ - An unknown `type` falls back to `MeshStandardMaterial`.
63
+ - A shader must implement any required skinning and texture sampling itself; setting `map` does not automatically add a map uniform to custom GLSL.
64
+
65
+ ## Minimal example
66
+
67
+ ```ts
68
+ hero.addComponent(new Model3D({ resource: 'hero' }));
69
+ hero.addComponent(new Material3D({
70
+ type: 'physical',
71
+ metalness: 0.8,
72
+ roughness: 0.2,
73
+ node: 'Armor',
74
+ }));
75
+ ```
76
+
77
+ ## Verification
78
+
79
+ `pnpm --filter @combos-fun/plugin-renderer-3d-material run build`
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@combos-fun/plugin-renderer-3d-material",
3
+ "pluginId": "renderer-3d-material",
4
+ "category": "rendering",
5
+ "dimension": "3d",
6
+ "isCore": false,
7
+ "keywords": ["3d", "material", "shader", "pbr"],
8
+ "agentSkill": "./agent-skill.md",
9
+ "requires": ["@combos-fun/engine", "@combos-fun/plugin-renderer-3d"],
10
+ "exports": ["Material3D", "Material3DSystem"]
11
+ }
@@ -0,0 +1,455 @@
1
+ 'use strict';
2
+
3
+ var tslib = require('tslib');
4
+ var engine = require('@combos-fun/engine');
5
+ var inspectorDecorator = require('@combos-fun/inspector-decorator');
6
+ var pluginRenderer3d = require('@combos-fun/plugin-renderer-3d');
7
+ var three = require('three');
8
+
9
+ class Material3D extends engine.Component {
10
+ constructor() {
11
+ super(...arguments);
12
+ this.type = 'standard';
13
+ this.resource = '';
14
+ this.node = '';
15
+ this.uniforms = {};
16
+ this.color = 0xffffff;
17
+ this.opacity = 1;
18
+ this.roughness = 1;
19
+ this.metalness = 0;
20
+ this.emissive = 0;
21
+ this.wireframe = false;
22
+ this.doubleSide = false;
23
+ this.map = '';
24
+ }
25
+ static { this.componentName = 'Material3D'; }
26
+ init(obj) {
27
+ if (!obj)
28
+ return;
29
+ Object.assign(this, obj);
30
+ if (obj.uniforms)
31
+ this.uniforms = obj.uniforms;
32
+ }
33
+ }
34
+ tslib.__decorate([
35
+ inspectorDecorator.Field({
36
+ type: 'string',
37
+ group: 'Material3D',
38
+ label: 'type',
39
+ description: 'Three material kind.',
40
+ editor: 'enum',
41
+ enumOptions: ['standard', 'basic', 'lambert', 'toon', 'physical', 'shader'],
42
+ })
43
+ ], Material3D.prototype, "type", void 0);
44
+ tslib.__decorate([
45
+ inspectorDecorator.Field({
46
+ type: 'string',
47
+ group: 'Material3D',
48
+ label: 'resource',
49
+ description: 'SHADER resource name when type is shader.',
50
+ editor: 'text',
51
+ })
52
+ ], Material3D.prototype, "resource", void 0);
53
+ tslib.__decorate([
54
+ inspectorDecorator.Field({
55
+ type: 'string',
56
+ group: 'Material3D',
57
+ label: 'node',
58
+ description: 'Object3D.name to target. Empty = all meshes on this GO.',
59
+ editor: 'text',
60
+ })
61
+ ], Material3D.prototype, "node", void 0);
62
+ tslib.__decorate([
63
+ inspectorDecorator.Field({
64
+ type: 'number',
65
+ group: 'Material3D',
66
+ label: 'color',
67
+ description: 'Tint (hex). Multiplies map.',
68
+ editor: 'number-stepper',
69
+ })
70
+ ], Material3D.prototype, "color", void 0);
71
+ tslib.__decorate([
72
+ inspectorDecorator.Field({
73
+ type: 'number',
74
+ step: 0.01,
75
+ min: 0,
76
+ max: 1,
77
+ group: 'Material3D',
78
+ label: 'opacity',
79
+ editor: 'number-slider',
80
+ })
81
+ ], Material3D.prototype, "opacity", void 0);
82
+ tslib.__decorate([
83
+ inspectorDecorator.Field({
84
+ type: 'number',
85
+ step: 0.05,
86
+ min: 0,
87
+ max: 1,
88
+ group: 'Material3D',
89
+ label: 'roughness',
90
+ editor: 'number-slider',
91
+ })
92
+ ], Material3D.prototype, "roughness", void 0);
93
+ tslib.__decorate([
94
+ inspectorDecorator.Field({
95
+ type: 'number',
96
+ step: 0.05,
97
+ min: 0,
98
+ max: 1,
99
+ group: 'Material3D',
100
+ label: 'metalness',
101
+ editor: 'number-slider',
102
+ })
103
+ ], Material3D.prototype, "metalness", void 0);
104
+ tslib.__decorate([
105
+ inspectorDecorator.Field({
106
+ type: 'number',
107
+ group: 'Material3D',
108
+ label: 'emissive',
109
+ editor: 'number-stepper',
110
+ })
111
+ ], Material3D.prototype, "emissive", void 0);
112
+ tslib.__decorate([
113
+ inspectorDecorator.Field({
114
+ type: 'boolean',
115
+ group: 'Material3D',
116
+ label: 'wireframe',
117
+ editor: 'toggle',
118
+ })
119
+ ], Material3D.prototype, "wireframe", void 0);
120
+ tslib.__decorate([
121
+ inspectorDecorator.Field({
122
+ type: 'boolean',
123
+ group: 'Material3D',
124
+ label: 'doubleSide',
125
+ editor: 'toggle',
126
+ })
127
+ ], Material3D.prototype, "doubleSide", void 0);
128
+ tslib.__decorate([
129
+ inspectorDecorator.Field({
130
+ type: 'string',
131
+ group: 'Material3D',
132
+ label: 'map',
133
+ description: 'IMAGE resource name. Empty keeps the mesh map.',
134
+ editor: 'text',
135
+ })
136
+ ], Material3D.prototype, "map", void 0);
137
+
138
+ const TRANSFORM3D_ROOT = 'combosTransform3D';
139
+ function isTransform3DRoot(object) {
140
+ return !!object.userData?.[TRANSFORM3D_ROOT];
141
+ }
142
+ function isMesh(object) {
143
+ return !!object.isMesh;
144
+ }
145
+ function isInstanced(object) {
146
+ return !!object.isInstancedMesh;
147
+ }
148
+ function matchesNode(object, node) {
149
+ if (!node)
150
+ return true;
151
+ let current = object;
152
+ while (current) {
153
+ if (current.name === node)
154
+ return true;
155
+ current = current.parent;
156
+ }
157
+ return false;
158
+ }
159
+ function collectTargetMeshes(root, node = '', skipNestedRoots = false) {
160
+ if (!root)
161
+ return [];
162
+ const out = [];
163
+ const walk = (object) => {
164
+ if (skipNestedRoots && object !== root && isTransform3DRoot(object))
165
+ return;
166
+ if (isMesh(object) && !isInstanced(object) && matchesNode(object, node)) {
167
+ out.push(object);
168
+ }
169
+ for (const child of object.children) {
170
+ walk(child);
171
+ }
172
+ };
173
+ walk(root);
174
+ return out;
175
+ }
176
+
177
+ const MAP_KEYS = [
178
+ 'map',
179
+ 'normalMap',
180
+ 'roughnessMap',
181
+ 'metalnessMap',
182
+ 'emissiveMap',
183
+ 'aoMap',
184
+ 'alphaMap',
185
+ ];
186
+ function firstMaterial(material) {
187
+ return Array.isArray(material) ? material[0] : material;
188
+ }
189
+ function copyMaps(target, source, skipMap) {
190
+ if (!source)
191
+ return;
192
+ for (const key of MAP_KEYS) {
193
+ if (skipMap && key === 'map')
194
+ continue;
195
+ if (key in target && key in source) {
196
+ target[key] =
197
+ source[key];
198
+ }
199
+ }
200
+ }
201
+ function threeUniformValue(value) {
202
+ if (typeof value === 'boolean')
203
+ return value ? 1 : 0;
204
+ return value;
205
+ }
206
+ function createMaterial(component, template, shader, mapOverride) {
207
+ const skipMap = !!mapOverride;
208
+ const hasMap = !!(mapOverride || (template && 'map' in template && template.map));
209
+ const common = {
210
+ color: component.color,
211
+ opacity: component.opacity,
212
+ transparent: component.opacity < 1 || hasMap || !!template?.transparent,
213
+ wireframe: component.wireframe,
214
+ side: component.doubleSide ? three.DoubleSide : three.FrontSide,
215
+ map: mapOverride,
216
+ };
217
+ if (component.type === 'shader') {
218
+ if (!shader) {
219
+ throw new Error('Material3D shader type requires a SHADER resource');
220
+ }
221
+ const uniforms = {};
222
+ for (const [key, value] of Object.entries(component.uniforms || {})) {
223
+ uniforms[key] = { value: threeUniformValue(value) };
224
+ }
225
+ return new three.ShaderMaterial({
226
+ vertexShader: shader.vertex,
227
+ fragmentShader: shader.fragment,
228
+ uniforms,
229
+ transparent: common.transparent,
230
+ opacity: common.opacity,
231
+ wireframe: common.wireframe,
232
+ side: common.side,
233
+ });
234
+ }
235
+ let material;
236
+ switch (component.type) {
237
+ case 'basic':
238
+ material = new three.MeshBasicMaterial(common);
239
+ break;
240
+ case 'lambert':
241
+ material = new three.MeshLambertMaterial({ ...common, emissive: component.emissive });
242
+ break;
243
+ case 'toon':
244
+ material = new three.MeshToonMaterial(common);
245
+ break;
246
+ case 'physical':
247
+ material = new three.MeshPhysicalMaterial({
248
+ ...common,
249
+ roughness: component.roughness,
250
+ metalness: component.metalness,
251
+ emissive: component.emissive,
252
+ });
253
+ break;
254
+ case 'standard':
255
+ default:
256
+ material = new three.MeshStandardMaterial({
257
+ ...common,
258
+ roughness: component.roughness,
259
+ metalness: component.metalness,
260
+ emissive: component.emissive,
261
+ });
262
+ break;
263
+ }
264
+ copyMaps(material, template, skipMap);
265
+ if (mapOverride) {
266
+ material.map = mapOverride;
267
+ }
268
+ return material;
269
+ }
270
+ function writeShaderUniforms(material, uniforms, time) {
271
+ const shader = material;
272
+ if (!shader.isShaderMaterial || !shader.uniforms)
273
+ return;
274
+ for (const [key, value] of Object.entries(uniforms || {})) {
275
+ const next = (key === 'time' || key === 'uTime') ? time : threeUniformValue(value);
276
+ if (shader.uniforms[key]) {
277
+ shader.uniforms[key].value = next;
278
+ }
279
+ else {
280
+ shader.uniforms[key] = { value: next };
281
+ }
282
+ }
283
+ }
284
+
285
+ const DEFAULT_THREE_VERTEX = `varying vec2 vUv;
286
+ void main() {
287
+ vUv = uv;
288
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
289
+ }
290
+ `;
291
+ async function loadShaderSource(name) {
292
+ const trimmed = String(name ?? '').trim();
293
+ if (!trimmed) {
294
+ throw new Error('SHADER resource name is required when Material3D.type is shader');
295
+ }
296
+ const res = await engine.resource.getResource(trimmed);
297
+ if (!res?.name) {
298
+ throw new Error(`Unknown resource "${trimmed}"; register RESOURCE_TYPE.SHADER first`);
299
+ }
300
+ const vertex = typeof res.data?.vertex === 'string'
301
+ ? res.data.vertex
302
+ : typeof res.instance?.vertex === 'string' ? res.instance.vertex : '';
303
+ const fragment = typeof res.data?.fragment === 'string'
304
+ ? res.data.fragment
305
+ : typeof res.instance?.fragment === 'string' ? res.instance.fragment : '';
306
+ if (!fragment) {
307
+ throw new Error(`SHADER "${trimmed}" missing fragment`);
308
+ }
309
+ return { vertex: vertex || DEFAULT_THREE_VERTEX, fragment };
310
+ }
311
+
312
+ let Material3DSystem = class Material3DSystem extends pluginRenderer3d.Renderer3D {
313
+ constructor() {
314
+ super(...arguments);
315
+ this.name = 'Material3DSystem';
316
+ this.entries = new Map();
317
+ this.deltaSeconds = 1 / 60;
318
+ }
319
+ static { this.systemName = 'Material3DSystem'; }
320
+ init() {
321
+ const renderer3DSystem = this.game.getSystem(pluginRenderer3d.Renderer3DSystem);
322
+ renderer3DSystem.rendererManager.register(this);
323
+ }
324
+ update(e) {
325
+ this.deltaSeconds = (e?.deltaTime ?? 16.67) / 1000;
326
+ super.update(e);
327
+ }
328
+ componentChanged(changed) {
329
+ if (changed.componentName !== 'Material3D')
330
+ return;
331
+ if (changed.type === engine.OBSERVER_TYPE.REMOVE) {
332
+ this.restore(changed.gameObject.id);
333
+ return;
334
+ }
335
+ if (changed.type === engine.OBSERVER_TYPE.CHANGE && changed.prop?.prop?.[0] === 'uniforms') {
336
+ this.writeUniforms(changed.gameObject.id, changed.component);
337
+ return;
338
+ }
339
+ const entry = this.entries.get(changed.gameObject.id);
340
+ if (entry)
341
+ entry.signature = '';
342
+ this.apply(changed.gameObject, changed.component);
343
+ }
344
+ rendererUpdate(gameObject) {
345
+ const component = gameObject.getComponent(Material3D);
346
+ if (!component)
347
+ return;
348
+ const entry = this.entries.get(gameObject.id);
349
+ const root = this.visualRoot(gameObject.id);
350
+ const meshes = collectTargetMeshes(root, component.node, !this.threeContext.models.get(gameObject.id));
351
+ const signature = this.signature(root, meshes);
352
+ if (!entry || entry.signature !== signature) {
353
+ this.apply(gameObject, component);
354
+ return;
355
+ }
356
+ entry.time += this.deltaSeconds;
357
+ this.writeUniforms(gameObject.id, component);
358
+ }
359
+ visualRoot(id) {
360
+ return this.threeContext.models.get(id) || this.threeContext.object3DFor(id);
361
+ }
362
+ signature(root, meshes) {
363
+ if (!root || !meshes.length)
364
+ return '';
365
+ return `${root.uuid}|${meshes.map((mesh) => mesh.uuid).join(',')}`;
366
+ }
367
+ async apply(gameObject, component) {
368
+ const root = this.visualRoot(gameObject.id);
369
+ const skipNested = !this.threeContext.models.get(gameObject.id);
370
+ const meshes = collectTargetMeshes(root, component.node, skipNested);
371
+ if (!meshes.length)
372
+ return;
373
+ const asyncId = this.increaseAsyncId(gameObject.id);
374
+ let shader = null;
375
+ let mapOverride = null;
376
+ try {
377
+ if (component.type === 'shader') {
378
+ shader = await loadShaderSource(component.resource);
379
+ }
380
+ if (component.map) {
381
+ mapOverride = await pluginRenderer3d.textureFromNamedImage(component.map);
382
+ }
383
+ }
384
+ catch (err) {
385
+ mapOverride?.dispose();
386
+ throw err;
387
+ }
388
+ if (!this.validateAsyncId(gameObject.id, asyncId)) {
389
+ mapOverride?.dispose();
390
+ return;
391
+ }
392
+ this.restore(gameObject.id);
393
+ const applied = [];
394
+ const ownedTextures = mapOverride ? [mapOverride] : [];
395
+ for (const mesh of meshes) {
396
+ const originals = mesh.material;
397
+ const created = createMaterial(component, firstMaterial(originals), shader, mapOverride);
398
+ mesh.material = created;
399
+ applied.push({ mesh, originals, created });
400
+ }
401
+ this.entries.set(gameObject.id, {
402
+ applied,
403
+ ownedTextures,
404
+ signature: this.signature(root, meshes),
405
+ time: 0,
406
+ });
407
+ this.writeUniforms(gameObject.id, component);
408
+ }
409
+ writeUniforms(id, component) {
410
+ const entry = this.entries.get(id);
411
+ if (!entry)
412
+ return;
413
+ for (const item of entry.applied) {
414
+ writeShaderUniforms(item.created, component.uniforms, entry.time);
415
+ }
416
+ }
417
+ restore(id, putBack = true) {
418
+ this.increaseAsyncId(id);
419
+ const entry = this.entries.get(id);
420
+ if (!entry)
421
+ return;
422
+ for (const item of entry.applied) {
423
+ if (putBack && item.mesh) {
424
+ item.mesh.material = item.originals;
425
+ }
426
+ if (item.created && item.created !== item.originals) {
427
+ item.created.dispose();
428
+ }
429
+ }
430
+ for (const texture of entry.ownedTextures) {
431
+ texture.dispose();
432
+ }
433
+ this.entries.delete(id);
434
+ }
435
+ onDestroy() {
436
+ for (const id of [...this.entries.keys()]) {
437
+ this.restore(id);
438
+ }
439
+ }
440
+ };
441
+ Material3DSystem = tslib.__decorate([
442
+ engine.decorators.componentObserver({
443
+ Material3D: [
444
+ 'type', 'resource', 'node', 'color', 'opacity', 'roughness', 'metalness',
445
+ 'emissive', 'wireframe', 'doubleSide', 'map',
446
+ { prop: ['uniforms'], deep: true },
447
+ ],
448
+ })
449
+ ], Material3DSystem);
450
+ var Material3DSystem_default = Material3DSystem;
451
+
452
+ exports.Material3D = Material3D;
453
+ exports.Material3DSystem = Material3DSystem_default;
454
+ exports.collectTargetMeshes = collectTargetMeshes;
455
+ //# sourceMappingURL=plugin-renderer-3d-material.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-renderer-3d-material.cjs.js","sources":["../lib/component.ts","../lib/meshes.ts","../lib/createMaterial.ts","../lib/shaderSource.ts","../lib/system.ts"],"sourcesContent":["import { Component } from '@combos-fun/engine';\nimport { Field } from '@combos-fun/inspector-decorator';\n\nexport type Material3DKind = 'standard' | 'basic' | 'lambert' | 'toon' | 'physical' | 'shader';\n\nexport type Material3DUniformValue = number | boolean | number[];\n\nexport interface Material3DParams {\n type?: Material3DKind | string;\n resource?: string;\n node?: string;\n uniforms?: Record<string, Material3DUniformValue>;\n color?: number;\n opacity?: number;\n roughness?: number;\n metalness?: number;\n emissive?: number;\n wireframe?: boolean;\n doubleSide?: boolean;\n map?: string;\n}\n\nexport default class Material3D extends Component<Material3DParams> {\n static componentName: string = 'Material3D';\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'type',\n description: 'Three material kind.',\n editor: 'enum',\n enumOptions: ['standard', 'basic', 'lambert', 'toon', 'physical', 'shader'],\n })\n type: string = 'standard';\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'resource',\n description: 'SHADER resource name when type is shader.',\n editor: 'text',\n })\n resource: string = '';\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'node',\n description: 'Object3D.name to target. Empty = all meshes on this GO.',\n editor: 'text',\n })\n node: string = '';\n\n uniforms: Record<string, Material3DUniformValue> = {};\n\n @Field({\n type: 'number',\n group: 'Material3D',\n label: 'color',\n description: 'Tint (hex). Multiplies map.',\n editor: 'number-stepper',\n })\n color: number = 0xffffff;\n\n @Field({\n type: 'number',\n step: 0.01,\n min: 0,\n max: 1,\n group: 'Material3D',\n label: 'opacity',\n editor: 'number-slider',\n })\n opacity: number = 1;\n\n @Field({\n type: 'number',\n step: 0.05,\n min: 0,\n max: 1,\n group: 'Material3D',\n label: 'roughness',\n editor: 'number-slider',\n })\n roughness: number = 1;\n\n @Field({\n type: 'number',\n step: 0.05,\n min: 0,\n max: 1,\n group: 'Material3D',\n label: 'metalness',\n editor: 'number-slider',\n })\n metalness: number = 0;\n\n @Field({\n type: 'number',\n group: 'Material3D',\n label: 'emissive',\n editor: 'number-stepper',\n })\n emissive: number = 0;\n\n @Field({\n type: 'boolean',\n group: 'Material3D',\n label: 'wireframe',\n editor: 'toggle',\n })\n wireframe: boolean = false;\n\n @Field({\n type: 'boolean',\n group: 'Material3D',\n label: 'doubleSide',\n editor: 'toggle',\n })\n doubleSide: boolean = false;\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'map',\n description: 'IMAGE resource name. Empty keeps the mesh map.',\n editor: 'text',\n })\n map: string = '';\n\n init(obj?: Material3DParams) {\n if (!obj) return;\n Object.assign(this, obj);\n if (obj.uniforms) this.uniforms = obj.uniforms;\n }\n}\n","import type { Mesh, Object3D } from 'three';\n\nconst TRANSFORM3D_ROOT = 'combosTransform3D';\n\nfunction isTransform3DRoot(object: Object3D): boolean {\n return !!object.userData?.[TRANSFORM3D_ROOT];\n}\n\nfunction isMesh(object: Object3D): object is Mesh {\n return !!(object as Mesh).isMesh;\n}\n\nfunction isInstanced(object: Object3D): boolean {\n return !!(object as Mesh & { isInstancedMesh?: boolean }).isInstancedMesh;\n}\n\nfunction matchesNode(object: Object3D, node: string): boolean {\n if (!node) return true;\n let current: Object3D | null = object;\n while (current) {\n if (current.name === node) return true;\n current = current.parent;\n }\n return false;\n}\n\nexport function collectTargetMeshes(\n root: Object3D | undefined,\n node = '',\n skipNestedRoots = false,\n): Mesh[] {\n if (!root) return [];\n const out: Mesh[] = [];\n const walk = (object: Object3D) => {\n if (skipNestedRoots && object !== root && isTransform3DRoot(object)) return;\n if (isMesh(object) && !isInstanced(object) && matchesNode(object, node)) {\n out.push(object);\n }\n for (const child of object.children) {\n walk(child);\n }\n };\n walk(root);\n return out;\n}\n","import {\n DoubleSide,\n FrontSide,\n MeshBasicMaterial,\n MeshLambertMaterial,\n MeshPhysicalMaterial,\n MeshStandardMaterial,\n MeshToonMaterial,\n ShaderMaterial,\n} from 'three';\nimport type { Material, Texture } from 'three';\nimport type Material3D from './component';\nimport type { Material3DUniformValue } from './component';\n\nconst MAP_KEYS = [\n 'map',\n 'normalMap',\n 'roughnessMap',\n 'metalnessMap',\n 'emissiveMap',\n 'aoMap',\n 'alphaMap',\n] as const;\n\nexport function firstMaterial(material: Material | Material[]): Material {\n return Array.isArray(material) ? material[0] : material;\n}\n\nfunction copyMaps(target: Material, source: Material | undefined, skipMap: boolean) {\n if (!source) return;\n for (const key of MAP_KEYS) {\n if (skipMap && key === 'map') continue;\n if (key in target && key in source) {\n (target as Material & Record<string, unknown>)[key] =\n (source as Material & Record<string, unknown>)[key];\n }\n }\n}\n\nexport function threeUniformValue(value: Material3DUniformValue): number | number[] {\n if (typeof value === 'boolean') return value ? 1 : 0;\n return value;\n}\n\nexport function createMaterial(\n component: Material3D,\n template: Material | undefined,\n shader: { vertex: string; fragment: string } | null,\n mapOverride: Texture | null,\n): Material {\n const skipMap = !!mapOverride;\n const hasMap = !!(mapOverride || (template && 'map' in template && (template as MeshStandardMaterial).map));\n const common = {\n color: component.color,\n opacity: component.opacity,\n transparent: component.opacity < 1 || hasMap || !!(template as MeshStandardMaterial | undefined)?.transparent,\n wireframe: component.wireframe,\n side: component.doubleSide ? DoubleSide : FrontSide,\n map: mapOverride,\n };\n\n if (component.type === 'shader') {\n if (!shader) {\n throw new Error('Material3D shader type requires a SHADER resource');\n }\n const uniforms: Record<string, { value: number | number[] }> = {};\n for (const [key, value] of Object.entries(component.uniforms || {})) {\n uniforms[key] = { value: threeUniformValue(value) };\n }\n return new ShaderMaterial({\n vertexShader: shader.vertex,\n fragmentShader: shader.fragment,\n uniforms,\n transparent: common.transparent,\n opacity: common.opacity,\n wireframe: common.wireframe,\n side: common.side,\n });\n }\n\n let material: Material;\n switch (component.type) {\n case 'basic':\n material = new MeshBasicMaterial(common);\n break;\n case 'lambert':\n material = new MeshLambertMaterial({ ...common, emissive: component.emissive });\n break;\n case 'toon':\n material = new MeshToonMaterial(common);\n break;\n case 'physical':\n material = new MeshPhysicalMaterial({\n ...common,\n roughness: component.roughness,\n metalness: component.metalness,\n emissive: component.emissive,\n });\n break;\n case 'standard':\n default:\n material = new MeshStandardMaterial({\n ...common,\n roughness: component.roughness,\n metalness: component.metalness,\n emissive: component.emissive,\n });\n break;\n }\n\n copyMaps(material, template, skipMap);\n if (mapOverride) {\n (material as MeshStandardMaterial).map = mapOverride;\n }\n return material;\n}\n\nexport function writeShaderUniforms(\n material: Material,\n uniforms: Record<string, Material3DUniformValue> | undefined,\n time: number,\n) {\n const shader = material as ShaderMaterial;\n if (!shader.isShaderMaterial || !shader.uniforms) return;\n for (const [key, value] of Object.entries(uniforms || {})) {\n const next = (key === 'time' || key === 'uTime') ? time : threeUniformValue(value);\n if (shader.uniforms[key]) {\n shader.uniforms[key].value = next;\n } else {\n shader.uniforms[key] = { value: next };\n }\n }\n}\n","import { resource } from '@combos-fun/engine';\n\nexport const DEFAULT_THREE_VERTEX = `varying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport async function loadShaderSource(name: string): Promise<{ vertex: string; fragment: string }> {\n const trimmed = String(name ?? '').trim();\n if (!trimmed) {\n throw new Error('SHADER resource name is required when Material3D.type is shader');\n }\n const res = await resource.getResource(trimmed);\n if (!res?.name) {\n throw new Error(`Unknown resource \"${trimmed}\"; register RESOURCE_TYPE.SHADER first`);\n }\n const vertex = typeof res.data?.vertex === 'string'\n ? res.data.vertex\n : typeof res.instance?.vertex === 'string' ? res.instance.vertex : '';\n const fragment = typeof res.data?.fragment === 'string'\n ? res.data.fragment\n : typeof res.instance?.fragment === 'string' ? res.instance.fragment : '';\n if (!fragment) {\n throw new Error(`SHADER \"${trimmed}\" missing fragment`);\n }\n return { vertex: vertex || DEFAULT_THREE_VERTEX, fragment };\n}\n","import { decorators, ComponentChanged, OBSERVER_TYPE, GameObject, UpdateParams } from '@combos-fun/engine';\nimport {\n Renderer3D,\n Renderer3DSystem,\n textureFromNamedImage,\n} from '@combos-fun/plugin-renderer-3d';\nimport type { Material, Mesh, Object3D, Texture } from 'three';\nimport Material3D from './component';\nimport { collectTargetMeshes } from './meshes';\nimport { createMaterial, firstMaterial, writeShaderUniforms } from './createMaterial';\nimport { loadShaderSource } from './shaderSource';\n\ninterface AppliedMesh {\n mesh: Mesh;\n originals: Material | Material[];\n created: Material;\n}\n\ninterface MaterialEntry {\n applied: AppliedMesh[];\n ownedTextures: Texture[];\n signature: string;\n time: number;\n}\n\n@decorators.componentObserver({\n Material3D: [\n 'type', 'resource', 'node', 'color', 'opacity', 'roughness', 'metalness',\n 'emissive', 'wireframe', 'doubleSide', 'map',\n { prop: ['uniforms'], deep: true },\n ],\n})\nexport default class Material3DSystem extends Renderer3D {\n static systemName = 'Material3DSystem';\n name: string = 'Material3DSystem';\n private entries: Map<number, MaterialEntry> = new Map();\n private deltaSeconds = 1 / 60;\n\n init() {\n const renderer3DSystem = this.game.getSystem(Renderer3DSystem) as Renderer3DSystem;\n renderer3DSystem.rendererManager.register(this);\n }\n\n override update(e?: UpdateParams) {\n this.deltaSeconds = (e?.deltaTime ?? 16.67) / 1000;\n super.update(e);\n }\n\n componentChanged(changed: ComponentChanged) {\n if (changed.componentName !== 'Material3D') return;\n if (changed.type === OBSERVER_TYPE.REMOVE) {\n this.restore(changed.gameObject.id);\n return;\n }\n if (changed.type === OBSERVER_TYPE.CHANGE && changed.prop?.prop?.[0] === 'uniforms') {\n this.writeUniforms(changed.gameObject.id, changed.component as Material3D);\n return;\n }\n const entry = this.entries.get(changed.gameObject.id);\n if (entry) entry.signature = '';\n this.apply(changed.gameObject, changed.component as Material3D);\n }\n\n rendererUpdate(gameObject: GameObject) {\n const component = gameObject.getComponent(Material3D) as Material3D;\n if (!component) return;\n const entry = this.entries.get(gameObject.id);\n const root = this.visualRoot(gameObject.id);\n const meshes = collectTargetMeshes(root, component.node, !this.threeContext.models.get(gameObject.id));\n const signature = this.signature(root, meshes);\n if (!entry || entry.signature !== signature) {\n this.apply(gameObject, component);\n return;\n }\n entry.time += this.deltaSeconds;\n this.writeUniforms(gameObject.id, component);\n }\n\n private visualRoot(id: number): Object3D | undefined {\n return this.threeContext.models.get(id) || this.threeContext.object3DFor(id);\n }\n\n private signature(root: Object3D | undefined, meshes: Mesh[]): string {\n if (!root || !meshes.length) return '';\n return `${root.uuid}|${meshes.map((mesh) => mesh.uuid).join(',')}`;\n }\n\n private async apply(gameObject: GameObject, component: Material3D) {\n const root = this.visualRoot(gameObject.id);\n const skipNested = !this.threeContext.models.get(gameObject.id);\n const meshes = collectTargetMeshes(root, component.node, skipNested);\n if (!meshes.length) return;\n const asyncId = this.increaseAsyncId(gameObject.id);\n\n let shader: { vertex: string; fragment: string } | null = null;\n let mapOverride: Texture | null = null;\n try {\n if (component.type === 'shader') {\n shader = await loadShaderSource(component.resource);\n }\n if (component.map) {\n mapOverride = await textureFromNamedImage(component.map);\n }\n } catch (err) {\n mapOverride?.dispose();\n throw err;\n }\n if (!this.validateAsyncId(gameObject.id, asyncId)) {\n mapOverride?.dispose();\n return;\n }\n\n this.restore(gameObject.id);\n const applied: AppliedMesh[] = [];\n const ownedTextures = mapOverride ? [mapOverride] : [];\n for (const mesh of meshes) {\n const originals = mesh.material;\n const created = createMaterial(\n component,\n firstMaterial(originals),\n shader,\n mapOverride,\n );\n mesh.material = created;\n applied.push({ mesh, originals, created });\n }\n this.entries.set(gameObject.id, {\n applied,\n ownedTextures,\n signature: this.signature(root, meshes),\n time: 0,\n });\n this.writeUniforms(gameObject.id, component);\n }\n\n private writeUniforms(id: number, component: Material3D) {\n const entry = this.entries.get(id);\n if (!entry) return;\n for (const item of entry.applied) {\n writeShaderUniforms(item.created, component.uniforms, entry.time);\n }\n }\n\n private restore(id: number, putBack = true) {\n this.increaseAsyncId(id);\n const entry = this.entries.get(id);\n if (!entry) return;\n for (const item of entry.applied) {\n if (putBack && item.mesh) {\n item.mesh.material = item.originals;\n }\n if (item.created && item.created !== item.originals) {\n item.created.dispose();\n }\n }\n for (const texture of entry.ownedTextures) {\n texture.dispose();\n }\n this.entries.delete(id);\n }\n\n onDestroy() {\n for (const id of [...this.entries.keys()]) {\n this.restore(id);\n }\n }\n}\n"],"names":["Component","__decorate","Field","DoubleSide","FrontSide","ShaderMaterial","MeshBasicMaterial","MeshLambertMaterial","MeshToonMaterial","MeshPhysicalMaterial","MeshStandardMaterial","resource","Renderer3D","Renderer3DSystem","OBSERVER_TYPE","textureFromNamedImage","decorators"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ "use strict";var e=require("tslib"),t=require("@combos-fun/engine"),r=require("@combos-fun/inspector-decorator"),i=require("@combos-fun/plugin-renderer-3d"),s=require("three");class o extends t.Component{constructor(){super(...arguments),this.type="standard",this.resource="",this.node="",this.uniforms={},this.color=16777215,this.opacity=1,this.roughness=1,this.metalness=0,this.emissive=0,this.wireframe=!1,this.doubleSide=!1,this.map=""}static{this.componentName="Material3D"}init(e){e&&(Object.assign(this,e),e.uniforms&&(this.uniforms=e.uniforms))}}e.__decorate([r.Field({type:"string",group:"Material3D",label:"type",description:"Three material kind.",editor:"enum",enumOptions:["standard","basic","lambert","toon","physical","shader"]})],o.prototype,"type",void 0),e.__decorate([r.Field({type:"string",group:"Material3D",label:"resource",description:"SHADER resource name when type is shader.",editor:"text"})],o.prototype,"resource",void 0),e.__decorate([r.Field({type:"string",group:"Material3D",label:"node",description:"Object3D.name to target. Empty = all meshes on this GO.",editor:"text"})],o.prototype,"node",void 0),e.__decorate([r.Field({type:"number",group:"Material3D",label:"color",description:"Tint (hex). Multiplies map.",editor:"number-stepper"})],o.prototype,"color",void 0),e.__decorate([r.Field({type:"number",step:.01,min:0,max:1,group:"Material3D",label:"opacity",editor:"number-slider"})],o.prototype,"opacity",void 0),e.__decorate([r.Field({type:"number",step:.05,min:0,max:1,group:"Material3D",label:"roughness",editor:"number-slider"})],o.prototype,"roughness",void 0),e.__decorate([r.Field({type:"number",step:.05,min:0,max:1,group:"Material3D",label:"metalness",editor:"number-slider"})],o.prototype,"metalness",void 0),e.__decorate([r.Field({type:"number",group:"Material3D",label:"emissive",editor:"number-stepper"})],o.prototype,"emissive",void 0),e.__decorate([r.Field({type:"boolean",group:"Material3D",label:"wireframe",editor:"toggle"})],o.prototype,"wireframe",void 0),e.__decorate([r.Field({type:"boolean",group:"Material3D",label:"doubleSide",editor:"toggle"})],o.prototype,"doubleSide",void 0),e.__decorate([r.Field({type:"string",group:"Material3D",label:"map",description:"IMAGE resource name. Empty keeps the mesh map.",editor:"text"})],o.prototype,"map",void 0);function n(e,t="",r=!1){if(!e)return[];const i=[],s=o=>{if(!r||o===e||!function(e){return!!e.userData?.combosTransform3D}(o)){(function(e){return!!e.isMesh})(o)&&!function(e){return!!e.isInstancedMesh}(o)&&function(e,t){if(!t)return!0;let r=e;for(;r;){if(r.name===t)return!0;r=r.parent}return!1}(o,t)&&i.push(o);for(const e of o.children)s(e)}};return s(e),i}const a=["map","normalMap","roughnessMap","metalnessMap","emissiveMap","aoMap","alphaMap"];function d(e){return Array.isArray(e)?e[0]:e}function p(e){return"boolean"==typeof e?e?1:0:e}function c(e,t,r,i){const o=!!i,n=!!(i||t&&"map"in t&&t.map),d={color:e.color,opacity:e.opacity,transparent:e.opacity<1||n||!!t?.transparent,wireframe:e.wireframe,side:e.doubleSide?s.DoubleSide:s.FrontSide,map:i};if("shader"===e.type){if(!r)throw new Error("Material3D shader type requires a SHADER resource");const t={};for(const[r,i]of Object.entries(e.uniforms||{}))t[r]={value:p(i)};return new s.ShaderMaterial({vertexShader:r.vertex,fragmentShader:r.fragment,uniforms:t,transparent:d.transparent,opacity:d.opacity,wireframe:d.wireframe,side:d.side})}let c;switch(e.type){case"basic":c=new s.MeshBasicMaterial(d);break;case"lambert":c=new s.MeshLambertMaterial({...d,emissive:e.emissive});break;case"toon":c=new s.MeshToonMaterial(d);break;case"physical":c=new s.MeshPhysicalMaterial({...d,roughness:e.roughness,metalness:e.metalness,emissive:e.emissive});break;default:c=new s.MeshStandardMaterial({...d,roughness:e.roughness,metalness:e.metalness,emissive:e.emissive})}return function(e,t,r){if(t)for(const i of a)r&&"map"===i||i in e&&i in t&&(e[i]=t[i])}(c,t,o),i&&(c.map=i),c}function m(e,t,r){const i=e;if(i.isShaderMaterial&&i.uniforms)for(const[e,s]of Object.entries(t||{})){const t="time"===e||"uTime"===e?r:p(s);i.uniforms[e]?i.uniforms[e].value=t:i.uniforms[e]={value:t}}}let l=class extends i.Renderer3D{constructor(){super(...arguments),this.name="Material3DSystem",this.entries=new Map,this.deltaSeconds=1/60}static{this.systemName="Material3DSystem"}init(){this.game.getSystem(i.Renderer3DSystem).rendererManager.register(this)}update(e){this.deltaSeconds=(e?.deltaTime??16.67)/1e3,super.update(e)}componentChanged(e){if("Material3D"!==e.componentName)return;if(e.type===t.OBSERVER_TYPE.REMOVE)return void this.restore(e.gameObject.id);if(e.type===t.OBSERVER_TYPE.CHANGE&&"uniforms"===e.prop?.prop?.[0])return void this.writeUniforms(e.gameObject.id,e.component);const r=this.entries.get(e.gameObject.id);r&&(r.signature=""),this.apply(e.gameObject,e.component)}rendererUpdate(e){const t=e.getComponent(o);if(!t)return;const r=this.entries.get(e.id),i=this.visualRoot(e.id),s=n(i,t.node,!this.threeContext.models.get(e.id)),a=this.signature(i,s);r&&r.signature===a?(r.time+=this.deltaSeconds,this.writeUniforms(e.id,t)):this.apply(e,t)}visualRoot(e){return this.threeContext.models.get(e)||this.threeContext.object3DFor(e)}signature(e,t){return e&&t.length?`${e.uuid}|${t.map(e=>e.uuid).join(",")}`:""}async apply(e,r){const s=this.visualRoot(e.id),o=!this.threeContext.models.get(e.id),a=n(s,r.node,o);if(!a.length)return;const p=this.increaseAsyncId(e.id);let m=null,l=null;try{"shader"===r.type&&(m=await async function(e){const r=String(e??"").trim();if(!r)throw new Error("SHADER resource name is required when Material3D.type is shader");const i=await t.resource.getResource(r);if(!i?.name)throw new Error(`Unknown resource "${r}"; register RESOURCE_TYPE.SHADER first`);const s="string"==typeof i.data?.vertex?i.data.vertex:"string"==typeof i.instance?.vertex?i.instance.vertex:"",o="string"==typeof i.data?.fragment?i.data.fragment:"string"==typeof i.instance?.fragment?i.instance.fragment:"";if(!o)throw new Error(`SHADER "${r}" missing fragment`);return{vertex:s||"varying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n",fragment:o}}(r.resource)),r.map&&(l=await i.textureFromNamedImage(r.map))}catch(e){throw l?.dispose(),e}if(!this.validateAsyncId(e.id,p))return void l?.dispose();this.restore(e.id);const u=[],h=l?[l]:[];for(const e of a){const t=e.material,i=c(r,d(t),m,l);e.material=i,u.push({mesh:e,originals:t,created:i})}this.entries.set(e.id,{applied:u,ownedTextures:h,signature:this.signature(s,a),time:0}),this.writeUniforms(e.id,r)}writeUniforms(e,t){const r=this.entries.get(e);if(r)for(const e of r.applied)m(e.created,t.uniforms,r.time)}restore(e,t=!0){this.increaseAsyncId(e);const r=this.entries.get(e);if(r){for(const e of r.applied)t&&e.mesh&&(e.mesh.material=e.originals),e.created&&e.created!==e.originals&&e.created.dispose();for(const e of r.ownedTextures)e.dispose();this.entries.delete(e)}}onDestroy(){for(const e of[...this.entries.keys()])this.restore(e)}};l=e.__decorate([t.decorators.componentObserver({Material3D:["type","resource","node","color","opacity","roughness","metalness","emissive","wireframe","doubleSide","map",{prop:["uniforms"],deep:!0}]})],l);var u=l;exports.Material3D=o,exports.Material3DSystem=u,exports.collectTargetMeshes=n;
@@ -0,0 +1,58 @@
1
+ import { Component, UpdateParams, ComponentChanged, GameObject } from '@combos-fun/engine';
2
+ import { Renderer3D } from '@combos-fun/plugin-renderer-3d';
3
+ import { Object3D, Mesh } from 'three';
4
+
5
+ type Material3DKind = 'standard' | 'basic' | 'lambert' | 'toon' | 'physical' | 'shader';
6
+ type Material3DUniformValue = number | boolean | number[];
7
+ interface Material3DParams {
8
+ type?: Material3DKind | string;
9
+ resource?: string;
10
+ node?: string;
11
+ uniforms?: Record<string, Material3DUniformValue>;
12
+ color?: number;
13
+ opacity?: number;
14
+ roughness?: number;
15
+ metalness?: number;
16
+ emissive?: number;
17
+ wireframe?: boolean;
18
+ doubleSide?: boolean;
19
+ map?: string;
20
+ }
21
+ declare class Material3D extends Component<Material3DParams> {
22
+ static componentName: string;
23
+ type: string;
24
+ resource: string;
25
+ node: string;
26
+ uniforms: Record<string, Material3DUniformValue>;
27
+ color: number;
28
+ opacity: number;
29
+ roughness: number;
30
+ metalness: number;
31
+ emissive: number;
32
+ wireframe: boolean;
33
+ doubleSide: boolean;
34
+ map: string;
35
+ init(obj?: Material3DParams): void;
36
+ }
37
+
38
+ declare class Material3DSystem extends Renderer3D {
39
+ static systemName: string;
40
+ name: string;
41
+ private entries;
42
+ private deltaSeconds;
43
+ init(): void;
44
+ update(e?: UpdateParams): void;
45
+ componentChanged(changed: ComponentChanged): void;
46
+ rendererUpdate(gameObject: GameObject): void;
47
+ private visualRoot;
48
+ private signature;
49
+ private apply;
50
+ private writeUniforms;
51
+ private restore;
52
+ onDestroy(): void;
53
+ }
54
+
55
+ declare function collectTargetMeshes(root: Object3D | undefined, node?: string, skipNestedRoots?: boolean): Mesh[];
56
+
57
+ export { Material3D, Material3DSystem, collectTargetMeshes };
58
+ export type { Material3DKind, Material3DParams, Material3DUniformValue };
@@ -0,0 +1,451 @@
1
+ import { __decorate } from 'tslib';
2
+ import { Component, resource, OBSERVER_TYPE, decorators } from '@combos-fun/engine';
3
+ import { Field } from '@combos-fun/inspector-decorator';
4
+ import { Renderer3D, Renderer3DSystem, textureFromNamedImage } from '@combos-fun/plugin-renderer-3d';
5
+ import { DoubleSide, FrontSide, ShaderMaterial, MeshStandardMaterial, MeshPhysicalMaterial, MeshToonMaterial, MeshLambertMaterial, MeshBasicMaterial } from 'three';
6
+
7
+ class Material3D extends Component {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.type = 'standard';
11
+ this.resource = '';
12
+ this.node = '';
13
+ this.uniforms = {};
14
+ this.color = 0xffffff;
15
+ this.opacity = 1;
16
+ this.roughness = 1;
17
+ this.metalness = 0;
18
+ this.emissive = 0;
19
+ this.wireframe = false;
20
+ this.doubleSide = false;
21
+ this.map = '';
22
+ }
23
+ static { this.componentName = 'Material3D'; }
24
+ init(obj) {
25
+ if (!obj)
26
+ return;
27
+ Object.assign(this, obj);
28
+ if (obj.uniforms)
29
+ this.uniforms = obj.uniforms;
30
+ }
31
+ }
32
+ __decorate([
33
+ Field({
34
+ type: 'string',
35
+ group: 'Material3D',
36
+ label: 'type',
37
+ description: 'Three material kind.',
38
+ editor: 'enum',
39
+ enumOptions: ['standard', 'basic', 'lambert', 'toon', 'physical', 'shader'],
40
+ })
41
+ ], Material3D.prototype, "type", void 0);
42
+ __decorate([
43
+ Field({
44
+ type: 'string',
45
+ group: 'Material3D',
46
+ label: 'resource',
47
+ description: 'SHADER resource name when type is shader.',
48
+ editor: 'text',
49
+ })
50
+ ], Material3D.prototype, "resource", void 0);
51
+ __decorate([
52
+ Field({
53
+ type: 'string',
54
+ group: 'Material3D',
55
+ label: 'node',
56
+ description: 'Object3D.name to target. Empty = all meshes on this GO.',
57
+ editor: 'text',
58
+ })
59
+ ], Material3D.prototype, "node", void 0);
60
+ __decorate([
61
+ Field({
62
+ type: 'number',
63
+ group: 'Material3D',
64
+ label: 'color',
65
+ description: 'Tint (hex). Multiplies map.',
66
+ editor: 'number-stepper',
67
+ })
68
+ ], Material3D.prototype, "color", void 0);
69
+ __decorate([
70
+ Field({
71
+ type: 'number',
72
+ step: 0.01,
73
+ min: 0,
74
+ max: 1,
75
+ group: 'Material3D',
76
+ label: 'opacity',
77
+ editor: 'number-slider',
78
+ })
79
+ ], Material3D.prototype, "opacity", void 0);
80
+ __decorate([
81
+ Field({
82
+ type: 'number',
83
+ step: 0.05,
84
+ min: 0,
85
+ max: 1,
86
+ group: 'Material3D',
87
+ label: 'roughness',
88
+ editor: 'number-slider',
89
+ })
90
+ ], Material3D.prototype, "roughness", void 0);
91
+ __decorate([
92
+ Field({
93
+ type: 'number',
94
+ step: 0.05,
95
+ min: 0,
96
+ max: 1,
97
+ group: 'Material3D',
98
+ label: 'metalness',
99
+ editor: 'number-slider',
100
+ })
101
+ ], Material3D.prototype, "metalness", void 0);
102
+ __decorate([
103
+ Field({
104
+ type: 'number',
105
+ group: 'Material3D',
106
+ label: 'emissive',
107
+ editor: 'number-stepper',
108
+ })
109
+ ], Material3D.prototype, "emissive", void 0);
110
+ __decorate([
111
+ Field({
112
+ type: 'boolean',
113
+ group: 'Material3D',
114
+ label: 'wireframe',
115
+ editor: 'toggle',
116
+ })
117
+ ], Material3D.prototype, "wireframe", void 0);
118
+ __decorate([
119
+ Field({
120
+ type: 'boolean',
121
+ group: 'Material3D',
122
+ label: 'doubleSide',
123
+ editor: 'toggle',
124
+ })
125
+ ], Material3D.prototype, "doubleSide", void 0);
126
+ __decorate([
127
+ Field({
128
+ type: 'string',
129
+ group: 'Material3D',
130
+ label: 'map',
131
+ description: 'IMAGE resource name. Empty keeps the mesh map.',
132
+ editor: 'text',
133
+ })
134
+ ], Material3D.prototype, "map", void 0);
135
+
136
+ const TRANSFORM3D_ROOT = 'combosTransform3D';
137
+ function isTransform3DRoot(object) {
138
+ return !!object.userData?.[TRANSFORM3D_ROOT];
139
+ }
140
+ function isMesh(object) {
141
+ return !!object.isMesh;
142
+ }
143
+ function isInstanced(object) {
144
+ return !!object.isInstancedMesh;
145
+ }
146
+ function matchesNode(object, node) {
147
+ if (!node)
148
+ return true;
149
+ let current = object;
150
+ while (current) {
151
+ if (current.name === node)
152
+ return true;
153
+ current = current.parent;
154
+ }
155
+ return false;
156
+ }
157
+ function collectTargetMeshes(root, node = '', skipNestedRoots = false) {
158
+ if (!root)
159
+ return [];
160
+ const out = [];
161
+ const walk = (object) => {
162
+ if (skipNestedRoots && object !== root && isTransform3DRoot(object))
163
+ return;
164
+ if (isMesh(object) && !isInstanced(object) && matchesNode(object, node)) {
165
+ out.push(object);
166
+ }
167
+ for (const child of object.children) {
168
+ walk(child);
169
+ }
170
+ };
171
+ walk(root);
172
+ return out;
173
+ }
174
+
175
+ const MAP_KEYS = [
176
+ 'map',
177
+ 'normalMap',
178
+ 'roughnessMap',
179
+ 'metalnessMap',
180
+ 'emissiveMap',
181
+ 'aoMap',
182
+ 'alphaMap',
183
+ ];
184
+ function firstMaterial(material) {
185
+ return Array.isArray(material) ? material[0] : material;
186
+ }
187
+ function copyMaps(target, source, skipMap) {
188
+ if (!source)
189
+ return;
190
+ for (const key of MAP_KEYS) {
191
+ if (skipMap && key === 'map')
192
+ continue;
193
+ if (key in target && key in source) {
194
+ target[key] =
195
+ source[key];
196
+ }
197
+ }
198
+ }
199
+ function threeUniformValue(value) {
200
+ if (typeof value === 'boolean')
201
+ return value ? 1 : 0;
202
+ return value;
203
+ }
204
+ function createMaterial(component, template, shader, mapOverride) {
205
+ const skipMap = !!mapOverride;
206
+ const hasMap = !!(mapOverride || (template && 'map' in template && template.map));
207
+ const common = {
208
+ color: component.color,
209
+ opacity: component.opacity,
210
+ transparent: component.opacity < 1 || hasMap || !!template?.transparent,
211
+ wireframe: component.wireframe,
212
+ side: component.doubleSide ? DoubleSide : FrontSide,
213
+ map: mapOverride,
214
+ };
215
+ if (component.type === 'shader') {
216
+ if (!shader) {
217
+ throw new Error('Material3D shader type requires a SHADER resource');
218
+ }
219
+ const uniforms = {};
220
+ for (const [key, value] of Object.entries(component.uniforms || {})) {
221
+ uniforms[key] = { value: threeUniformValue(value) };
222
+ }
223
+ return new ShaderMaterial({
224
+ vertexShader: shader.vertex,
225
+ fragmentShader: shader.fragment,
226
+ uniforms,
227
+ transparent: common.transparent,
228
+ opacity: common.opacity,
229
+ wireframe: common.wireframe,
230
+ side: common.side,
231
+ });
232
+ }
233
+ let material;
234
+ switch (component.type) {
235
+ case 'basic':
236
+ material = new MeshBasicMaterial(common);
237
+ break;
238
+ case 'lambert':
239
+ material = new MeshLambertMaterial({ ...common, emissive: component.emissive });
240
+ break;
241
+ case 'toon':
242
+ material = new MeshToonMaterial(common);
243
+ break;
244
+ case 'physical':
245
+ material = new MeshPhysicalMaterial({
246
+ ...common,
247
+ roughness: component.roughness,
248
+ metalness: component.metalness,
249
+ emissive: component.emissive,
250
+ });
251
+ break;
252
+ case 'standard':
253
+ default:
254
+ material = new MeshStandardMaterial({
255
+ ...common,
256
+ roughness: component.roughness,
257
+ metalness: component.metalness,
258
+ emissive: component.emissive,
259
+ });
260
+ break;
261
+ }
262
+ copyMaps(material, template, skipMap);
263
+ if (mapOverride) {
264
+ material.map = mapOverride;
265
+ }
266
+ return material;
267
+ }
268
+ function writeShaderUniforms(material, uniforms, time) {
269
+ const shader = material;
270
+ if (!shader.isShaderMaterial || !shader.uniforms)
271
+ return;
272
+ for (const [key, value] of Object.entries(uniforms || {})) {
273
+ const next = (key === 'time' || key === 'uTime') ? time : threeUniformValue(value);
274
+ if (shader.uniforms[key]) {
275
+ shader.uniforms[key].value = next;
276
+ }
277
+ else {
278
+ shader.uniforms[key] = { value: next };
279
+ }
280
+ }
281
+ }
282
+
283
+ const DEFAULT_THREE_VERTEX = `varying vec2 vUv;
284
+ void main() {
285
+ vUv = uv;
286
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
287
+ }
288
+ `;
289
+ async function loadShaderSource(name) {
290
+ const trimmed = String(name ?? '').trim();
291
+ if (!trimmed) {
292
+ throw new Error('SHADER resource name is required when Material3D.type is shader');
293
+ }
294
+ const res = await resource.getResource(trimmed);
295
+ if (!res?.name) {
296
+ throw new Error(`Unknown resource "${trimmed}"; register RESOURCE_TYPE.SHADER first`);
297
+ }
298
+ const vertex = typeof res.data?.vertex === 'string'
299
+ ? res.data.vertex
300
+ : typeof res.instance?.vertex === 'string' ? res.instance.vertex : '';
301
+ const fragment = typeof res.data?.fragment === 'string'
302
+ ? res.data.fragment
303
+ : typeof res.instance?.fragment === 'string' ? res.instance.fragment : '';
304
+ if (!fragment) {
305
+ throw new Error(`SHADER "${trimmed}" missing fragment`);
306
+ }
307
+ return { vertex: vertex || DEFAULT_THREE_VERTEX, fragment };
308
+ }
309
+
310
+ let Material3DSystem = class Material3DSystem extends Renderer3D {
311
+ constructor() {
312
+ super(...arguments);
313
+ this.name = 'Material3DSystem';
314
+ this.entries = new Map();
315
+ this.deltaSeconds = 1 / 60;
316
+ }
317
+ static { this.systemName = 'Material3DSystem'; }
318
+ init() {
319
+ const renderer3DSystem = this.game.getSystem(Renderer3DSystem);
320
+ renderer3DSystem.rendererManager.register(this);
321
+ }
322
+ update(e) {
323
+ this.deltaSeconds = (e?.deltaTime ?? 16.67) / 1000;
324
+ super.update(e);
325
+ }
326
+ componentChanged(changed) {
327
+ if (changed.componentName !== 'Material3D')
328
+ return;
329
+ if (changed.type === OBSERVER_TYPE.REMOVE) {
330
+ this.restore(changed.gameObject.id);
331
+ return;
332
+ }
333
+ if (changed.type === OBSERVER_TYPE.CHANGE && changed.prop?.prop?.[0] === 'uniforms') {
334
+ this.writeUniforms(changed.gameObject.id, changed.component);
335
+ return;
336
+ }
337
+ const entry = this.entries.get(changed.gameObject.id);
338
+ if (entry)
339
+ entry.signature = '';
340
+ this.apply(changed.gameObject, changed.component);
341
+ }
342
+ rendererUpdate(gameObject) {
343
+ const component = gameObject.getComponent(Material3D);
344
+ if (!component)
345
+ return;
346
+ const entry = this.entries.get(gameObject.id);
347
+ const root = this.visualRoot(gameObject.id);
348
+ const meshes = collectTargetMeshes(root, component.node, !this.threeContext.models.get(gameObject.id));
349
+ const signature = this.signature(root, meshes);
350
+ if (!entry || entry.signature !== signature) {
351
+ this.apply(gameObject, component);
352
+ return;
353
+ }
354
+ entry.time += this.deltaSeconds;
355
+ this.writeUniforms(gameObject.id, component);
356
+ }
357
+ visualRoot(id) {
358
+ return this.threeContext.models.get(id) || this.threeContext.object3DFor(id);
359
+ }
360
+ signature(root, meshes) {
361
+ if (!root || !meshes.length)
362
+ return '';
363
+ return `${root.uuid}|${meshes.map((mesh) => mesh.uuid).join(',')}`;
364
+ }
365
+ async apply(gameObject, component) {
366
+ const root = this.visualRoot(gameObject.id);
367
+ const skipNested = !this.threeContext.models.get(gameObject.id);
368
+ const meshes = collectTargetMeshes(root, component.node, skipNested);
369
+ if (!meshes.length)
370
+ return;
371
+ const asyncId = this.increaseAsyncId(gameObject.id);
372
+ let shader = null;
373
+ let mapOverride = null;
374
+ try {
375
+ if (component.type === 'shader') {
376
+ shader = await loadShaderSource(component.resource);
377
+ }
378
+ if (component.map) {
379
+ mapOverride = await textureFromNamedImage(component.map);
380
+ }
381
+ }
382
+ catch (err) {
383
+ mapOverride?.dispose();
384
+ throw err;
385
+ }
386
+ if (!this.validateAsyncId(gameObject.id, asyncId)) {
387
+ mapOverride?.dispose();
388
+ return;
389
+ }
390
+ this.restore(gameObject.id);
391
+ const applied = [];
392
+ const ownedTextures = mapOverride ? [mapOverride] : [];
393
+ for (const mesh of meshes) {
394
+ const originals = mesh.material;
395
+ const created = createMaterial(component, firstMaterial(originals), shader, mapOverride);
396
+ mesh.material = created;
397
+ applied.push({ mesh, originals, created });
398
+ }
399
+ this.entries.set(gameObject.id, {
400
+ applied,
401
+ ownedTextures,
402
+ signature: this.signature(root, meshes),
403
+ time: 0,
404
+ });
405
+ this.writeUniforms(gameObject.id, component);
406
+ }
407
+ writeUniforms(id, component) {
408
+ const entry = this.entries.get(id);
409
+ if (!entry)
410
+ return;
411
+ for (const item of entry.applied) {
412
+ writeShaderUniforms(item.created, component.uniforms, entry.time);
413
+ }
414
+ }
415
+ restore(id, putBack = true) {
416
+ this.increaseAsyncId(id);
417
+ const entry = this.entries.get(id);
418
+ if (!entry)
419
+ return;
420
+ for (const item of entry.applied) {
421
+ if (putBack && item.mesh) {
422
+ item.mesh.material = item.originals;
423
+ }
424
+ if (item.created && item.created !== item.originals) {
425
+ item.created.dispose();
426
+ }
427
+ }
428
+ for (const texture of entry.ownedTextures) {
429
+ texture.dispose();
430
+ }
431
+ this.entries.delete(id);
432
+ }
433
+ onDestroy() {
434
+ for (const id of [...this.entries.keys()]) {
435
+ this.restore(id);
436
+ }
437
+ }
438
+ };
439
+ Material3DSystem = __decorate([
440
+ decorators.componentObserver({
441
+ Material3D: [
442
+ 'type', 'resource', 'node', 'color', 'opacity', 'roughness', 'metalness',
443
+ 'emissive', 'wireframe', 'doubleSide', 'map',
444
+ { prop: ['uniforms'], deep: true },
445
+ ],
446
+ })
447
+ ], Material3DSystem);
448
+ var Material3DSystem_default = Material3DSystem;
449
+
450
+ export { Material3D, Material3DSystem_default as Material3DSystem, collectTargetMeshes };
451
+ //# sourceMappingURL=plugin-renderer-3d-material.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-renderer-3d-material.esm.js","sources":["../lib/component.ts","../lib/meshes.ts","../lib/createMaterial.ts","../lib/shaderSource.ts","../lib/system.ts"],"sourcesContent":["import { Component } from '@combos-fun/engine';\nimport { Field } from '@combos-fun/inspector-decorator';\n\nexport type Material3DKind = 'standard' | 'basic' | 'lambert' | 'toon' | 'physical' | 'shader';\n\nexport type Material3DUniformValue = number | boolean | number[];\n\nexport interface Material3DParams {\n type?: Material3DKind | string;\n resource?: string;\n node?: string;\n uniforms?: Record<string, Material3DUniformValue>;\n color?: number;\n opacity?: number;\n roughness?: number;\n metalness?: number;\n emissive?: number;\n wireframe?: boolean;\n doubleSide?: boolean;\n map?: string;\n}\n\nexport default class Material3D extends Component<Material3DParams> {\n static componentName: string = 'Material3D';\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'type',\n description: 'Three material kind.',\n editor: 'enum',\n enumOptions: ['standard', 'basic', 'lambert', 'toon', 'physical', 'shader'],\n })\n type: string = 'standard';\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'resource',\n description: 'SHADER resource name when type is shader.',\n editor: 'text',\n })\n resource: string = '';\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'node',\n description: 'Object3D.name to target. Empty = all meshes on this GO.',\n editor: 'text',\n })\n node: string = '';\n\n uniforms: Record<string, Material3DUniformValue> = {};\n\n @Field({\n type: 'number',\n group: 'Material3D',\n label: 'color',\n description: 'Tint (hex). Multiplies map.',\n editor: 'number-stepper',\n })\n color: number = 0xffffff;\n\n @Field({\n type: 'number',\n step: 0.01,\n min: 0,\n max: 1,\n group: 'Material3D',\n label: 'opacity',\n editor: 'number-slider',\n })\n opacity: number = 1;\n\n @Field({\n type: 'number',\n step: 0.05,\n min: 0,\n max: 1,\n group: 'Material3D',\n label: 'roughness',\n editor: 'number-slider',\n })\n roughness: number = 1;\n\n @Field({\n type: 'number',\n step: 0.05,\n min: 0,\n max: 1,\n group: 'Material3D',\n label: 'metalness',\n editor: 'number-slider',\n })\n metalness: number = 0;\n\n @Field({\n type: 'number',\n group: 'Material3D',\n label: 'emissive',\n editor: 'number-stepper',\n })\n emissive: number = 0;\n\n @Field({\n type: 'boolean',\n group: 'Material3D',\n label: 'wireframe',\n editor: 'toggle',\n })\n wireframe: boolean = false;\n\n @Field({\n type: 'boolean',\n group: 'Material3D',\n label: 'doubleSide',\n editor: 'toggle',\n })\n doubleSide: boolean = false;\n\n @Field({\n type: 'string',\n group: 'Material3D',\n label: 'map',\n description: 'IMAGE resource name. Empty keeps the mesh map.',\n editor: 'text',\n })\n map: string = '';\n\n init(obj?: Material3DParams) {\n if (!obj) return;\n Object.assign(this, obj);\n if (obj.uniforms) this.uniforms = obj.uniforms;\n }\n}\n","import type { Mesh, Object3D } from 'three';\n\nconst TRANSFORM3D_ROOT = 'combosTransform3D';\n\nfunction isTransform3DRoot(object: Object3D): boolean {\n return !!object.userData?.[TRANSFORM3D_ROOT];\n}\n\nfunction isMesh(object: Object3D): object is Mesh {\n return !!(object as Mesh).isMesh;\n}\n\nfunction isInstanced(object: Object3D): boolean {\n return !!(object as Mesh & { isInstancedMesh?: boolean }).isInstancedMesh;\n}\n\nfunction matchesNode(object: Object3D, node: string): boolean {\n if (!node) return true;\n let current: Object3D | null = object;\n while (current) {\n if (current.name === node) return true;\n current = current.parent;\n }\n return false;\n}\n\nexport function collectTargetMeshes(\n root: Object3D | undefined,\n node = '',\n skipNestedRoots = false,\n): Mesh[] {\n if (!root) return [];\n const out: Mesh[] = [];\n const walk = (object: Object3D) => {\n if (skipNestedRoots && object !== root && isTransform3DRoot(object)) return;\n if (isMesh(object) && !isInstanced(object) && matchesNode(object, node)) {\n out.push(object);\n }\n for (const child of object.children) {\n walk(child);\n }\n };\n walk(root);\n return out;\n}\n","import {\n DoubleSide,\n FrontSide,\n MeshBasicMaterial,\n MeshLambertMaterial,\n MeshPhysicalMaterial,\n MeshStandardMaterial,\n MeshToonMaterial,\n ShaderMaterial,\n} from 'three';\nimport type { Material, Texture } from 'three';\nimport type Material3D from './component';\nimport type { Material3DUniformValue } from './component';\n\nconst MAP_KEYS = [\n 'map',\n 'normalMap',\n 'roughnessMap',\n 'metalnessMap',\n 'emissiveMap',\n 'aoMap',\n 'alphaMap',\n] as const;\n\nexport function firstMaterial(material: Material | Material[]): Material {\n return Array.isArray(material) ? material[0] : material;\n}\n\nfunction copyMaps(target: Material, source: Material | undefined, skipMap: boolean) {\n if (!source) return;\n for (const key of MAP_KEYS) {\n if (skipMap && key === 'map') continue;\n if (key in target && key in source) {\n (target as Material & Record<string, unknown>)[key] =\n (source as Material & Record<string, unknown>)[key];\n }\n }\n}\n\nexport function threeUniformValue(value: Material3DUniformValue): number | number[] {\n if (typeof value === 'boolean') return value ? 1 : 0;\n return value;\n}\n\nexport function createMaterial(\n component: Material3D,\n template: Material | undefined,\n shader: { vertex: string; fragment: string } | null,\n mapOverride: Texture | null,\n): Material {\n const skipMap = !!mapOverride;\n const hasMap = !!(mapOverride || (template && 'map' in template && (template as MeshStandardMaterial).map));\n const common = {\n color: component.color,\n opacity: component.opacity,\n transparent: component.opacity < 1 || hasMap || !!(template as MeshStandardMaterial | undefined)?.transparent,\n wireframe: component.wireframe,\n side: component.doubleSide ? DoubleSide : FrontSide,\n map: mapOverride,\n };\n\n if (component.type === 'shader') {\n if (!shader) {\n throw new Error('Material3D shader type requires a SHADER resource');\n }\n const uniforms: Record<string, { value: number | number[] }> = {};\n for (const [key, value] of Object.entries(component.uniforms || {})) {\n uniforms[key] = { value: threeUniformValue(value) };\n }\n return new ShaderMaterial({\n vertexShader: shader.vertex,\n fragmentShader: shader.fragment,\n uniforms,\n transparent: common.transparent,\n opacity: common.opacity,\n wireframe: common.wireframe,\n side: common.side,\n });\n }\n\n let material: Material;\n switch (component.type) {\n case 'basic':\n material = new MeshBasicMaterial(common);\n break;\n case 'lambert':\n material = new MeshLambertMaterial({ ...common, emissive: component.emissive });\n break;\n case 'toon':\n material = new MeshToonMaterial(common);\n break;\n case 'physical':\n material = new MeshPhysicalMaterial({\n ...common,\n roughness: component.roughness,\n metalness: component.metalness,\n emissive: component.emissive,\n });\n break;\n case 'standard':\n default:\n material = new MeshStandardMaterial({\n ...common,\n roughness: component.roughness,\n metalness: component.metalness,\n emissive: component.emissive,\n });\n break;\n }\n\n copyMaps(material, template, skipMap);\n if (mapOverride) {\n (material as MeshStandardMaterial).map = mapOverride;\n }\n return material;\n}\n\nexport function writeShaderUniforms(\n material: Material,\n uniforms: Record<string, Material3DUniformValue> | undefined,\n time: number,\n) {\n const shader = material as ShaderMaterial;\n if (!shader.isShaderMaterial || !shader.uniforms) return;\n for (const [key, value] of Object.entries(uniforms || {})) {\n const next = (key === 'time' || key === 'uTime') ? time : threeUniformValue(value);\n if (shader.uniforms[key]) {\n shader.uniforms[key].value = next;\n } else {\n shader.uniforms[key] = { value: next };\n }\n }\n}\n","import { resource } from '@combos-fun/engine';\n\nexport const DEFAULT_THREE_VERTEX = `varying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\nexport async function loadShaderSource(name: string): Promise<{ vertex: string; fragment: string }> {\n const trimmed = String(name ?? '').trim();\n if (!trimmed) {\n throw new Error('SHADER resource name is required when Material3D.type is shader');\n }\n const res = await resource.getResource(trimmed);\n if (!res?.name) {\n throw new Error(`Unknown resource \"${trimmed}\"; register RESOURCE_TYPE.SHADER first`);\n }\n const vertex = typeof res.data?.vertex === 'string'\n ? res.data.vertex\n : typeof res.instance?.vertex === 'string' ? res.instance.vertex : '';\n const fragment = typeof res.data?.fragment === 'string'\n ? res.data.fragment\n : typeof res.instance?.fragment === 'string' ? res.instance.fragment : '';\n if (!fragment) {\n throw new Error(`SHADER \"${trimmed}\" missing fragment`);\n }\n return { vertex: vertex || DEFAULT_THREE_VERTEX, fragment };\n}\n","import { decorators, ComponentChanged, OBSERVER_TYPE, GameObject, UpdateParams } from '@combos-fun/engine';\nimport {\n Renderer3D,\n Renderer3DSystem,\n textureFromNamedImage,\n} from '@combos-fun/plugin-renderer-3d';\nimport type { Material, Mesh, Object3D, Texture } from 'three';\nimport Material3D from './component';\nimport { collectTargetMeshes } from './meshes';\nimport { createMaterial, firstMaterial, writeShaderUniforms } from './createMaterial';\nimport { loadShaderSource } from './shaderSource';\n\ninterface AppliedMesh {\n mesh: Mesh;\n originals: Material | Material[];\n created: Material;\n}\n\ninterface MaterialEntry {\n applied: AppliedMesh[];\n ownedTextures: Texture[];\n signature: string;\n time: number;\n}\n\n@decorators.componentObserver({\n Material3D: [\n 'type', 'resource', 'node', 'color', 'opacity', 'roughness', 'metalness',\n 'emissive', 'wireframe', 'doubleSide', 'map',\n { prop: ['uniforms'], deep: true },\n ],\n})\nexport default class Material3DSystem extends Renderer3D {\n static systemName = 'Material3DSystem';\n name: string = 'Material3DSystem';\n private entries: Map<number, MaterialEntry> = new Map();\n private deltaSeconds = 1 / 60;\n\n init() {\n const renderer3DSystem = this.game.getSystem(Renderer3DSystem) as Renderer3DSystem;\n renderer3DSystem.rendererManager.register(this);\n }\n\n override update(e?: UpdateParams) {\n this.deltaSeconds = (e?.deltaTime ?? 16.67) / 1000;\n super.update(e);\n }\n\n componentChanged(changed: ComponentChanged) {\n if (changed.componentName !== 'Material3D') return;\n if (changed.type === OBSERVER_TYPE.REMOVE) {\n this.restore(changed.gameObject.id);\n return;\n }\n if (changed.type === OBSERVER_TYPE.CHANGE && changed.prop?.prop?.[0] === 'uniforms') {\n this.writeUniforms(changed.gameObject.id, changed.component as Material3D);\n return;\n }\n const entry = this.entries.get(changed.gameObject.id);\n if (entry) entry.signature = '';\n this.apply(changed.gameObject, changed.component as Material3D);\n }\n\n rendererUpdate(gameObject: GameObject) {\n const component = gameObject.getComponent(Material3D) as Material3D;\n if (!component) return;\n const entry = this.entries.get(gameObject.id);\n const root = this.visualRoot(gameObject.id);\n const meshes = collectTargetMeshes(root, component.node, !this.threeContext.models.get(gameObject.id));\n const signature = this.signature(root, meshes);\n if (!entry || entry.signature !== signature) {\n this.apply(gameObject, component);\n return;\n }\n entry.time += this.deltaSeconds;\n this.writeUniforms(gameObject.id, component);\n }\n\n private visualRoot(id: number): Object3D | undefined {\n return this.threeContext.models.get(id) || this.threeContext.object3DFor(id);\n }\n\n private signature(root: Object3D | undefined, meshes: Mesh[]): string {\n if (!root || !meshes.length) return '';\n return `${root.uuid}|${meshes.map((mesh) => mesh.uuid).join(',')}`;\n }\n\n private async apply(gameObject: GameObject, component: Material3D) {\n const root = this.visualRoot(gameObject.id);\n const skipNested = !this.threeContext.models.get(gameObject.id);\n const meshes = collectTargetMeshes(root, component.node, skipNested);\n if (!meshes.length) return;\n const asyncId = this.increaseAsyncId(gameObject.id);\n\n let shader: { vertex: string; fragment: string } | null = null;\n let mapOverride: Texture | null = null;\n try {\n if (component.type === 'shader') {\n shader = await loadShaderSource(component.resource);\n }\n if (component.map) {\n mapOverride = await textureFromNamedImage(component.map);\n }\n } catch (err) {\n mapOverride?.dispose();\n throw err;\n }\n if (!this.validateAsyncId(gameObject.id, asyncId)) {\n mapOverride?.dispose();\n return;\n }\n\n this.restore(gameObject.id);\n const applied: AppliedMesh[] = [];\n const ownedTextures = mapOverride ? [mapOverride] : [];\n for (const mesh of meshes) {\n const originals = mesh.material;\n const created = createMaterial(\n component,\n firstMaterial(originals),\n shader,\n mapOverride,\n );\n mesh.material = created;\n applied.push({ mesh, originals, created });\n }\n this.entries.set(gameObject.id, {\n applied,\n ownedTextures,\n signature: this.signature(root, meshes),\n time: 0,\n });\n this.writeUniforms(gameObject.id, component);\n }\n\n private writeUniforms(id: number, component: Material3D) {\n const entry = this.entries.get(id);\n if (!entry) return;\n for (const item of entry.applied) {\n writeShaderUniforms(item.created, component.uniforms, entry.time);\n }\n }\n\n private restore(id: number, putBack = true) {\n this.increaseAsyncId(id);\n const entry = this.entries.get(id);\n if (!entry) return;\n for (const item of entry.applied) {\n if (putBack && item.mesh) {\n item.mesh.material = item.originals;\n }\n if (item.created && item.created !== item.originals) {\n item.created.dispose();\n }\n }\n for (const texture of entry.ownedTextures) {\n texture.dispose();\n }\n this.entries.delete(id);\n }\n\n onDestroy() {\n for (const id of [...this.entries.keys()]) {\n this.restore(id);\n }\n }\n}\n"],"names":[],"mappings":"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"}
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === 'production') {
4
+ module.exports = require('./dist/plugin-renderer-3d-material.cjs.prod.js');
5
+ } else {
6
+ module.exports = require('./dist/plugin-renderer-3d-material.cjs.js');
7
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@combos-fun/plugin-renderer-3d-material",
3
+ "version": "0.0.46",
4
+ "description": "Per-object 3D materials and custom shaders",
5
+ "main": "index.js",
6
+ "module": "dist/plugin-renderer-3d-material.esm.js",
7
+ "bundle": "CombosFun.plugin.renderer.3d.material",
8
+ "unpkg": "dist/CombosFun.plugin.renderer.3d.material.min.js",
9
+ "types": "dist/plugin-renderer-3d-material.d.ts",
10
+ "files": [
11
+ "index.js",
12
+ "dist",
13
+ "agent-skill.md",
14
+ "combos-plugin.json"
15
+ ],
16
+ "exports": {
17
+ ".": {
18
+ "import": "./dist/plugin-renderer-3d-material.esm.js",
19
+ "require": "./index.js",
20
+ "types": "./dist/plugin-renderer-3d-material.d.ts"
21
+ },
22
+ "./plugin-manifest": "./combos-plugin.json",
23
+ "./agent-skill": "./agent-skill.md"
24
+ },
25
+ "combos": {
26
+ "pluginManifest": "./combos-plugin.json"
27
+ },
28
+ "dependencies": {
29
+ "three": "^0.172.0",
30
+ "@combos-fun/engine": "0.0.46",
31
+ "@combos-fun/plugin-renderer-3d": "0.0.46",
32
+ "@combos-fun/inspector-decorator": "0.0.46"
33
+ },
34
+ "keywords": [
35
+ "combos-fun",
36
+ "3d",
37
+ "material",
38
+ "shader",
39
+ "pbr"
40
+ ],
41
+ "scripts": {
42
+ "build": "node ../../scripts/build-package.mjs"
43
+ }
44
+ }