@volter/blender-engine 0.1.135 → 0.1.137

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.
@@ -1216,11 +1216,16 @@ def _depsgraph_placements(frame, depsgraph):
1216
1216
  continue
1217
1217
  if not instance.show_self:
1218
1218
  continue
1219
+ # AN UNSUPPORTED PLACEMENT IS LEFT OUT, NAMED ONCE (`warn`): raising here
1220
+ # lost the whole frame, so one odd instance kept the entire scene from
1221
+ # opening. The native exporter skips the same duplis.
1219
1222
  if (instance.instance_object is not None and instance.object.data != instance.instance_object.data and
1220
1223
  (instance.object.data is None or geometry.get(source.name) != instance.object.data.as_pointer())):
1221
- raise NotImplementedError("Depsgraph instance geometry differs from its native source: %s" % source.name)
1224
+ warn("Depsgraph instances of %r are not drawn: their geometry differs from the exported source" % source.name)
1225
+ continue
1222
1226
  if row is None or (source.type == "MESH" and row["mesh"] is None):
1223
- raise NotImplementedError("Depsgraph instance source has no native exported geometry: %s" % source.name)
1227
+ warn("Depsgraph instances of %r are not drawn: the source has no exported geometry" % source.name)
1228
+ continue
1224
1229
  if row["mesh"] is None:
1225
1230
  if source.type not in ("EMPTY", "CAMERA"):
1226
1231
  warn("Depsgraph instance %r of type %s is not drawn" % (source.name, source.type))
@@ -1228,7 +1233,8 @@ def _depsgraph_placements(frame, depsgraph):
1228
1233
  owner = instance.parent.original if instance.parent is not None else None
1229
1234
  owner_row = by_name.get(owner.name) if owner is not None else None
1230
1235
  if owner is not None and owner_row is None:
1231
- raise NotImplementedError("Depsgraph instance owner is not exported: %s" % owner.name)
1236
+ warn("Depsgraph instances owned by %r are not drawn: the owner is not exported" % owner.name)
1237
+ continue
1232
1238
  persistent = list(instance.persistent_id)
1233
1239
  particle_index = persistent[0]
1234
1240
  psys = instance.particle_system
@@ -1687,7 +1693,11 @@ class Session:
1687
1693
  for row in frame["objects"]:
1688
1694
  if not any(m in graphs for m in row["materials"] if m is not None):
1689
1695
  continue
1690
- obj = by_name.get(row["name"])
1696
+ # An instance source from a collection outside the scene is exported
1697
+ # too (`instance_sources`) and shares these graphs; its Object Info
1698
+ # and Particle Info inputs come from the file's own objects. Only
1699
+ # those few rows miss the scene's map.
1700
+ obj = by_name.get(row["name"]) or bpy.data.objects.get(row["name"])
1691
1701
  if obj is not None:
