@volter/blender-engine 0.1.89 → 0.1.91

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.
@@ -207,6 +207,25 @@ function channelsFor(compiled: CompiledGraph, geometry: THREE.BufferGeometry): R
207
207
  return channels;
208
208
  }
209
209
 
210
+ /** Compare the actual named-layer inputs without allocating and serializing
211
+ * two channel maps on every draw. Missing layers still get their zero buffer,
212
+ * including when this geometry first shares an already-bound material. */
213
+ function channelsMatch(compiled: CompiledGraph, channels: Record<string, number>, geometry: THREE.BufferGeometry): boolean {
214
+ const named = geometry.userData['blenderUvChannels'] as Record<string, number> | undefined;
215
+ const count = geometry.getAttribute('position')?.count ?? 0;
216
+ let same = Object.keys(channels).length === compiled.uvs.length;
217
+ for (const name of compiled.uvs) {
218
+ let channel = name === '' ? 0 : named?.[name];
219
+ if (channel === undefined) {
220
+ channel = 9;
221
+ if (geometry.getAttribute('uv9')?.count !== count)
222
+ geometry.setAttribute('uv9', new THREE.BufferAttribute(new Float32Array(count * 2), 2));
223
+ }
224
+ if (channels[name] !== channel) same = false;
225
+ }
226
+ return same;
227
+ }
228
+
210
229
  /** Per draw: a pending program started compiling (see `Pending`), a compiled
211
230
  * one swapped in, a refused one dropped, and the channel of each named layer
212
231
  * on this geometry. `shadowOf` makes the proxy a program compiles through:
@@ -261,9 +280,8 @@ export function bindGraphDraw(material: THREE.MeshPhysicalMaterial, geometry: TH
261
280
  `so it is drawn with its constant values. ${first}`);
262
281
  return;
263
282
  }
264
- const channels = channelsFor(binding.compiled, geometry);
265
- if (JSON.stringify(channels) !== JSON.stringify(binding.channels)) {
266
- binding.channels = channels;
283
+ if (!channelsMatch(binding.compiled, binding.channels, geometry)) {
284
+ binding.channels = channelsFor(binding.compiled, geometry);
267
285
  material.needsUpdate = true;
268
286
  }
269
287
  }
@@ -281,6 +281,7 @@ export class BlenderTransparentInstances {
281
281
  for (const n of this.projection.elements) this.observe(n);
282
282
  for (const n of this.root.matrixWorld.elements) this.observe(n);
283
283
  const observedGeometry = new Set<THREE.BufferGeometry>();
284
+ const observedMaterials = new Set<THREE.Material>();
284
285
  for (const mesh of this.objects) {
285
286
  // Check eligibility every frame, including opaque -> transparent
286
287
  // transitions, but opaque geometry/attribute versions cannot affect a
@@ -323,17 +324,23 @@ export class BlenderTransparentInstances {
323
324
  this.observe(sphere?.center.z ?? 0); this.observe(sphere?.radius ?? -1);
324
325
  const materials = Array.isArray(mesh.material) ? mesh.material : null;
325
326
  this.observe(materials?.length ?? -1);
326
- if (materials) for (const material of materials) this.observeMaterial(material);
327
- else this.observeMaterial(mesh.material as THREE.Material);
327
+ if (materials) for (const material of materials) this.observeMaterial(material, observedMaterials);
328
+ else this.observeMaterial(mesh.material as THREE.Material, observedMaterials);
328
329
  }
329
330
  this.stateChanged ||= this.planState.length !== this.stateIndex;
330
331
  this.planState.length = this.stateIndex;
331
332
  return this.stateChanged;
332
333
  }
333
334
 
334
- private observeMaterial(material: THREE.Material): void {
335
+ private observeMaterial(material: THREE.Material, observed: Set<THREE.Material>): void {
336
+ // Every slot still observes its identity. Shared state needs one exact
337
+ // census per material, rather than thousands of identical hook/value
338
+ // comparisons for repeated foliage using the same material.
339
+ this.observe(material.uuid);
340
+ if (observed.has(material)) return;
341
+ observed.add(material);
335
342
  this.observe(materialDrawHooksSupported(material) ? 1 : 0);
336
- this.observe(material.uuid); this.observe(material.version);
343
+ this.observe(material.version);
337
344
  this.observe(material.visible ? 1 : 0); this.observe(material.transparent ? 1 : 0);
338
345
  this.observe(material.side); this.observe(material.forceSinglePass ? 1 : 0);
339
346
  this.observe((material as THREE.MeshPhysicalMaterial).transmission ?? 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.89",
5
+ "version": "0.1.91",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },