@needle-tools/engine 4.14.0-next.31f837e → 4.14.0-next.52fdb13
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/CHANGELOG.md +9 -0
- package/components.needle.json +1 -1
- package/dist/{generateMeshBVH.worker-DFcS3P04.js → generateMeshBVH.worker-DiCnZlf3.js} +1 -1
- package/dist/{gltf-progressive-8voIgNp_.js → gltf-progressive-Bm_6aEi4.js} +4 -4
- package/dist/{gltf-progressive-BRRBj-nY.umd.cjs → gltf-progressive-BttGBXw6.umd.cjs} +3 -3
- package/dist/{gltf-progressive-Dkh3tG4-.min.js → gltf-progressive-T5WKTux5.min.js} +1 -1
- package/dist/{loader.worker-C6cXDgR1.js → loader.worker-BqODMeeW.js} +1 -1
- package/dist/{materialx-Dx8st96L.min.js → materialx-CJyQZtjt.min.js} +1 -1
- package/dist/{materialx-D66rYPqe.js → materialx-DMs1E08Z.js} +2 -2
- package/dist/{materialx-CxlgposR.umd.cjs → materialx-DaKKOoVk.umd.cjs} +1 -1
- package/dist/{needle-engine.bundle-D7w0XD7M.min.js → needle-engine.bundle-BwfaInTa.min.js} +156 -148
- package/dist/{needle-engine.bundle-BQXG5qbQ.umd.cjs → needle-engine.bundle-DJE-Bjpa.umd.cjs} +141 -133
- package/dist/{needle-engine.bundle-Byl5i6zJ.js → needle-engine.bundle-TmE5-_na.js} +6741 -6449
- package/dist/needle-engine.d.ts +896 -51
- package/dist/needle-engine.js +571 -570
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/dist/{postprocessing-Ce5-UWiA.min.js → postprocessing-06AXuvdv.min.js} +2 -2
- package/dist/{postprocessing-BkSpxpYB.js → postprocessing-CI2x8Cln.js} +4 -4
- package/dist/{postprocessing-DFVElmAh.umd.cjs → postprocessing-CPDcA21P.umd.cjs} +2 -2
- package/dist/{three-Bad8p1pf.umd.cjs → three-BjYim-vL.umd.cjs} +47 -47
- package/dist/{three-DFV1-P9z.js → three-Bvk2VKbF.js} +2 -2
- package/dist/{three-CWn13_u1.min.js → three-IG2qPafA.min.js} +2 -2
- package/dist/{three-examples-CO-tx3Sp.umd.cjs → three-examples-BMmNgNCN.umd.cjs} +1 -1
- package/dist/{three-examples-43yqn3mL.js → three-examples-CMYCd5nH.js} +1 -1
- package/dist/{three-examples-DKuJVGT4.min.js → three-examples-CQl1fFZp.min.js} +1 -1
- package/dist/{three-mesh-ui-DyEA5HQF.min.js → three-mesh-ui-5HVE2RV-.min.js} +1 -1
- package/dist/{three-mesh-ui-fSAQJxdI.js → three-mesh-ui-BlakAItG.js} +1 -1
- package/dist/{three-mesh-ui-ChzVOraf.umd.cjs → three-mesh-ui-D828VbQp.umd.cjs} +1 -1
- package/dist/{vendor-pe19S9r5.min.js → vendor-BxK0WKmT.min.js} +1 -1
- package/dist/{vendor-B51YffMU.umd.cjs → vendor-CIDkyBaO.umd.cjs} +1 -1
- package/dist/{vendor-CgpZ5ivC.js → vendor-ixwD-vv2.js} +1 -1
- package/lib/engine/api.d.ts +203 -18
- package/lib/engine/api.js +271 -18
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_accessibility.d.ts +58 -0
- package/lib/engine/engine_accessibility.js +143 -0
- package/lib/engine/engine_accessibility.js.map +1 -0
- package/lib/engine/engine_context.d.ts +2 -0
- package/lib/engine/engine_context.js +7 -0
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_materialpropertyblock.d.ts +309 -11
- package/lib/engine/engine_materialpropertyblock.js +396 -36
- package/lib/engine/engine_materialpropertyblock.js.map +1 -1
- package/lib/engine/engine_math.d.ts +34 -1
- package/lib/engine/engine_math.js +34 -1
- package/lib/engine/engine_math.js.map +1 -1
- package/lib/engine/engine_networking.js +1 -1
- package/lib/engine/engine_networking.js.map +1 -1
- package/lib/engine/engine_types.d.ts +2 -0
- package/lib/engine/engine_types.js +2 -0
- package/lib/engine/engine_types.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +3 -0
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/logo-element.d.ts +1 -0
- package/lib/engine/webcomponents/logo-element.js +3 -1
- package/lib/engine/webcomponents/logo-element.js.map +1 -1
- package/lib/engine/webcomponents/needle-button.d.ts +37 -11
- package/lib/engine/webcomponents/needle-button.js +42 -11
- package/lib/engine/webcomponents/needle-button.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +10 -2
- package/lib/engine/webcomponents/needle-engine.js +13 -3
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine-components/Camera.js.map +1 -1
- package/lib/engine-components/Component.d.ts +1 -2
- package/lib/engine-components/Component.js +1 -2
- package/lib/engine-components/Component.js.map +1 -1
- package/lib/engine-components/DragControls.d.ts +1 -0
- package/lib/engine-components/DragControls.js +21 -0
- package/lib/engine-components/DragControls.js.map +1 -1
- package/lib/engine-components/DropListener.js.map +1 -1
- package/lib/engine-components/Duplicatable.js.map +1 -1
- package/lib/engine-components/GroundProjection.js.map +1 -1
- package/lib/engine-components/NeedleMenu.d.ts +2 -0
- package/lib/engine-components/NeedleMenu.js +2 -0
- package/lib/engine-components/NeedleMenu.js.map +1 -1
- package/lib/engine-components/NestedGltf.js.map +1 -1
- package/lib/engine-components/Networking.d.ts +28 -3
- package/lib/engine-components/Networking.js +28 -3
- package/lib/engine-components/Networking.js.map +1 -1
- package/lib/engine-components/ReflectionProbe.d.ts +33 -3
- package/lib/engine-components/ReflectionProbe.js +76 -27
- package/lib/engine-components/ReflectionProbe.js.map +1 -1
- package/lib/engine-components/Renderer.d.ts +2 -0
- package/lib/engine-components/Renderer.js +30 -6
- package/lib/engine-components/Renderer.js.map +1 -1
- package/lib/engine-components/RendererLightmap.js +2 -3
- package/lib/engine-components/RendererLightmap.js.map +1 -1
- package/lib/engine-components/SeeThrough.d.ts +0 -2
- package/lib/engine-components/SeeThrough.js +114 -88
- package/lib/engine-components/SeeThrough.js.map +1 -1
- package/lib/engine-components/SmoothFollow.js.map +1 -1
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.d.ts +107 -13
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js +167 -30
- package/lib/engine-components/export/usdz/extensions/behavior/BehaviourComponents.js.map +1 -1
- package/lib/engine-components/ui/Button.d.ts +1 -0
- package/lib/engine-components/ui/Button.js +11 -0
- package/lib/engine-components/ui/Button.js.map +1 -1
- package/lib/engine-components/ui/Raycaster.js.map +1 -1
- package/lib/engine-components/ui/Text.d.ts +1 -0
- package/lib/engine-components/ui/Text.js +11 -0
- package/lib/engine-components/ui/Text.js.map +1 -1
- package/lib/engine-components/web/ViewBox.d.ts +2 -2
- package/lib/engine-components/web/ViewBox.js +2 -2
- package/lib/engine-components/web/ViewBox.js.map +1 -1
- package/lib/engine-components/webxr/WebXRPlaneTracking.js.map +1 -1
- package/lib/engine-components/webxr/controllers/XRControllerFollow.js.map +1 -1
- package/lib/engine-components/webxr/controllers/XRControllerMovement.js.map +1 -1
- package/package.json +4 -4
- package/src/engine/api.ts +371 -19
- package/src/engine/engine_accessibility.ts +178 -0
- package/src/engine/engine_context.ts +9 -0
- package/src/engine/engine_materialpropertyblock.ts +499 -42
- package/src/engine/engine_math.ts +34 -1
- package/src/engine/engine_networking.ts +1 -1
- package/src/engine/engine_types.ts +5 -0
- package/src/engine/webcomponents/icons.ts +3 -0
- package/src/engine/webcomponents/logo-element.ts +4 -1
- package/src/engine/webcomponents/needle-button.ts +44 -13
- package/src/engine/webcomponents/needle-engine.ts +18 -7
- package/src/engine-components/Camera.ts +2 -2
- package/src/engine-components/Component.ts +1 -3
- package/src/engine-components/DragControls.ts +29 -4
- package/src/engine-components/DropListener.ts +1 -1
- package/src/engine-components/Duplicatable.ts +1 -1
- package/src/engine-components/GroundProjection.ts +3 -0
- package/src/engine-components/NeedleMenu.ts +8 -3
- package/src/engine-components/NestedGltf.ts +1 -1
- package/src/engine-components/Networking.ts +29 -4
- package/src/engine-components/ReflectionProbe.ts +81 -31
- package/src/engine-components/Renderer.ts +34 -6
- package/src/engine-components/RendererLightmap.ts +2 -3
- package/src/engine-components/SeeThrough.ts +122 -107
- package/src/engine-components/SmoothFollow.ts +2 -2
- package/src/engine-components/export/usdz/extensions/behavior/BehaviourComponents.ts +198 -65
- package/src/engine-components/ui/Button.ts +13 -1
- package/src/engine-components/ui/Raycaster.ts +1 -1
- package/src/engine-components/ui/Text.ts +13 -0
- package/src/engine-components/web/ViewBox.ts +9 -2
- package/src/engine-components/webxr/WebXRPlaneTracking.ts +3 -3
- package/src/engine-components/webxr/controllers/XRControllerFollow.ts +1 -1
- package/src/engine-components/webxr/controllers/XRControllerMovement.ts +4 -4
|
@@ -1,30 +1,56 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BufferGeometry, Camera, Color, Euler,Group, Material, Object3D, Scene, Texture, Vector2, Vector3, Vector4, WebGLRenderer } from "three";
|
|
2
2
|
|
|
3
|
-
type MaterialPropertyType = number | number[] | Color | Texture | Vector2 | Vector3 | Vector4 | null;
|
|
4
3
|
|
|
4
|
+
// @TODO: we need to detect objects with materials both transparent and NOT transparent. These need to be updated in scene.onBeforeRender to have correct renderlists
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Valid types that can be used as material property overrides
|
|
8
|
+
*/
|
|
9
|
+
type MaterialPropertyType = number | number[] | Color | Texture | Vector2 | Vector3 | Vector4 | null | Euler;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Defines offset and repeat transformations for texture coordinates
|
|
13
|
+
*/
|
|
5
14
|
export interface TextureTransform {
|
|
15
|
+
/** UV offset applied to the texture */
|
|
6
16
|
offset?: Vector2;
|
|
17
|
+
/** UV repeat/scale applied to the texture */
|
|
7
18
|
repeat?: Vector2;
|
|
8
19
|
}
|
|
9
20
|
|
|
10
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Represents a single material property override with optional texture transformation
|
|
23
|
+
* @template T The type of the property value
|
|
24
|
+
*/
|
|
25
|
+
export interface PropertyBlockOverride<T extends MaterialPropertyType = MaterialPropertyType> {
|
|
26
|
+
/** The name of the material property to override (e.g., "color", "map", "roughness") */
|
|
11
27
|
name: string;
|
|
12
|
-
value
|
|
28
|
+
/** The value to set for this property */
|
|
29
|
+
value: T;
|
|
30
|
+
/** Optional texture coordinate transformation (only used when value is a Texture) */
|
|
13
31
|
textureTransform?: TextureTransform;
|
|
14
32
|
}
|
|
15
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Utility type that extracts only non-function property names from a type
|
|
36
|
+
* @template T The type to extract property names from
|
|
37
|
+
*/
|
|
16
38
|
type NonFunctionPropertyNames<T> = {
|
|
17
39
|
[K in keyof T]: T[K] extends Function ? never : K
|
|
18
40
|
}[keyof T];
|
|
19
41
|
|
|
20
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Centralized registry for all property block related data.
|
|
44
|
+
* Uses WeakMaps to allow automatic garbage collection when objects are destroyed.
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
21
47
|
class PropertyBlockRegistry {
|
|
22
48
|
// Map from object to its property block
|
|
23
49
|
private objectToBlock = new WeakMap<Object3D, MaterialPropertyBlock>();
|
|
24
50
|
|
|
25
51
|
// Track which materials belong to which property block (to prevent applying to wrong materials)
|
|
26
52
|
// Use WeakSet for automatic cleanup when materials are garbage collected
|
|
27
|
-
private objectToMaterials = new WeakMap<Object3D, WeakSet<Material>>();
|
|
53
|
+
// private objectToMaterials = new WeakMap<Object3D, WeakSet<Material>>();
|
|
28
54
|
|
|
29
55
|
// Track which meshes have callbacks for which property block owners
|
|
30
56
|
private meshToOwners = new WeakMap<Object3D, Set<Object3D>>();
|
|
@@ -45,21 +71,21 @@ class PropertyBlockRegistry {
|
|
|
45
71
|
|
|
46
72
|
deleteBlock(object: Object3D): void {
|
|
47
73
|
this.objectToBlock.delete(object);
|
|
48
|
-
this.objectToMaterials.delete(object);
|
|
74
|
+
// this.objectToMaterials.delete(object);
|
|
49
75
|
}
|
|
50
76
|
|
|
51
|
-
addMaterial(object: Object3D, material: Material): void {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
77
|
+
// addMaterial(object: Object3D, material: Material): void {
|
|
78
|
+
// let materials = this.objectToMaterials.get(object);
|
|
79
|
+
// if (!materials) {
|
|
80
|
+
// materials = new WeakSet();
|
|
81
|
+
// this.objectToMaterials.set(object, materials);
|
|
82
|
+
// }
|
|
83
|
+
// materials.add(material);
|
|
84
|
+
// }
|
|
59
85
|
|
|
60
|
-
hasMaterial(object: Object3D, material: Material): boolean {
|
|
61
|
-
|
|
62
|
-
}
|
|
86
|
+
// hasMaterial(object: Object3D, material: Material): boolean {
|
|
87
|
+
// return this.objectToMaterials.get(object)?.has(material) ?? false;
|
|
88
|
+
// }
|
|
63
89
|
|
|
64
90
|
isHooked(mesh: Object3D, owner: Object3D): boolean {
|
|
65
91
|
return this.meshToOwners.get(mesh)?.has(owner) ?? false;
|
|
@@ -95,17 +121,130 @@ class PropertyBlockRegistry {
|
|
|
95
121
|
|
|
96
122
|
const registry = new PropertyBlockRegistry();
|
|
97
123
|
|
|
124
|
+
/**
|
|
125
|
+
* MaterialPropertyBlock allows per-object material property overrides without creating new material instances.
|
|
126
|
+
* This is useful for rendering multiple objects with the same base material but different properties
|
|
127
|
+
* (e.g., different colors, textures, or shader parameters).
|
|
128
|
+
*
|
|
129
|
+
* ## How Property Blocks Work
|
|
130
|
+
*
|
|
131
|
+
* **Important**: Overrides are registered on the **Object3D**, not on the material.
|
|
132
|
+
* This means:
|
|
133
|
+
* - If you change the object's material, the overrides will still be applied to the new material
|
|
134
|
+
* - Multiple objects can share the same material but have different property overrides
|
|
135
|
+
* - If you don't want overrides applied after changing a material, you must remove them using {@link removeOveride}, {@link clearAllOverrides}, or {@link dispose}
|
|
136
|
+
*
|
|
137
|
+
* The property block system works by:
|
|
138
|
+
* - Temporarily applying overrides in onBeforeRender
|
|
139
|
+
* - Restoring original values in onAfterRender
|
|
140
|
+
* - Managing shader defines and program cache keys for correct shader compilation
|
|
141
|
+
* - Supporting texture coordinate transforms per object
|
|
142
|
+
*
|
|
143
|
+
* ## Common Use Cases
|
|
144
|
+
*
|
|
145
|
+
* - **Lightmaps**: Apply unique lightmap textures to individual objects sharing the same material
|
|
146
|
+
* - **Reflection Probes**: Apply different environment maps per object for localized reflections
|
|
147
|
+
* - **See-through effects**: Temporarily override transparency/transmission properties for X-ray effects
|
|
148
|
+
*
|
|
149
|
+
* ## Getting a MaterialPropertyBlock
|
|
150
|
+
*
|
|
151
|
+
* **Important**: Do not use the constructor directly. Instead, use the static {@link MaterialPropertyBlock.get} method:
|
|
152
|
+
*
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const block = MaterialPropertyBlock.get(myMesh);
|
|
155
|
+
* ```
|
|
156
|
+
*
|
|
157
|
+
* This method will either return an existing property block or create a new one if it doesn't exist.
|
|
158
|
+
* It automatically:
|
|
159
|
+
* - Creates the property block instance
|
|
160
|
+
* - Registers it in the internal registry
|
|
161
|
+
* - Attaches the necessary render callbacks to the object
|
|
162
|
+
* - Handles Groups by applying overrides to all child meshes
|
|
163
|
+
*
|
|
164
|
+
* @example Basic usage
|
|
165
|
+
* ```typescript
|
|
166
|
+
* // Get or create a property block for an object
|
|
167
|
+
* const block = MaterialPropertyBlock.get(myMesh);
|
|
168
|
+
*
|
|
169
|
+
* // Override the color property
|
|
170
|
+
* block.setOverride("color", new Color(1, 0, 0));
|
|
171
|
+
*
|
|
172
|
+
* // Override a texture with custom UV transform (useful for lightmaps)
|
|
173
|
+
* block.setOverride("lightMap", myLightmapTexture, {
|
|
174
|
+
* offset: new Vector2(0.5, 0.5),
|
|
175
|
+
* repeat: new Vector2(2, 2)
|
|
176
|
+
* });
|
|
177
|
+
*
|
|
178
|
+
* // Set a shader define
|
|
179
|
+
* block.setDefine("USE_CUSTOM_FEATURE", 1);
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @example Material swapping behavior
|
|
183
|
+
* ```typescript
|
|
184
|
+
* const mesh = new Mesh(geometry, materialA);
|
|
185
|
+
* const block = MaterialPropertyBlock.get(mesh);
|
|
186
|
+
* block.setOverride("color", new Color(1, 0, 0));
|
|
187
|
+
*
|
|
188
|
+
* // The color override is red for materialA
|
|
189
|
+
*
|
|
190
|
+
* // Swap the material - overrides persist and apply to the new material!
|
|
191
|
+
* mesh.material = materialB;
|
|
192
|
+
* // The color override is now red for materialB too
|
|
193
|
+
*
|
|
194
|
+
* // If you don't want overrides on the new material, remove them:
|
|
195
|
+
* block.clearAllOverrides(); // Remove all overrides
|
|
196
|
+
* // or
|
|
197
|
+
* block.removeOveride("color"); // Remove specific override
|
|
198
|
+
* // or
|
|
199
|
+
* block.dispose(); // Remove the entire property block
|
|
200
|
+
* ```
|
|
201
|
+
*
|
|
202
|
+
* @example Lightmap usage
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const block = MaterialPropertyBlock.get(mesh);
|
|
205
|
+
* block.setOverride("lightMap", lightmapTexture);
|
|
206
|
+
* block.setOverride("lightMapIntensity", 1.5);
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* @example See-through effect
|
|
210
|
+
* ```typescript
|
|
211
|
+
* const block = MaterialPropertyBlock.get(mesh);
|
|
212
|
+
* block.setOverride("transparent", true);
|
|
213
|
+
* block.setOverride("opacity", 0.3);
|
|
214
|
+
* ```
|
|
215
|
+
*
|
|
216
|
+
* @template T The material type this property block is associated with
|
|
217
|
+
*/
|
|
98
218
|
export class MaterialPropertyBlock<T extends Material = Material> {
|
|
99
219
|
private _overrides: PropertyBlockOverride[] = [];
|
|
100
220
|
private _defines: Record<string, string | number | boolean> = {};
|
|
101
221
|
private _object: Object3D | null = null;
|
|
102
222
|
|
|
223
|
+
/** The object this property block is attached to */
|
|
103
224
|
get object(): Object3D | null { return this._object; }
|
|
104
225
|
|
|
105
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Creates a new MaterialPropertyBlock
|
|
228
|
+
* @param object The object this property block is for (optional)
|
|
229
|
+
*/
|
|
230
|
+
protected constructor(object: Object3D | null = null) {
|
|
106
231
|
this._object = object;
|
|
107
232
|
}
|
|
108
233
|
|
|
234
|
+
/**
|
|
235
|
+
* Gets or creates a MaterialPropertyBlock for the given object.
|
|
236
|
+
* This is the recommended way to obtain a property block instance.
|
|
237
|
+
*
|
|
238
|
+
* @template T The material type
|
|
239
|
+
* @param object The object to get/create a property block for
|
|
240
|
+
* @returns The MaterialPropertyBlock associated with this object
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```typescript
|
|
244
|
+
* const block = MaterialPropertyBlock.get(myMesh);
|
|
245
|
+
* block.setOverride("roughness", 0.5);
|
|
246
|
+
* ```
|
|
247
|
+
*/
|
|
109
248
|
static get<T extends Material = Material>(object: Object3D): MaterialPropertyBlock<T> {
|
|
110
249
|
let block = registry.getBlock(object);
|
|
111
250
|
if (!block) {
|
|
@@ -116,6 +255,20 @@ export class MaterialPropertyBlock<T extends Material = Material> {
|
|
|
116
255
|
return block as MaterialPropertyBlock<T>;
|
|
117
256
|
}
|
|
118
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Checks if an object has any property overrides
|
|
260
|
+
* @param object The object to check
|
|
261
|
+
* @returns True if the object has a property block with overrides
|
|
262
|
+
*/
|
|
263
|
+
static hasOverrides(object: Object3D): boolean {
|
|
264
|
+
const block = registry.getBlock(object);
|
|
265
|
+
return block ? block.hasOverrides() : false;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Disposes this property block and cleans up associated resources.
|
|
270
|
+
* After calling dispose, this property block should not be used.
|
|
271
|
+
*/
|
|
119
272
|
dispose() {
|
|
120
273
|
if (this._object) {
|
|
121
274
|
registry.deleteBlock(this._object);
|
|
@@ -125,6 +278,29 @@ export class MaterialPropertyBlock<T extends Material = Material> {
|
|
|
125
278
|
this._object = null;
|
|
126
279
|
}
|
|
127
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Sets or updates a material property override.
|
|
283
|
+
* The override will be applied to the material during rendering.
|
|
284
|
+
*
|
|
285
|
+
* @param name The name of the material property to override (e.g., "color", "map", "roughness")
|
|
286
|
+
* @param value The value to set
|
|
287
|
+
* @param textureTransform Optional UV transform (only used when value is a Texture)
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```typescript
|
|
291
|
+
* // Override a simple property
|
|
292
|
+
* block.setOverride("roughness", 0.8);
|
|
293
|
+
*
|
|
294
|
+
* // Override a color
|
|
295
|
+
* block.setOverride("color", new Color(0xff0000));
|
|
296
|
+
*
|
|
297
|
+
* // Override a texture with UV transform
|
|
298
|
+
* block.setOverride("map", texture, {
|
|
299
|
+
* offset: new Vector2(0, 0),
|
|
300
|
+
* repeat: new Vector2(2, 2)
|
|
301
|
+
* });
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
128
304
|
setOverride<K extends NonFunctionPropertyNames<T>>(name: K, value: T[K], textureTransform?: TextureTransform): void;
|
|
129
305
|
setOverride(name: string, value: MaterialPropertyType, textureTransform?: TextureTransform): void;
|
|
130
306
|
setOverride(name: string, value: MaterialPropertyType, textureTransform?: TextureTransform): void {
|
|
@@ -137,36 +313,144 @@ export class MaterialPropertyBlock<T extends Material = Material> {
|
|
|
137
313
|
}
|
|
138
314
|
}
|
|
139
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Gets the override for a specific property with type-safe value inference
|
|
318
|
+
* @param name The property name to get
|
|
319
|
+
* @returns The PropertyBlockOverride with correctly typed value if it exists, undefined otherwise
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```typescript
|
|
323
|
+
* const block = MaterialPropertyBlock.get<MeshStandardMaterial>(mesh);
|
|
324
|
+
*
|
|
325
|
+
* // Value is inferred as number | undefined
|
|
326
|
+
* const roughness = block.getOverride("roughness")?.value;
|
|
327
|
+
*
|
|
328
|
+
* // Value is inferred as Color | undefined
|
|
329
|
+
* const color = block.getOverride("color")?.value;
|
|
330
|
+
*
|
|
331
|
+
* // Value is inferred as Texture | null | undefined
|
|
332
|
+
* const map = block.getOverride("map")?.value;
|
|
333
|
+
*
|
|
334
|
+
* // Explicitly specify the type for properties not on the base material type
|
|
335
|
+
* const transmission = block.getOverride<number>("transmission")?.value;
|
|
336
|
+
*
|
|
337
|
+
* // Or use a more specific material type
|
|
338
|
+
* const physicalBlock = block as MaterialPropertyBlock<MeshPhysicalMaterial>;
|
|
339
|
+
* const transmissionTyped = physicalBlock.getOverride("transmission")?.value; // number
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
getOverride<K extends NonFunctionPropertyNames<T>>(name: K): PropertyBlockOverride<T[K] & MaterialPropertyType> | undefined;
|
|
343
|
+
getOverride<V extends MaterialPropertyType = MaterialPropertyType>(name: string): PropertyBlockOverride<V> | undefined;
|
|
140
344
|
getOverride(name: string): PropertyBlockOverride | undefined {
|
|
141
345
|
return this._overrides.find(o => o.name === name);
|
|
142
346
|
}
|
|
143
347
|
|
|
144
|
-
|
|
348
|
+
/**
|
|
349
|
+
* Removes a specific property override.
|
|
350
|
+
* After removal, the material will use its original property value for this property.
|
|
351
|
+
*
|
|
352
|
+
* @param name The property name to remove the override for
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```typescript
|
|
356
|
+
* const block = MaterialPropertyBlock.get(mesh);
|
|
357
|
+
*
|
|
358
|
+
* // Set some overrides
|
|
359
|
+
* block.setOverride("color", new Color(1, 0, 0));
|
|
360
|
+
* block.setOverride("roughness", 0.5);
|
|
361
|
+
* block.setOverride("lightMap", lightmapTexture);
|
|
362
|
+
*
|
|
363
|
+
* // Remove a specific override - the material will now use its original color
|
|
364
|
+
* block.removeOveride("color");
|
|
365
|
+
*
|
|
366
|
+
* // Other overrides (roughness, lightMap) remain active
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
removeOveride<K extends NonFunctionPropertyNames<T>>(name: K | ({} & string)): void {
|
|
145
370
|
const index = this._overrides.findIndex(o => o.name === name);
|
|
146
371
|
if (index >= 0) {
|
|
147
372
|
this._overrides.splice(index, 1);
|
|
148
373
|
}
|
|
149
374
|
}
|
|
150
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Removes all property overrides from this block.
|
|
378
|
+
* After calling this, the material will use its original values for all properties.
|
|
379
|
+
*
|
|
380
|
+
* **Note**: This does NOT remove shader defines. Use {@link clearDefine} or {@link dispose} for that.
|
|
381
|
+
*
|
|
382
|
+
* @example Remove all overrides but keep the property block
|
|
383
|
+
* ```typescript
|
|
384
|
+
* const block = MaterialPropertyBlock.get(mesh);
|
|
385
|
+
*
|
|
386
|
+
* // Set multiple overrides
|
|
387
|
+
* block.setOverride("color", new Color(1, 0, 0));
|
|
388
|
+
* block.setOverride("roughness", 0.5);
|
|
389
|
+
* block.setOverride("lightMap", lightmapTexture);
|
|
390
|
+
*
|
|
391
|
+
* // Later, remove all overrides at once
|
|
392
|
+
* block.clearAllOverrides();
|
|
393
|
+
*
|
|
394
|
+
* // The material now uses its original values
|
|
395
|
+
* // The property block still exists and can be reused with new overrides
|
|
396
|
+
* ```
|
|
397
|
+
*
|
|
398
|
+
* @example Temporarily disable all overrides
|
|
399
|
+
* ```typescript
|
|
400
|
+
* const block = MaterialPropertyBlock.get(mesh);
|
|
401
|
+
*
|
|
402
|
+
* // Save current overrides if you want to restore them later
|
|
403
|
+
* const savedOverrides = [...block.overrides];
|
|
404
|
+
*
|
|
405
|
+
* // Clear all overrides temporarily
|
|
406
|
+
* block.clearAllOverrides();
|
|
407
|
+
*
|
|
408
|
+
* // Do some rendering without overrides...
|
|
409
|
+
*
|
|
410
|
+
* // Restore overrides
|
|
411
|
+
* savedOverrides.forEach(override => {
|
|
412
|
+
* block.setOverride(override.name, override.value, override.textureTransform);
|
|
413
|
+
* });
|
|
414
|
+
* ```
|
|
415
|
+
*
|
|
416
|
+
* @see {@link removeOveride} - To remove a single override
|
|
417
|
+
* @see {@link dispose} - To completely remove the property block and clean up resources
|
|
418
|
+
*/
|
|
151
419
|
clearAllOverrides(): void {
|
|
152
420
|
this._overrides = [];
|
|
153
421
|
}
|
|
154
422
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
get overrides(): PropertyBlockOverride[] {
|
|
423
|
+
/**
|
|
424
|
+
* Gets all property overrides as a readonly array
|
|
425
|
+
* @returns Array of all property overrides
|
|
426
|
+
*/
|
|
427
|
+
get overrides(): readonly PropertyBlockOverride[] {
|
|
160
428
|
return this._overrides;
|
|
161
429
|
}
|
|
162
430
|
|
|
431
|
+
/**
|
|
432
|
+
* Checks if this property block has any overrides
|
|
433
|
+
* @returns True if there are any overrides set
|
|
434
|
+
*/
|
|
163
435
|
hasOverrides(): boolean {
|
|
164
436
|
return this._overrides.length > 0;
|
|
165
437
|
}
|
|
166
438
|
|
|
167
439
|
/**
|
|
168
|
-
* Set a shader define that will be included in the program cache key
|
|
169
|
-
* This allows different objects sharing the same material to have different shader programs
|
|
440
|
+
* Set a shader define that will be included in the program cache key.
|
|
441
|
+
* This allows different objects sharing the same material to have different shader programs.
|
|
442
|
+
*
|
|
443
|
+
* Defines affect shader compilation and are useful for enabling/disabling features per-object.
|
|
444
|
+
*
|
|
445
|
+
* @param name The define name (e.g., "USE_LIGHTMAP", "ENABLE_REFLECTIONS")
|
|
446
|
+
* @param value The define value (typically a boolean, number, or string)
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```typescript
|
|
450
|
+
* // Enable a feature for this specific object
|
|
451
|
+
* block.setDefine("USE_CUSTOM_SHADER", true);
|
|
452
|
+
* block.setDefine("QUALITY_LEVEL", 2);
|
|
453
|
+
* ```
|
|
170
454
|
*/
|
|
171
455
|
setDefine(name: string, value: string | number | boolean): void {
|
|
172
456
|
this._defines[name] = value;
|
|
@@ -174,6 +458,7 @@ export class MaterialPropertyBlock<T extends Material = Material> {
|
|
|
174
458
|
|
|
175
459
|
/**
|
|
176
460
|
* Remove a shader define
|
|
461
|
+
* @param name The define name to remove
|
|
177
462
|
*/
|
|
178
463
|
clearDefine(name: string): void {
|
|
179
464
|
this._defines[name] = undefined as any;
|
|
@@ -181,11 +466,20 @@ export class MaterialPropertyBlock<T extends Material = Material> {
|
|
|
181
466
|
|
|
182
467
|
/**
|
|
183
468
|
* Get all defines set on this property block
|
|
469
|
+
* @returns A readonly record of all defines
|
|
184
470
|
*/
|
|
185
471
|
getDefines(): Readonly<Record<string, string | number | boolean>> {
|
|
186
472
|
return this._defines;
|
|
187
473
|
}
|
|
188
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Generates a cache key based on the current overrides and defines.
|
|
477
|
+
* This key is used internally to ensure correct shader program selection
|
|
478
|
+
* when objects share materials but have different property blocks.
|
|
479
|
+
*
|
|
480
|
+
* @returns A string representing the current state of this property block
|
|
481
|
+
* @internal
|
|
482
|
+
*/
|
|
189
483
|
getCacheKey(): string {
|
|
190
484
|
const parts: string[] = [];
|
|
191
485
|
|
|
@@ -227,13 +521,25 @@ export class MaterialPropertyBlock<T extends Material = Material> {
|
|
|
227
521
|
}
|
|
228
522
|
}
|
|
229
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Symbol used to store original material values on the material object
|
|
526
|
+
* @internal
|
|
527
|
+
*/
|
|
230
528
|
const $originalValues = Symbol("originalValues");
|
|
231
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Stores an original material property value before override
|
|
532
|
+
* @internal
|
|
533
|
+
*/
|
|
232
534
|
interface OriginalValue {
|
|
233
535
|
name: string;
|
|
234
536
|
value: unknown;
|
|
235
537
|
}
|
|
236
538
|
|
|
539
|
+
/**
|
|
540
|
+
* Stores saved texture transform state for restoration
|
|
541
|
+
* @internal
|
|
542
|
+
*/
|
|
237
543
|
interface SavedTextureTransform {
|
|
238
544
|
name: string;
|
|
239
545
|
offsetX: number;
|
|
@@ -242,11 +548,22 @@ interface SavedTextureTransform {
|
|
|
242
548
|
repeatY: number;
|
|
243
549
|
}
|
|
244
550
|
|
|
551
|
+
/**
|
|
552
|
+
* Symbol used to store saved texture transforms on the material object
|
|
553
|
+
* @internal
|
|
554
|
+
*/
|
|
245
555
|
const $savedTextureTransforms = Symbol("savedTextureTransforms");
|
|
246
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Type for Three.js object render callbacks
|
|
559
|
+
* @internal
|
|
560
|
+
*/
|
|
247
561
|
type ObjectRenderCallback = (this: Object3D, renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) => void;
|
|
248
562
|
|
|
249
|
-
|
|
563
|
+
/**
|
|
564
|
+
* Collect all materials from an object and its children
|
|
565
|
+
* @internal
|
|
566
|
+
*/
|
|
250
567
|
function collectMaterials(object: Object3D, materials: Set<Material>): void {
|
|
251
568
|
const obj = object as Object3D & { material?: Material | Material[] };
|
|
252
569
|
if (obj.material) {
|
|
@@ -263,8 +580,14 @@ function collectMaterials(object: Object3D, materials: Set<Material>): void {
|
|
|
263
580
|
}
|
|
264
581
|
}
|
|
265
582
|
|
|
266
|
-
|
|
267
|
-
|
|
583
|
+
/**
|
|
584
|
+
* Find property block by checking this object and parent (if parent is a Group).
|
|
585
|
+
* Returns both the block and the owner object.
|
|
586
|
+
*
|
|
587
|
+
* @param obj The object to search from
|
|
588
|
+
* @returns The property block and its owner object, or undefined if not found
|
|
589
|
+
* @internal
|
|
590
|
+
*/
|
|
268
591
|
function findPropertyBlockAndOwner(obj: Object3D): { block: MaterialPropertyBlock; owner: Object3D } | undefined {
|
|
269
592
|
// First check if this object itself has a property block
|
|
270
593
|
let block = registry.getBlock(obj);
|
|
@@ -279,8 +602,93 @@ function findPropertyBlockAndOwner(obj: Object3D): { block: MaterialPropertyBloc
|
|
|
279
602
|
return undefined;
|
|
280
603
|
}
|
|
281
604
|
|
|
605
|
+
/**
|
|
606
|
+
* Symbol to track which materials are currently being rendered for an object
|
|
607
|
+
* @internal
|
|
608
|
+
*/
|
|
609
|
+
const currentlyRenderingFlag = Symbol("beforeRenderingFlag");
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Tracks original transparent values that were changed during render list building
|
|
613
|
+
* @internal
|
|
614
|
+
*/
|
|
615
|
+
const beforeRenderListTransparentChanged = new WeakMap<Object3D, boolean>();
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Tracks original transmission values that were changed during render list building
|
|
619
|
+
* @internal
|
|
620
|
+
*/
|
|
621
|
+
const beforeRenderListTransparentChangedTransmission = new WeakMap<Object3D, number>();
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Callback invoked before an object is added to the render list.
|
|
625
|
+
* Used to temporarily override transparency/transmission for correct render list assignment.
|
|
626
|
+
* @internal
|
|
627
|
+
*/
|
|
628
|
+
const onBeforeRenderListPush = function (this: Object3D, _object: Object3D, _geometry: BufferGeometry, material: Material, _group: Group) {
|
|
629
|
+
const block = registry.getBlock(_object);
|
|
630
|
+
if (!block) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
if (block.hasOverrides()) {
|
|
634
|
+
const transmission = block.getOverride<number>("transmission")?.value;
|
|
635
|
+
const transparent = block.getOverride("transparent")?.value;
|
|
636
|
+
|
|
637
|
+
if (transmission !== undefined && typeof transmission === "number" && "transmission" in material && transmission !== material.transmission) {
|
|
638
|
+
beforeRenderListTransparentChangedTransmission.set(this, material.transmission as number);
|
|
639
|
+
material.transmission = transmission;
|
|
640
|
+
}
|
|
641
|
+
if (transparent !== undefined && typeof transparent === "boolean" && transparent !== material.transparent) {
|
|
642
|
+
beforeRenderListTransparentChanged.set(this, material.transparent);
|
|
643
|
+
material.transparent = transparent;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Callback invoked after an object is added to the render list.
|
|
649
|
+
* Restores the original transparency/transmission values that were overridden in onBeforeRenderListPush.
|
|
650
|
+
* @internal
|
|
651
|
+
*/
|
|
652
|
+
const onAfterRenderListPush = function (this: Object3D, _object: Object3D, _geometry: BufferGeometry, material: Material, _group: Group) {
|
|
653
|
+
const prevTransparent = beforeRenderListTransparentChanged.get(_object);
|
|
654
|
+
if (prevTransparent !== undefined) {
|
|
655
|
+
beforeRenderListTransparentChanged.delete(_object);
|
|
656
|
+
material.transparent = prevTransparent;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const prevTransmission = beforeRenderListTransparentChangedTransmission.get(_object);
|
|
660
|
+
if (prevTransmission !== undefined) {
|
|
661
|
+
beforeRenderListTransparentChangedTransmission.delete(_object);
|
|
662
|
+
(material as any).transmission = prevTransmission;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
282
666
|
// #region OnBeforeRender
|
|
667
|
+
/**
|
|
668
|
+
* Main callback invoked before rendering an object.
|
|
669
|
+
* Applies property block overrides and defines to the material.
|
|
670
|
+
* @internal
|
|
671
|
+
*/
|
|
283
672
|
const onBeforeRender_MaterialBlock: ObjectRenderCallback = function (this: Object3D, _renderer: WebGLRenderer, _scene: Scene, _camera: Camera, _geometry: BufferGeometry, material: Material, _group: Group) {
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
// Only run if the material belongs to this object and is a "regular" material (not depth or other override material)
|
|
676
|
+
const materials = (this as any).material as Array<Material> | Material | undefined;
|
|
677
|
+
if (!materials) return;
|
|
678
|
+
if (Array.isArray(materials)) {
|
|
679
|
+
if (!materials.includes(material)) return;
|
|
680
|
+
}
|
|
681
|
+
else if (materials !== material) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Keep track of which materials rendering started for so we can check in onAfterRender if it was processed
|
|
686
|
+
// (in case of override materials like depth material where onBeforeRender runs but we don't want to apply overrides)
|
|
687
|
+
if (this[currentlyRenderingFlag] === undefined) this[currentlyRenderingFlag] = new WeakSet<Material>();
|
|
688
|
+
this[currentlyRenderingFlag].add(material);
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
// Before rendering, check if this object (or its parent Group) has a property block with overrides for this material.
|
|
284
692
|
const result = findPropertyBlockAndOwner(this);
|
|
285
693
|
if (!result) {
|
|
286
694
|
return;
|
|
@@ -289,9 +697,9 @@ const onBeforeRender_MaterialBlock: ObjectRenderCallback = function (this: Objec
|
|
|
289
697
|
const { block: propertyBlock, owner } = result;
|
|
290
698
|
|
|
291
699
|
// Only apply if this material was registered with this property block
|
|
292
|
-
if (!registry.hasMaterial(owner, material)) {
|
|
293
|
-
|
|
294
|
-
}
|
|
700
|
+
// if (!registry.hasMaterial(owner, material)) {
|
|
701
|
+
// return;
|
|
702
|
+
// }
|
|
295
703
|
|
|
296
704
|
const overrides = propertyBlock.overrides;
|
|
297
705
|
const mat = material as any;
|
|
@@ -373,7 +781,19 @@ const onBeforeRender_MaterialBlock: ObjectRenderCallback = function (this: Objec
|
|
|
373
781
|
};
|
|
374
782
|
|
|
375
783
|
// #region OnAfterRender
|
|
784
|
+
/**
|
|
785
|
+
* Main callback invoked after rendering an object.
|
|
786
|
+
* Restores the original material property values and defines.
|
|
787
|
+
* @internal
|
|
788
|
+
*/
|
|
376
789
|
const onAfterRender_MaterialBlock: ObjectRenderCallback = function (this: Object3D, _renderer: WebGLRenderer, _scene: Scene, _camera: Camera, _geometry: BufferGeometry, material: Material, _group: Group) {
|
|
790
|
+
|
|
791
|
+
// We don't want to run this logic if onBeforeRender didn't run for this material (e.g. due to DepthMaterial or other override material), so we check the flag set in onBeforeRender
|
|
792
|
+
if (this[currentlyRenderingFlag] === undefined) return;
|
|
793
|
+
if (!this[currentlyRenderingFlag].has(material)) return;
|
|
794
|
+
this[currentlyRenderingFlag].delete(material);
|
|
795
|
+
|
|
796
|
+
|
|
377
797
|
const result = findPropertyBlockAndOwner(this);
|
|
378
798
|
if (!result) {
|
|
379
799
|
return;
|
|
@@ -382,9 +802,9 @@ const onAfterRender_MaterialBlock: ObjectRenderCallback = function (this: Object
|
|
|
382
802
|
const { block: propertyBlock, owner } = result;
|
|
383
803
|
|
|
384
804
|
// Only restore if this material was registered with this property block
|
|
385
|
-
if (!registry.hasMaterial(owner, material)) {
|
|
386
|
-
|
|
387
|
-
}
|
|
805
|
+
// if (!registry.hasMaterial(owner, material)) {
|
|
806
|
+
// return;
|
|
807
|
+
// }
|
|
388
808
|
|
|
389
809
|
const overrides = propertyBlock.overrides;
|
|
390
810
|
|
|
@@ -446,25 +866,38 @@ const onAfterRender_MaterialBlock: ObjectRenderCallback = function (this: Object
|
|
|
446
866
|
|
|
447
867
|
|
|
448
868
|
// #region Attach Callbacks
|
|
869
|
+
/**
|
|
870
|
+
* Attaches the property block render callbacks to an object and its child meshes.
|
|
871
|
+
* @param object The object to attach callbacks to
|
|
872
|
+
* @param _propertyBlock The property block being attached (unused but kept for clarity)
|
|
873
|
+
* @internal
|
|
874
|
+
*/
|
|
449
875
|
function attachPropertyBlockToObject(object: Object3D, _propertyBlock: MaterialPropertyBlock): void {
|
|
450
876
|
// Collect and register all materials that belong to this property block
|
|
451
|
-
const materials = new Set<Material>();
|
|
452
|
-
collectMaterials(object, materials);
|
|
453
|
-
materials.forEach(mat => registry.addMaterial(object, mat));
|
|
877
|
+
// const materials = new Set<Material>();
|
|
878
|
+
// collectMaterials(object, materials);
|
|
879
|
+
// materials.forEach(mat => registry.addMaterial(object, mat));
|
|
454
880
|
|
|
455
881
|
// Attach callbacks to renderable objects (Mesh, SkinnedMesh)
|
|
456
882
|
// Groups don't render themselves but we still need to handle child meshes
|
|
457
883
|
if (object.type === "Group") {
|
|
458
884
|
object.children.forEach(child => {
|
|
459
885
|
if (child.type === "Mesh" || child.type === "SkinnedMesh") {
|
|
460
|
-
attachCallbacksToMesh(child, object);
|
|
886
|
+
attachCallbacksToMesh(child, object, _propertyBlock);
|
|
461
887
|
}
|
|
462
888
|
});
|
|
463
889
|
} else if (object.type === "Mesh" || object.type === "SkinnedMesh") {
|
|
464
|
-
attachCallbacksToMesh(object, object);
|
|
890
|
+
attachCallbacksToMesh(object, object, _propertyBlock);
|
|
465
891
|
}
|
|
466
892
|
}
|
|
467
|
-
|
|
893
|
+
/**
|
|
894
|
+
* Attaches render callbacks to a specific mesh object.
|
|
895
|
+
* Chains with existing callbacks if they exist.
|
|
896
|
+
* @param mesh The mesh to attach callbacks to
|
|
897
|
+
* @param propertyBlockOwner The object that owns the property block (may be the mesh itself or its parent Group)
|
|
898
|
+
* @internal
|
|
899
|
+
*/
|
|
900
|
+
function attachCallbacksToMesh(mesh: Object3D, propertyBlockOwner: Object3D, _propertyBlock: MaterialPropertyBlock): void {
|
|
468
901
|
// Check if this specific mesh already has our callbacks attached for this property block owner
|
|
469
902
|
if (registry.isHooked(mesh, propertyBlockOwner)) {
|
|
470
903
|
// Already hooked for this property block owner
|
|
@@ -473,6 +906,11 @@ function attachCallbacksToMesh(mesh: Object3D, propertyBlockOwner: Object3D): vo
|
|
|
473
906
|
|
|
474
907
|
registry.addHook(mesh, propertyBlockOwner);
|
|
475
908
|
|
|
909
|
+
/**
|
|
910
|
+
* Expose the property block for e.g. Needle Inspector
|
|
911
|
+
*/
|
|
912
|
+
mesh["needle:materialPropertyBlock"] = _propertyBlock;
|
|
913
|
+
|
|
476
914
|
if (!mesh.onBeforeRender) {
|
|
477
915
|
mesh.onBeforeRender = onBeforeRender_MaterialBlock;
|
|
478
916
|
} else {
|
|
@@ -492,9 +930,28 @@ function attachCallbacksToMesh(mesh: Object3D, propertyBlockOwner: Object3D): vo
|
|
|
492
930
|
original.call(this, renderer, scene, camera, geometry, material, group);
|
|
493
931
|
};
|
|
494
932
|
}
|
|
933
|
+
|
|
934
|
+
/** @ts-ignore patched in three.js */
|
|
935
|
+
mesh.onBeforeRenderListPush = onBeforeRenderListPush;
|
|
936
|
+
/** @ts-ignore patched in three.js */
|
|
937
|
+
mesh.onAfterRenderListPush = onAfterRenderListPush;
|
|
938
|
+
|
|
495
939
|
}
|
|
496
940
|
//#endregion
|
|
497
941
|
|
|
942
|
+
/**
|
|
943
|
+
* Checks if an object has a MaterialPropertyBlock attached to it.
|
|
944
|
+
*
|
|
945
|
+
* @param object The object to check
|
|
946
|
+
* @returns True if the object has a property block registered
|
|
947
|
+
*
|
|
948
|
+
* @example
|
|
949
|
+
* ```typescript
|
|
950
|
+
* if (objectHasPropertyBlock(myMesh)) {
|
|
951
|
+
* console.log("This mesh has property overrides");
|
|
952
|
+
* }
|
|
953
|
+
* ```
|
|
954
|
+
*/
|
|
498
955
|
export function objectHasPropertyBlock(object: Object3D): boolean {
|
|
499
956
|
return registry.getBlock(object) !== undefined;
|
|
500
957
|
}
|