rayzee 11.7.0 → 11.9.0
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/README.md +31 -6
- package/dist/{ArchiveImporter-DYcRXeKE.js → ArchiveImporter-BJxszU_k.js} +2353 -1356
- package/dist/{BidirectionalIntegrator-BvjRpbTP.js → BidirectionalIntegrator-DWg66vAd.js} +302 -301
- package/dist/{BufferLayout-xPRH9jYD.js → BufferLayout-Bzgccg1o.js} +1 -0
- package/dist/{ColorManagement-oeEk7oSu.js → ColorManagement-B_HjAiy2.js} +1 -1
- package/dist/{EngineIssues-CIZnXHsF.js → EngineIssues-CfJiBaNj.js} +19 -8
- package/dist/{NeuralRetouch-ClaMiKYg.js → NeuralRetouch-BD9E-Yht.js} +2 -2
- package/dist/{NeuralSuperRes-WNEXun9s.js → NeuralSuperRes-C8Mf3tJX.js} +2 -2
- package/dist/{PathTracer-B9wZPPVL.js → PathTracer-DeE9UJ1n.js} +3530 -3402
- package/dist/{PhysicalSky-DcjItqKi.js → PhysicalSky-cKy75-oR.js} +45 -45
- package/dist/{RayzeeRenderer-p-NeE6E2.js → RayzeeRenderer-CzLyBA3G.js} +378 -374
- package/dist/SceneMetadata-C17UfOEp.js +183 -0
- package/dist/{SolarLimb-bZE6y63S.js → SolarLimb-DRYy8tln.js} +184 -172
- package/dist/{ToneMapGPU-sAs8wdgI.js → ToneMapGPU-4GPwoG_c.js} +13 -6
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/color.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/addons/storage.es.js +1 -1
- package/dist/{openStorage-Fe9BoS1_.js → openStorage-CdyVjmps.js} +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +821 -794
- package/dist/rayzee.umd.js +112 -103
- package/dist/{sceneCachePolicy-B9w3BnaP.js → sceneCachePolicy-ywbGKHD3.js} +9 -6
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -0
- package/src/EngineIssues.js +21 -6
- package/src/Passes/AIUpscaler.js +4 -0
- package/src/Passes/OIDNDenoiser.js +8 -6
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/PathTracerApp.js +10 -1
- package/src/Processor/ArchiveImporter.js +106 -27
- package/src/Processor/AssetLoader.js +22 -0
- package/src/Processor/BufferLayout.js +3 -3
- package/src/Processor/GeometryExtractor.js +12 -4
- package/src/Processor/LightSerializer.js +6 -2
- package/src/Processor/MaterialPacking.js +2 -2
- package/src/Processor/PBRT/PBRTMaterials.js +49 -13
- package/src/Processor/PBRT/PBRTParser.js +144 -22
- package/src/Processor/PBRT/PBRTSceneBuilder.js +287 -67
- package/src/Processor/PBRT/PBRTTextureBake.js +191 -9
- package/src/Processor/PBRT/index.js +19 -4
- package/src/Processor/PlatformImageLoader.js +9 -0
- package/src/Processor/SceneProcessor.js +42 -26
- package/src/Processor/TextureCreator.js +44 -4
- package/src/Processor/ToneMapCPU.js +34 -5
- package/src/Processor/ToneMapGPU.js +15 -9
- package/src/Processor/USD/USDScene.js +981 -17
- package/src/RayzeeRenderer.js +85 -28
- package/src/Stages/AutoExposure.js +21 -8
- package/src/Stages/Compositor.js +39 -1
- package/src/Stages/DenoiserSettings.js +0 -4
- package/src/Stages/LocalExposure.js +44 -18
- package/src/Stages/NRD.js +53 -24
- package/src/Stages/NormalDepth.js +12 -6
- package/src/Stages/PathTracer.js +27 -1
- package/src/Stages/PathTracerStage.js +14 -2
- package/src/Storage/SceneGraphCodec.js +6 -3
- package/src/TSL/BVHTraversal.js +33 -20
- package/src/TSL/CameraRay.js +12 -1
- package/src/TSL/Common.js +25 -8
- package/src/TSL/DebugKernel.js +3 -2
- package/src/TSL/Displacement.js +66 -113
- package/src/TSL/FinalWriteKernel.js +4 -7
- package/src/TSL/GenerateKernel.js +6 -2
- package/src/TSL/LightSplatKernel.js +7 -4
- package/src/TSL/LightsCore.js +6 -2
- package/src/TSL/LightsDirect.js +33 -11
- package/src/TSL/LightsSampling.js +45 -12
- package/src/TSL/SceneResources.js +19 -0
- package/src/TSL/ShadeKernel.js +3 -4
- package/src/TSL/Struct.js +1 -0
- package/src/integrators/BidirectionalIntegrator.js +1 -0
- package/src/managers/CameraManager.js +9 -0
- package/src/managers/DenoisingManager.js +1 -0
- package/src/managers/InteractionManager.js +6 -0
- package/src/managers/MaterialDataManager.js +11 -0
- package/src/managers/UniformManager.js +3 -1
- package/dist/SceneMetadata-D2Sh-0e-.js +0 -110
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as e } from "./EngineEvents-CLckhYbL.js";
|
|
2
2
|
import { a as t, i as n, r } from "./Logger-dDDGj9Av.js";
|
|
3
|
-
import { f as i, v as a, y as o } from "./BufferLayout-
|
|
4
|
-
import { n as s } from "./EngineIssues-
|
|
3
|
+
import { f as i, v as a, y as o } from "./BufferLayout-Bzgccg1o.js";
|
|
4
|
+
import { n as s } from "./EngineIssues-CfJiBaNj.js";
|
|
5
5
|
import { n as c, r as l } from "./shared-BGBcSxBQ.js";
|
|
6
6
|
import { n as u } from "./AssetConfig-DSEntE65.js";
|
|
7
7
|
import { BackSide as d, BufferAttribute as f, Color as p, DoubleSide as m, FrontSide as h, Matrix3 as g, Matrix4 as _, RGBAFormat as v, Vector2 as y, Vector3 as b } from "three";
|
|
@@ -230,6 +230,7 @@ var E = de({
|
|
|
230
230
|
normalScale: [1, 1],
|
|
231
231
|
bumpScale: 1,
|
|
232
232
|
displacementScale: 1,
|
|
233
|
+
displacementBias: 0,
|
|
233
234
|
transparent: 0,
|
|
234
235
|
alphaTest: 0,
|
|
235
236
|
alphaMode: 0,
|
|
@@ -1069,7 +1070,7 @@ var Be = class {
|
|
|
1069
1070
|
this.expandedTriangleCount += u.count, this.meshTriangleRanges.push(u), u.count > 0 && !l && a && this._geometryRanges.set(r, {
|
|
1070
1071
|
...u,
|
|
1071
1072
|
meshIndex: n
|
|
1072
|
-
}), this._recordPlacements(e, n, l, c,
|
|
1073
|
+
}), u.count > 0 && (this._recordPlacements(e, n, l, c, a), this._onRange?.(n, u));
|
|
1073
1074
|
}
|
|
1074
1075
|
_recordPlacements(e, t, n = !1, r = !1, i = !1) {
|
|
1075
1076
|
let a = e.matrixWorld.elements, o = this.instanceSource;
|
|
@@ -1232,6 +1233,7 @@ var Be = class {
|
|
|
1232
1233
|
normalScale: o("normalScale"),
|
|
1233
1234
|
bumpScale: o("bumpScale"),
|
|
1234
1235
|
displacementScale: o("displacementScale"),
|
|
1236
|
+
displacementBias: o("displacementBias"),
|
|
1235
1237
|
transparent: +!!e.transparent,
|
|
1236
1238
|
alphaTest: o("alphaTest"),
|
|
1237
1239
|
alphaMode: Pe(e),
|
|
@@ -1298,9 +1300,10 @@ var Be = class {
|
|
|
1298
1300
|
}
|
|
1299
1301
|
extractGeometry(e, t, n, r = !1, i = !1) {
|
|
1300
1302
|
e.updateMatrix(), e.updateMatrixWorld();
|
|
1301
|
-
let a = e.geometry;
|
|
1303
|
+
let a = e.geometry, o = a.attributes.position;
|
|
1304
|
+
if (!o) return;
|
|
1302
1305
|
a.attributes.normal || a.computeVertexNormals();
|
|
1303
|
-
let
|
|
1306
|
+
let s = a.attributes.normal, c = a.attributes.uv, l = a.index ? a.index.array : null, u = Math.floor((l ? l.length : o.count) / 3);
|
|
1304
1307
|
if (i) {
|
|
1305
1308
|
let r = e.count ?? e.instanceMatrix.array.length / 16, i = this._matrixPool.mat4, a = new _();
|
|
1306
1309
|
for (let d = 0; d < r; d++) {
|
|
@@ -1594,7 +1597,7 @@ var vt = class {
|
|
|
1594
1597
|
samples: this.samples.slice()
|
|
1595
1598
|
};
|
|
1596
1599
|
}
|
|
1597
|
-
}, yt = "11.
|
|
1600
|
+
}, yt = "11.9.0", $ = 1e4, bt = 15e5;
|
|
1598
1601
|
function xt(e, t, n = $) {
|
|
1599
1602
|
return e >= n && t / bt < e / 3;
|
|
1600
1603
|
}
|
package/package.json
CHANGED
package/src/EngineDefaults.js
CHANGED
package/src/EngineIssues.js
CHANGED
|
@@ -6,10 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export const ISSUE_CODES = Object.freeze( {
|
|
8
8
|
ADAPTER_SOFTWARE: 'adapter.software',
|
|
9
|
+
ADAPTER_UNAVAILABLE: 'adapter.unavailable',
|
|
10
|
+
DEVICE_UNAVAILABLE: 'device.unavailable',
|
|
11
|
+
WEBGPU_UNAVAILABLE: 'webgpu.unavailable',
|
|
9
12
|
ASSET_UNREACHABLE: 'asset.unreachable',
|
|
10
13
|
ASSET_AMBIGUOUS_ENTRY: 'asset.ambiguous_entry',
|
|
11
14
|
ASSET_ARCHIVE_TOO_LARGE: 'asset.archive_too_large',
|
|
12
15
|
ASSET_ENTRY_TOO_LARGE: 'asset.entry_too_large',
|
|
16
|
+
ASSET_PARAMETERS_IGNORED: 'asset.parameters_ignored',
|
|
13
17
|
TEXTURE_BUILD_FAILED: 'texture.build_failed',
|
|
14
18
|
TEXTURE_PROCESSING_FALLBACK: 'texture.processing_fallback',
|
|
15
19
|
TEXTURE_LIMIT_EXCEEDED: 'texture.limit_exceeded',
|
|
@@ -42,9 +46,9 @@ export const ISSUE_SEVERITY = Object.freeze( { ERROR: 'error', WARNING: 'warning
|
|
|
42
46
|
|
|
43
47
|
export class EngineIssueError extends Error {
|
|
44
48
|
|
|
45
|
-
constructor( issue ) {
|
|
49
|
+
constructor( issue, options ) {
|
|
46
50
|
|
|
47
|
-
super( `[${issue.code}] ${issue.message}
|
|
51
|
+
super( `[${issue.code}] ${issue.message}`, options );
|
|
48
52
|
this.name = 'EngineIssueError';
|
|
49
53
|
this.code = issue.code;
|
|
50
54
|
this.detail = issue.detail;
|
|
@@ -67,13 +71,24 @@ export class IssueLog {
|
|
|
67
71
|
/** @throws {EngineIssueError} when strict and severity is 'error' */
|
|
68
72
|
record( code, message, detail = {}, severity = ISSUE_SEVERITY.ERROR ) {
|
|
69
73
|
|
|
70
|
-
const issue =
|
|
71
|
-
this.
|
|
74
|
+
const issue = this._add( code, message, detail, severity );
|
|
75
|
+
if ( this.strict && severity === ISSUE_SEVERITY.ERROR ) throw new EngineIssueError( issue );
|
|
76
|
+
return issue;
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
}
|
|
74
79
|
|
|
75
|
-
|
|
80
|
+
/** For what nothing can continue past: records an error and throws it, strict or not. */
|
|
81
|
+
fail( code, message, detail = {}, cause ) {
|
|
76
82
|
|
|
83
|
+
throw new EngineIssueError( this._add( code, message, detail, ISSUE_SEVERITY.ERROR ), cause === undefined ? undefined : { cause } );
|
|
84
|
+
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_add( code, message, detail, severity ) {
|
|
88
|
+
|
|
89
|
+
const issue = Object.freeze( { code, message, detail, severity, at: Date.now() } );
|
|
90
|
+
this._entries.push( issue );
|
|
91
|
+
this._onIssue?.( issue );
|
|
77
92
|
return issue;
|
|
78
93
|
|
|
79
94
|
}
|
package/src/Passes/AIUpscaler.js
CHANGED
|
@@ -78,6 +78,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
78
78
|
* @param {Function} [options.getGPUTextures] - Returns { color: GPUTexture } for HDR path
|
|
79
79
|
* @param {Function} [options.getExposure] - Returns current exposure multiplier
|
|
80
80
|
* @param {Function} [options.getToneMapping] - Returns Three.js ToneMapping constant
|
|
81
|
+
* @param {Function} [options.getTransparentBackground] - Whether the HDR path keeps the render's coverage as alpha
|
|
81
82
|
* @param {number} [options.tileSize] - Override default tile size
|
|
82
83
|
*/
|
|
83
84
|
constructor( output, renderer, options = {} ) {
|
|
@@ -101,6 +102,7 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
101
102
|
this.getToneMapping = options.getToneMapping || ( () => ACESFilmicToneMapping );
|
|
102
103
|
this.getSaturation = options.getSaturation || ( () => 1.0 );
|
|
103
104
|
this.getDisplayGain = options.getDisplayGain || ( () => null );
|
|
105
|
+
this.getTransparentBackground = options.getTransparentBackground || ( () => false );
|
|
104
106
|
|
|
105
107
|
// Configuration
|
|
106
108
|
this.enabled = false;
|
|
@@ -506,6 +508,8 @@ export class AIUpscaler extends EventDispatcher {
|
|
|
506
508
|
gain: this.getDisplayGain(),
|
|
507
509
|
} );
|
|
508
510
|
this._toneMapper.release();
|
|
511
|
+
// The render's alpha is coverage; the canvas shows an opaque backdrop opaque, and so does the upscale.
|
|
512
|
+
if ( ! this.getTransparentBackground() ) planes.fill( 1, 3 * width * height );
|
|
509
513
|
|
|
510
514
|
return { planes, width, height, isHDR: true };
|
|
511
515
|
|
|
@@ -443,10 +443,10 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
443
443
|
|
|
444
444
|
} while ( this._reloadPending );
|
|
445
445
|
|
|
446
|
-
// Fire a start() that arrived mid-load
|
|
447
|
-
//
|
|
448
|
-
// no-op reload still releases it — it used to be stranded by the early return below.
|
|
449
|
-
if ( this._pendingStart && this.
|
|
446
|
+
// Fire a start() that arrived mid-load, unless it was disabled meanwhile. After a failed
|
|
447
|
+
// load too: it denoises nothing but still sends 'end', which its caller waits for. Outside
|
|
448
|
+
// the loop, so a no-op reload still releases it — it used to be stranded by the early return below.
|
|
449
|
+
if ( this._pendingStart && this.enabled ) {
|
|
450
450
|
|
|
451
451
|
const continuous = this._pendingStartContinuous;
|
|
452
452
|
this._pendingStart = false;
|
|
@@ -617,8 +617,6 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
617
617
|
|
|
618
618
|
async execute( continuous = false ) {
|
|
619
619
|
|
|
620
|
-
if ( ! this.enabled || ! this.unet ) return false;
|
|
621
|
-
|
|
622
620
|
// Create abort controller for this execution
|
|
623
621
|
const runId = ++ this._runId;
|
|
624
622
|
this.state.abortController = new AbortController();
|
|
@@ -626,6 +624,10 @@ export class OIDNDenoiser extends EventDispatcher {
|
|
|
626
624
|
|
|
627
625
|
try {
|
|
628
626
|
|
|
627
|
+
// Inside the try, so the finally ends what start() announced: with no weights (a failed
|
|
628
|
+
// load) the Denoising badge, the closing upscaler and a video export waited forever.
|
|
629
|
+
if ( ! this.enabled || ! this.unet ) return false;
|
|
630
|
+
|
|
629
631
|
// The GPU path bails out early — no textures yet, no device — by returning false, and
|
|
630
632
|
// a caller that read that as success published a picture nothing had written to.
|
|
631
633
|
return await this._executeUNetGPU( continuous ) !== false;
|
|
@@ -221,7 +221,7 @@ fn main( @builtin(global_invocation_id) gid: vec3<u32> ) {
|
|
|
221
221
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
let rough = textureLoad( shading, p, 0 ).w;
|
|
224
|
+
let rough = abs( textureLoad( shading, p, 0 ).w );
|
|
225
225
|
var cap = mix( P.smoothLength, P.maxLength, smoothstep( P.roughStart, P.roughEnd, rough ) );
|
|
226
226
|
if ( movedPx ) { cap = min( cap, P.movedLength ); }
|
|
227
227
|
let newLen = min( len + 1.0, cap );
|
package/src/PathTracerApp.js
CHANGED
|
@@ -5,6 +5,7 @@ import { MotionVector } from './Stages/MotionVector.js';
|
|
|
5
5
|
import { NRD } from './Stages/NRD.js';
|
|
6
6
|
import { AutoExposure, AUTO_EXPOSURE_DEFAULTS } from './Stages/AutoExposure.js';
|
|
7
7
|
import { LocalExposure, LOCAL_EXPOSURE_DEFAULTS } from './Stages/LocalExposure.js';
|
|
8
|
+
import { backdropGain } from './Processor/ToneMapCPU.js';
|
|
8
9
|
import { PRODUCTION_RENDER_CONFIG, INTERACTIVE_RENDER_CONFIG, modePresetSettings } from './EngineDefaults.js';
|
|
9
10
|
import { createLogger } from './utils/Logger.js';
|
|
10
11
|
import { EngineEvents } from './EngineEvents.js';
|
|
@@ -323,6 +324,7 @@ export class PathTracerApp extends RayzeeRenderer {
|
|
|
323
324
|
|
|
324
325
|
if ( this.stages.autoExposure ) this.stages.autoExposure.setCompensation( value );
|
|
325
326
|
else core.applyExposure( value );
|
|
327
|
+
this.stages.compositor?.setBackdropExposure( value );
|
|
326
328
|
|
|
327
329
|
},
|
|
328
330
|
onCameraProjection: ( value ) => {
|
|
@@ -423,7 +425,12 @@ export class PathTracerApp extends RayzeeRenderer {
|
|
|
423
425
|
const le = this.stages.localExposure;
|
|
424
426
|
// A readback outside the loop (headless) can come before the loop would have built it from this image.
|
|
425
427
|
if ( le?.wantsBuild ) le.build( this.pipeline.context );
|
|
426
|
-
|
|
428
|
+
const local = le?.toneGain() ?? null;
|
|
429
|
+
if ( this.pipeline?.context.getState( 'pathtracer:backdrop' ) !== 'color' ) return local;
|
|
430
|
+
|
|
431
|
+
// As the compositor shows it: a solid backdrop at the manual exposure, untouched by auto and local exposure.
|
|
432
|
+
const { toneMappingExposure, toneMapping } = this.renderer;
|
|
433
|
+
return { ...local, backdrop: backdropGain( this.settings.get( 'exposure' ) ?? 1, toneMappingExposure, toneMapping ) };
|
|
427
434
|
|
|
428
435
|
}
|
|
429
436
|
|
|
@@ -1130,6 +1137,8 @@ export class PathTracerApp extends RayzeeRenderer {
|
|
|
1130
1137
|
if ( ! ae ) return;
|
|
1131
1138
|
|
|
1132
1139
|
ae.setCompensation( this.settings.get( 'exposure' ) ?? 1 );
|
|
1140
|
+
// A solid backdrop stays at the manual exposure while auto exposure moves the picture.
|
|
1141
|
+
this.stages.compositor.setBackdropExposure( this.settings.get( 'exposure' ) ?? 1 );
|
|
1133
1142
|
if ( this.stages.localExposure ) this.stages.localExposure.onChange = () => this._refreshFinished();
|
|
1134
1143
|
ae.onChange = () => this._refreshFinished();
|
|
1135
1144
|
|
|
@@ -26,9 +26,10 @@ import { loadPBRTScene, pickEntryPath, VirtualFS, PBRT_BUILD_REVISION } from './
|
|
|
26
26
|
import { loadUSDScene, listUSDRootLayers, listUSDParts, USDFiles, USDStage, USD_LAYER } from './USD/index.js';
|
|
27
27
|
import { pfmTexture } from './PBRT/PFM.js';
|
|
28
28
|
import { extractSceneMetadata } from './SceneMetadata.js';
|
|
29
|
-
import { ISSUE_CODES, ISSUE_SEVERITY } from '../EngineIssues.js';
|
|
29
|
+
import { ISSUE_CODES, ISSUE_SEVERITY, EngineIssueError } from '../EngineIssues.js';
|
|
30
30
|
import { ARCHIVE_FORMATS } from './archiveFormats.js';
|
|
31
|
-
import { withHostWorker } from '../Platform.js';
|
|
31
|
+
import { withHostWorker, getPlatform } from '../Platform.js';
|
|
32
|
+
import { platformImageFromBytes } from './PlatformImageLoader.js';
|
|
32
33
|
|
|
33
34
|
const MTL_TEXTURE_TIMEOUT_MS = 30000;
|
|
34
35
|
const MAIN_MODEL_FILES = [ 'scene.gltf', 'scene.glb', 'model.gltf', 'model.glb', 'main.gltf', 'main.glb', 'asset.gltf', 'asset.glb' ];
|
|
@@ -81,16 +82,17 @@ export class ArchiveImporter {
|
|
|
81
82
|
constructor( loader ) {
|
|
82
83
|
|
|
83
84
|
this.loader = loader;
|
|
84
|
-
this.
|
|
85
|
+
this._store = null;
|
|
85
86
|
this.lastPBRTStats = null;
|
|
86
87
|
this.lastUSDStats = null;
|
|
87
88
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
/**
|
|
91
|
+
/** Abandons a parsed scene the engine has not accepted: the model it was for is gone, or its load failed. */
|
|
91
92
|
release() {
|
|
92
93
|
|
|
93
|
-
this.
|
|
94
|
+
this._store?.decide( false );
|
|
95
|
+
this._store = null;
|
|
94
96
|
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -382,7 +384,8 @@ export class ArchiveImporter {
|
|
|
382
384
|
|
|
383
385
|
/**
|
|
384
386
|
* Stores a freshly parsed scene for the next open. Encoded here, before onModelLoad or the
|
|
385
|
-
* build touch it; written in the background
|
|
387
|
+
* build touch it; written in the background, and visible to an open only once the engine
|
|
388
|
+
* accepts the scene ({@link commitPendingGraph}).
|
|
386
389
|
* @private
|
|
387
390
|
*/
|
|
388
391
|
_storeGraph( key, built, label ) {
|
|
@@ -400,7 +403,7 @@ export class ArchiveImporter {
|
|
|
400
403
|
meshCount: built.meshCount, entryPath: built.entryPath, candidates: built.candidates, frames: built.frames,
|
|
401
404
|
warnings: built.warnings, triangleCount: built.triangleCount, placementCount: built.placementCount,
|
|
402
405
|
mergedShapes: built.mergedShapes, skippedForBudget: built.skippedForBudget, droppedNoTemplate: built.droppedNoTemplate,
|
|
403
|
-
render: built.render ?? null,
|
|
406
|
+
render: built.render ?? null, parameters: built.parameters ?? null,
|
|
404
407
|
},
|
|
405
408
|
} );
|
|
406
409
|
|
|
@@ -412,13 +415,29 @@ export class ArchiveImporter {
|
|
|
412
415
|
|
|
413
416
|
}
|
|
414
417
|
|
|
415
|
-
|
|
418
|
+
let settle;
|
|
419
|
+
const stop = new AbortController();
|
|
420
|
+
const store = {
|
|
421
|
+
key, encoded, label, model: built.group, writing: false, signal: stop.signal,
|
|
422
|
+
parseMs: ( built.parseMs ?? 0 ) + ( built.buildMs ?? 0 ), triangles: built.triangleCount ?? null,
|
|
423
|
+
accepted: new Promise( resolve => {
|
|
424
|
+
|
|
425
|
+
settle = resolve;
|
|
426
|
+
|
|
427
|
+
} ),
|
|
428
|
+
decide: accepted => {
|
|
429
|
+
|
|
430
|
+
settle( accepted );
|
|
431
|
+
if ( ! accepted ) stop.abort();
|
|
432
|
+
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
this._store = store;
|
|
416
436
|
|
|
417
437
|
// A parse slow enough on its own is written now, during the build. Held until the build
|
|
418
438
|
// ends, it kept every array the build replaces alive with it — the float normals and the
|
|
419
439
|
// instance matrices, ~1 GB on the whole Moana subset, at the build's peak.
|
|
420
|
-
if ( worthStoring(
|
|
421
|
-
else this._pendingGraph = pending;
|
|
440
|
+
if ( worthStoring( store.parseMs, encoded.byteLength ) ) this._writeGraph( area, store );
|
|
422
441
|
|
|
423
442
|
}
|
|
424
443
|
|
|
@@ -429,34 +448,57 @@ export class ArchiveImporter {
|
|
|
429
448
|
*/
|
|
430
449
|
flushPendingGraph( buildMs ) {
|
|
431
450
|
|
|
432
|
-
const
|
|
433
|
-
|
|
451
|
+
const store = this._store;
|
|
452
|
+
if ( ! store || store.writing ) return;
|
|
434
453
|
const area = this.loader.storage?.area( ENGINE_AREAS.SCENES );
|
|
435
|
-
if (
|
|
436
|
-
this.
|
|
454
|
+
if ( area && worthStoring( store.parseMs + buildMs, store.encoded.byteLength ) ) this._writeGraph( area, store );
|
|
455
|
+
else this._store = null;
|
|
456
|
+
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* The engine accepted `model`: the scene stored for it is committed once written. Until then the
|
|
461
|
+
* entry has no metadata, so no open can restore it.
|
|
462
|
+
* @param {import('three').Object3D} model - the model whose load the engine finished
|
|
463
|
+
*/
|
|
464
|
+
commitPendingGraph( model ) {
|
|
465
|
+
|
|
466
|
+
const store = this._store;
|
|
467
|
+
if ( ! store || store.model !== model ) return;
|
|
468
|
+
this._store = null;
|
|
469
|
+
store.model = null;
|
|
470
|
+
store.decide( store.writing );
|
|
437
471
|
|
|
438
472
|
}
|
|
439
473
|
|
|
440
474
|
/** Writes an encoded graph in the background, letting each array go once it is on disk. @private */
|
|
441
|
-
_writeGraph( area,
|
|
475
|
+
_writeGraph( area, store ) {
|
|
442
476
|
|
|
477
|
+
store.writing = true;
|
|
443
478
|
( async () => {
|
|
444
479
|
|
|
445
|
-
|
|
446
|
-
if ( ! writer ) return;
|
|
480
|
+
let writer = null;
|
|
447
481
|
try {
|
|
448
482
|
|
|
449
|
-
await
|
|
450
|
-
|
|
483
|
+
writer = await area.create( store.key, { label: `${store.label} (scene)`, expectedBytes: store.encoded.byteLength } );
|
|
484
|
+
if ( ! writer ) return;
|
|
485
|
+
await writeSceneGraph( writer, store.encoded, { release: true, signal: store.signal } );
|
|
486
|
+
store.encoded = null;
|
|
487
|
+
if ( await store.accepted ) await writer.commit( { triangles: store.triangles } );
|
|
488
|
+
else await writer.abort();
|
|
451
489
|
|
|
452
490
|
} catch ( error ) {
|
|
453
491
|
|
|
454
|
-
await writer
|
|
455
|
-
console.warn( 'Storing the scene failed:', error );
|
|
492
|
+
await writer?.abort().catch( () => {} );
|
|
493
|
+
if ( ! store.signal.aborted ) console.warn( 'Storing the scene failed:', error );
|
|
494
|
+
|
|
495
|
+
} finally {
|
|
496
|
+
|
|
497
|
+
store.encoded = null;
|
|
456
498
|
|
|
457
499
|
}
|
|
458
500
|
|
|
459
|
-
} )()
|
|
501
|
+
} )();
|
|
460
502
|
|
|
461
503
|
}
|
|
462
504
|
|
|
@@ -646,10 +688,11 @@ export class ArchiveImporter {
|
|
|
646
688
|
|
|
647
689
|
const stored = graphKey ? await this._loadStoredGraph( graphKey, source, { imageFromBytes, envFromBytes } ) : null;
|
|
648
690
|
const built = stored ?? await loadPBRTScene( { vfs: zip, source, entryPath, plyParser, imageFromBytes, envFromBytes, ...shape } );
|
|
691
|
+
this.loader._throwDeferred();
|
|
649
692
|
|
|
650
693
|
const { group, environment, animations, report, warnings, meshCount, entryPath: loadedEntry, candidates,
|
|
651
694
|
frames, parseMs, buildMs, triangleCount, placementCount, mergedShapes, skippedForBudget,
|
|
652
|
-
droppedNoTemplate } = built;
|
|
695
|
+
droppedNoTemplate, parameters } = built;
|
|
653
696
|
if ( stored ) console.info( `PBRT scene "${loadedEntry}" opened from storage in ${( buildMs / 1000 ).toFixed( 1 )} s, skipping the parse` );
|
|
654
697
|
|
|
655
698
|
// Phase breakdown for scaling work; the engine's own build timings live in
|
|
@@ -677,6 +720,8 @@ export class ArchiveImporter {
|
|
|
677
720
|
|
|
678
721
|
}
|
|
679
722
|
|
|
723
|
+
if ( parameters ) this._reportParameters( parameters, loadedEntry );
|
|
724
|
+
|
|
680
725
|
// Diagnostics — surface what each mesh resolved to (helps debug black/wrong materials).
|
|
681
726
|
if ( report && report.length && typeof console.table === 'function' ) {
|
|
682
727
|
|
|
@@ -726,10 +771,42 @@ export class ArchiveImporter {
|
|
|
726
771
|
|
|
727
772
|
}
|
|
728
773
|
|
|
774
|
+
/**
|
|
775
|
+
* Every parameter the scene sets that the loader ignored or approximated, once, each with the number of directives
|
|
776
|
+
* that set it: one warning and one console list, never cut short.
|
|
777
|
+
* @private
|
|
778
|
+
*/
|
|
779
|
+
_reportParameters( { unused, approximated }, entry ) {
|
|
780
|
+
|
|
781
|
+
const ignored = Object.keys( unused ).length, approx = Object.keys( approximated ).length;
|
|
782
|
+
if ( ignored + approx === 0 ) return;
|
|
783
|
+
const message = `${ignored} pbrt parameter(s) ignored and ${approx} approximated in "${entry}"`;
|
|
784
|
+
const lines = [
|
|
785
|
+
...Object.entries( unused ).map( ( [ key, count ] ) => ` ignored ${key} ×${count}` ),
|
|
786
|
+
...Object.entries( approximated ).map( ( [ key, count ] ) => ` approximated ${key} ×${count}` )
|
|
787
|
+
];
|
|
788
|
+
console.warn( `PBRT loader: ${message} (× directives that set it)\n${lines.join( '\n' )}` );
|
|
789
|
+
this.loader._issues?.warn( ISSUE_CODES.ASSET_PARAMETERS_IGNORED, message, { entry, unused, approximated } );
|
|
790
|
+
|
|
791
|
+
}
|
|
792
|
+
|
|
729
793
|
/** A texture map from an archive, tagged with its path so a stored scene can read it back. @private */
|
|
730
794
|
async _archiveImage( bytes, fname ) {
|
|
731
795
|
|
|
732
|
-
|
|
796
|
+
let texture;
|
|
797
|
+
try {
|
|
798
|
+
|
|
799
|
+
texture = await this._pbrtTextureFromBytes( bytes, fname );
|
|
800
|
+
|
|
801
|
+
} catch ( error ) {
|
|
802
|
+
|
|
803
|
+
// As a glTF's image: its surfaces render untextured, and a strict host hears of it at the end of the load.
|
|
804
|
+
if ( error instanceof EngineIssueError ) throw error;
|
|
805
|
+
this.loader._reportImageFailure( fname, error );
|
|
806
|
+
return null;
|
|
807
|
+
|
|
808
|
+
}
|
|
809
|
+
|
|
733
810
|
if ( texture ) texture.userData[ ARCHIVE_PATH ] = fname;
|
|
734
811
|
return texture;
|
|
735
812
|
|
|
@@ -803,6 +880,7 @@ export class ArchiveImporter {
|
|
|
803
880
|
maxTriangles: options.maxTriangles, maxPlacements: options.maxPlacements, mergeShapesAbove: options.mergeShapesAbove,
|
|
804
881
|
curveSteps: options.curveSteps, curveSides: options.curveSides, curveTolerance: options.curveTolerance,
|
|
805
882
|
} );
|
|
883
|
+
this.loader._throwDeferred();
|
|
806
884
|
|
|
807
885
|
const { group, environment, warnings, triangleCount, placementCount, skippedForBudget, meshCount, parseMs, buildMs, fitNote } = built;
|
|
808
886
|
this.lastUSDStats = { parseMs, buildMs, loaderMs: performance.now() - start, triangleCount, placementCount, skippedForBudget, meshCount, fitNote };
|
|
@@ -915,9 +993,10 @@ export class ArchiveImporter {
|
|
|
915
993
|
|
|
916
994
|
} else {
|
|
917
995
|
|
|
918
|
-
// png / jpg / webp / gif / bmp
|
|
919
|
-
|
|
920
|
-
tex = new Texture(
|
|
996
|
+
// png / jpg / webp / gif / bmp: the host's decoder where it has one (Node), else the browser's.
|
|
997
|
+
if ( getPlatform().decodeImage ) tex = await platformImageFromBytes( bytes, fname );
|
|
998
|
+
else if ( typeof createImageBitmap === 'function' ) tex = new Texture( await createImageBitmap( new Blob( [ bytes ] ) ) );
|
|
999
|
+
else throw new Error( 'no image decoder in this runtime: configurePlatform( nodePlatform( { decodeImage } ) ) — see "Running in Node"' );
|
|
921
1000
|
tex.colorSpace = SRGBColorSpace;
|
|
922
1001
|
|
|
923
1002
|
}
|
|
@@ -52,6 +52,20 @@ function standInForSplit( source ) {
|
|
|
52
52
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// glTF's znear is a camera's near clipping plane (Blender writes its clip start there): the path tracer honours it.
|
|
56
|
+
export const cameraClipPlugin = {
|
|
57
|
+
name: 'RAYZEE_camera_clip',
|
|
58
|
+
afterRoot( result ) {
|
|
59
|
+
|
|
60
|
+
for ( const scene of result.scenes ) scene.traverse( ( object ) => {
|
|
61
|
+
|
|
62
|
+
if ( object.isCamera && object.near > 0 ) object.userData.clipStart = object.near;
|
|
63
|
+
|
|
64
|
+
} );
|
|
65
|
+
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
55
69
|
// Luminous efficacy glTF, three.js and Blender's exporter all assume (PBR_WATTS_TO_LUMENS).
|
|
56
70
|
const LUMENS_PER_WATT = 683;
|
|
57
71
|
|
|
@@ -406,6 +420,13 @@ export class AssetLoader extends EventDispatcher {
|
|
|
406
420
|
|
|
407
421
|
}
|
|
408
422
|
|
|
423
|
+
/** @see ArchiveImporter#commitPendingGraph */
|
|
424
|
+
commitPendingGraph( model ) {
|
|
425
|
+
|
|
426
|
+
this.archives?.commitPendingGraph( model );
|
|
427
|
+
|
|
428
|
+
}
|
|
429
|
+
|
|
409
430
|
// Blob's own readers, not FileReader: Node has no FileReader.
|
|
410
431
|
readFileAsArrayBuffer( file ) {
|
|
411
432
|
|
|
@@ -768,6 +789,7 @@ export class AssetLoader extends EventDispatcher {
|
|
|
768
789
|
if ( getPlatform().decodeImage ) loader.register( ( parser ) => platformImagesPlugin( parser, onImageFailure ) );
|
|
769
790
|
else if ( ! hasImageDecoder() ) loader.register( ( parser ) => missingImageDecoderPlugin( parser, onImageFailure ) );
|
|
770
791
|
loader.register( ( parser ) => diffuseTransmissionPlugin( parser ) );
|
|
792
|
+
loader.register( () => cameraClipPlugin );
|
|
771
793
|
|
|
772
794
|
return loader;
|
|
773
795
|
|
|
@@ -149,8 +149,8 @@ export const MATERIAL_DATA_LAYOUT = {
|
|
|
149
149
|
SPECULAR_INTENSITY: 40, SPECULAR_COLOR: 41,
|
|
150
150
|
// Slot 11: iridescence + iridescenceIOR + iridescenceThicknessRange [extended BxDF]
|
|
151
151
|
IRIDESCENCE: 44, IRIDESCENCE_IOR: 45, IRIDESCENCE_THICKNESS_RANGE: 46,
|
|
152
|
-
// Slot 12: bumpScale + displacementScale + displacementMapIndex +
|
|
153
|
-
BUMP_SCALE: 48, DISPLACEMENT_SCALE: 49, DISPLACEMENT_MAP_INDEX: 50,
|
|
152
|
+
// Slot 12: bumpScale + displacementScale + displacementMapIndex + displacementBias
|
|
153
|
+
BUMP_SCALE: 48, DISPLACEMENT_SCALE: 49, DISPLACEMENT_MAP_INDEX: 50, DISPLACEMENT_BIAS: 51,
|
|
154
154
|
|
|
155
155
|
// ── Transform float offsets (8 floats each: 7 matrix values + 1 padding) ──
|
|
156
156
|
ALBEDO_TRANSFORM: 52,
|
|
@@ -193,7 +193,7 @@ export const MATERIAL_DATA_LAYOUT = {
|
|
|
193
193
|
SHEEN_COLOR: 9, // [extended] sheenColor, diffuseTransmission
|
|
194
194
|
SPECULAR: 10, // [extended] specularIntensity, specularColor
|
|
195
195
|
IRIDESCENCE: 11, // [extended] iridescence, iridescenceIOR, iridescenceThicknessRange
|
|
196
|
-
BUMP_DISPLACEMENT: 12, // bumpScale, displacementScale, displacementMapIndex
|
|
196
|
+
BUMP_DISPLACEMENT: 12, // bumpScale, displacementScale, displacementMapIndex, displacementBias
|
|
197
197
|
ALBEDO_TRANSFORM_A: 13, ALBEDO_TRANSFORM_B: 14,
|
|
198
198
|
NORMAL_TRANSFORM_A: 15, NORMAL_TRANSFORM_B: 16,
|
|
199
199
|
ROUGHNESS_TRANSFORM_A: 17, ROUGHNESS_TRANSFORM_B: 18,
|
|
@@ -862,8 +862,13 @@ export class GeometryExtractor {
|
|
|
862
862
|
this.expandedTriangleCount += range.count;
|
|
863
863
|
this.meshTriangleRanges.push( range );
|
|
864
864
|
if ( range.count > 0 && ! bake && shareable ) this._geometryRanges.set( key, { ...range, meshIndex } );
|
|
865
|
-
|
|
866
|
-
if ( range.count > 0 )
|
|
865
|
+
// A placement with nothing to trace would get a TLAS leaf with no BLAS to point at.
|
|
866
|
+
if ( range.count > 0 ) {
|
|
867
|
+
|
|
868
|
+
this._recordPlacements( mesh, meshIndex, bake, expand, shareable );
|
|
869
|
+
this._onRange?.( meshIndex, range );
|
|
870
|
+
|
|
871
|
+
}
|
|
867
872
|
|
|
868
873
|
}
|
|
869
874
|
|
|
@@ -1227,6 +1232,7 @@ export class GeometryExtractor {
|
|
|
1227
1232
|
normalScale: pick( 'normalScale' ),
|
|
1228
1233
|
bumpScale: pick( 'bumpScale' ),
|
|
1229
1234
|
displacementScale: pick( 'displacementScale' ),
|
|
1235
|
+
displacementBias: pick( 'displacementBias' ),
|
|
1230
1236
|
|
|
1231
1237
|
transparent: material.transparent ? 1 : 0,
|
|
1232
1238
|
alphaTest: pick( 'alphaTest' ),
|
|
@@ -1345,13 +1351,15 @@ export class GeometryExtractor {
|
|
|
1345
1351
|
mesh.updateMatrixWorld();
|
|
1346
1352
|
|
|
1347
1353
|
const geometry = mesh.geometry;
|
|
1348
|
-
if ( ! geometry.attributes.normal ) geometry.computeVertexNormals();
|
|
1349
1354
|
const positions = geometry.attributes.position;
|
|
1355
|
+
if ( ! positions ) return;
|
|
1356
|
+
if ( ! geometry.attributes.normal ) geometry.computeVertexNormals();
|
|
1350
1357
|
const normals = geometry.attributes.normal;
|
|
1351
1358
|
const uvs = geometry.attributes.uv;
|
|
1352
1359
|
const indices = geometry.index ? geometry.index.array : null;
|
|
1353
1360
|
|
|
1354
|
-
|
|
1361
|
+
// Whole triangles only, as three.js draws them: a trailing vertex or two read past the end as NaN.
|
|
1362
|
+
const triangleCount = Math.floor( ( indices ? indices.length : positions.count ) / 3 );
|
|
1355
1363
|
|
|
1356
1364
|
if ( expandInstances ) {
|
|
1357
1365
|
|
|
@@ -32,6 +32,9 @@ function effectiveEmission( light ) {
|
|
|
32
32
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// A point or spot light's radius (Blender's Radius, USD's SphereLight radius): `userData.radius`, in scene units.
|
|
36
|
+
const lightRadius = ( light ) => Math.max( 0, Number( light.userData?.radius ) || 0 );
|
|
37
|
+
|
|
35
38
|
export class LightSerializer {
|
|
36
39
|
|
|
37
40
|
constructor() {
|
|
@@ -216,7 +219,8 @@ export class LightSerializer {
|
|
|
216
219
|
em.r, em.g, em.b, // color (3) — incl. temperature tint
|
|
217
220
|
em.intensity * INV_4PI, // radiant intensity W/sr = power(W)/4π, incl. exposure (1)
|
|
218
221
|
light.distance || 0.0, // cutoff distance (0 = infinite) (1)
|
|
219
|
-
light.decay !== undefined ? light.decay : 2.0 // decay exponent (1)
|
|
222
|
+
light.decay !== undefined ? light.decay : 2.0, // decay exponent (1)
|
|
223
|
+
lightRadius( light ), // radius: a disc this size facing the lit point, 0 = a point (1)
|
|
220
224
|
],
|
|
221
225
|
importance: importance,
|
|
222
226
|
light: light
|
|
@@ -266,7 +270,7 @@ export class LightSerializer {
|
|
|
266
270
|
goboIntensity, // signed gobo strength (1)
|
|
267
271
|
iesIndex, // IES profile index, -1 = none (1)
|
|
268
272
|
iesIntensity, // IES blend [0,1] (1)
|
|
269
|
-
|
|
273
|
+
lightRadius( light ), // radius, as a point light's (1)
|
|
270
274
|
0.0, // reserved (1) — keeps stride at 20 (vec4 aligned)
|
|
271
275
|
],
|
|
272
276
|
importance: importance,
|
|
@@ -16,7 +16,7 @@ const TRANSFORMS = [
|
|
|
16
16
|
|
|
17
17
|
/** Floats no field writes; the packer gives them fixed values. */
|
|
18
18
|
export const MATERIAL_RESERVED_FLOATS = Object.freeze( [
|
|
19
|
-
M.VISIBLE, M.
|
|
19
|
+
M.VISIBLE, M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 1, M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 2,
|
|
20
20
|
] );
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -129,6 +129,7 @@ export function packMaterial( data, base, mat ) {
|
|
|
129
129
|
s( M.BUMP_SCALE, mat.bumpScale, D.bumpScale );
|
|
130
130
|
s( M.DISPLACEMENT_SCALE, mat.displacementScale, D.displacementScale );
|
|
131
131
|
data[ base + M.DISPLACEMENT_MAP_INDEX ] = mapIndex( mat.displacementMap );
|
|
132
|
+
s( M.DISPLACEMENT_BIAS, mat.displacementBias, D.displacementBias );
|
|
132
133
|
|
|
133
134
|
for ( const [ key, offset ] of TRANSFORMS ) {
|
|
134
135
|
|
|
@@ -163,7 +164,6 @@ export function packMaterial( data, base, mat ) {
|
|
|
163
164
|
data[ base + M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX ] = mapIndex( mat.diffuseTransmissionColorMap );
|
|
164
165
|
|
|
165
166
|
data[ base + M.VISIBLE ] = 1;
|
|
166
|
-
data[ base + M.DISPLACEMENT_MAP_INDEX + 1 ] = 0;
|
|
167
167
|
data[ base + M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 1 ] = 0;
|
|
168
168
|
data[ base + M.DIFFUSE_TRANSMISSION_COLOR_MAP_INDEX + 2 ] = 0;
|
|
169
169
|
|