@aura3d/rendering 4.0.2 → 4.0.4
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/dist/EnvironmentPlatform.d.ts.map +1 -1
- package/dist/EnvironmentPlatform.js +6 -2
- package/dist/EnvironmentPlatform.js.map +1 -1
- package/dist/ForwardPass.d.ts +2 -0
- package/dist/ForwardPass.d.ts.map +1 -1
- package/dist/ForwardPass.js +70 -5
- package/dist/ForwardPass.js.map +1 -1
- package/dist/Geometry.d.ts +40 -0
- package/dist/Geometry.d.ts.map +1 -1
- package/dist/Geometry.js +39 -2
- package/dist/Geometry.js.map +1 -1
- package/dist/IndexBuffer.d.ts +16 -1
- package/dist/IndexBuffer.d.ts.map +1 -1
- package/dist/IndexBuffer.js +46 -1
- package/dist/IndexBuffer.js.map +1 -1
- package/dist/InstancedPBRMaterial.d.ts +2 -0
- package/dist/InstancedPBRMaterial.d.ts.map +1 -1
- package/dist/InstancedPBRMaterial.js +1 -0
- package/dist/InstancedPBRMaterial.js.map +1 -1
- package/dist/Material.d.ts +9 -0
- package/dist/Material.d.ts.map +1 -1
- package/dist/Material.js +3 -0
- package/dist/Material.js.map +1 -1
- package/dist/PBRMaterial.d.ts +2 -0
- package/dist/PBRMaterial.d.ts.map +1 -1
- package/dist/PBRMaterial.js +1 -0
- package/dist/PBRMaterial.js.map +1 -1
- package/dist/PortableShaderMaterial.d.ts +13 -3
- package/dist/PortableShaderMaterial.d.ts.map +1 -1
- package/dist/PortableShaderMaterial.js +24 -12
- package/dist/PortableShaderMaterial.js.map +1 -1
- package/dist/RenderDevice.d.ts +98 -0
- package/dist/RenderDevice.d.ts.map +1 -1
- package/dist/RenderDevice.js +129 -0
- package/dist/RenderDevice.js.map +1 -1
- package/dist/Renderer.d.ts +94 -0
- package/dist/Renderer.d.ts.map +1 -1
- package/dist/Renderer.js +321 -26
- package/dist/Renderer.js.map +1 -1
- package/dist/Sampler.d.ts +35 -0
- package/dist/Sampler.d.ts.map +1 -1
- package/dist/Sampler.js +96 -0
- package/dist/Sampler.js.map +1 -1
- package/dist/ShaderLibraryCore.d.ts +14 -0
- package/dist/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/ShaderLibraryCore.js +94 -4
- package/dist/ShaderLibraryCore.js.map +1 -1
- package/dist/Texture.d.ts +53 -1
- package/dist/Texture.d.ts.map +1 -1
- package/dist/Texture.js +53 -2
- package/dist/Texture.js.map +1 -1
- package/dist/UnlitMaterial.d.ts +46 -0
- package/dist/UnlitMaterial.d.ts.map +1 -1
- package/dist/UnlitMaterial.js +32 -3
- package/dist/UnlitMaterial.js.map +1 -1
- package/dist/VertexFormat.d.ts +19 -1
- package/dist/VertexFormat.d.ts.map +1 -1
- package/dist/VertexFormat.js +40 -1
- package/dist/VertexFormat.js.map +1 -1
- package/dist/WebGL2Device.d.ts +16 -0
- package/dist/WebGL2Device.d.ts.map +1 -1
- package/dist/WebGL2Device.js +31 -6
- package/dist/WebGL2Device.js.map +1 -1
- package/dist/WebGPUDevice.d.ts +92 -1
- package/dist/WebGPUDevice.d.ts.map +1 -1
- package/dist/WebGPUDevice.js +417 -44
- package/dist/WebGPUDevice.js.map +1 -1
- package/dist/batching/BatchedGeometryPool.d.ts +22 -1
- package/dist/batching/BatchedGeometryPool.d.ts.map +1 -1
- package/dist/batching/BatchedGeometryPool.js +50 -2
- package/dist/batching/BatchedGeometryPool.js.map +1 -1
- package/dist/batching/DrawDataTexture.d.ts +21 -1
- package/dist/batching/DrawDataTexture.d.ts.map +1 -1
- package/dist/batching/DrawDataTexture.js +30 -4
- package/dist/batching/DrawDataTexture.js.map +1 -1
- package/dist/batching/MultiDrawBatch.d.ts +26 -0
- package/dist/batching/MultiDrawBatch.d.ts.map +1 -1
- package/dist/batching/MultiDrawBatch.js +64 -2
- package/dist/batching/MultiDrawBatch.js.map +1 -1
- package/dist/batching/index.d.ts +2 -2
- package/dist/batching/index.d.ts.map +1 -1
- package/dist/batching/index.js +2 -2
- package/dist/batching/index.js.map +1 -1
- package/dist/compute/ComputeContext.d.ts +84 -0
- package/dist/compute/ComputeContext.d.ts.map +1 -0
- package/dist/compute/ComputeContext.js +371 -0
- package/dist/compute/ComputeContext.js.map +1 -0
- package/dist/compute/index.d.ts +4 -0
- package/dist/compute/index.d.ts.map +1 -0
- package/dist/compute/index.js +3 -0
- package/dist/compute/index.js.map +1 -0
- package/dist/compute/types.d.ts +108 -0
- package/dist/compute/types.d.ts.map +1 -0
- package/dist/compute/types.js +25 -0
- package/dist/compute/types.js.map +1 -0
- package/dist/contracts/core.d.ts +1 -1
- package/dist/contracts/core.d.ts.map +1 -1
- package/dist/contracts/core.js.map +1 -1
- package/dist/contracts/flags.state.d.ts.map +1 -1
- package/dist/contracts/flags.state.js +20 -1
- package/dist/contracts/flags.state.js.map +1 -1
- package/dist/contracts/output.d.ts +1 -1
- package/dist/contracts/output.d.ts.map +1 -1
- package/dist/contracts/output.js.map +1 -1
- package/dist/contracts/post.d.ts +7 -0
- package/dist/contracts/post.d.ts.map +1 -1
- package/dist/contracts/post.js.map +1 -1
- package/dist/contracts/renderItem.d.ts +15 -0
- package/dist/contracts/renderItem.d.ts.map +1 -1
- package/dist/contracts/renderSource.d.ts +3 -1
- package/dist/contracts/renderSource.d.ts.map +1 -1
- package/dist/contracts/sampling.d.ts +23 -0
- package/dist/contracts/sampling.d.ts.map +1 -1
- package/dist/contracts/sampling.js.map +1 -1
- package/dist/environment/EnvironmentCache.d.ts.map +1 -1
- package/dist/environment/EnvironmentCache.js +7 -3
- package/dist/environment/EnvironmentCache.js.map +1 -1
- package/dist/environment/RoomEnvironmentScene.d.ts +2 -2
- package/dist/environment/RoomEnvironmentScene.d.ts.map +1 -1
- package/dist/environment/RoomEnvironmentScene.js.map +1 -1
- package/dist/geometry/Circle.d.ts +14 -0
- package/dist/geometry/Circle.d.ts.map +1 -0
- package/dist/geometry/Circle.js +31 -0
- package/dist/geometry/Circle.js.map +1 -0
- package/dist/geometry/Cone.d.ts +37 -0
- package/dist/geometry/Cone.d.ts.map +1 -0
- package/dist/geometry/Cone.js +129 -0
- package/dist/geometry/Cone.js.map +1 -0
- package/dist/geometry/Convex.d.ts +19 -0
- package/dist/geometry/Convex.d.ts.map +1 -0
- package/dist/geometry/Convex.js +27 -0
- package/dist/geometry/Convex.js.map +1 -0
- package/dist/geometry/Edges.d.ts +15 -0
- package/dist/geometry/Edges.d.ts.map +1 -0
- package/dist/geometry/Edges.js +98 -0
- package/dist/geometry/Edges.js.map +1 -0
- package/dist/geometry/Extrude.d.ts +29 -0
- package/dist/geometry/Extrude.d.ts.map +1 -0
- package/dist/geometry/Extrude.js +403 -0
- package/dist/geometry/Extrude.js.map +1 -0
- package/dist/geometry/Lathe.d.ts +20 -0
- package/dist/geometry/Lathe.d.ts.map +1 -0
- package/dist/geometry/Lathe.js +94 -0
- package/dist/geometry/Lathe.js.map +1 -0
- package/dist/geometry/Polyhedron.d.ts +21 -0
- package/dist/geometry/Polyhedron.d.ts.map +1 -0
- package/dist/geometry/Polyhedron.js +201 -0
- package/dist/geometry/Polyhedron.js.map +1 -0
- package/dist/geometry/Primitives.d.ts +38 -2
- package/dist/geometry/Primitives.d.ts.map +1 -1
- package/dist/geometry/Primitives.js +114 -0
- package/dist/geometry/Primitives.js.map +1 -1
- package/dist/geometry/Ring.d.ts +16 -0
- package/dist/geometry/Ring.d.ts.map +1 -0
- package/dist/geometry/Ring.js +44 -0
- package/dist/geometry/Ring.js.map +1 -0
- package/dist/geometry/RoundedBox.d.ts +22 -0
- package/dist/geometry/RoundedBox.d.ts.map +1 -0
- package/dist/geometry/RoundedBox.js +191 -0
- package/dist/geometry/RoundedBox.js.map +1 -0
- package/dist/geometry/Shape.d.ts +15 -0
- package/dist/geometry/Shape.d.ts.map +1 -0
- package/dist/geometry/Shape.js +44 -0
- package/dist/geometry/Shape.js.map +1 -0
- package/dist/geometry/Support.d.ts +137 -0
- package/dist/geometry/Support.d.ts.map +1 -0
- package/dist/geometry/Support.js +1066 -0
- package/dist/geometry/Support.js.map +1 -0
- package/dist/geometry/TorusKnot.d.ts +18 -0
- package/dist/geometry/TorusKnot.d.ts.map +1 -0
- package/dist/geometry/TorusKnot.js +83 -0
- package/dist/geometry/TorusKnot.js.map +1 -0
- package/dist/geometry/Tube.d.ts +19 -0
- package/dist/geometry/Tube.d.ts.map +1 -0
- package/dist/geometry/Tube.js +70 -0
- package/dist/geometry/Tube.js.map +1 -0
- package/dist/geometry/Wireframe.d.ts +19 -0
- package/dist/geometry/Wireframe.d.ts.map +1 -0
- package/dist/geometry/Wireframe.js +63 -0
- package/dist/geometry/Wireframe.js.map +1 -0
- package/dist/index.d.ts +38 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -3
- package/dist/index.js.map +1 -1
- package/dist/lanes/prd11.js +1 -1
- package/dist/lanes/prd11.js.map +1 -1
- package/dist/output/ToneMappingOperators.glsl.d.ts +1 -1
- package/dist/output/ToneMappingOperators.glsl.d.ts.map +1 -1
- package/dist/output/ToneMappingOperators.glsl.js +9 -1
- package/dist/output/ToneMappingOperators.glsl.js.map +1 -1
- package/dist/post/v2Entry.d.ts +1 -0
- package/dist/post/v2Entry.d.ts.map +1 -1
- package/dist/post/v2Entry.js.map +1 -1
- package/dist/post/v2Stages.d.ts +30 -1
- package/dist/post/v2Stages.d.ts.map +1 -1
- package/dist/post/v2Stages.js +87 -21
- package/dist/post/v2Stages.js.map +1 -1
- package/dist/program/ProgramCache.d.ts.map +1 -1
- package/dist/program/ProgramCache.js +12 -0
- package/dist/program/ProgramCache.js.map +1 -1
- package/dist/program/ProgramGenerator.d.ts.map +1 -1
- package/dist/program/ProgramGenerator.js +3 -0
- package/dist/program/ProgramGenerator.js.map +1 -1
- package/dist/reference/PostProcessPass.d.ts +1 -1
- package/dist/reference/PostProcessPass.d.ts.map +1 -1
- package/dist/reference/PostProcessPass.js +8 -1
- package/dist/reference/PostProcessPass.js.map +1 -1
- package/dist/renderer/CullingBatching.d.ts +44 -2
- package/dist/renderer/CullingBatching.d.ts.map +1 -1
- package/dist/renderer/CullingBatching.js +66 -11
- package/dist/renderer/CullingBatching.js.map +1 -1
- package/dist/shaders/chunks/lighting_mirrors.d.ts +2 -2
- package/dist/shaders/chunks/lighting_mirrors.d.ts.map +1 -1
- package/dist/shaders/chunks/lighting_mirrors.js.map +1 -1
- package/dist/shadows/DirectionalCascadeFitter.d.ts +8 -8
- package/dist/shadows/DirectionalCascadeFitter.d.ts.map +1 -1
- package/dist/shadows/DirectionalCascadeFitter.js.map +1 -1
- package/dist/shadows/ShadowAtlas.d.ts +5 -5
- package/dist/shadows/ShadowAtlas.d.ts.map +1 -1
- package/dist/shadows/ShadowAtlas.js.map +1 -1
- package/dist/vfx/ProceduralVolumeEmitter.d.ts +11 -0
- package/dist/vfx/ProceduralVolumeEmitter.d.ts.map +1 -1
- package/dist/vfx/ProceduralVolumeEmitter.js +20 -6
- package/dist/vfx/ProceduralVolumeEmitter.js.map +1 -1
- package/dist/webgl2/ContextLifecycle.d.ts +36 -0
- package/dist/webgl2/ContextLifecycle.d.ts.map +1 -1
- package/dist/webgl2/ContextLifecycle.js +110 -2
- package/dist/webgl2/ContextLifecycle.js.map +1 -1
- package/dist/webgl2/LegacyPost.d.ts +16 -1
- package/dist/webgl2/LegacyPost.d.ts.map +1 -1
- package/dist/webgl2/LegacyPost.js +49 -16
- package/dist/webgl2/LegacyPost.js.map +1 -1
- package/dist/webgl2/MultiDraw.d.ts.map +1 -1
- package/dist/webgl2/MultiDraw.js +42 -2
- package/dist/webgl2/MultiDraw.js.map +1 -1
- package/dist/webgl2/TextureUpload.d.ts +7 -0
- package/dist/webgl2/TextureUpload.d.ts.map +1 -1
- package/dist/webgl2/TextureUpload.js +54 -10
- package/dist/webgl2/TextureUpload.js.map +1 -1
- package/package.json +3 -3
package/dist/Renderer.js
CHANGED
|
@@ -258,6 +258,27 @@ export class Renderer {
|
|
|
258
258
|
}
|
|
259
259
|
return this.device.getDiagnostics();
|
|
260
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* threejs-parity L7/SR-07: `renderer.precompile` — compiles (and awaits) the
|
|
263
|
+
* generated programs a source needs before first render, without submitting
|
|
264
|
+
* a frame. Returns the number of programs this call compiled and wall time.
|
|
265
|
+
*/
|
|
266
|
+
async precompile(sourceOrInput, camera) {
|
|
267
|
+
this.assertAlive();
|
|
268
|
+
const startedAt = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
269
|
+
const { source, camera: inputCamera } = normalizeRendererInput(sourceOrInput, camera);
|
|
270
|
+
sceneFromSource(source)?.updateWorldTransforms();
|
|
271
|
+
const resolvedCamera = resolveCamera(source, inputCamera, { width: this.width, height: this.height }, { allowSceneCamera: true });
|
|
272
|
+
const cameraViewProjection = resolvedCamera?.viewProjectionMatrix;
|
|
273
|
+
const collected = collectRenderItemsWithDiagnostics(source, cameraViewProjection, resolvedCamera?.camera);
|
|
274
|
+
const before = this.device.getDiagnostics().programCompileCount ?? 0;
|
|
275
|
+
await this.warmGeneratedPrograms(collected.items, collectRenderLights(source), collectEnvironmentLighting(source), collectEnvironmentFog(source), collectForwardShadowMap(source), cameraViewProjection);
|
|
276
|
+
const after = this.device.getDiagnostics().programCompileCount ?? before;
|
|
277
|
+
return {
|
|
278
|
+
compiledPrograms: Math.max(0, after - before),
|
|
279
|
+
durationMs: (typeof performance !== "undefined" ? performance.now() : Date.now()) - startedAt
|
|
280
|
+
};
|
|
281
|
+
}
|
|
261
282
|
/** C-28 (#91): run `fn` under the frameStats "post" scope when a monitor exists. */
|
|
262
283
|
scopePost(fn) {
|
|
263
284
|
return this.frameStatsMonitor ? this.frameStatsMonitor.scope("post", fn) : fn();
|
|
@@ -314,7 +335,7 @@ export class Renderer {
|
|
|
314
335
|
const devicePixelRatio = resolveCanvasPixelRatio({
|
|
315
336
|
devicePixelRatio: globalThis.devicePixelRatio ?? 1,
|
|
316
337
|
tier: this.qualityTier ?? this.liveQualityTier()?.settings ?? { maxPixelRatio: Number.POSITIVE_INFINITY },
|
|
317
|
-
explicit: options.devicePixelRatio,
|
|
338
|
+
explicit: options.devicePixelRatio ?? this.pixelRatioOverride,
|
|
318
339
|
resolution: this.resolutionOptions
|
|
319
340
|
});
|
|
320
341
|
if (![cssWidth, cssHeight, devicePixelRatio].every(Number.isFinite) || cssWidth <= 0 || cssHeight <= 0 || devicePixelRatio <= 0) {
|
|
@@ -362,6 +383,68 @@ export class Renderer {
|
|
|
362
383
|
backingHeight: this.height
|
|
363
384
|
};
|
|
364
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* threejs-parity L7/SH-05: `setPixelRatio` — overrides the device pixel
|
|
388
|
+
* ratio used by `resizeToDisplay` (a `devicePixelRatio` option there still
|
|
389
|
+
* wins for that call). Applies immediately on a canvas-backed renderer;
|
|
390
|
+
* stored for the next resize otherwise.
|
|
391
|
+
*/
|
|
392
|
+
pixelRatioOverride;
|
|
393
|
+
setPixelRatio(ratio) {
|
|
394
|
+
this.assertAlive();
|
|
395
|
+
if (!Number.isFinite(ratio) || ratio <= 0) {
|
|
396
|
+
throw new RenderDeviceError("Pixel ratio must be a positive finite number", "INVALID_PIXEL_RATIO", { ratio });
|
|
397
|
+
}
|
|
398
|
+
this.pixelRatioOverride = ratio;
|
|
399
|
+
if (this.canvas)
|
|
400
|
+
this.resizeToDisplay();
|
|
401
|
+
}
|
|
402
|
+
get pixelRatio() {
|
|
403
|
+
return this.pixelRatioOverride ?? this.resolutionReport.pixelRatio;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* threejs-parity L7/SH-05: `setClearColor` — the default clear used when the
|
|
407
|
+
* scene has no background (three's `setClearColor(color, alpha)`).
|
|
408
|
+
*/
|
|
409
|
+
setClearColor(color, alpha) {
|
|
410
|
+
this.assertAlive();
|
|
411
|
+
const [r, g, b, a] = color;
|
|
412
|
+
const resolved = [r ?? 0, g ?? 0, b ?? 0, alpha ?? a ?? 1];
|
|
413
|
+
if (!resolved.every((v) => Number.isFinite(v) && v >= 0 && v <= 1)) {
|
|
414
|
+
throw new RenderDeviceError("Clear color channels must be finite numbers in [0, 1]", "INVALID_CLEAR_COLOR", { color: [...color], alpha });
|
|
415
|
+
}
|
|
416
|
+
this.clearColor = resolved;
|
|
417
|
+
}
|
|
418
|
+
get clearColorValue() {
|
|
419
|
+
return this.clearColor;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* threejs-parity L7/SR-05: three's `WebGLRenderer.info` — per-frame render
|
|
423
|
+
* counters plus live resource gauges from the device diagnostics.
|
|
424
|
+
*/
|
|
425
|
+
submittedFrames = 0;
|
|
426
|
+
get info() {
|
|
427
|
+
const d = this.device.getDiagnostics();
|
|
428
|
+
const memoryBytes = (d.bufferBytes ?? 0) + (d.textureBytes ?? 0);
|
|
429
|
+
return {
|
|
430
|
+
render: {
|
|
431
|
+
calls: d.drawCalls,
|
|
432
|
+
triangles: d.triangles ?? 0,
|
|
433
|
+
points: d.points ?? 0,
|
|
434
|
+
lines: d.lines ?? 0,
|
|
435
|
+
frame: this.submittedFrames
|
|
436
|
+
},
|
|
437
|
+
memory: {
|
|
438
|
+
geometries: d.buffers,
|
|
439
|
+
textures: d.textures ?? 0,
|
|
440
|
+
...(d.renderTargets !== undefined ? { renderTargets: d.renderTargets } : {}),
|
|
441
|
+
programs: d.shaders
|
|
442
|
+
},
|
|
443
|
+
...(d.approximateGpuMemoryBytes !== undefined || memoryBytes > 0
|
|
444
|
+
? { approximateGpuMemoryBytes: d.approximateGpuMemoryBytes ?? memoryBytes }
|
|
445
|
+
: {})
|
|
446
|
+
};
|
|
447
|
+
}
|
|
365
448
|
/**
|
|
366
449
|
* Feeds one frame's timings to the resolution governor (C-28 `gpuMs` wins
|
|
367
450
|
* over the CPU interval when lane 11's timer queries provide it).
|
|
@@ -399,10 +482,101 @@ export class Renderer {
|
|
|
399
482
|
this.temporalHistory.commit();
|
|
400
483
|
}
|
|
401
484
|
resetTemporalHistory(_reason = "explicit-reset") { this.temporalHistory.reset(); this.lastTemporalCameraPosition = undefined; }
|
|
485
|
+
/**
|
|
486
|
+
* threejs-parity L7/SH-06: a `{ target }` (or `source.renderTarget`) frame
|
|
487
|
+
* executes at the target's own dimensions — three's `setRenderTarget` +
|
|
488
|
+
* `render` semantics. Viewport state restores when the frame exits.
|
|
489
|
+
*/
|
|
490
|
+
adoptExplicitTargetSize(source, run) {
|
|
491
|
+
const target = collectRenderTarget(source);
|
|
492
|
+
if (!target || (target.width === this.width && target.height === this.height))
|
|
493
|
+
return run();
|
|
494
|
+
const prevWidth = this.width;
|
|
495
|
+
const prevHeight = this.height;
|
|
496
|
+
this.width = target.width;
|
|
497
|
+
this.height = target.height;
|
|
498
|
+
// Stale temporal history at the old size would misproject; drop it.
|
|
499
|
+
this.temporalHistory.dispose();
|
|
500
|
+
try {
|
|
501
|
+
return run();
|
|
502
|
+
}
|
|
503
|
+
finally {
|
|
504
|
+
this.width = prevWidth;
|
|
505
|
+
this.height = prevHeight;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
async adoptExplicitTargetSizeAsync(source, run) {
|
|
509
|
+
const target = collectRenderTarget(source);
|
|
510
|
+
if (!target || (target.width === this.width && target.height === this.height))
|
|
511
|
+
return run();
|
|
512
|
+
const prevWidth = this.width;
|
|
513
|
+
const prevHeight = this.height;
|
|
514
|
+
this.width = target.width;
|
|
515
|
+
this.height = target.height;
|
|
516
|
+
this.temporalHistory.dispose();
|
|
517
|
+
try {
|
|
518
|
+
return await run();
|
|
519
|
+
}
|
|
520
|
+
finally {
|
|
521
|
+
this.width = prevWidth;
|
|
522
|
+
this.height = prevHeight;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
402
525
|
render(sourceOrInput, camera) {
|
|
403
526
|
// T0-28: ambient `rendererQrFlags()` readers (Lighting, Background, post…)
|
|
404
527
|
// see this renderer's flags for the duration of the synchronous frame.
|
|
405
|
-
|
|
528
|
+
const views = collectRendererViews(sourceOrInput);
|
|
529
|
+
if (views) {
|
|
530
|
+
let last;
|
|
531
|
+
views.forEach((view, index) => {
|
|
532
|
+
const viewInput = viewFrameInput(sourceOrInput, camera, view);
|
|
533
|
+
this.pendingViewState = { viewport: view.viewport, scissor: view.scissor ?? view.viewport, skipClear: index > 0 };
|
|
534
|
+
try {
|
|
535
|
+
last = withRendererQrFlags(this.device, () => this.adoptExplicitTargetSize(viewInput.source, () => this.renderFrame(viewInput)));
|
|
536
|
+
}
|
|
537
|
+
finally {
|
|
538
|
+
this.pendingViewState = null;
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
return last;
|
|
542
|
+
}
|
|
543
|
+
this.pendingViewState = collectSingleViewState(sourceOrInput);
|
|
544
|
+
try {
|
|
545
|
+
return withRendererQrFlags(this.device, () => this.adoptExplicitTargetSize(normalizeRendererInput(sourceOrInput, camera).source, () => this.renderFrame(sourceOrInput, camera)));
|
|
546
|
+
}
|
|
547
|
+
finally {
|
|
548
|
+
this.pendingViewState = null;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/** SC-01: viewport/scissor requested for the frame about to bind its main target; consumed by `clearAndApplyView`. */
|
|
552
|
+
pendingViewState = null;
|
|
553
|
+
clearAndApplyView(clearColor) {
|
|
554
|
+
const state = this.pendingViewState;
|
|
555
|
+
this.pendingViewState = null;
|
|
556
|
+
if (!state) {
|
|
557
|
+
this.device.clear(clearColor);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
if (!this.device.setViewport || !this.device.setScissor || !this.device.setScissorTest) {
|
|
561
|
+
throw new RenderDeviceError("This render device does not support viewport/scissor rendering.", "VIEWPORT_UNSUPPORTED", { backend: this.device.kind });
|
|
562
|
+
}
|
|
563
|
+
// The clear runs before the viewport/scissor apply so it covers the whole
|
|
564
|
+
// target (a WebGPU clear is a full-attachment load op and cannot be scissored).
|
|
565
|
+
if (!state.skipClear)
|
|
566
|
+
this.device.clear(clearColor);
|
|
567
|
+
validateViewRect("viewport", state.viewport, this.width, this.height);
|
|
568
|
+
this.device.setViewport(state.viewport.x, state.viewport.y, state.viewport.width, state.viewport.height);
|
|
569
|
+
if (state.scissor) {
|
|
570
|
+
validateViewRect("scissor", state.scissor, this.width, this.height);
|
|
571
|
+
this.device.setScissor(state.scissor.x, state.scissor.y, state.scissor.width, state.scissor.height);
|
|
572
|
+
this.device.setScissorTest(true);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
assertViewsSupported(postprocess) {
|
|
576
|
+
if (this.pendingViewState && postprocess) {
|
|
577
|
+
this.pendingViewState = null;
|
|
578
|
+
throw new RenderDeviceError("Viewport/scissor views (ArrayCamera / StereoCamera) cannot be combined with postprocess; render the views without effects.", "VIEWPORT_POSTPROCESS_UNSUPPORTED", { backend: this.device.kind });
|
|
579
|
+
}
|
|
406
580
|
}
|
|
407
581
|
renderFrame(sourceOrInput, camera) {
|
|
408
582
|
this.assertAlive();
|
|
@@ -426,7 +600,7 @@ export class Renderer {
|
|
|
426
600
|
throw new RenderDeviceError("RenderSource requires an explicit camera but none was supplied or found in the scene.", "CAMERA_REQUIRED");
|
|
427
601
|
}
|
|
428
602
|
let cameraViewProjection = resolvedCamera?.viewProjectionMatrix;
|
|
429
|
-
const collectedItems = collectRenderItemsWithDiagnostics(source, cameraViewProjection, resolvedCamera?.camera, rendererQrFlags().on("A3D_QR_LIGHTING"));
|
|
603
|
+
const collectedItems = collectRenderItemsWithDiagnostics(source, cameraViewProjection, resolvedCamera?.camera, rendererQrFlags().on("A3D_QR_LIGHTING"), resolvedCamera?.layerMask);
|
|
430
604
|
const collectionDiagnostics = collectedItems.diagnostics;
|
|
431
605
|
let items = collectedItems.items;
|
|
432
606
|
const lights = collectRenderLights(source);
|
|
@@ -440,7 +614,7 @@ export class Renderer {
|
|
|
440
614
|
this.warmGeneratedProgramsSync(items, lights, environmentLighting, environmentFog, explicitShadowMap, cameraViewProjection);
|
|
441
615
|
const sourceCameraPosition = collectSourceCameraPosition(source);
|
|
442
616
|
const explicitRenderTarget = collectRenderTarget(source);
|
|
443
|
-
validateExplicitRenderTarget(explicitRenderTarget
|
|
617
|
+
validateExplicitRenderTarget(explicitRenderTarget);
|
|
444
618
|
let cameraPosition = sourceCameraPosition ?? resolvedCamera?.cameraPosition;
|
|
445
619
|
// C-05/T0-07: decide the frame's output route before the hooks exist, so
|
|
446
620
|
// contributors see the same v2/legacy resource names the graph writes.
|
|
@@ -464,6 +638,7 @@ export class Renderer {
|
|
|
464
638
|
}
|
|
465
639
|
}
|
|
466
640
|
let postprocess = collectPostprocess(source);
|
|
641
|
+
this.assertViewsSupported(postprocess);
|
|
467
642
|
if (!postprocess?.temporal || (!postprocess.motionBlur && !postprocess.taa)) {
|
|
468
643
|
this.temporalHistory.reset();
|
|
469
644
|
this.lastTemporalCameraPosition = undefined;
|
|
@@ -588,7 +763,7 @@ export class Renderer {
|
|
|
588
763
|
else {
|
|
589
764
|
this.device.setRenderTarget(explicitRenderTarget ?? null);
|
|
590
765
|
}
|
|
591
|
-
this.
|
|
766
|
+
this.clearAndApplyView(qrOutput && !postprocess ? this.outputClearColor ?? this.clearColor : this.clearColor);
|
|
592
767
|
if (environmentBackground) {
|
|
593
768
|
this.graph.addPass(new EnvironmentBackgroundPass({
|
|
594
769
|
...environmentBackground,
|
|
@@ -646,6 +821,9 @@ export class Renderer {
|
|
|
646
821
|
cameraPosition,
|
|
647
822
|
cameraViewMatrix: resolvedCamera?.viewMatrix,
|
|
648
823
|
cameraViewProjectionMatrix: rasterViewProjection,
|
|
824
|
+
// SH-02: `material.custom` frame built-ins (u_projectionMatrix, u_time).
|
|
825
|
+
auraFrameCamera: resolvedCamera?.projectionMatrix ? { projectionMatrix: resolvedCamera.projectionMatrix } : undefined,
|
|
826
|
+
timeSeconds: typeof performance !== "undefined" ? performance.now() / 1000 : Date.now() / 1000,
|
|
649
827
|
outputColorSpace: "linear",
|
|
650
828
|
shaderLibrary: this.shaderLibrary,
|
|
651
829
|
backgroundCoverage: coverage,
|
|
@@ -687,6 +865,9 @@ export class Renderer {
|
|
|
687
865
|
cameraPosition,
|
|
688
866
|
cameraViewMatrix: resolvedCamera?.viewMatrix,
|
|
689
867
|
cameraViewProjectionMatrix: rasterViewProjection,
|
|
868
|
+
// SH-02: `material.custom` frame built-ins (u_projectionMatrix, u_time).
|
|
869
|
+
auraFrameCamera: resolvedCamera?.projectionMatrix ? { projectionMatrix: resolvedCamera.projectionMatrix } : undefined,
|
|
870
|
+
timeSeconds: typeof performance !== "undefined" ? performance.now() / 1000 : Date.now() / 1000,
|
|
690
871
|
outputColorSpace: postprocess ? "linear" : "srgb",
|
|
691
872
|
shaderLibrary: this.shaderLibrary
|
|
692
873
|
}));
|
|
@@ -760,6 +941,7 @@ export class Renderer {
|
|
|
760
941
|
target.dispose();
|
|
761
942
|
}
|
|
762
943
|
}
|
|
944
|
+
this.submittedFrames += 1;
|
|
763
945
|
this.submittedShadowFrameId += 1;
|
|
764
946
|
if (this.lastShadowEvidence)
|
|
765
947
|
this.lastShadowEvidence = { ...this.lastShadowEvidence, submissionFrameId: this.submittedShadowFrameId };
|
|
@@ -770,6 +952,32 @@ export class Renderer {
|
|
|
770
952
|
}));
|
|
771
953
|
}
|
|
772
954
|
async renderAsync(sourceOrInput, camera) {
|
|
955
|
+
const views = collectRendererViews(sourceOrInput);
|
|
956
|
+
if (views) {
|
|
957
|
+
let last;
|
|
958
|
+
for (let index = 0; index < views.length; index += 1) {
|
|
959
|
+
const view = views[index];
|
|
960
|
+
const viewInput = viewFrameInput(sourceOrInput, camera, view);
|
|
961
|
+
this.pendingViewState = { viewport: view.viewport, scissor: view.scissor ?? view.viewport, skipClear: index > 0 };
|
|
962
|
+
try {
|
|
963
|
+
last = await this.adoptExplicitTargetSizeAsync(viewInput.source, () => this.renderAsyncFrame(viewInput));
|
|
964
|
+
}
|
|
965
|
+
finally {
|
|
966
|
+
this.pendingViewState = null;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return last;
|
|
970
|
+
}
|
|
971
|
+
this.pendingViewState = collectSingleViewState(sourceOrInput);
|
|
972
|
+
// threejs-parity L7/SH-06: `{ target }` frames run at the target's size.
|
|
973
|
+
try {
|
|
974
|
+
return await this.adoptExplicitTargetSizeAsync(normalizeRendererInput(sourceOrInput, camera).source, () => this.renderAsyncFrame(sourceOrInput, camera));
|
|
975
|
+
}
|
|
976
|
+
finally {
|
|
977
|
+
this.pendingViewState = null;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
async renderAsyncFrame(sourceOrInput, camera) {
|
|
773
981
|
this.assertAlive();
|
|
774
982
|
this.frameStatsMonitor?.begin(typeof performance !== "undefined" ? performance.now() : Date.now());
|
|
775
983
|
// T0-06: reset the per-frame sync-compile bound for this render.
|
|
@@ -790,7 +998,7 @@ export class Renderer {
|
|
|
790
998
|
throw new RenderDeviceError("RenderSource requires an explicit camera but none was supplied or found in the scene.", "CAMERA_REQUIRED");
|
|
791
999
|
}
|
|
792
1000
|
let cameraViewProjection = resolvedCamera?.viewProjectionMatrix;
|
|
793
|
-
const collectedItems = collectRenderItemsWithDiagnostics(source, cameraViewProjection, resolvedCamera?.camera, rendererQrFlags().on("A3D_QR_LIGHTING"));
|
|
1001
|
+
const collectedItems = collectRenderItemsWithDiagnostics(source, cameraViewProjection, resolvedCamera?.camera, rendererQrFlags().on("A3D_QR_LIGHTING"), resolvedCamera?.layerMask);
|
|
794
1002
|
const collectionDiagnostics = collectedItems.diagnostics;
|
|
795
1003
|
let items = collectedItems.items;
|
|
796
1004
|
const lights = collectRenderLights(source);
|
|
@@ -807,7 +1015,7 @@ export class Renderer {
|
|
|
807
1015
|
}
|
|
808
1016
|
const sourceCameraPosition = collectSourceCameraPosition(source);
|
|
809
1017
|
const explicitRenderTarget = collectRenderTarget(source);
|
|
810
|
-
validateExplicitRenderTarget(explicitRenderTarget
|
|
1018
|
+
validateExplicitRenderTarget(explicitRenderTarget);
|
|
811
1019
|
let cameraPosition = sourceCameraPosition ?? resolvedCamera?.cameraPosition;
|
|
812
1020
|
// C-05/T0-07: decide the frame's output route before the hooks exist, so
|
|
813
1021
|
// contributors see the same v2/legacy resource names the graph writes.
|
|
@@ -831,6 +1039,7 @@ export class Renderer {
|
|
|
831
1039
|
}
|
|
832
1040
|
}
|
|
833
1041
|
let postprocess = collectPostprocess(source);
|
|
1042
|
+
this.assertViewsSupported(postprocess);
|
|
834
1043
|
if (!postprocess?.temporal || (!postprocess.motionBlur && !postprocess.taa)) {
|
|
835
1044
|
this.temporalHistory.reset();
|
|
836
1045
|
this.lastTemporalCameraPosition = undefined;
|
|
@@ -955,7 +1164,7 @@ export class Renderer {
|
|
|
955
1164
|
else {
|
|
956
1165
|
this.device.setRenderTarget(explicitRenderTarget ?? null);
|
|
957
1166
|
}
|
|
958
|
-
this.
|
|
1167
|
+
this.clearAndApplyView(qrOutput && !postprocess ? this.outputClearColor ?? this.clearColor : this.clearColor);
|
|
959
1168
|
if (environmentBackground) {
|
|
960
1169
|
this.graph.addPass(new EnvironmentBackgroundPass({
|
|
961
1170
|
...environmentBackground,
|
|
@@ -1013,6 +1222,9 @@ export class Renderer {
|
|
|
1013
1222
|
cameraPosition,
|
|
1014
1223
|
cameraViewMatrix: resolvedCamera?.viewMatrix,
|
|
1015
1224
|
cameraViewProjectionMatrix: rasterViewProjection,
|
|
1225
|
+
// SH-02: `material.custom` frame built-ins (u_projectionMatrix, u_time).
|
|
1226
|
+
auraFrameCamera: resolvedCamera?.projectionMatrix ? { projectionMatrix: resolvedCamera.projectionMatrix } : undefined,
|
|
1227
|
+
timeSeconds: typeof performance !== "undefined" ? performance.now() / 1000 : Date.now() / 1000,
|
|
1016
1228
|
outputColorSpace: "linear",
|
|
1017
1229
|
shaderLibrary: this.shaderLibrary,
|
|
1018
1230
|
backgroundCoverage: coverage,
|
|
@@ -1054,6 +1266,9 @@ export class Renderer {
|
|
|
1054
1266
|
cameraPosition,
|
|
1055
1267
|
cameraViewMatrix: resolvedCamera?.viewMatrix,
|
|
1056
1268
|
cameraViewProjectionMatrix: rasterViewProjection,
|
|
1269
|
+
// SH-02: `material.custom` frame built-ins (u_projectionMatrix, u_time).
|
|
1270
|
+
auraFrameCamera: resolvedCamera?.projectionMatrix ? { projectionMatrix: resolvedCamera.projectionMatrix } : undefined,
|
|
1271
|
+
timeSeconds: typeof performance !== "undefined" ? performance.now() / 1000 : Date.now() / 1000,
|
|
1057
1272
|
outputColorSpace: postprocess ? "linear" : "srgb",
|
|
1058
1273
|
shaderLibrary: this.shaderLibrary
|
|
1059
1274
|
}));
|
|
@@ -1135,6 +1350,7 @@ export class Renderer {
|
|
|
1135
1350
|
target.dispose();
|
|
1136
1351
|
}
|
|
1137
1352
|
}
|
|
1353
|
+
this.submittedFrames += 1;
|
|
1138
1354
|
this.submittedShadowFrameId += 1;
|
|
1139
1355
|
if (this.lastShadowEvidence)
|
|
1140
1356
|
this.lastShadowEvidence = { ...this.lastShadowEvidence, submissionFrameId: this.submittedShadowFrameId };
|
|
@@ -1548,7 +1764,7 @@ function inferInitialCanvasDimension(canvas, axis) {
|
|
|
1548
1764
|
function collectRenderTarget(source) {
|
|
1549
1765
|
return source instanceof Scene || isIterable(source) ? undefined : source.renderTarget;
|
|
1550
1766
|
}
|
|
1551
|
-
function validateExplicitRenderTarget(target
|
|
1767
|
+
function validateExplicitRenderTarget(target) {
|
|
1552
1768
|
if (!target)
|
|
1553
1769
|
return;
|
|
1554
1770
|
if (target.disposed) {
|
|
@@ -1556,25 +1772,81 @@ function validateExplicitRenderTarget(target, width, height) {
|
|
|
1556
1772
|
renderTarget: target.label
|
|
1557
1773
|
});
|
|
1558
1774
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
renderTarget: target.label,
|
|
1562
|
-
targetWidth: target.width,
|
|
1563
|
-
targetHeight: target.height,
|
|
1564
|
-
rendererWidth: width,
|
|
1565
|
-
rendererHeight: height
|
|
1566
|
-
});
|
|
1567
|
-
}
|
|
1775
|
+
// threejs-parity L7/SH-06: no viewport-dimensions check — a `{ target }`
|
|
1776
|
+
// frame executes at the target's own size (`adoptExplicitTargetSize`).
|
|
1568
1777
|
}
|
|
1569
1778
|
function normalizeRendererInput(sourceOrInput, camera) {
|
|
1570
1779
|
if (isRendererInput(sourceOrInput)) {
|
|
1571
|
-
|
|
1780
|
+
const target = sourceOrInput.target;
|
|
1781
|
+
const source = target ? withExplicitRenderTarget(sourceOrInput.source, target) : sourceOrInput.source;
|
|
1782
|
+
return { source, camera: camera ?? sourceOrInput.camera };
|
|
1572
1783
|
}
|
|
1573
1784
|
return { source: sourceOrInput, ...(camera ? { camera } : {}) };
|
|
1574
1785
|
}
|
|
1786
|
+
function collectRendererViews(sourceOrInput) {
|
|
1787
|
+
if (sourceOrInput instanceof Scene || isIterable(sourceOrInput))
|
|
1788
|
+
return undefined;
|
|
1789
|
+
const views = isRendererInput(sourceOrInput)
|
|
1790
|
+
? sourceOrInput.views ?? (sourceOrInput.source instanceof Scene || isIterable(sourceOrInput.source) ? undefined : sourceOrInput.source.views)
|
|
1791
|
+
: sourceOrInput.views;
|
|
1792
|
+
if (!views)
|
|
1793
|
+
return undefined;
|
|
1794
|
+
if (views.length === 0) {
|
|
1795
|
+
throw new RenderDeviceError("Renderer views must contain at least one view.", "INVALID_VIEWPORT", { views: 0 });
|
|
1796
|
+
}
|
|
1797
|
+
return views;
|
|
1798
|
+
}
|
|
1799
|
+
function collectSingleViewState(sourceOrInput) {
|
|
1800
|
+
if (sourceOrInput instanceof Scene || isIterable(sourceOrInput) || !isRendererInput(sourceOrInput))
|
|
1801
|
+
return null;
|
|
1802
|
+
if (!sourceOrInput.viewport && !sourceOrInput.scissor)
|
|
1803
|
+
return null;
|
|
1804
|
+
const viewport = sourceOrInput.viewport;
|
|
1805
|
+
const scissor = sourceOrInput.scissor ?? viewport;
|
|
1806
|
+
return {
|
|
1807
|
+
viewport: viewport ?? scissor,
|
|
1808
|
+
scissor,
|
|
1809
|
+
skipClear: false
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
function viewFrameInput(sourceOrInput, camera, view) {
|
|
1813
|
+
const normalized = normalizeRendererInput(sourceOrInput, camera);
|
|
1814
|
+
const base = normalized.source instanceof Scene
|
|
1815
|
+
? { scene: normalized.source }
|
|
1816
|
+
: isIterable(normalized.source) ? { renderItems: normalized.source } : normalized.source;
|
|
1817
|
+
const { views: _views, ...rest } = base;
|
|
1818
|
+
return {
|
|
1819
|
+
source: { ...rest, ...(view.cameraPosition ? { cameraPosition: view.cameraPosition } : {}) },
|
|
1820
|
+
camera: view.camera
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
function validateViewRect(label, rect, targetWidth, targetHeight) {
|
|
1824
|
+
const values = [rect.x, rect.y, rect.width, rect.height];
|
|
1825
|
+
if (values.some((value) => !Number.isFinite(value))) {
|
|
1826
|
+
throw new RenderDeviceError(`Renderer ${label} values must be finite.`, "INVALID_VIEWPORT", { label, rect });
|
|
1827
|
+
}
|
|
1828
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
1829
|
+
throw new RenderDeviceError(`Renderer ${label} dimensions must be positive.`, "INVALID_VIEWPORT", { label, rect });
|
|
1830
|
+
}
|
|
1831
|
+
if (rect.x < 0 || rect.y < 0 || rect.x + rect.width > targetWidth || rect.y + rect.height > targetHeight) {
|
|
1832
|
+
throw new RenderDeviceError(`Renderer ${label} must lie inside the ${targetWidth}x${targetHeight} target.`, "INVALID_VIEWPORT", { label, rect, targetWidth, targetHeight });
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1575
1835
|
function isRendererInput(value) {
|
|
1576
1836
|
return !(value instanceof Scene) && !isIterable(value) && isRecord(value) && "source" in value;
|
|
1577
1837
|
}
|
|
1838
|
+
/**
|
|
1839
|
+
* threejs-parity L7/SH-06: fold a `{ target }` render input into the source's
|
|
1840
|
+
* `renderTarget` carry — Scene wraps as `{ scene }`, iterables as
|
|
1841
|
+
* `{ renderItems }`, a RenderSource keeps every other authored field.
|
|
1842
|
+
*/
|
|
1843
|
+
function withExplicitRenderTarget(source, target) {
|
|
1844
|
+
if (source instanceof Scene)
|
|
1845
|
+
return { scene: source, renderTarget: target };
|
|
1846
|
+
if (isIterable(source))
|
|
1847
|
+
return { renderItems: source, renderTarget: target };
|
|
1848
|
+
return { ...source, renderTarget: target };
|
|
1849
|
+
}
|
|
1578
1850
|
function isRecord(value) {
|
|
1579
1851
|
return typeof value === "object" && value !== null;
|
|
1580
1852
|
}
|
|
@@ -1605,7 +1877,7 @@ function collectSourceCameraPosition(source) {
|
|
|
1605
1877
|
export function collectRenderItems(source, cameraViewProjection, camera) {
|
|
1606
1878
|
return collectRenderItemsWithDiagnostics(source, cameraViewProjection, camera).items;
|
|
1607
1879
|
}
|
|
1608
|
-
function collectRenderItemsWithDiagnostics(source, cameraViewProjection, camera, keepCulledShadowCasters = false) {
|
|
1880
|
+
function collectRenderItemsWithDiagnostics(source, cameraViewProjection, camera, keepCulledShadowCasters = false, cameraLayerMask) {
|
|
1609
1881
|
const diagnostics = createRenderCollectionDiagnostics();
|
|
1610
1882
|
// 02-S5 (A3D_QR_LIGHTING): frustum-culled items that still cast. A caster
|
|
1611
1883
|
// outside the view (a pole behind the camera under a low sun) must reach
|
|
@@ -1618,7 +1890,7 @@ function collectRenderItemsWithDiagnostics(source, cameraViewProjection, camera,
|
|
|
1618
1890
|
return { items, diagnostics, culledShadowCasters: culledShadowCasters ?? [] };
|
|
1619
1891
|
}
|
|
1620
1892
|
if (source instanceof Scene) {
|
|
1621
|
-
const items = collectSceneRenderItems(source, {}, cameraViewProjection, camera, diagnostics, culledShadowCasters);
|
|
1893
|
+
const items = collectSceneRenderItems(source, {}, cameraViewProjection, camera, diagnostics, culledShadowCasters, cameraLayerMask);
|
|
1622
1894
|
return { items, diagnostics, culledShadowCasters: culledShadowCasters ?? [], ...(camera ? { cullingFrustum: camera.frustum } : {}) };
|
|
1623
1895
|
}
|
|
1624
1896
|
const items = [];
|
|
@@ -1626,7 +1898,7 @@ function collectRenderItemsWithDiagnostics(source, cameraViewProjection, camera,
|
|
|
1626
1898
|
if (source.scene) {
|
|
1627
1899
|
if (source.frustumCulling !== false && camera)
|
|
1628
1900
|
cullingFrustum = camera.frustum;
|
|
1629
|
-
items.push(...collectSceneRenderItems(source.scene, source, cameraViewProjection, source.frustumCulling === false ? undefined : camera, diagnostics, culledShadowCasters));
|
|
1901
|
+
items.push(...collectSceneRenderItems(source.scene, source, cameraViewProjection, source.frustumCulling === false ? undefined : camera, diagnostics, culledShadowCasters, cameraLayerMask));
|
|
1630
1902
|
}
|
|
1631
1903
|
const explicitItems = [];
|
|
1632
1904
|
if (source.collectRenderItems) {
|
|
@@ -1638,14 +1910,14 @@ function collectRenderItemsWithDiagnostics(source, cameraViewProjection, camera,
|
|
|
1638
1910
|
if (explicitItems.length > 0) {
|
|
1639
1911
|
const explicitFrustum = explicitCullingFrustum(source, cameraViewProjection, camera);
|
|
1640
1912
|
cullingFrustum ??= explicitFrustum;
|
|
1641
|
-
const visibleItems = cullExplicitRenderItems(explicitItems, explicitFrustum, diagnostics, culledShadowCasters);
|
|
1913
|
+
const visibleItems = cullExplicitRenderItems(explicitItems, explicitFrustum, diagnostics, culledShadowCasters, cameraLayerMask ?? camera?.layerMask);
|
|
1642
1914
|
const optimizedItems = applyRendererOwnedStaticBatching(source, applyRendererOwnedStaticMeshConsolidation(source, visibleItems));
|
|
1643
1915
|
const projectedItems = applyViewProjection(optimizedItems, cameraViewProjection);
|
|
1644
1916
|
items.push(...projectedItems);
|
|
1645
1917
|
}
|
|
1646
1918
|
return { items, diagnostics, culledShadowCasters: culledShadowCasters ?? [], ...(cullingFrustum ? { cullingFrustum } : {}) };
|
|
1647
1919
|
}
|
|
1648
|
-
function collectSceneRenderItems(scene, source, cameraViewProjection, camera, diagnostics, culledShadowCasters) {
|
|
1920
|
+
function collectSceneRenderItems(scene, source, cameraViewProjection, camera, diagnostics, culledShadowCasters, explicitCameraLayerMask) {
|
|
1649
1921
|
scene.updateWorldTransforms();
|
|
1650
1922
|
const renderables = scene.collectRenderables();
|
|
1651
1923
|
if (renderables.length === 0) {
|
|
@@ -1657,7 +1929,12 @@ function collectSceneRenderItems(scene, source, cameraViewProjection, camera, di
|
|
|
1657
1929
|
});
|
|
1658
1930
|
}
|
|
1659
1931
|
const items = [];
|
|
1932
|
+
const cameraLayerMask = explicitCameraLayerMask ?? camera?.layerMask;
|
|
1660
1933
|
for (const { node, renderable } of renderables) {
|
|
1934
|
+
// threejs-parity L7/SG-10: three's camera.layers test — a node whose
|
|
1935
|
+
// layers do not intersect the camera mask is absent from the frame.
|
|
1936
|
+
if (cameraLayerMask !== undefined && (node.layerMask & cameraLayerMask) === 0)
|
|
1937
|
+
continue;
|
|
1661
1938
|
const geometry = lookupRenderResource(source.geometryLibrary, renderable.geometry);
|
|
1662
1939
|
const material = lookupRenderResource(source.materialLibrary, renderable.material);
|
|
1663
1940
|
if (!geometry) {
|
|
@@ -1687,7 +1964,9 @@ function collectSceneRenderItems(scene, source, cameraViewProjection, camera, di
|
|
|
1687
1964
|
if (camera)
|
|
1688
1965
|
diagnostics.frustumTestedObjects += 1;
|
|
1689
1966
|
}
|
|
1690
|
-
|
|
1967
|
+
// threejs-parity L7/SG-09: `node.frustumCulled === false` skips the test
|
|
1968
|
+
// (three.js `object.frustumCulled` semantics).
|
|
1969
|
+
const culled = camera !== undefined && node.frustumCulled !== false && !camera.frustum.intersectsBox(bounds.toMathBox());
|
|
1691
1970
|
if (culled && diagnostics)
|
|
1692
1971
|
diagnostics.culledObjects += 1;
|
|
1693
1972
|
if (culled && !culledShadowCasters)
|
|
@@ -1699,6 +1978,15 @@ function collectSceneRenderItems(scene, source, cameraViewProjection, camera, di
|
|
|
1699
1978
|
material,
|
|
1700
1979
|
label: node.name,
|
|
1701
1980
|
modelMatrix,
|
|
1981
|
+
// threejs-parity L7/SG-09: carry the node's parity fields onto the item
|
|
1982
|
+
// so sort/cull/onBeforeRender behave like three's Object3D.
|
|
1983
|
+
renderOrder: node.renderOrder,
|
|
1984
|
+
layerMask: node.layerMask,
|
|
1985
|
+
frustumCulled: node.frustumCulled,
|
|
1986
|
+
userData: node.userData,
|
|
1987
|
+
...(node.onBeforeRender
|
|
1988
|
+
? { onBeforeRender: () => node.onBeforeRender(undefined, node) }
|
|
1989
|
+
: {}),
|
|
1702
1990
|
normalMatrix: normalMatrixFromModel(modelMatrix),
|
|
1703
1991
|
modelViewProjectionMatrix: multiplyMat4(cameraViewProjection ?? identityMat4(), modelMatrix),
|
|
1704
1992
|
...(renderable.skinning ? { skinning: renderable.skinning } : {}),
|
|
@@ -2055,6 +2343,7 @@ function resolveCamera(source, camera, viewport, options = {}) {
|
|
|
2055
2343
|
if (!resolved) {
|
|
2056
2344
|
return undefined;
|
|
2057
2345
|
}
|
|
2346
|
+
const resolvedLayerMask = typeof resolved.layerMask === "number" ? { layerMask: resolved.layerMask } : {};
|
|
2058
2347
|
const resize = maybeCameraResize(resolved);
|
|
2059
2348
|
if (viewport && resize) {
|
|
2060
2349
|
resize(viewport.width, viewport.height);
|
|
@@ -2064,7 +2353,10 @@ function resolveCamera(source, camera, viewport, options = {}) {
|
|
|
2064
2353
|
const viewMatrix = resolved.viewMatrix ? toMat4(resolved.viewMatrix, "viewMatrix") : undefined;
|
|
2065
2354
|
return {
|
|
2066
2355
|
viewProjectionMatrix: toMat4(resolved.viewProjectionMatrix, "viewProjectionMatrix"),
|
|
2356
|
+
...resolvedLayerMask,
|
|
2067
2357
|
...(viewMatrix ? { viewMatrix } : {}),
|
|
2358
|
+
// threejs-parity SH-02: custom-shader `u_projectionMatrix` source.
|
|
2359
|
+
...(resolved.projectionMatrix ? { projectionMatrix: toMat4(resolved.projectionMatrix, "projectionMatrix") } : {}),
|
|
2068
2360
|
...(resolved instanceof Camera
|
|
2069
2361
|
? { camera: resolved, cameraPosition: cameraWorldPosition(resolved) }
|
|
2070
2362
|
: viewMatrix
|
|
@@ -2074,9 +2366,12 @@ function resolveCamera(source, camera, viewport, options = {}) {
|
|
|
2074
2366
|
}
|
|
2075
2367
|
if (resolved.projectionMatrix && resolved.viewMatrix) {
|
|
2076
2368
|
const viewMatrix = toMat4(resolved.viewMatrix, "viewMatrix");
|
|
2369
|
+
const projectionMatrix = toMat4(resolved.projectionMatrix, "projectionMatrix");
|
|
2077
2370
|
return {
|
|
2078
|
-
viewProjectionMatrix: multiplyMat4(
|
|
2371
|
+
viewProjectionMatrix: multiplyMat4(projectionMatrix, viewMatrix),
|
|
2372
|
+
...resolvedLayerMask,
|
|
2079
2373
|
viewMatrix,
|
|
2374
|
+
projectionMatrix,
|
|
2080
2375
|
...(resolved instanceof Camera
|
|
2081
2376
|
? { camera: resolved, cameraPosition: cameraWorldPosition(resolved) }
|
|
2082
2377
|
: { cameraPosition: cameraPositionFromViewMatrix(viewMatrix) })
|