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
|
@@ -24,8 +24,9 @@ import {
|
|
|
24
24
|
RepeatWrapping
|
|
25
25
|
} from 'three';
|
|
26
26
|
import { buildMaterial, pBool, pFloat, pString, resolveSpectrum } from './PBRTMaterials.js';
|
|
27
|
-
import { makeLayer, layerMean, bake, mixOf, srgbToLinear, linearToSRGB, hasAlpha } from './PBRTTextureBake.js';
|
|
27
|
+
import { makeLayer, layerMean, bake, mixOf, srgbToLinear, linearToSRGB, hasAlpha, generateUV, uvKey } from './PBRTTextureBake.js';
|
|
28
28
|
import { loopSubdivide } from './LoopSubdivision.js';
|
|
29
|
+
import { approximated, param } from './PBRTParser.js';
|
|
29
30
|
import { octahedralToEquirect } from './EqualAreaOctahedral.js';
|
|
30
31
|
import { tessellateCurve, tessellateCurves } from './PBRTCurves.js';
|
|
31
32
|
import * as M from './PBRTMath.js';
|
|
@@ -36,7 +37,7 @@ const LAMP_TYPES = new Set( [ 'distant', 'point', 'spot' ] );
|
|
|
36
37
|
// pbrt scales a light's spectrum to luminance 1 unless it is RGB, so a blackbody of any temperature is equally bright.
|
|
37
38
|
async function lightRGB( params, name, ctx ) {
|
|
38
39
|
|
|
39
|
-
const p = params
|
|
40
|
+
const p = param( params, name );
|
|
40
41
|
if ( p?.type === 'spectrum' && typeof p.value[ 0 ] === 'string' && p.value[ 0 ].startsWith( 'stdillum' ) ) return [ 1, 1, 1 ];
|
|
41
42
|
const rgb = ( await resolveSpectrum( params, name, ctx, [ 1, 1, 1 ] ) ).rgb || [ 1, 1, 1 ];
|
|
42
43
|
if ( ! p || p.type === 'rgb' || p.type === 'color' || p.type === 'float' ) return rgb;
|
|
@@ -98,6 +99,8 @@ export { SPILL_TRIANGLE_BUDGET, SPILL_PLACEMENT_BUDGET } from '../HostMemory.js'
|
|
|
98
99
|
const DEFAULT_MERGE_MIN_SHAPES = 256;
|
|
99
100
|
const MERGE_VERTEX_LIMIT = 4096;
|
|
100
101
|
const MERGE_BATCH_TRIANGLES = 500_000;
|
|
102
|
+
// Copies of a template shape textured by position, each stored with its own uv, up to this many triangles in all.
|
|
103
|
+
const MAPPED_COPY_TRIANGLES = 1_000_000;
|
|
101
104
|
// Curve tessellation defaults. pbrt sweeps a spline analytically; a triangle tracer has to
|
|
102
105
|
// pick a resolution, and isMountainB alone declares 5.18M curves — two samples per span and
|
|
103
106
|
// a crossed pair of ribbons is 16 triangles a curve.
|
|
@@ -108,7 +111,7 @@ const DEFAULT_CURVE_SIDES = { flat: 1, ribbon: 1, cylinder: 2 };
|
|
|
108
111
|
const DEFAULT_CURVE_TOLERANCE = 0.05;
|
|
109
112
|
|
|
110
113
|
/** Bumped whenever the same scene files build a different graph, so a stored graph is not reused. */
|
|
111
|
-
export const PBRT_BUILD_REVISION =
|
|
114
|
+
export const PBRT_BUILD_REVISION = 13;
|
|
112
115
|
|
|
113
116
|
function samePlacements( a, b ) {
|
|
114
117
|
|
|
@@ -209,10 +212,62 @@ function uvMapping( params ) {
|
|
|
209
212
|
|
|
210
213
|
}
|
|
211
214
|
|
|
212
|
-
|
|
215
|
+
const POSITION_MAPPINGS = new Set( [ 'planar', 'spherical', 'cylindrical' ] );
|
|
216
|
+
|
|
217
|
+
function pVector( params, name, dflt ) {
|
|
218
|
+
|
|
219
|
+
const v = param( params, name )?.value;
|
|
220
|
+
return v && v.length >= 3 ? [ v[ 0 ], v[ 1 ], v[ 2 ] ] : dflt;
|
|
221
|
+
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* pbrt's TextureMapping2D::Create: `uv` scales and shifts the mesh's uv; a position mapping is a uv set of its own (`uv`),
|
|
226
|
+
* generated per shape and read as it is. It maps from where the texture was declared (`ctm`).
|
|
227
|
+
*/
|
|
228
|
+
function textureMapping( params, ctm ) {
|
|
229
|
+
|
|
230
|
+
const type = pString( params, 'mapping', 'uv' );
|
|
231
|
+
if ( ! POSITION_MAPPINGS.has( type ) ) return { ...uvMapping( params ), uv: null };
|
|
232
|
+
|
|
233
|
+
const uv = { type, textureFromWorld: M.invert( ctm ?? M.identity() ) };
|
|
234
|
+
if ( type === 'planar' ) Object.assign( uv, {
|
|
235
|
+
vs: pVector( params, 'v1', [ 1, 0, 0 ] ), vt: pVector( params, 'v2', [ 0, 1, 0 ] ),
|
|
236
|
+
ds: pFloat( params, 'udelta', 0 ), dt: pFloat( params, 'vdelta', 0 )
|
|
237
|
+
} );
|
|
238
|
+
uv.key = JSON.stringify( uv );
|
|
239
|
+
return { su: 1, sv: 1, du: 0, dv: 0, uv };
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function setMapping( texture, { su, sv, du, dv, uv = null } ) {
|
|
213
244
|
|
|
214
245
|
texture.repeat.set( su, sv );
|
|
215
246
|
texture.offset.set( du, dv );
|
|
247
|
+
if ( uv ) texture.userData.pbrtUV = uv;
|
|
248
|
+
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Material maps in the order that picks a surface's one uv set: its base colour's, else the first it has.
|
|
252
|
+
const UV_MAPS = [ 'map', 'roughnessMap', 'clearcoatRoughnessMap', 'normalMap' ];
|
|
253
|
+
const MAP_SCALARS = { roughnessMap: 'roughness', clearcoatRoughnessMap: 'clearcoatRoughness' };
|
|
254
|
+
|
|
255
|
+
/** The position mapping a material's textures are read in, or null for the mesh's own uv. */
|
|
256
|
+
function uvSetOf( material ) {
|
|
257
|
+
|
|
258
|
+
for ( const slot of UV_MAPS ) if ( material[ slot ] ) return material[ slot ].userData.pbrtUV ?? null;
|
|
259
|
+
return null;
|
|
260
|
+
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// An attribute with `sources`' values appended, one copy each.
|
|
264
|
+
function withCopies( attribute, sources ) {
|
|
265
|
+
|
|
266
|
+
const { array, itemSize, count } = attribute;
|
|
267
|
+
const out = new array.constructor( ( count + sources.length ) * itemSize );
|
|
268
|
+
out.set( array.subarray( 0, count * itemSize ) );
|
|
269
|
+
for ( let i = 0; i < sources.length; i ++ ) for ( let c = 0; c < itemSize; c ++ ) out[ ( count + i ) * itemSize + c ] = array[ sources[ i ] * itemSize + c ];
|
|
270
|
+
return new BufferAttribute( out, itemSize, attribute.normalized );
|
|
216
271
|
|
|
217
272
|
}
|
|
218
273
|
|
|
@@ -464,8 +519,9 @@ function translucent( material, alpha ) {
|
|
|
464
519
|
|
|
465
520
|
}
|
|
466
521
|
|
|
467
|
-
const sameMapping = ( texture, { su, sv, du, dv } ) =>
|
|
468
|
-
texture.repeat.x === su && texture.repeat.y === sv && texture.offset.x === du && texture.offset.y === dv
|
|
522
|
+
const sameMapping = ( texture, { su, sv, du, dv, uv } ) =>
|
|
523
|
+
texture.repeat.x === su && texture.repeat.y === sv && texture.offset.x === du && texture.offset.y === dv &&
|
|
524
|
+
uvKey( texture.userData.pbrtUV ) === uvKey( uv );
|
|
469
525
|
|
|
470
526
|
// An 8-bit image's colour with its alpha decoded as pbrt reads it; null when its alpha is opaque throughout.
|
|
471
527
|
function withDecodedAlpha( pixels, decode ) {
|
|
@@ -547,6 +603,7 @@ export class PBRTSceneBuilder {
|
|
|
547
603
|
this.resolveImage = resolvers.resolveImage || ( async () => null );
|
|
548
604
|
this.resolveEnvironment = resolvers.resolveEnvironment || resolvers.resolveImage || ( async () => null );
|
|
549
605
|
this.convertHandedness = resolvers.convertHandedness === true;
|
|
606
|
+
this._usage = resolvers.usage ?? null;
|
|
550
607
|
|
|
551
608
|
this.warnings = [];
|
|
552
609
|
this.report = []; // per-mesh diagnostics for debugging imports
|
|
@@ -560,6 +617,9 @@ export class PBRTSceneBuilder {
|
|
|
560
617
|
this._termCache = new Map(); // texture name + channels -> Promise<{rgb}|{layer, tint?}|null>
|
|
561
618
|
this._mediumMaterials = new Map(); // material -> Map(medium name -> material with its attenuation)
|
|
562
619
|
this._alphaMaterials = new Map(); // material -> Map(alpha texture name or #value -> Promise<material>)
|
|
620
|
+
this._mappings = new Map(); // texture name -> its textureMapping()
|
|
621
|
+
this._oneUVSets = new WeakMap(); // material -> itself, or its maps read in one uv set
|
|
622
|
+
this._uvWarnings = new Set();
|
|
563
623
|
|
|
564
624
|
}
|
|
565
625
|
|
|
@@ -576,9 +636,15 @@ export class PBRTSceneBuilder {
|
|
|
576
636
|
async build( ir ) {
|
|
577
637
|
|
|
578
638
|
this.ir = ir;
|
|
579
|
-
// Built again for their moving placements
|
|
639
|
+
// Built again for their moving placements, so freed and accounted for only at the end.
|
|
580
640
|
this._keepShapes = new Set();
|
|
581
|
-
for ( const { name } of ir.animatedInstances ?? [] ) for ( const shape of ir.objects.get( name ) ?? [] )
|
|
641
|
+
for ( const { name } of ir.animatedInstances ?? [] ) for ( const shape of ir.objects.get( name ) ?? [] ) {
|
|
642
|
+
|
|
643
|
+
this._keepShapes.add( shape );
|
|
644
|
+
this._usage?.open( shape.params, `Shape ${shape.type}`, 'alpha' );
|
|
645
|
+
|
|
646
|
+
}
|
|
647
|
+
|
|
582
648
|
// One decoded .ply serves every shape naming it: a merge frees it only as its last direct
|
|
583
649
|
// user, and never while a template or an unmerged shape holds its geometry.
|
|
584
650
|
this._plyUsers = new Map();
|
|
@@ -635,7 +701,7 @@ export class PBRTSceneBuilder {
|
|
|
635
701
|
|
|
636
702
|
if ( shape.type === 'plymesh' ) this._plyHeld.add( pString( shape.params, 'filename', null ) );
|
|
637
703
|
|
|
638
|
-
const mesh = this._meshFromGeometry( shape, geometry, sharedMaterial, shape.ctm, `shape_${i}` );
|
|
704
|
+
const mesh = this._meshFromGeometry( shape, this._mappedGeometry( geometry, sharedMaterial, shape.ctm ), sharedMaterial, shape.ctm, `shape_${i}` );
|
|
639
705
|
if ( ! mesh ) continue;
|
|
640
706
|
group.add( mesh );
|
|
641
707
|
if ( shape.motion ) this._animate( mesh, shape.motion );
|
|
@@ -675,6 +741,7 @@ export class PBRTSceneBuilder {
|
|
|
675
741
|
|
|
676
742
|
const animations = ir.animation ? this._buildClip( ir, camera ) : [];
|
|
677
743
|
|
|
744
|
+
for ( const light of ir.lights ) this._usage?.open( light.params, `LightSource ${light.type}` );
|
|
678
745
|
// Infinite light → environment
|
|
679
746
|
const environment = await this._buildEnvironment( ir.lights );
|
|
680
747
|
await this._buildLamps( ir.lights, group );
|
|
@@ -828,16 +895,23 @@ export class PBRTSceneBuilder {
|
|
|
828
895
|
|
|
829
896
|
}
|
|
830
897
|
|
|
831
|
-
const [
|
|
898
|
+
const [ built, sharedMaterial ] = await Promise.all( [
|
|
832
899
|
this._buildGeometry( shape ),
|
|
833
900
|
this._getMaterial( shape )
|
|
834
901
|
] );
|
|
835
|
-
if ( !
|
|
902
|
+
if ( ! built ) continue;
|
|
903
|
+
let geometry = built;
|
|
904
|
+
|
|
905
|
+
const rel = shape.relativeCTM || shape.ctm;
|
|
906
|
+
if ( uvSetOf( sharedMaterial ) ) {
|
|
836
907
|
|
|
837
|
-
|
|
908
|
+
if ( this._placeMapped( list, shape, geometry, sharedMaterial, rel, group, name ) ) continue;
|
|
909
|
+
geometry = this._mappedGeometry( geometry, sharedMaterial, M.multiplyInto( new Array( 16 ), list.matrices, 0, rel ) );
|
|
910
|
+
|
|
911
|
+
} else if ( batches && shape.type !== 'plymesh' && ! shape.shared && geometry.getAttribute( 'position' ).count <= MERGE_VERTEX_LIMIT ) {
|
|
838
912
|
|
|
839
913
|
this._geometryCache.delete( shape );
|
|
840
|
-
const full = this._appendToBatch( batches, shape, geometry, sharedMaterial,
|
|
914
|
+
const full = this._appendToBatch( batches, shape, geometry, sharedMaterial, rel );
|
|
841
915
|
this._releaseMerged( shape, geometry );
|
|
842
916
|
placeBatch( list, full );
|
|
843
917
|
continue;
|
|
@@ -846,7 +920,7 @@ export class PBRTSceneBuilder {
|
|
|
846
920
|
|
|
847
921
|
const tris = this._accountGeometry( geometry );
|
|
848
922
|
const material = this._materialForGeometry( shape, geometry, sharedMaterial, `instances_${name}` );
|
|
849
|
-
place( list, geometry, material,
|
|
923
|
+
place( list, geometry, material, rel, shape.type, shape.material?.type || 'diffuse', tris );
|
|
850
924
|
|
|
851
925
|
}
|
|
852
926
|
|
|
@@ -865,6 +939,34 @@ export class PBRTSceneBuilder {
|
|
|
865
939
|
|
|
866
940
|
}
|
|
867
941
|
|
|
942
|
+
/**
|
|
943
|
+
* pbrt maps each copy of a template where it stands, so a shape textured by position is placed as a mesh a copy, each
|
|
944
|
+
* with its own uv set. False, placing nothing, past MAPPED_COPY_TRIANGLES: the copies then share the first one's.
|
|
945
|
+
* @private
|
|
946
|
+
*/
|
|
947
|
+
_placeMapped( list, shape, geometry, material, rel, group, name ) {
|
|
948
|
+
|
|
949
|
+
const tris = ( geometry.index ? geometry.index.count : geometry.getAttribute( 'position' ).count ) / 3;
|
|
950
|
+
if ( list.count * tris > MAPPED_COPY_TRIANGLES ) {
|
|
951
|
+
|
|
952
|
+
this.warn( `ObjectInstance "${name}": its ${list.count.toLocaleString()} copies share the first one's uv from a position-mapped texture` );
|
|
953
|
+
return false;
|
|
954
|
+
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
let i = 0;
|
|
958
|
+
for ( ; i < list.count && ! this._overBudget(); i ++ ) {
|
|
959
|
+
|
|
960
|
+
const world = M.multiplyInto( new Array( 16 ), list.matrices, i * 16, rel );
|
|
961
|
+
group.add( this._meshFromGeometry( shape, this._mappedGeometry( geometry, material, world ), material, world, `instances_${name}_${i}` ) );
|
|
962
|
+
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
this.skippedForBudget += list.count - i;
|
|
966
|
+
return true;
|
|
967
|
+
|
|
968
|
+
}
|
|
969
|
+
|
|
868
970
|
/**
|
|
869
971
|
* Templates placed at exactly the same transforms, as one template. Each Pandanus tree
|
|
870
972
|
* includes ten leaf files; kept apart they were ten overlapping instances a ray entered
|
|
@@ -942,7 +1044,9 @@ export class PBRTSceneBuilder {
|
|
|
942
1044
|
const shape = template[ s ];
|
|
943
1045
|
const [ geometry, sharedMaterial ] = await Promise.all( [ this._buildGeometry( shape ), this._getMaterial( shape ) ] );
|
|
944
1046
|
if ( ! geometry ) continue;
|
|
945
|
-
const
|
|
1047
|
+
const rel = shape.relativeCTM || shape.ctm;
|
|
1048
|
+
const mapped = this._mappedGeometry( geometry, sharedMaterial, M.multiply( start, rel ) );
|
|
1049
|
+
const mesh = this._meshFromGeometry( shape, mapped, sharedMaterial, rel, `${node.name}_${s}`, true );
|
|
946
1050
|
if ( mesh ) node.add( mesh );
|
|
947
1051
|
|
|
948
1052
|
}
|
|
@@ -1145,7 +1249,7 @@ export class PBRTSceneBuilder {
|
|
|
1145
1249
|
|
|
1146
1250
|
this.reportedMeshes ++;
|
|
1147
1251
|
const world = this.convertHandedness ? M.multiply( FLIP_Z, shape.ctm ) : shape.ctm;
|
|
1148
|
-
const full = this._appendToBatch( this._batches, shape, geometry, sharedMaterial, world );
|
|
1252
|
+
const full = this._appendToBatch( this._batches, shape, this._mappedGeometry( geometry, sharedMaterial, shape.ctm ), sharedMaterial, world );
|
|
1149
1253
|
this._releaseMerged( shape, geometry );
|
|
1150
1254
|
if ( full ) this._flushBatch( full, group );
|
|
1151
1255
|
|
|
@@ -1322,7 +1426,9 @@ export class PBRTSceneBuilder {
|
|
|
1322
1426
|
|
|
1323
1427
|
async _createGeometry( shape ) {
|
|
1324
1428
|
|
|
1325
|
-
|
|
1429
|
+
// A shape's parameters are all read before the first await; its `alpha`, by its material.
|
|
1430
|
+
const read = () => this._shapeGeometry( shape );
|
|
1431
|
+
const geometry = await ( this._usage ? this._usage.translate( shape.params, `Shape ${shape.type}`, read, 'alpha' ) : read() );
|
|
1326
1432
|
return geometry && shape.areaLight && ! pBool( shape.areaLight.params, 'twosided', false ) ? this._facingEmission( shape, geometry ) : geometry;
|
|
1327
1433
|
|
|
1328
1434
|
}
|
|
@@ -1395,7 +1501,7 @@ export class PBRTSceneBuilder {
|
|
|
1395
1501
|
|
|
1396
1502
|
_triangleMesh( params ) {
|
|
1397
1503
|
|
|
1398
|
-
const P = params
|
|
1504
|
+
const P = param( params, 'P' )?.value;
|
|
1399
1505
|
if ( ! P || P.length < 9 ) {
|
|
1400
1506
|
|
|
1401
1507
|
this.warn( 'trianglemesh missing P' ); return null;
|
|
@@ -1405,7 +1511,7 @@ export class PBRTSceneBuilder {
|
|
|
1405
1511
|
const geo = new BufferGeometry();
|
|
1406
1512
|
geo.setAttribute( 'position', new Float32BufferAttribute( float32( P ), 3 ) );
|
|
1407
1513
|
|
|
1408
|
-
const N = params
|
|
1514
|
+
const N = param( params, 'N' )?.value;
|
|
1409
1515
|
if ( N && N.length === P.length ) {
|
|
1410
1516
|
|
|
1411
1517
|
geo.setAttribute( 'normal', new Float32BufferAttribute( float32( N ), 3 ) );
|
|
@@ -1413,10 +1519,10 @@ export class PBRTSceneBuilder {
|
|
|
1413
1519
|
|
|
1414
1520
|
}
|
|
1415
1521
|
|
|
1416
|
-
const uv = ( params.uv
|
|
1522
|
+
const uv = param( params, params.uv ? 'uv' : 'st' )?.value;
|
|
1417
1523
|
if ( uv && uv.length === ( P.length / 3 ) * 2 ) geo.setAttribute( 'uv', new Float32BufferAttribute( float32( uv ), 2 ) );
|
|
1418
1524
|
|
|
1419
|
-
const indices = params
|
|
1525
|
+
const indices = param( params, 'indices' )?.value;
|
|
1420
1526
|
if ( indices && indices.length ) geo.setIndex( indexAttribute( indices ) );
|
|
1421
1527
|
|
|
1422
1528
|
if ( ! N ) geo.computeVertexNormals();
|
|
@@ -1426,7 +1532,7 @@ export class PBRTSceneBuilder {
|
|
|
1426
1532
|
|
|
1427
1533
|
_curve( params ) {
|
|
1428
1534
|
|
|
1429
|
-
const P = params
|
|
1535
|
+
const P = param( params, 'P' )?.value;
|
|
1430
1536
|
if ( ! P || P.length < 12 ) {
|
|
1431
1537
|
|
|
1432
1538
|
this.warn( 'curve needs at least 4 control points' ); return null;
|
|
@@ -1447,7 +1553,7 @@ export class PBRTSceneBuilder {
|
|
|
1447
1553
|
basis: pString( params, 'basis', 'bezier' ),
|
|
1448
1554
|
width0: pFloat( params, 'width0', width ),
|
|
1449
1555
|
width1: pFloat( params, 'width1', width ),
|
|
1450
|
-
N: params
|
|
1556
|
+
N: param( params, 'N' )?.value || null,
|
|
1451
1557
|
steps: this.curveSteps,
|
|
1452
1558
|
sides: this.curveSides ?? DEFAULT_CURVE_SIDES[ type ] ?? 1,
|
|
1453
1559
|
tolerance: this.curveTolerance
|
|
@@ -1473,11 +1579,11 @@ export class PBRTSceneBuilder {
|
|
|
1473
1579
|
_curves( params ) {
|
|
1474
1580
|
|
|
1475
1581
|
const built = tessellateCurves( {
|
|
1476
|
-
P: params
|
|
1477
|
-
counts: params
|
|
1478
|
-
widths: params
|
|
1582
|
+
P: param( params, 'P' ).value,
|
|
1583
|
+
counts: param( params, 'counts' ).value,
|
|
1584
|
+
widths: param( params, 'widths' )?.value ?? null,
|
|
1479
1585
|
widthMode: pString( params, 'widthMode', 'constant' ),
|
|
1480
|
-
N: params
|
|
1586
|
+
N: param( params, 'N' )?.value ?? null,
|
|
1481
1587
|
normalMode: pString( params, 'normalMode', 'vertex' ),
|
|
1482
1588
|
type: pString( params, 'type', 'cubic' ),
|
|
1483
1589
|
basis: pString( params, 'basis', 'bspline' ),
|
|
@@ -1506,8 +1612,8 @@ export class PBRTSceneBuilder {
|
|
|
1506
1612
|
|
|
1507
1613
|
_loopSubdiv( params ) {
|
|
1508
1614
|
|
|
1509
|
-
const P = params
|
|
1510
|
-
const indices = params
|
|
1615
|
+
const P = param( params, 'P' )?.value;
|
|
1616
|
+
const indices = param( params, 'indices' )?.value;
|
|
1511
1617
|
if ( ! P || P.length < 9 || ! indices || indices.length < 3 ) {
|
|
1512
1618
|
|
|
1513
1619
|
this.warn( 'loopsubdiv missing P/indices' ); return null;
|
|
@@ -1538,8 +1644,8 @@ export class PBRTSceneBuilder {
|
|
|
1538
1644
|
*/
|
|
1539
1645
|
_bilinearMesh( params ) {
|
|
1540
1646
|
|
|
1541
|
-
const P = params
|
|
1542
|
-
const quad = params
|
|
1647
|
+
const P = param( params, 'P' )?.value;
|
|
1648
|
+
const quad = param( params, 'indices' )?.value ?? ( P?.length === 12 ? [ 0, 1, 2, 3 ] : null );
|
|
1543
1649
|
if ( ! P || ! quad ) {
|
|
1544
1650
|
|
|
1545
1651
|
this.warn( 'bilinearmesh missing P/indices' ); return null;
|
|
@@ -1547,8 +1653,8 @@ export class PBRTSceneBuilder {
|
|
|
1547
1653
|
}
|
|
1548
1654
|
|
|
1549
1655
|
const count = P.length / 3;
|
|
1550
|
-
const uv = params
|
|
1551
|
-
const N = params
|
|
1656
|
+
const uv = param( params, 'uv' )?.value?.length === count * 2 ? params.uv.value : null;
|
|
1657
|
+
const N = param( params, 'N' )?.value?.length === count * 3 ? params.N.value : null;
|
|
1552
1658
|
const geo = new BufferGeometry();
|
|
1553
1659
|
|
|
1554
1660
|
if ( uv ) {
|
|
@@ -1691,7 +1797,75 @@ export class PBRTSceneBuilder {
|
|
|
1691
1797
|
|
|
1692
1798
|
let material = await this._surfaceMaterial( shape );
|
|
1693
1799
|
if ( shape.interior ) material = await this._inMedium( material, shape.interior );
|
|
1694
|
-
|
|
1800
|
+
if ( shape.params.alpha ) material = await this._withAlpha( material, shape.params.alpha );
|
|
1801
|
+
return this._oneUVSet( material );
|
|
1802
|
+
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
/**
|
|
1806
|
+
* The material with every map in its one uv set (`uvSetOf`): the engine reads one a triangle. A map in another
|
|
1807
|
+
* counts as its mean (a roughness) or is dropped, reported once.
|
|
1808
|
+
* @private
|
|
1809
|
+
*/
|
|
1810
|
+
_oneUVSet( material ) {
|
|
1811
|
+
|
|
1812
|
+
let out = this._oneUVSets.get( material );
|
|
1813
|
+
if ( out ) return out;
|
|
1814
|
+
out = material;
|
|
1815
|
+
const set = uvSetOf( material );
|
|
1816
|
+
const what = ( uv ) => ( uv ? `a "${uv.type}" mapping` : 'the mesh\'s uv' );
|
|
1817
|
+
|
|
1818
|
+
for ( const slot of UV_MAPS ) {
|
|
1819
|
+
|
|
1820
|
+
const map = material[ slot ];
|
|
1821
|
+
if ( ! map || uvKey( map.userData.pbrtUV ) === uvKey( set ) ) continue;
|
|
1822
|
+
if ( out === material ) out = cloneMaterial( material );
|
|
1823
|
+
out[ slot ] = null;
|
|
1824
|
+
const scalar = MAP_SCALARS[ slot ];
|
|
1825
|
+
const layer = scalar && this._textureLayer( map, 1 );
|
|
1826
|
+
if ( layer ) out[ scalar ] *= layerMean( layer );
|
|
1827
|
+
const message = `a ${slot} in ${what( map.userData.pbrtUV )} on a surface textured in ${what( set )}: ` +
|
|
1828
|
+
`one uv set a surface — ${layer ? 'its mean used' : 'dropped'}`;
|
|
1829
|
+
if ( ! this._uvWarnings.has( message ) ) {
|
|
1830
|
+
|
|
1831
|
+
this._uvWarnings.add( message );
|
|
1832
|
+
this.warn( message );
|
|
1833
|
+
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
this._oneUVSets.set( material, out );
|
|
1839
|
+
return out;
|
|
1840
|
+
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
/**
|
|
1844
|
+
* `geometry` with the uv set its material's position mapping gives it at `world` (pbrt's, column-major); as it is
|
|
1845
|
+
* when the material reads the mesh's own uv. Attributes are shared unless a seam copied vertices.
|
|
1846
|
+
* @private
|
|
1847
|
+
*/
|
|
1848
|
+
_mappedGeometry( geometry, material, world ) {
|
|
1849
|
+
|
|
1850
|
+
const mapping = uvSetOf( material );
|
|
1851
|
+
if ( ! mapping ) return geometry;
|
|
1852
|
+
|
|
1853
|
+
const { uv, index, extra } = generateUV(
|
|
1854
|
+
geometry.getAttribute( 'position' ).array, geometry.index?.array ?? null, M.multiply( mapping.textureFromWorld, world ), mapping
|
|
1855
|
+
);
|
|
1856
|
+
const out = new BufferGeometry();
|
|
1857
|
+
out.name = geometry.name;
|
|
1858
|
+
Object.assign( out.userData, geometry.userData );
|
|
1859
|
+
for ( const name in geometry.attributes ) {
|
|
1860
|
+
|
|
1861
|
+
if ( name !== 'uv' ) out.setAttribute( name, extra.length ? withCopies( geometry.getAttribute( name ), extra ) : geometry.getAttribute( name ) );
|
|
1862
|
+
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
out.setAttribute( 'uv', new BufferAttribute( uv, 2 ) );
|
|
1866
|
+
if ( index ) out.setIndex( new Uint32BufferAttribute( index, 1 ) );
|
|
1867
|
+
else if ( geometry.index ) out.setIndex( geometry.index );
|
|
1868
|
+
return out;
|
|
1695
1869
|
|
|
1696
1870
|
}
|
|
1697
1871
|
|
|
@@ -1734,17 +1908,18 @@ export class PBRTSceneBuilder {
|
|
|
1734
1908
|
*/
|
|
1735
1909
|
async _alphaMap( colorMap, name ) {
|
|
1736
1910
|
|
|
1737
|
-
const def = this.
|
|
1911
|
+
const def = this._textureDef( name );
|
|
1738
1912
|
const filename = def?.class === 'imagemap' ? pString( def.params, 'filename', null ) : null;
|
|
1913
|
+
const mapping = filename && this._textureMapping( name, def );
|
|
1739
1914
|
// Most often the alpha is the colour map's own image: its colour stays as it is.
|
|
1740
|
-
if ( filename && colorMap?.userData.pbrtImage === filename
|
|
1741
|
-
&& pFloat( def.params, 'scale', 1 ) === 1 && ! pBool( def.params, 'invert', false ) && sameMapping( colorMap,
|
|
1915
|
+
if ( filename && colorMap?.userData.pbrtImage === filename
|
|
1916
|
+
&& pFloat( def.params, 'scale', 1 ) === 1 && ! pBool( def.params, 'invert', false ) && sameMapping( colorMap, mapping ) ) {
|
|
1742
1917
|
|
|
1743
1918
|
const pixels = texturePixels( colorMap );
|
|
1744
1919
|
const own = pixels && withDecodedAlpha( pixels, decoderFor( def.params ) );
|
|
1745
1920
|
if ( own ) {
|
|
1746
1921
|
|
|
1747
|
-
setMapping( own,
|
|
1922
|
+
setMapping( own, mapping );
|
|
1748
1923
|
return own;
|
|
1749
1924
|
|
|
1750
1925
|
}
|
|
@@ -1756,7 +1931,7 @@ export class PBRTSceneBuilder {
|
|
|
1756
1931
|
if ( ! alpha.layer ) return { constant: alpha.rgb[ 0 ] };
|
|
1757
1932
|
const color = colorMap ? this._textureLayer( colorMap, 3 ) : null;
|
|
1758
1933
|
if ( colorMap && ! color ) return null;
|
|
1759
|
-
const baked =
|
|
1934
|
+
const baked = this._bake( `alpha "${name}"`, [ color ? { layer: color } : { rgb: [ 1, 1, 1 ] }, alpha ], ( [ c ] ) => c, { alphaOf: ( [ , a ] ) => a[ 0 ] } );
|
|
1760
1935
|
return baked && bakedTexture( baked );
|
|
1761
1936
|
|
|
1762
1937
|
}
|
|
@@ -1792,6 +1967,7 @@ export class PBRTSceneBuilder {
|
|
|
1792
1967
|
return this._ctx ??= {
|
|
1793
1968
|
resolveNamedTexture: ( n ) => this._resolveNamedTexture( n ),
|
|
1794
1969
|
namedMaterials: this.ir.namedMaterials,
|
|
1970
|
+
trackParams: ( params, label, consumed ) => this._usage?.open( params, label, consumed ),
|
|
1795
1971
|
warn: ( m ) => this.warn( m ),
|
|
1796
1972
|
floatTextureMean: async ( n ) => {
|
|
1797
1973
|
|
|
@@ -1844,6 +2020,7 @@ export class PBRTSceneBuilder {
|
|
|
1844
2020
|
|
|
1845
2021
|
const params = this.ir.media?.get( name );
|
|
1846
2022
|
const type = params && pString( params, 'type', '' );
|
|
2023
|
+
this._usage?.open( params, `MakeNamedMedium ${type}`, 'type' );
|
|
1847
2024
|
if ( type !== 'homogeneous' ) {
|
|
1848
2025
|
|
|
1849
2026
|
this.warn( params ? `medium "${name}" (${type}) is not supported — "${name}" glass left clear` : `medium "${name}" not defined` );
|
|
@@ -1851,7 +2028,13 @@ export class PBRTSceneBuilder {
|
|
|
1851
2028
|
|
|
1852
2029
|
}
|
|
1853
2030
|
|
|
1854
|
-
if ( params.preset )
|
|
2031
|
+
if ( params.preset ) {
|
|
2032
|
+
|
|
2033
|
+
this.warn( `medium preset "${pString( params, 'preset', '' )}" is not supported — using sigma_a / sigma_s` );
|
|
2034
|
+
approximated( params, 'preset' );
|
|
2035
|
+
|
|
2036
|
+
}
|
|
2037
|
+
|
|
1855
2038
|
const ctx = this._materialContext();
|
|
1856
2039
|
const a = ( await resolveSpectrum( params, 'sigma_a', ctx, [ 1, 1, 1 ] ) ).rgb;
|
|
1857
2040
|
const s = ( await resolveSpectrum( params, 'sigma_s', ctx, [ 1, 1, 1 ] ) ).rgb;
|
|
@@ -1909,6 +2092,7 @@ export class PBRTSceneBuilder {
|
|
|
1909
2092
|
|
|
1910
2093
|
async _applyAreaLight( material, areaLight, ctx ) {
|
|
1911
2094
|
|
|
2095
|
+
this._usage?.open( areaLight.params, `AreaLightSource ${areaLight.type}` );
|
|
1912
2096
|
const rgb = await lightRGB( areaLight.params, 'L', ctx );
|
|
1913
2097
|
const scale = pFloat( areaLight.params, 'scale', 1 );
|
|
1914
2098
|
material.emissive.setRGB( rgb[ 0 ], rgb[ 1 ], rgb[ 2 ] );
|
|
@@ -1918,11 +2102,20 @@ export class PBRTSceneBuilder {
|
|
|
1918
2102
|
|
|
1919
2103
|
}
|
|
1920
2104
|
|
|
2105
|
+
/** A named texture's definition, its parameters accounted from here to the end of the build. @private */
|
|
2106
|
+
_textureDef( name ) {
|
|
2107
|
+
|
|
2108
|
+
const def = this.ir.namedTextures.get( name );
|
|
2109
|
+
if ( def ) this._usage?.open( def.params, `Texture ${def.class}` );
|
|
2110
|
+
return def;
|
|
2111
|
+
|
|
2112
|
+
}
|
|
2113
|
+
|
|
1921
2114
|
async _resolveNamedTexture( name ) {
|
|
1922
2115
|
|
|
1923
2116
|
if ( this._textureCache.has( name ) ) return this._textureCache.get( name );
|
|
1924
2117
|
|
|
1925
|
-
const def = this.
|
|
2118
|
+
const def = this._textureDef( name );
|
|
1926
2119
|
let result = null;
|
|
1927
2120
|
|
|
1928
2121
|
if ( ! def ) {
|
|
@@ -1939,7 +2132,7 @@ export class PBRTSceneBuilder {
|
|
|
1939
2132
|
|
|
1940
2133
|
} else if ( def.class === 'constant' ) {
|
|
1941
2134
|
|
|
1942
|
-
const v = def.params
|
|
2135
|
+
const v = param( def.params, 'value' );
|
|
1943
2136
|
if ( v && v.type === 'rgb' ) result = { constant: [ v.value[ 0 ], v.value[ 1 ], v.value[ 2 ] ] };
|
|
1944
2137
|
else if ( v ) result = { constant: [ v.value[ 0 ], v.value[ 0 ], v.value[ 0 ] ] };
|
|
1945
2138
|
|
|
@@ -1987,15 +2180,19 @@ export class PBRTSceneBuilder {
|
|
|
1987
2180
|
const ctx = this._materialContext();
|
|
1988
2181
|
const value = async ( key, dflt ) => {
|
|
1989
2182
|
|
|
1990
|
-
const p = params
|
|
1991
|
-
if ( p?.type === 'texture' )
|
|
2183
|
+
const p = param( params, key );
|
|
2184
|
+
if ( p?.type === 'texture' ) {
|
|
2185
|
+
|
|
2186
|
+
approximated( params, key );
|
|
2187
|
+
return ( await ctx.textureMeanRGB( p.value[ 0 ] ) ) ?? dflt;
|
|
2188
|
+
|
|
2189
|
+
}
|
|
2190
|
+
|
|
1992
2191
|
return ( await resolveSpectrum( params, key, ctx, dflt ) ).rgb ?? dflt;
|
|
1993
2192
|
|
|
1994
2193
|
};
|
|
1995
2194
|
|
|
1996
|
-
const
|
|
1997
|
-
if ( mapping !== 'uv' ) this.warn( `texture "${name}": "${mapping}" mapping is not supported — using the mesh's uv` );
|
|
1998
|
-
const { su, sv, du, dv } = uvMapping( params );
|
|
2195
|
+
const { su, sv, du, dv, uv } = this._textureMapping( name, def );
|
|
1999
2196
|
|
|
2000
2197
|
if ( def.class === 'checkerboard' ) {
|
|
2001
2198
|
|
|
@@ -2009,7 +2206,7 @@ export class PBRTSceneBuilder {
|
|
|
2009
2206
|
const [ a, b ] = await Promise.all( [ value( 'tex1', [ 1, 1, 1 ] ), value( 'tex2', [ 0, 0, 0 ] ) ] );
|
|
2010
2207
|
const n = 2 * CHECK_TEXELS;
|
|
2011
2208
|
const data = rasterize( n, n, ( s, t ) => ( ( Math.floor( 2 * s ) + Math.floor( 2 * t ) ) % 2 ? b : a ) );
|
|
2012
|
-
return { texture: bakedTexture( { data, width: n, height: n, mapping: { su: su / 2, sv: sv / 2, du: du / 2, dv: dv / 2 } } ) };
|
|
2209
|
+
return { texture: bakedTexture( { data, width: n, height: n, mapping: { su: su / 2, sv: sv / 2, du: du / 2, dv: dv / 2, uv } } ) };
|
|
2013
2210
|
|
|
2014
2211
|
}
|
|
2015
2212
|
|
|
@@ -2018,7 +2215,32 @@ export class PBRTSceneBuilder {
|
|
|
2018
2215
|
] );
|
|
2019
2216
|
const data = rasterize( BILERP_TEXELS, BILERP_TEXELS, ( s, t ) => [ 0, 1, 2 ].map( ( c ) =>
|
|
2020
2217
|
( 1 - s ) * ( 1 - t ) * v00[ c ] + s * ( 1 - t ) * v10[ c ] + ( 1 - s ) * t * v01[ c ] + s * t * v11[ c ] ) );
|
|
2021
|
-
return { texture: bakedTexture( { data, width: BILERP_TEXELS, height: BILERP_TEXELS, mapping: { su, sv, du, dv } } ) };
|
|
2218
|
+
return { texture: bakedTexture( { data, width: BILERP_TEXELS, height: BILERP_TEXELS, mapping: { su, sv, du, dv, uv } } ) };
|
|
2219
|
+
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
/** A named texture's textureMapping(), once; a mapping pbrt does not know is reported and read as `uv`. @private */
|
|
2223
|
+
_textureMapping( name, def ) {
|
|
2224
|
+
|
|
2225
|
+
let mapping = this._mappings.get( name );
|
|
2226
|
+
if ( ! mapping ) {
|
|
2227
|
+
|
|
2228
|
+
const type = pString( def.params, 'mapping', 'uv' );
|
|
2229
|
+
if ( type !== 'uv' && ! POSITION_MAPPINGS.has( type ) ) this.warn( `texture "${name}": unknown mapping "${type}" — using the mesh's uv` );
|
|
2230
|
+
this._mappings.set( name, mapping = textureMapping( def.params, def.ctm ) );
|
|
2231
|
+
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
return mapping;
|
|
2235
|
+
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
/** bake(), reporting the inputs it had to read as their mean. @private */
|
|
2239
|
+
_bake( label, terms, combine, options ) {
|
|
2240
|
+
|
|
2241
|
+
const baked = bake( terms, combine, options );
|
|
2242
|
+
if ( baked?.averaged ) this.warn( `${label}: ${baked.averaged} input(s) in another uv set than the first image — read as their mean` );
|
|
2243
|
+
return baked;
|
|
2022
2244
|
|
|
2023
2245
|
}
|
|
2024
2246
|
|
|
@@ -2059,14 +2281,12 @@ export class PBRTSceneBuilder {
|
|
|
2059
2281
|
|
|
2060
2282
|
}
|
|
2061
2283
|
|
|
2062
|
-
const mapping =
|
|
2063
|
-
if ( mapping !== 'uv' ) this.warn( `texture "${name}": "${mapping}" mapping is not supported — using the mesh's uv` );
|
|
2064
|
-
|
|
2284
|
+
const mapping = this._textureMapping( name, def );
|
|
2065
2285
|
const scale = pFloat( def.params, 'scale', 1 );
|
|
2066
2286
|
if ( scale > 1 || pBool( def.params, 'invert', false ) ) {
|
|
2067
2287
|
|
|
2068
2288
|
const term = await this._bakeTerm( name, 3 );
|
|
2069
|
-
const baked = term?.layer &&
|
|
2289
|
+
const baked = term?.layer && this._bake( `texture "${name}"`, [ { ...term, clamp: true } ], ( [ v ] ) => v );
|
|
2070
2290
|
if ( baked ) return { texture: bakedTexture( baked ) };
|
|
2071
2291
|
this.warn( `texture "${name}": its scale or invert could not be baked here — ignored` );
|
|
2072
2292
|
|
|
@@ -2075,7 +2295,7 @@ export class PBRTSceneBuilder {
|
|
|
2075
2295
|
const color = eightBit( image, { srgb: true } );
|
|
2076
2296
|
const texture = color === image ? image.clone() : color;
|
|
2077
2297
|
texture.userData.pbrtImage = filename;
|
|
2078
|
-
setMapping( texture,
|
|
2298
|
+
setMapping( texture, mapping );
|
|
2079
2299
|
texture.needsUpdate = true;
|
|
2080
2300
|
return scale < 1 ? { texture, constant: [ scale, scale, scale ] } : { texture };
|
|
2081
2301
|
|
|
@@ -2090,7 +2310,7 @@ export class PBRTSceneBuilder {
|
|
|
2090
2310
|
|
|
2091
2311
|
const terms = await Promise.all( keys.map( ( key ) => this._paramTerm( params, key, 1, [ 0, 0, 0 ] ) ) );
|
|
2092
2312
|
if ( terms.some( ( term ) => ! term ) ) return null;
|
|
2093
|
-
const baked =
|
|
2313
|
+
const baked = this._bake( `"${keys.join( '", "' )}"`, terms, ( values ) => {
|
|
2094
2314
|
|
|
2095
2315
|
const v = combine( values.map( ( value ) => value[ 0 ] ) );
|
|
2096
2316
|
return [ v, v, v ];
|
|
@@ -2116,7 +2336,7 @@ export class PBRTSceneBuilder {
|
|
|
2116
2336
|
}
|
|
2117
2337
|
|
|
2118
2338
|
const unit = ( rgb ) => rgb.map( ( v ) => Math.min( 1, Math.max( 0, v ) ) );
|
|
2119
|
-
const baked =
|
|
2339
|
+
const baked = this._bake( `texture "${name}"`, [ { ...a, clamp: true }, { ...b, clamp: true }, amount ], mixOf );
|
|
2120
2340
|
return baked ? { texture: bakedTexture( baked ) } : { constant: mixOf( [ unit( a.rgb ), unit( b.rgb ), amount.rgb ] ) };
|
|
2121
2341
|
|
|
2122
2342
|
}
|
|
@@ -2124,7 +2344,7 @@ export class PBRTSceneBuilder {
|
|
|
2124
2344
|
/** A texture parameter as a bake term: a constant, or the texture it names. @private */
|
|
2125
2345
|
async _paramTerm( params, key, channels, dflt ) {
|
|
2126
2346
|
|
|
2127
|
-
const p = params
|
|
2347
|
+
const p = param( params, key );
|
|
2128
2348
|
if ( ! p ) return { rgb: dflt };
|
|
2129
2349
|
if ( p.type === 'texture' ) return this._bakeTerm( p.value[ 0 ], channels );
|
|
2130
2350
|
const rgb = ( await resolveSpectrum( params, key, this._materialContext(), dflt ) ).rgb;
|
|
@@ -2150,16 +2370,15 @@ export class PBRTSceneBuilder {
|
|
|
2150
2370
|
|
|
2151
2371
|
async _createBakeTerm( name, channels ) {
|
|
2152
2372
|
|
|
2153
|
-
const def = this.
|
|
2373
|
+
const def = this._textureDef( name );
|
|
2154
2374
|
if ( def?.class === 'imagemap' ) {
|
|
2155
2375
|
|
|
2156
|
-
if ( pString( def.params, 'mapping', 'uv' ) !== 'uv' ) return null;
|
|
2157
2376
|
const filename = pString( def.params, 'filename', null );
|
|
2158
2377
|
const pixels = texturePixels( filename && await this._image( filename ) );
|
|
2159
2378
|
if ( ! pixels ) return null;
|
|
2160
2379
|
return { layer: makeLayer( pixels, {
|
|
2161
2380
|
channels, decode: pixels.float ? null : decoderFor( def.params ),
|
|
2162
|
-
scale: pFloat( def.params, 'scale', 1 ), invert: pBool( def.params, 'invert', false ), mapping:
|
|
2381
|
+
scale: pFloat( def.params, 'scale', 1 ), invert: pBool( def.params, 'invert', false ), mapping: this._textureMapping( name, def )
|
|
2163
2382
|
} ) };
|
|
2164
2383
|
|
|
2165
2384
|
}
|
|
@@ -2180,7 +2399,7 @@ export class PBRTSceneBuilder {
|
|
|
2180
2399
|
const srgb = ! pixels.float && texture.colorSpace === SRGBColorSpace;
|
|
2181
2400
|
return makeLayer( pixels, {
|
|
2182
2401
|
channels, decode: pixels.float ? null : ( srgb ? srgbToLinear : ( v ) => v ),
|
|
2183
|
-
mapping: { su: texture.repeat.x, sv: texture.repeat.y, du: texture.offset.x, dv: texture.offset.y }
|
|
2402
|
+
mapping: { su: texture.repeat.x, sv: texture.repeat.y, du: texture.offset.x, dv: texture.offset.y, uv: texture.userData.pbrtUV ?? null }
|
|
2184
2403
|
} );
|
|
2185
2404
|
|
|
2186
2405
|
}
|
|
@@ -2207,7 +2426,7 @@ export class PBRTSceneBuilder {
|
|
|
2207
2426
|
|
|
2208
2427
|
const a = term( matA ), b = term( matB );
|
|
2209
2428
|
if ( ! a || ! b ) return { mean, texture: null };
|
|
2210
|
-
const baked =
|
|
2429
|
+
const baked = this._bake( `mix amount "${amountName}"`, [ a, b, amount ], mixOf );
|
|
2211
2430
|
return { mean, texture: baked ? bakedTexture( baked ) : null };
|
|
2212
2431
|
|
|
2213
2432
|
}
|
|
@@ -2243,7 +2462,7 @@ export class PBRTSceneBuilder {
|
|
|
2243
2462
|
const product = ( [ a, f ] ) => [ a[ 0 ] * f[ 0 ], a[ 1 ] * f[ 0 ], a[ 2 ] * f[ 0 ] ];
|
|
2244
2463
|
if ( inner && factor ) {
|
|
2245
2464
|
|
|
2246
|
-
const baked =
|
|
2465
|
+
const baked = this._bake( `texture "${name}"`, [ { ...inner, clamp: true }, factor ], product );
|
|
2247
2466
|
return baked ? { texture: bakedTexture( baked ) } : { constant: product( [ inner.rgb, factor.rgb ] ) };
|
|
2248
2467
|
|
|
2249
2468
|
}
|
|
@@ -2252,7 +2471,7 @@ export class PBRTSceneBuilder {
|
|
|
2252
2471
|
|
|
2253
2472
|
}
|
|
2254
2473
|
|
|
2255
|
-
const innerP = def.params
|
|
2474
|
+
const innerP = param( def.params, 'tex' );
|
|
2256
2475
|
let inner = null;
|
|
2257
2476
|
|
|
2258
2477
|
if ( innerP?.type === 'texture' && typeof innerP.value[ 0 ] === 'string' ) {
|
|
@@ -2270,7 +2489,7 @@ export class PBRTSceneBuilder {
|
|
|
2270
2489
|
|
|
2271
2490
|
}
|
|
2272
2491
|
|
|
2273
|
-
const sP = def.params
|
|
2492
|
+
const sP = param( def.params, 'scale' );
|
|
2274
2493
|
let scale = [ 1, 1, 1 ];
|
|
2275
2494
|
if ( sP?.type === 'rgb' || sP?.type === 'color' ) scale = [ sP.value[ 0 ], sP.value[ 1 ], sP.value[ 2 ] ];
|
|
2276
2495
|
else if ( sP?.type === 'float' ) {
|
|
@@ -2299,12 +2518,13 @@ export class PBRTSceneBuilder {
|
|
|
2299
2518
|
_buildCamera( cam, film ) {
|
|
2300
2519
|
|
|
2301
2520
|
const aspect = film && film.yresolution ? film.xresolution / film.yresolution : 16 / 9;
|
|
2521
|
+
this._usage?.open( cam.params, `Camera ${cam.type}` );
|
|
2302
2522
|
|
|
2303
2523
|
let camera;
|
|
2304
2524
|
if ( cam.type === 'orthographic' ) {
|
|
2305
2525
|
|
|
2306
2526
|
// pbrt's default screen window spans [-1, 1] along the shorter image axis.
|
|
2307
|
-
const window = cam.params
|
|
2527
|
+
const window = param( cam.params, 'screenwindow' )?.value;
|
|
2308
2528
|
const half = window?.length === 4 ? ( window[ 3 ] - window[ 2 ] ) / 2 : Math.max( 1, 1 / aspect );
|
|
2309
2529
|
camera = new OrthographicCamera( - half * aspect, half * aspect, half, - half, 0.01, 10000 );
|
|
2310
2530
|
|
|
@@ -2493,7 +2713,7 @@ export class PBRTSceneBuilder {
|
|
|
2493
2713
|
const world = new Matrix4().fromArray( this.convertHandedness ? M.multiply( FLIP_Z, l.ctm ) : l.ctm );
|
|
2494
2714
|
const point = ( name, dflt ) => {
|
|
2495
2715
|
|
|
2496
|
-
const v = l.params
|
|
2716
|
+
const v = param( l.params, name )?.value;
|
|
2497
2717
|
return new Vector3().fromArray( v?.length >= 3 ? v : dflt ).applyMatrix4( world );
|
|
2498
2718
|
|
|
2499
2719
|
};
|