1692
1702
  row["object_info"] = {
1693
1703
  "color": [float(v) for v in obj.color],
@@ -7,7 +7,7 @@ import {z} from 'zod';
7
7
  import {applyGraphShader, bindGraphDraw, bindPaletteGraph, borrowGraphDrawBinding, graphProgramKey, graphUvChannels, setGraphViewport} from './blender-graph-material';
8
8
  import type {CompiledGraph} from './blender-node-graph';
9
9
  import {bindNamedUvChannels} from './blender-texture-samplers';
10
- import {copyWorldVisibility,worldVisibilityShader,worldVisibilityUniforms} from './blender-world-visibility';
10
+ import {WORLD_VISIBILITY,copyWorldVisibility,worldVisibilityShader,worldVisibilityUniforms} from './blender-world-visibility';
11
11
 
12
12
  const scalar = z.number().finite();
13
13
  const color = z.tuple([scalar, scalar, scalar]);
@@ -195,7 +195,7 @@ export function applyPhysicalMaterial(material: THREE.MeshPhysicalMaterial, inpu
195
195
  blenderWorldExtinction: {value: new THREE.Vector3()}};
196
196
  uniforms.set(material, values);
197
197
  const held = values;
198
- material.customProgramCacheKey = () => `blender-principled-physical-v6${graphProgramKey(material)}`;
198
+ material.customProgramCacheKey = () => `blender-principled-physical-v6${WORLD_VISIBILITY ? '-atlas' : ''}${graphProgramKey(material)}`;
199
199
  material.onBeforeRender = (renderer, scene, camera, geometry, object) => {
200
200
  bindNamedUvChannels(material, geometry);
201
201
  bindGraphDraw(material, geometry, renderer, scene, camera, object, graphShadow);
@@ -213,12 +213,16 @@ export function applyPhysicalMaterial(material: THREE.MeshPhysicalMaterial, inpu
213
213
  .map(i => (i <= 3 ? `#ifndef USE_UV${i}\nattribute vec2 uv${i};\n#endif` : `attribute vec2 uv${i};`))
214
214
  .join('\n') + '\n' + shader.vertexShader;
215
215
  Object.assign(shader.uniforms, held);
216
- worldVisibilityUniforms(material,shader.uniforms);
217
- shader.vertexShader='varying vec3 blenderWorldPosition;\n'+shader.vertexShader;
218
- shader.vertexShader=shader.vertexShader.replace('#include <worldpos_vertex>',
219
- '#include <worldpos_vertex>\n#if defined(USE_ENVMAP) || defined(USE_SHADOWMAP) || defined(USE_TRANSMISSION)\nblenderWorldPosition=worldPosition.xyz;\n#else\nblenderWorldPosition=(modelMatrix*vec4(transformed,1.)).xyz;\n#endif');
220
- shader.fragmentShader=shader.fragmentShader.replace('#include <envmap_physical_pars_fragment>',
221
- '#include <envmap_physical_pars_fragment>\n'+worldVisibilityShader);
216
+ // The sky-light atlas exists in the shader only while it is on: its three
217
+ // samplers would count against every material's sixteen (WORLD_VISIBILITY).
218
+ if (WORLD_VISIBILITY) {
219
+ worldVisibilityUniforms(material,shader.uniforms);
220
+ shader.vertexShader='varying vec3 blenderWorldPosition;\n'+shader.vertexShader;
221
+ shader.vertexShader=shader.vertexShader.replace('#include <worldpos_vertex>',
222
+ '#include <worldpos_vertex>\n#if defined(USE_ENVMAP) || defined(USE_SHADOWMAP) || defined(USE_TRANSMISSION)\nblenderWorldPosition=worldPosition.xyz;\n#else\nblenderWorldPosition=(modelMatrix*vec4(transformed,1.)).xyz;\n#endif');
223
+ shader.fragmentShader=shader.fragmentShader.replace('#include <envmap_physical_pars_fragment>',
224
+ '#include <envmap_physical_pars_fragment>\n'+worldVisibilityShader);
225
+ }
222
226
  shader.fragmentShader = `uniform float blenderCoatIor;
223
227
  uniform vec3 blenderWorldExtinction;
224
228
  vec3 blenderInfiniteTransmission() { return vec3(
@@ -263,11 +267,13 @@ export function applyPhysicalMaterial(material: THREE.MeshPhysicalMaterial, inpu
263
267
  .replace('rectAreaLight = rectAreaLights[ i ];',
264
268
  'rectAreaLight = rectAreaLights[ i ]; rectAreaLight.color *= exp(-blenderWorldExtinction * length(rectAreaLight.position-geometryPosition));')
265
269
  .replace('getAmbientLightIrradiance( ambientLightColor )', 'getAmbientLightIrradiance( ambientLightColor ) * blenderInfiniteTransmission()');
266
- const environment = THREE.ShaderChunk.lights_fragment_maps
270
+ const visible = WORLD_VISIBILITY ? THREE.ShaderChunk.lights_fragment_maps
267
271
  .replace('getIBLIrradiance( geometryNormal )',
268
272
  'blenderWorldIrradiance( geometryNormal, getIBLIrradiance( geometryNormal ) )')
269
273
  .replaceAll(/(getIBL(?:Radiance|AnisotropyRadiance)\([^;]+\))/g,
270
274
  '$1 * blenderWorldReflectionVisibility(geometryViewDir,geometryNormal,material.roughness)')
275
+ : THREE.ShaderChunk.lights_fragment_maps;
276
+ const environment = visible
271
277
  .replaceAll(/(getIBL(?:Irradiance|Radiance|AnisotropyRadiance)\([^;]+\))/g, '$1 * blenderInfiniteTransmission()');
272
278
  shader.fragmentShader = shader.fragmentShader.replace('#include <lights_physical_fragment>', physical)
273
279
  .replace('#include <lights_fragment_begin>', lighting)
@@ -27,7 +27,7 @@ import {
27
27
  worldSchema,
28
28
  } from './blender-runtime-lighting';
29
29
  import { fitModelDirectionalShadow, visibleShadowReceivers } from './blender-runtime-shadows';
30
- import {BlenderWorldVisibility,bindWorldVisibility} from './blender-world-visibility';
30
+ import {BlenderWorldVisibility,WORLD_VISIBILITY,bindWorldVisibility} from './blender-world-visibility';
31
31
  import { volumeMesh, volumeSchema } from './blender-runtime-volume';
32
32
  import { applyPhysicalMaterial, applyWorldExtinction, physicalMaterialSchema } from './blender-physical-material';
33
33
  import { prepareGraphGeometry, setMaterialGraph } from './blender-graph-material';
@@ -799,7 +799,7 @@ export class BlenderRuntimeView {
799
799
  * (`blender-workbench-material.ts`) over their viewport display. */
800
800
  private workbench = false;
801
801
  private readonly workbenchMaterials = new Map<string, THREE.Material>();
802
- private readonly fallback = new THREE.MeshPhysicalMaterial({ color: 0xb9bec6, roughness: 0.72, side: THREE.DoubleSide });
802
+ private readonly fallback = new THREE.MeshPhysicalMaterial({ color: 0xb9bec6, roughness: 0.72, side: THREE.DoubleSide, shadowSide: THREE.BackSide });
803
803
  /** Base Color images, by image name -- ONE texture per image however many
804
804
  * materials read it, and the cache OWNS it: a material points at one and
805
805
  * never disposes it. Held with the size and revision the resident bytes
@@ -1429,13 +1429,15 @@ export class BlenderRuntimeView {
1429
1429
  // objects. Fit shadows for that draw, rather than the camera's pose
1430
1430
  // when Rendered shading was first entered. Captures use this path too.
1431
1431
  if (this.rendered) this.applyShadows(true, camera);
1432
- this.worldVisibility.sync(this.objects.values(),this.world.lightingTexture(),this.root,this.rendered);
1433
- for(const material of [...this.materials.values(),this.fallback])bindWorldVisibility(material,this.worldVisibility);
1434
- for(const object of this.objects.values()) {
1435
- const mesh=object as THREE.Mesh;if(!mesh.isMesh)continue;
1436
- for(const material of Array.isArray(mesh.material)?mesh.material:[mesh.material])bindWorldVisibility(material,this.worldVisibility);
1432
+ if (WORLD_VISIBILITY) {
1433
+ this.worldVisibility.sync(this.objects.values(),this.world.lightingTexture(),this.root,this.rendered);
1434
+ for(const material of [...this.materials.values(),this.fallback])bindWorldVisibility(material,this.worldVisibility);
1435
+ for(const object of this.objects.values()) {
1436
+ const mesh=object as THREE.Mesh;if(!mesh.isMesh)continue;
1437
+ for(const material of Array.isArray(mesh.material)?mesh.material:[mesh.material])bindWorldVisibility(material,this.worldVisibility);
1438
+ }
1439
+ if(options?.renderer)this.worldVisibility.prepare(options.renderer);
1437
1440
  }
1438
- if(options?.renderer)this.worldVisibility.prepare(options.renderer);
1439
1441
  const opaqueDone = performance.now();
1440
1442
  const motion = this.motionGeometry.prepare(camera, !this.rendered && (this.mode ?? this.frame?.mode) === 'OBJECT' &&
1441
1443
  options?.interactive === true, options?.height ?? 0);
@@ -1995,7 +1997,11 @@ export class BlenderRuntimeView {
1995
1997
  for (const [id, data] of Object.entries(next.materials)) {
1996
1998
  // Cycles intersects and shades both faces, including an arcade viewed
1997
1999
  // from underneath. Three's front-face default drops those surfaces.
1998
- const material = this.materials.get(id) ?? new THREE.MeshPhysicalMaterial({ side: THREE.DoubleSide });
2000
+ // The shadow map keeps the back faces a front-faced material draws
2001
+ // there: a two-sided depth pass puts every lit face into its own map, and
2002
+ // the normal offset cannot hold that off at grazing light.
2003
+ const material = this.materials.get(id) ??
2004
+ new THREE.MeshPhysicalMaterial({ side: THREE.DoubleSide, shadowSide: THREE.BackSide });
1999
2005
  material.name = data.name;
2000
2006
  // A LINKED BASE COLOUR REPLACES THE SOCKET'S VALUE, it does not multiply
2001
2007
  // it. Python's `_reduce` fills `color` from the Base Color socket's
@@ -4,6 +4,20 @@
4
4
  * directional quadrature, not indirect light transport. */
5
5
  import * as THREE from 'three';
6
6
 
7
+ /** SKY-LIGHT OCCLUSION IS OFF IN EVERY VIEW, and compiled out of every material. Its
8
+ * sixteen 512-square tiles each span the whole scene, so a 360 m level gets
9
+ * about a metre per texel, and each lookup is one nearest texel answering
10
+ * yes or no. Faceted surfaces then occlude their own sky at random pixels --
11
+ * static on every polygon in Rendered and Play -- and narrow specular lobes,
12
+ * dominated by a few of the sixteen directions, plausibly flash as the camera
13
+ * moves. It measured right in a closed room a few metres across. Turn it back
14
+ * on once its lookups are filtered and its resolution follows the scene's size
15
+ * (docs/BLENDER-RENDERING-PARITY.md lists the rest). Off, the view neither
16
+ * syncs nor draws the atlas (`blender-runtime-view.ts`), and physical
17
+ * materials declare none of its samplers (`blender-physical-material.ts`):
18
+ * three of them counted against a material's sixteen even while unused. */
19
+ export const WORLD_VISIBILITY = false;
20
+
7
21
  const SAMPLE_COUNT=16, TILES=4, TILE_SIZE=512;
8
22
  type Segment={mesh:THREE.Mesh; geometry:THREE.BufferGeometry; position:THREE.BufferAttribute|THREE.InterleavedBufferAttribute;
9
23
  index:THREE.BufferAttribute|null; positionVersion:number; indexVersion:number; start:number; count:number;
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.135",
5
+ "version": "0.1.137",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },