rayzee 11.8.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 +18 -6
- package/dist/{ArchiveImporter-_sJm5uaK.js → ArchiveImporter-BJxszU_k.js} +1549 -1232
- package/dist/{BidirectionalIntegrator-Dzt4Z97x.js → BidirectionalIntegrator-DWg66vAd.js} +240 -240
- package/dist/{BufferLayout-xPRH9jYD.js → BufferLayout-Bzgccg1o.js} +1 -0
- package/dist/{ColorManagement-30MfpHwQ.js → ColorManagement-B_HjAiy2.js} +1 -1
- package/dist/{EngineIssues-xljoXM0E.js → EngineIssues-CfJiBaNj.js} +1 -0
- package/dist/{NeuralRetouch-BKQCsmEs.js → NeuralRetouch-BD9E-Yht.js} +1 -1
- package/dist/{NeuralSuperRes-C4ymq68e.js → NeuralSuperRes-C8Mf3tJX.js} +1 -1
- package/dist/{PathTracer-C_to_i40.js → PathTracer-DeE9UJ1n.js} +3433 -3363
- package/dist/{PhysicalSky-bMHbm_7H.js → PhysicalSky-cKy75-oR.js} +45 -45
- package/dist/{RayzeeRenderer-B_aB6VTJ.js → RayzeeRenderer-CzLyBA3G.js} +13 -8
- package/dist/{SceneMetadata-CSmlJW4c.js → SceneMetadata-C17UfOEp.js} +1 -1
- package/dist/{SolarLimb-bZE6y63S.js → SolarLimb-DRYy8tln.js} +184 -172
- 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-B085D9FR.js → openStorage-CdyVjmps.js} +1 -1
- package/dist/rayzee-core.es.js +7 -7
- package/dist/rayzee.es.js +567 -559
- package/dist/rayzee.umd.js +106 -101
- package/dist/{sceneCachePolicy-D_uBSbQF.js → sceneCachePolicy-ywbGKHD3.js} +9 -6
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -0
- package/src/EngineIssues.js +1 -0
- package/src/Passes/OIDNTemporalHistory.js +1 -1
- package/src/Processor/ArchiveImporter.js +83 -21
- package/src/Processor/AssetLoader.js +7 -0
- package/src/Processor/BufferLayout.js +3 -3
- package/src/Processor/GeometryExtractor.js +12 -4
- package/src/Processor/MaterialPacking.js +2 -2
- package/src/Processor/PBRT/PBRTMaterials.js +47 -12
- 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/SceneProcessor.js +42 -26
- package/src/RayzeeRenderer.js +4 -2
- package/src/Stages/DenoiserSettings.js +0 -4
- package/src/Stages/NRD.js +53 -24
- package/src/Stages/NormalDepth.js +5 -3
- package/src/Stages/PathTracer.js +2 -0
- package/src/Stages/PathTracerStage.js +5 -1
- package/src/Storage/SceneGraphCodec.js +6 -3
- package/src/TSL/BVHTraversal.js +33 -20
- package/src/TSL/Common.js +25 -8
- package/src/TSL/Displacement.js +66 -113
- package/src/TSL/LightsDirect.js +33 -11
- package/src/TSL/SceneResources.js +8 -0
- package/src/TSL/ShadeKernel.js +2 -2
- package/src/TSL/Struct.js +1 -0
- package/src/managers/MaterialDataManager.js +11 -0
|
@@ -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
|
@@ -13,6 +13,7 @@ export const ISSUE_CODES = Object.freeze( {
|
|
|
13
13
|
ASSET_AMBIGUOUS_ENTRY: 'asset.ambiguous_entry',
|
|
14
14
|
ASSET_ARCHIVE_TOO_LARGE: 'asset.archive_too_large',
|
|
15
15
|
ASSET_ENTRY_TOO_LARGE: 'asset.entry_too_large',
|
|
16
|
+
ASSET_PARAMETERS_IGNORED: 'asset.parameters_ignored',
|
|
16
17
|
TEXTURE_BUILD_FAILED: 'texture.build_failed',
|
|
17
18
|
TEXTURE_PROCESSING_FALLBACK: 'texture.processing_fallback',
|
|
18
19
|
TEXTURE_LIMIT_EXCEEDED: 'texture.limit_exceeded',
|
|
@@ -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 );
|
|
@@ -82,16 +82,17 @@ export class ArchiveImporter {
|
|
|
82
82
|
constructor( loader ) {
|
|
83
83
|
|
|
84
84
|
this.loader = loader;
|
|
85
|
-
this.
|
|
85
|
+
this._store = null;
|
|
86
86
|
this.lastPBRTStats = null;
|
|
87
87
|
this.lastUSDStats = null;
|
|
88
88
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
/**
|
|
91
|
+
/** Abandons a parsed scene the engine has not accepted: the model it was for is gone, or its load failed. */
|
|
92
92
|
release() {
|
|
93
93
|
|
|
94
|
-
this.
|
|
94
|
+
this._store?.decide( false );
|
|
95
|
+
this._store = null;
|
|
95
96
|
|
|
96
97
|
}
|
|
97
98
|
|
|
@@ -383,7 +384,8 @@ export class ArchiveImporter {
|
|
|
383
384
|
|
|
384
385
|
/**
|
|
385
386
|
* Stores a freshly parsed scene for the next open. Encoded here, before onModelLoad or the
|
|
386
|
-
* 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}).
|
|
387
389
|
* @private
|
|
388
390
|
*/
|
|
389
391
|
_storeGraph( key, built, label ) {
|
|
@@ -401,7 +403,7 @@ export class ArchiveImporter {
|
|
|
401
403
|
meshCount: built.meshCount, entryPath: built.entryPath, candidates: built.candidates, frames: built.frames,
|
|
402
404
|
warnings: built.warnings, triangleCount: built.triangleCount, placementCount: built.placementCount,
|
|
403
405
|
mergedShapes: built.mergedShapes, skippedForBudget: built.skippedForBudget, droppedNoTemplate: built.droppedNoTemplate,
|
|
404
|
-
render: built.render ?? null,
|
|
406
|
+
render: built.render ?? null, parameters: built.parameters ?? null,
|
|
405
407
|
},
|
|
406
408
|
} );
|
|
407
409
|
|
|
@@ -413,13 +415,29 @@ export class ArchiveImporter {
|
|
|
413
415
|
|
|
414
416
|
}
|
|
415
417
|
|
|
416
|
-
|
|
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;
|
|
417
436
|
|
|
418
437
|
// A parse slow enough on its own is written now, during the build. Held until the build
|
|
419
438
|
// ends, it kept every array the build replaces alive with it — the float normals and the
|
|
420
439
|
// instance matrices, ~1 GB on the whole Moana subset, at the build's peak.
|
|
421
|
-
if ( worthStoring(
|
|
422
|
-
else this._pendingGraph = pending;
|
|
440
|
+
if ( worthStoring( store.parseMs, encoded.byteLength ) ) this._writeGraph( area, store );
|
|
423
441
|
|
|
424
442
|
}
|
|
425
443
|
|
|
@@ -430,34 +448,57 @@ export class ArchiveImporter {
|
|
|
430
448
|
*/
|
|
431
449
|
flushPendingGraph( buildMs ) {
|
|
432
450
|
|
|
433
|
-
const
|
|
434
|
-
|
|
451
|
+
const store = this._store;
|
|
452
|
+
if ( ! store || store.writing ) return;
|
|
435
453
|
const area = this.loader.storage?.area( ENGINE_AREAS.SCENES );
|
|
436
|
-
if (
|
|
437
|
-
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 );
|
|
438
471
|
|
|
439
472
|
}
|
|
440
473
|
|
|
441
474
|
/** Writes an encoded graph in the background, letting each array go once it is on disk. @private */
|
|
442
|
-
_writeGraph( area,
|
|
475
|
+
_writeGraph( area, store ) {
|
|
443
476
|
|
|
477
|
+
store.writing = true;
|
|
444
478
|
( async () => {
|
|
445
479
|
|
|
446
|
-
|
|
447
|
-
if ( ! writer ) return;
|
|
480
|
+
let writer = null;
|
|
448
481
|
try {
|
|
449
482
|
|
|
450
|
-
await
|
|
451
|
-
|
|
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();
|
|
452
489
|
|
|
453
490
|
} catch ( error ) {
|
|
454
491
|
|
|
455
|
-
await writer
|
|
456
|
-
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;
|
|
457
498
|
|
|
458
499
|
}
|
|
459
500
|
|
|
460
|
-
} )()
|
|
501
|
+
} )();
|
|
461
502
|
|
|
462
503
|
}
|
|
463
504
|
|
|
@@ -651,7 +692,7 @@ export class ArchiveImporter {
|
|
|
651
692
|
|
|
652
693
|
const { group, environment, animations, report, warnings, meshCount, entryPath: loadedEntry, candidates,
|
|
653
694
|
frames, parseMs, buildMs, triangleCount, placementCount, mergedShapes, skippedForBudget,
|
|
654
|
-
droppedNoTemplate } = built;
|
|
695
|
+
droppedNoTemplate, parameters } = built;
|
|
655
696
|
if ( stored ) console.info( `PBRT scene "${loadedEntry}" opened from storage in ${( buildMs / 1000 ).toFixed( 1 )} s, skipping the parse` );
|
|
656
697
|
|
|
657
698
|
// Phase breakdown for scaling work; the engine's own build timings live in
|
|
@@ -679,6 +720,8 @@ export class ArchiveImporter {
|
|
|
679
720
|
|
|
680
721
|
}
|
|
681
722
|
|
|
723
|
+
if ( parameters ) this._reportParameters( parameters, loadedEntry );
|
|
724
|
+
|
|
682
725
|
// Diagnostics — surface what each mesh resolved to (helps debug black/wrong materials).
|
|
683
726
|
if ( report && report.length && typeof console.table === 'function' ) {
|
|
684
727
|
|
|
@@ -728,6 +771,25 @@ export class ArchiveImporter {
|
|
|
728
771
|
|
|
729
772
|
}
|
|
730
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
|
+
|
|
731
793
|
/** A texture map from an archive, tagged with its path so a stored scene can read it back. @private */
|
|
732
794
|
async _archiveImage( bytes, fname ) {
|
|
733
795
|
|
|
@@ -420,6 +420,13 @@ export class AssetLoader extends EventDispatcher {
|
|
|
420
420
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
/** @see ArchiveImporter#commitPendingGraph */
|
|
424
|
+
commitPendingGraph( model ) {
|
|
425
|
+
|
|
426
|
+
this.archives?.commitPendingGraph( model );
|
|
427
|
+
|
|
428
|
+
}
|
|
429
|
+
|
|
423
430
|
// Blob's own readers, not FileReader: Node has no FileReader.
|
|
424
431
|
readFileAsArrayBuffer( file ) {
|
|
425
432
|
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import { MeshPhysicalMaterial, Color, DoubleSide } from 'three';
|
|
12
12
|
import { namedScattering, dipoleReflectance, albedoForReflectance } from './PBRTScattering.js';
|
|
13
|
+
import { approximated, param } from './PBRTParser.js';
|
|
13
14
|
|
|
14
15
|
// Normal-incidence reflectance approximations for pbrt's named conductor spectra.
|
|
15
16
|
const METAL_ALBEDO = {
|
|
@@ -54,21 +55,37 @@ function blackbodyToRGB( kelvin ) {
|
|
|
54
55
|
|
|
55
56
|
export function pFloat( params, name, dflt ) {
|
|
56
57
|
|
|
57
|
-
const p = params
|
|
58
|
+
const p = param( params, name );
|
|
59
|
+
if ( p && p.type !== 'float' && p.type !== 'integer' ) approximated( params, name );
|
|
58
60
|
return p && typeof p.value[ 0 ] === 'number' ? p.value[ 0 ] : dflt;
|
|
59
61
|
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
/** An index of refraction: a sampled spectrum ( λ value pairs ) is read at 550 nm, linearly between its samples. */
|
|
65
|
+
export function pEta( params, name, dflt ) {
|
|
66
|
+
|
|
67
|
+
const p = param( params, name );
|
|
68
|
+
if ( p?.type !== 'spectrum' || typeof p.value[ 0 ] !== 'number' ) return pFloat( params, name, dflt );
|
|
69
|
+
approximated( params, name );
|
|
70
|
+
const v = p.value;
|
|
71
|
+
if ( v.length < 2 ) return dflt;
|
|
72
|
+
let i = 2;
|
|
73
|
+
while ( i < v.length - 1 && v[ i ] < 550 ) i += 2;
|
|
74
|
+
if ( i >= v.length - 1 || v[ i - 2 ] >= 550 ) return v[ i >= v.length - 1 ? i - 1 : 1 ];
|
|
75
|
+
return v[ i - 1 ] + ( v[ i + 1 ] - v[ i - 1 ] ) * ( 550 - v[ i - 2 ] ) / ( v[ i ] - v[ i - 2 ] );
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
62
79
|
export function pString( params, name, dflt ) {
|
|
63
80
|
|
|
64
|
-
const p = params
|
|
81
|
+
const p = param( params, name );
|
|
65
82
|
return p && typeof p.value[ 0 ] === 'string' ? p.value[ 0 ] : dflt;
|
|
66
83
|
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
export function pBool( params, name, dflt ) {
|
|
70
87
|
|
|
71
|
-
const v = params
|
|
88
|
+
const v = param( params, name )?.value?.[ 0 ];
|
|
72
89
|
return v === undefined ? dflt : v === true || v === 'true';
|
|
73
90
|
|
|
74
91
|
}
|
|
@@ -80,7 +97,7 @@ export function pBool( params, name, dflt ) {
|
|
|
80
97
|
*/
|
|
81
98
|
export async function resolveSpectrum( params, name, ctx, dfltRGB = null ) {
|
|
82
99
|
|
|
83
|
-
const p = params
|
|
100
|
+
const p = param( params, name );
|
|
84
101
|
if ( ! p ) return { rgb: dfltRGB, texture: null };
|
|
85
102
|
|
|
86
103
|
// Reference to a named texture.
|
|
@@ -91,6 +108,7 @@ export async function resolveSpectrum( params, name, ctx, dfltRGB = null ) {
|
|
|
91
108
|
if ( tex && tex.texture ) return { rgb: tex.constant ?? null, texture: tex.texture };
|
|
92
109
|
if ( tex && tex.constant ) return { rgb: tex.constant, texture: null };
|
|
93
110
|
ctx.warn( `texture "${texName}" could not be resolved` );
|
|
111
|
+
approximated( params, name );
|
|
94
112
|
return { rgb: dfltRGB, texture: null };
|
|
95
113
|
|
|
96
114
|
}
|
|
@@ -110,6 +128,7 @@ export async function resolveSpectrum( params, name, ctx, dfltRGB = null ) {
|
|
|
110
128
|
|
|
111
129
|
if ( p.type === 'blackbody' ) {
|
|
112
130
|
|
|
131
|
+
approximated( params, name );
|
|
113
132
|
return { rgb: blackbodyToRGB( p.value[ 0 ] ), texture: null };
|
|
114
133
|
|
|
115
134
|
}
|
|
@@ -117,6 +136,7 @@ export async function resolveSpectrum( params, name, ctx, dfltRGB = null ) {
|
|
|
117
136
|
if ( p.type === 'spectrum' ) {
|
|
118
137
|
|
|
119
138
|
// Named spectrum (e.g. "metal-Au-eta") or sampled [lambda val ...].
|
|
139
|
+
approximated( params, name );
|
|
120
140
|
if ( typeof p.value[ 0 ] === 'string' ) {
|
|
121
141
|
|
|
122
142
|
const key = namedMetalKey( p.value[ 0 ] );
|
|
@@ -139,6 +159,7 @@ export async function resolveSpectrum( params, name, ctx, dfltRGB = null ) {
|
|
|
139
159
|
|
|
140
160
|
}
|
|
141
161
|
|
|
162
|
+
approximated( params, name );
|
|
142
163
|
return { rgb: dfltRGB, texture: null };
|
|
143
164
|
|
|
144
165
|
}
|
|
@@ -155,9 +176,10 @@ function namedMetalKey( s ) {
|
|
|
155
176
|
// A float parameter's value, or a float texture's mean (`ctx.floatTextureMean`); null when absent or unreadable.
|
|
156
177
|
async function floatOrMean( params, name, ctx ) {
|
|
157
178
|
|
|
158
|
-
const p = params
|
|
179
|
+
const p = param( params, name );
|
|
159
180
|
if ( ! p ) return null;
|
|
160
181
|
if ( typeof p.value[ 0 ] === 'number' ) return p.value[ 0 ];
|
|
182
|
+
approximated( params, name );
|
|
161
183
|
if ( p.type === 'texture' ) return ( await ctx.floatTextureMean?.( p.value[ 0 ] ) ) ?? null;
|
|
162
184
|
return null;
|
|
163
185
|
|
|
@@ -171,7 +193,7 @@ async function floatOrMean( params, name, ctx ) {
|
|
|
171
193
|
*/
|
|
172
194
|
const remapsRoughness = ( params ) => {
|
|
173
195
|
|
|
174
|
-
const remap = params
|
|
196
|
+
const remap = param( params, 'remaproughness' )?.value?.[ 0 ];
|
|
175
197
|
return remap !== false && remap !== 'false';
|
|
176
198
|
|
|
177
199
|
};
|
|
@@ -225,8 +247,14 @@ async function applyRoughness( mat, target, params, dflt, ctx, prefix = '' ) {
|
|
|
225
247
|
// A spectrum parameter as one RGB; a texture counts as its mean (`ctx.textureMeanRGB`).
|
|
226
248
|
async function spectrumMean( params, key, ctx, dflt ) {
|
|
227
249
|
|
|
228
|
-
const p = params
|
|
229
|
-
if ( p?.type === 'texture' )
|
|
250
|
+
const p = param( params, key );
|
|
251
|
+
if ( p?.type === 'texture' ) {
|
|
252
|
+
|
|
253
|
+
approximated( params, key );
|
|
254
|
+
return ( await ctx.textureMeanRGB?.( p.value[ 0 ] ) ) ?? dflt;
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
|
|
230
258
|
return ( await resolveSpectrum( params, key, ctx, dflt ) ).rgb ?? dflt;
|
|
231
259
|
|
|
232
260
|
}
|
|
@@ -243,7 +271,7 @@ const SUBSURFACE_SIGMA_S = [ 2.55, 3.21, 3.77 ];
|
|
|
243
271
|
*/
|
|
244
272
|
async function applySubsurface( mat, params, ctx ) {
|
|
245
273
|
|
|
246
|
-
const eta =
|
|
274
|
+
const eta = pEta( params, 'eta', 1.33 );
|
|
247
275
|
const scale = pFloat( params, 'scale', 1 );
|
|
248
276
|
const scaled = ( rgb ) => rgb.map( ( v ) => Math.max( 0, v * scale ) );
|
|
249
277
|
let g = pFloat( params, 'g', 0 );
|
|
@@ -353,6 +381,7 @@ export async function buildMaterial( def, ctx ) {
|
|
|
353
381
|
|
|
354
382
|
const type = def?.type || 'diffuse';
|
|
355
383
|
const params = def?.params || {};
|
|
384
|
+
ctx.trackParams?.( params, `Material ${type}`, 'type' );
|
|
356
385
|
const mat = new MeshPhysicalMaterial( { side: DoubleSide, roughness: 1, metalness: 0 } );
|
|
357
386
|
|
|
358
387
|
const setColor = ( rgb ) => {
|
|
@@ -416,7 +445,7 @@ export async function buildMaterial( def, ctx ) {
|
|
|
416
445
|
mat.transmission = 1;
|
|
417
446
|
mat.metalness = 0;
|
|
418
447
|
mat.color.setRGB( 1, 1, 1 );
|
|
419
|
-
mat.ior =
|
|
448
|
+
mat.ior = pEta( params, 'eta', 1.5 );
|
|
420
449
|
await applyRoughness( mat, 'roughness', params, 0, ctx );
|
|
421
450
|
mat.thickness = type === 'thindielectric' ? 0 : pFloat( params, 'thickness', 0 );
|
|
422
451
|
break;
|
|
@@ -447,7 +476,13 @@ export async function buildMaterial( def, ctx ) {
|
|
|
447
476
|
const refl = await resolveSpectrum( params, 'reflectance', ctx, [ 0.25, 0.25, 0.25 ] );
|
|
448
477
|
const base = ( v ) => Math.min( 1, unit( v ) / ( 1 - dt ) );
|
|
449
478
|
applyAlbedo( { texture: refl.texture, rgb: refl.rgb ? refl.rgb.map( base ) : ( refl.texture ? [ 1, 1, 1 ].map( base ) : null ) } );
|
|
450
|
-
if ( trans.texture )
|
|
479
|
+
if ( trans.texture ) {
|
|
480
|
+
|
|
481
|
+
ctx.warn( 'diffusetransmission: a textured "transmittance" is not supported — using 0.5' );
|
|
482
|
+
approximated( params, 'transmittance' );
|
|
483
|
+
|
|
484
|
+
}
|
|
485
|
+
|
|
451
486
|
mat.diffuseTransmission = dt;
|
|
452
487
|
mat.diffuseTransmissionColor = new Color().setRGB( ...T.map( ( v ) => ( dt > 0 ? Math.min( 1, v / dt ) : 1 ) ) );
|
|
453
488
|
mat.roughness = 1;
|
|
@@ -477,7 +512,7 @@ export async function buildMaterial( def, ctx ) {
|
|
|
477
512
|
// — physically loose but visually faithful, and crucially silent on success
|
|
478
513
|
// instead of warning per shape. Recursive: mix-in-mix terminates as the
|
|
479
514
|
// chain bottoms out at a non-mix.
|
|
480
|
-
const matNames = params
|
|
515
|
+
const matNames = param( params, 'materials' )?.value || [];
|
|
481
516
|
let t = Math.max( 0, Math.min( 1, pFloat( params, 'amount', 0.5 ) ) );
|
|
482
517
|
const defA = matNames[ 0 ] ? ctx.namedMaterials?.get( matNames[ 0 ] ) : null;
|
|
483
518
|
const defB = matNames[ 1 ] ? ctx.namedMaterials?.get( matNames[ 1 ] ) : null;
|