@volter/blender-engine 0.1.84 → 0.1.86

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.
@@ -28,6 +28,16 @@ const uniforms = new WeakMap<THREE.MeshPhysicalMaterial, {
28
28
  blenderMapClip: {value: boolean}; blenderRoughnessClip: {value: boolean}; blenderNormalClip: {value: boolean};
29
29
  blenderWorldExtinction: {value: THREE.Vector3};
30
30
  }>();
31
+ const drawHooks = new WeakMap<THREE.Material, Pick<THREE.Material, 'onBeforeRender' | 'onBeforeCompile' | 'customProgramCacheKey'>>();
32
+
33
+ /** Internal draws can preserve the engine's known hooks or Three's defaults.
34
+ * A later custom hook may depend on the original object/material identity. */
35
+ export function materialDrawHooksSupported(material: THREE.Material): boolean {
36
+ const hooks = drawHooks.get(material) ?? THREE.Material.prototype;
37
+ return material.onBeforeRender === hooks.onBeforeRender &&
38
+ material.onBeforeCompile === hooks.onBeforeCompile &&
39
+ material.customProgramCacheKey === hooks.customProgramCacheKey;
40
+ }
31
41
 
32
42
  export function applyWorldExtinction(material: THREE.MeshPhysicalMaterial, extinction: THREE.Vector3): void {
33
43
  if (!uniforms.has(material)) applyPhysicalMaterial(material);
@@ -59,7 +69,7 @@ function graphShadow(material: THREE.MeshPhysicalMaterial): THREE.MeshPhysicalMa
59
69
  /** Copy physical values into an independently cached draw form. Textures and
60
70
  * graph uniform values remain borrowed from the authoritative source material. */
61
71
  export function syncPhysicalDrawMaterial(source: THREE.Material, target?: THREE.MeshPhysicalMaterial): THREE.MeshPhysicalMaterial | null {
62
- if (!(source instanceof THREE.MeshPhysicalMaterial) || !uniforms.has(source)) return null;
72
+ if (!(source instanceof THREE.MeshPhysicalMaterial) || !uniforms.has(source) || !materialDrawHooksSupported(source)) return null;
63
73
  const copy = target ?? graphShadow(source);
64
74
  if (!borrowGraphDrawBinding(source, copy)) {
65
75
  if (!target) copy.dispose();
@@ -180,6 +190,8 @@ export function applyPhysicalMaterial(material: THREE.MeshPhysicalMaterial, inpu
180
190
  // surface inputs it carries (`blender-graph-material.ts`).
181
191
  applyGraphShader(material, shader);
182
192
  };
193
+ drawHooks.set(material, {onBeforeRender: material.onBeforeRender,
194
+ onBeforeCompile: material.onBeforeCompile, customProgramCacheKey: material.customProgramCacheKey});
183
195
  material.needsUpdate = true;
184
196
  }
185
197
  values.blenderCoatIor.value = Math.max(1, data.coat_ior);
@@ -1,5 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import { graphDrawAttributes } from './blender-graph-material';
3
+ import { materialDrawHooksSupported } from './blender-physical-material';
3
4
 
4
5
  // Small spatial batches retain frustum rejection without duplicating geometry.
5
6
  const BATCH_SIZE = 64;
@@ -12,6 +13,11 @@ function sameMaterials(a: THREE.Material | THREE.Material[], b: THREE.Material |
12
13
  : a === b;
13
14
  }
14
15
 
16
+ export function instanceObjectHooksSupported(object: THREE.Object3D): boolean {
17
+ return object.onBeforeRender === THREE.Object3D.prototype.onBeforeRender &&
18
+ object.onAfterRender === THREE.Object3D.prototype.onAfterRender;
19
+ }
20
+
15
21
  export function instanceObjectShown(object: THREE.Object3D): boolean {
16
22
  for (let node: THREE.Object3D | null = object; node; node = node.parent)
17
23
  if (!node.visible) return false;
@@ -60,12 +66,13 @@ export class BlenderRuntimeInstances {
60
66
  for (const object of objects) {
61
67
  const mesh = object as THREE.Mesh;
62
68
  if (!mesh.isMesh) continue;
69
+ if (!instanceObjectHooksSupported(mesh)) { exclude('customHooks'); continue; }
63
70
  if ((mesh as THREE.SkinnedMesh).isSkinnedMesh ||
64
71
  (mesh as THREE.InstancedMesh).isInstancedMesh || mesh.morphTargetInfluences ||
65
72
  mesh.layers.mask !== 1) { exclude('deformationOrLayers'); continue; }
66
73
  const materials = Array.isArray(mesh.material) ? mesh.material : [mesh.material];
67
74
  if (materials.some(m => m.transparent || (m as THREE.MeshPhysicalMaterial).transmission > 0 ||
68
- (m as THREE.ShaderMaterial).isShaderMaterial)) { exclude('material'); continue; }
75
+ (m as THREE.ShaderMaterial).isShaderMaterial || !materialDrawHooksSupported(m))) { exclude('material'); continue; }
69
76
  // Four slots for instanceMatrix, at most twelve for each actual program.
70
77
  // Count active inputs, NOT every stored authored layer: retaining unused
71
78
  // UVs/colours must not disable batching. No channel is deleted to fit.
@@ -125,6 +132,8 @@ export class BlenderRuntimeInstances {
125
132
  if (!this.batches.length) return;
126
133
  this.inverse.copy(this.root.matrixWorld).invert();
127
134
  for (const {draw, members} of this.batches) {
135
+ const materials = Array.isArray(draw.material) ? draw.material : [draw.material];
136
+ const supportedHooks = materials.every(materialDrawHooksSupported);
128
137
  let count = 0;
129
138
  let changed = false;
130
139
  for (const member of members) {
@@ -133,6 +142,7 @@ export class BlenderRuntimeInstances {
133
142
  // A drag can introduce negative scale/shear, or a skin can replace a
134
143
  // mesh between frames. Fall back immediately without touching its data.
135
144
  const compatible = mesh.parent !== null && mesh.geometry === draw.geometry &&
145
+ supportedHooks && instanceObjectHooksSupported(mesh) &&
136
146
  sameMaterials(mesh.material, draw.material) && instanceMatrixSupported(this.matrix);
137
147
  if (!compatible) {
138
148
  mesh.layers.mask |= member.layers;
@@ -3,7 +3,8 @@ import {BlenderOrderedBatches, ORDERED_BATCH_CAPACITY} from './blender-ordered-b
3
3
  import {BlenderDrawMaterials} from './blender-draw-materials';
4
4
  import {presenterChanged} from './blender-presenter-change';
5
5
  import {graphDrawAttributes} from './blender-graph-material';
6
- import {instanceMatrixSupported, instanceObjectShown} from './blender-runtime-instances';
6
+ import {materialDrawHooksSupported} from './blender-physical-material';
7
+ import {instanceMatrixSupported, instanceObjectShown, instanceObjectHooksSupported} from './blender-runtime-instances';
7
8
 
8
9
  const CAPACITY = ORDERED_BATCH_CAPACITY;
9
10
  type Entry = {mesh: THREE.Mesh; material: THREE.Material | null; key: string | null; batchKey: string | null; groupOrder: number; z: number; centre: THREE.Vector3};
@@ -112,6 +113,7 @@ export class BlenderTransparentInstances {
112
113
  const candidate = materials.length === 1 ? materials[0]! : null;
113
114
  const order = groupOrder(mesh);
114
115
  if (candidate && candidate.visible && candidate.transparent &&
116
+ materialDrawHooksSupported(candidate) && instanceObjectHooksSupported(mesh) &&
115
117
  !(candidate as THREE.ShaderMaterial).isShaderMaterial &&
116
118
  !((candidate as THREE.MeshPhysicalMaterial).transmission > 0) &&
117
119
  (candidate.side !== THREE.DoubleSide || candidate.forceSinglePass) &&
@@ -291,6 +293,7 @@ export class BlenderTransparentInstances {
291
293
  this.observe(mesh.renderOrder);
292
294
  this.observe(groupOrder(mesh));
293
295
  this.observe(mesh.frustumCulled ? 1 : 0);
296
+ this.observe(instanceObjectHooksSupported(mesh) ? 1 : 0);
294
297
  this.observe(mesh.morphTargetInfluences ? 1 : 0);
295
298
  for (const n of mesh.matrixWorld.elements) this.observe(n);
296
299
  const geometry = mesh.geometry;
@@ -329,6 +332,7 @@ export class BlenderTransparentInstances {
329
332
  }
330
333
 
331
334
  private observeMaterial(material: THREE.Material): void {
335
+ this.observe(materialDrawHooksSupported(material) ? 1 : 0);
332
336
  this.observe(material.uuid); this.observe(material.version);
333
337
  this.observe(material.visible ? 1 : 0); this.observe(material.transparent ? 1 : 0);
334
338
  this.observe(material.side); this.observe(material.forceSinglePass ? 1 : 0);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@volter/blender-engine",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "GPL-3.0-or-later",
5
- "version": "0.1.84",
5
+ "version": "0.1.86",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },