rayzee 8.6.1 → 8.7.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/dist/rayzee.es.js +3507 -2955
- package/dist/rayzee.umd.js +16 -16
- package/package.json +1 -1
- package/src/PathTracerApp.js +97 -2
- package/src/Processor/AssetLoader.js +14 -8
- package/src/Processor/EmissiveTriangleBuilder.js +70 -0
- package/src/Processor/PBRT/PBRTAnimation.js +646 -0
- package/src/Processor/PBRT/PBRTParser.js +179 -52
- package/src/Processor/PBRT/PBRTSceneBuilder.js +181 -25
- package/src/Processor/PBRT/index.js +73 -14
- package/src/Processor/SceneProcessor.js +22 -0
- package/src/Stages/PathTracerStage.js +16 -1
- package/src/managers/AnimationManager.js +202 -8
|
@@ -15,12 +15,17 @@
|
|
|
15
15
|
* camera: { type, params, cameraToWorld:number[16] } | null,
|
|
16
16
|
* namedMaterials: Map<name, { type, params }>,
|
|
17
17
|
* namedTextures: Map<name, { dataType, class, params }>,
|
|
18
|
-
* shapes: [ { type, params, ctm, material, areaLight, reverseOrientation } ],
|
|
18
|
+
* shapes: [ { type, params, ctm, ctmEnd?, material, areaLight, reverseOrientation } ],
|
|
19
19
|
* lights: [ { type, params, ctm } ],
|
|
20
|
-
* instances:
|
|
20
|
+
* instances: Map<name, { name, count, matrices, matricesEnd? }>,
|
|
21
21
|
* objects: Map<name, shapes[]>,
|
|
22
|
+
* transformTimes: { start, end },
|
|
23
|
+
* hasMotion: boolean,
|
|
22
24
|
* warnings: string[]
|
|
23
25
|
* }
|
|
26
|
+
*
|
|
27
|
+
* `ctm` is pbrt's start transform. `ActiveTransform` can edit a second, end-of-shutter one,
|
|
28
|
+
* recorded as `ctmEnd` / `cameraToWorldEnd` / `matricesEnd` only where it differs.
|
|
24
29
|
*/
|
|
25
30
|
|
|
26
31
|
import { TokenStream, TokenType } from './PBRTTokenizer.js';
|
|
@@ -32,6 +37,16 @@ import * as M from './PBRTMath.js';
|
|
|
32
37
|
// ~100 bytes of overhead, and most parameters are one number or three.
|
|
33
38
|
const TYPED_ARRAY_THRESHOLD = 8;
|
|
34
39
|
|
|
40
|
+
const START = 1, END = 2, BOTH = 3;
|
|
41
|
+
|
|
42
|
+
function sameMatrix( a, b ) {
|
|
43
|
+
|
|
44
|
+
if ( a === b ) return true;
|
|
45
|
+
for ( let i = 0; i < 16; i ++ ) if ( a[ i ] !== b[ i ] ) return false;
|
|
46
|
+
return true;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
35
50
|
const NUMERIC_STORAGE = {
|
|
36
51
|
integer: Int32Array,
|
|
37
52
|
point3: Float32Array,
|
|
@@ -43,6 +58,14 @@ const NUMERIC_STORAGE = {
|
|
|
43
58
|
rgb: Float32Array
|
|
44
59
|
};
|
|
45
60
|
|
|
61
|
+
function growFloat32( array, length ) {
|
|
62
|
+
|
|
63
|
+
const grown = new Float32Array( length );
|
|
64
|
+
grown.set( array );
|
|
65
|
+
return grown;
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
46
69
|
export class PBRTParser {
|
|
47
70
|
|
|
48
71
|
/**
|
|
@@ -79,11 +102,15 @@ export class PBRTParser {
|
|
|
79
102
|
instanceCount: 0,
|
|
80
103
|
skippedInstances: 0,
|
|
81
104
|
objects: new Map(),
|
|
105
|
+
transformTimes: { start: 0, end: 1 },
|
|
106
|
+
hasMotion: false,
|
|
82
107
|
warnings: []
|
|
83
108
|
};
|
|
84
109
|
|
|
85
|
-
// Graphics state
|
|
110
|
+
// Graphics state. `ctmEnd` is null while it equals `ctm`.
|
|
86
111
|
this.ctm = M.identity();
|
|
112
|
+
this.ctmEnd = null;
|
|
113
|
+
this.activeTransform = BOTH;
|
|
87
114
|
this._ctmSource = null;
|
|
88
115
|
this._ctmValue = null;
|
|
89
116
|
this.state = { material: null, areaLight: null, reverseOrientation: false };
|
|
@@ -133,20 +160,31 @@ export class PBRTParser {
|
|
|
133
160
|
}
|
|
134
161
|
|
|
135
162
|
let list = this.ir.instances.get( name );
|
|
136
|
-
if ( ! list ) this.ir.instances.set( name, list = { name, count: 0, matrices: new Float32Array( 16 * 32 ) } );
|
|
163
|
+
if ( ! list ) this.ir.instances.set( name, list = { name, count: 0, matrices: new Float32Array( 16 * 32 ), matricesEnd: null } );
|
|
137
164
|
|
|
138
165
|
const need = ( list.count + 1 ) * 16;
|
|
139
166
|
if ( need > list.matrices.length ) {
|
|
140
167
|
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
list.
|
|
168
|
+
const length = Math.max( need, list.matrices.length * 2 );
|
|
169
|
+
list.matrices = growFloat32( list.matrices, length );
|
|
170
|
+
if ( list.matricesEnd ) list.matricesEnd = growFloat32( list.matricesEnd, length );
|
|
144
171
|
|
|
145
172
|
}
|
|
146
173
|
|
|
147
174
|
const o = list.count * 16;
|
|
148
175
|
const m = this.ctm;
|
|
149
176
|
for ( let i = 0; i < 16; i ++ ) list.matrices[ o + i ] = m[ i ];
|
|
177
|
+
|
|
178
|
+
// Allocated once a placement of this template moves.
|
|
179
|
+
const end = this._ctmEndSnapshot();
|
|
180
|
+
if ( end && ! list.matricesEnd ) list.matricesEnd = list.matrices.slice();
|
|
181
|
+
if ( list.matricesEnd ) {
|
|
182
|
+
|
|
183
|
+
const e = end || m;
|
|
184
|
+
for ( let i = 0; i < 16; i ++ ) list.matricesEnd[ o + i ] = e[ i ];
|
|
185
|
+
|
|
186
|
+
}
|
|
187
|
+
|
|
150
188
|
list.count ++;
|
|
151
189
|
this.ir.instanceCount ++;
|
|
152
190
|
|
|
@@ -170,6 +208,72 @@ export class PBRTParser {
|
|
|
170
208
|
|
|
171
209
|
}
|
|
172
210
|
|
|
211
|
+
/** The end-of-shutter CTM when it differs from the start one, else null. */
|
|
212
|
+
_ctmEndSnapshot() {
|
|
213
|
+
|
|
214
|
+
if ( this.ctmEnd === null || sameMatrix( this.ctmEnd, this.ctm ) ) return null;
|
|
215
|
+
this.ir.hasMotion = true;
|
|
216
|
+
return this.ctmEnd.slice();
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Right-multiply `m` into whichever CTMs ActiveTransform selects. */
|
|
221
|
+
_concat( m ) {
|
|
222
|
+
|
|
223
|
+
if ( this.activeTransform === BOTH && this.ctmEnd === null ) {
|
|
224
|
+
|
|
225
|
+
this.ctm = M.multiply( this.ctm, m );
|
|
226
|
+
return;
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const end = this.ctmEnd ?? this.ctm;
|
|
231
|
+
this.ctmEnd = this.activeTransform & END ? M.multiply( end, m ) : end;
|
|
232
|
+
if ( this.activeTransform & START ) this.ctm = M.multiply( this.ctm, m );
|
|
233
|
+
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Replace whichever CTMs ActiveTransform selects. */
|
|
237
|
+
_setCTM( m ) {
|
|
238
|
+
|
|
239
|
+
if ( this.activeTransform === BOTH ) {
|
|
240
|
+
|
|
241
|
+
this.ctm = m;
|
|
242
|
+
this.ctmEnd = null;
|
|
243
|
+
return;
|
|
244
|
+
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const end = this.ctmEnd ?? this.ctm;
|
|
248
|
+
this.ctmEnd = this.activeTransform & END ? m : end;
|
|
249
|
+
if ( this.activeTransform & START ) this.ctm = m;
|
|
250
|
+
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
_pushGraphicsState() {
|
|
254
|
+
|
|
255
|
+
this.attributeStack.push( {
|
|
256
|
+
ctm: this.ctm,
|
|
257
|
+
ctmEnd: this.ctmEnd,
|
|
258
|
+
activeTransform: this.activeTransform,
|
|
259
|
+
material: this.state.material,
|
|
260
|
+
areaLight: this.state.areaLight,
|
|
261
|
+
reverseOrientation: this.state.reverseOrientation
|
|
262
|
+
} );
|
|
263
|
+
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
_popGraphicsState() {
|
|
267
|
+
|
|
268
|
+
const s = this.attributeStack.pop();
|
|
269
|
+
if ( ! s ) return;
|
|
270
|
+
this.ctm = s.ctm;
|
|
271
|
+
this.ctmEnd = s.ctmEnd;
|
|
272
|
+
this.activeTransform = s.activeTransform;
|
|
273
|
+
this.state = { material: s.material, areaLight: s.areaLight, reverseOrientation: s.reverseOrientation };
|
|
274
|
+
|
|
275
|
+
}
|
|
276
|
+
|
|
173
277
|
_peek() {
|
|
174
278
|
|
|
175
279
|
return this.stream.peek();
|
|
@@ -398,11 +502,11 @@ export class PBRTParser {
|
|
|
398
502
|
switch ( name ) {
|
|
399
503
|
|
|
400
504
|
// ── transforms ──
|
|
401
|
-
case 'Identity': this.
|
|
505
|
+
case 'Identity': this._setCTM( M.identity() ); break;
|
|
402
506
|
case 'Translate': {
|
|
403
507
|
|
|
404
508
|
const [ x, y, z ] = this._readNumbers( 3 );
|
|
405
|
-
this.
|
|
509
|
+
this._concat( M.translate( x, y, z ) );
|
|
406
510
|
break;
|
|
407
511
|
|
|
408
512
|
}
|
|
@@ -410,7 +514,7 @@ export class PBRTParser {
|
|
|
410
514
|
case 'Scale': {
|
|
411
515
|
|
|
412
516
|
const [ x, y, z ] = this._readNumbers( 3 );
|
|
413
|
-
this.
|
|
517
|
+
this._concat( M.scale( x, y, z ) );
|
|
414
518
|
break;
|
|
415
519
|
|
|
416
520
|
}
|
|
@@ -418,7 +522,7 @@ export class PBRTParser {
|
|
|
418
522
|
case 'Rotate': {
|
|
419
523
|
|
|
420
524
|
const [ angle, x, y, z ] = this._readNumbers( 4 );
|
|
421
|
-
this.
|
|
525
|
+
this._concat( M.rotate( angle, x, y, z ) );
|
|
422
526
|
break;
|
|
423
527
|
|
|
424
528
|
}
|
|
@@ -430,31 +534,37 @@ export class PBRTParser {
|
|
|
430
534
|
[ v[ 0 ], v[ 1 ], v[ 2 ] ], [ v[ 3 ], v[ 4 ], v[ 5 ] ], [ v[ 6 ], v[ 7 ], v[ 8 ] ]
|
|
431
535
|
);
|
|
432
536
|
// pbrt sets CTM to world-to-camera = inverse(cameraToWorld).
|
|
433
|
-
this.
|
|
537
|
+
this._concat( M.invert( camToWorld ) );
|
|
434
538
|
break;
|
|
435
539
|
|
|
436
540
|
}
|
|
437
541
|
|
|
438
542
|
case 'Transform': {
|
|
439
543
|
|
|
440
|
-
this.
|
|
544
|
+
this._setCTM( this._readBracketedOrBareNumbers( 16 ) );
|
|
441
545
|
break;
|
|
442
546
|
|
|
443
547
|
}
|
|
444
548
|
|
|
445
549
|
case 'ConcatTransform': {
|
|
446
550
|
|
|
447
|
-
|
|
448
|
-
this.ctm = M.multiply( this.ctm, m );
|
|
551
|
+
this._concat( this._readBracketedOrBareNumbers( 16 ) );
|
|
449
552
|
break;
|
|
450
553
|
|
|
451
554
|
}
|
|
452
555
|
|
|
453
|
-
|
|
556
|
+
// Both ends, whatever ActiveTransform says — as pbrt does.
|
|
557
|
+
case 'CoordinateSystem': this.coordSystems.set( this._expectString( 'CoordinateSystem' ), { ctm: this.ctm, ctmEnd: this.ctmEnd } ); break;
|
|
454
558
|
case 'CoordSysTransform': {
|
|
455
559
|
|
|
456
560
|
const cs = this.coordSystems.get( this._expectString( 'CoordSysTransform' ) );
|
|
457
|
-
if ( cs )
|
|
561
|
+
if ( cs ) {
|
|
562
|
+
|
|
563
|
+
this.ctm = cs.ctm;
|
|
564
|
+
this.ctmEnd = cs.ctmEnd;
|
|
565
|
+
|
|
566
|
+
}
|
|
567
|
+
|
|
458
568
|
break;
|
|
459
569
|
|
|
460
570
|
}
|
|
@@ -466,6 +576,8 @@ export class PBRTParser {
|
|
|
466
576
|
const params = this._parseParams();
|
|
467
577
|
// Camera-to-world is the inverse of the CTM at the Camera directive.
|
|
468
578
|
this.ir.camera = { type, params, cameraToWorld: M.invert( this.ctm ) };
|
|
579
|
+
const end = this._ctmEndSnapshot();
|
|
580
|
+
if ( end ) this.ir.camera.cameraToWorldEnd = M.invert( end );
|
|
469
581
|
break;
|
|
470
582
|
|
|
471
583
|
}
|
|
@@ -497,25 +609,24 @@ export class PBRTParser {
|
|
|
497
609
|
// ── world block ──
|
|
498
610
|
case 'WorldBegin':
|
|
499
611
|
this.ctm = M.identity();
|
|
612
|
+
this.ctmEnd = null;
|
|
613
|
+
this.activeTransform = BOTH;
|
|
500
614
|
this.state = { material: null, areaLight: null, reverseOrientation: false };
|
|
501
615
|
break;
|
|
502
616
|
case 'WorldEnd': break; // legacy v3
|
|
503
617
|
|
|
504
|
-
case 'AttributeBegin':
|
|
505
|
-
|
|
506
|
-
ctm: this.ctm.slice(),
|
|
507
|
-
material: this.state.material,
|
|
508
|
-
areaLight: this.state.areaLight,
|
|
509
|
-
reverseOrientation: this.state.reverseOrientation
|
|
510
|
-
} );
|
|
511
|
-
break;
|
|
512
|
-
case 'AttributeEnd': {
|
|
618
|
+
case 'AttributeBegin': this._pushGraphicsState(); break;
|
|
619
|
+
case 'AttributeEnd': this._popGraphicsState(); break;
|
|
513
620
|
|
|
514
|
-
|
|
515
|
-
|
|
621
|
+
case 'TransformBegin': this.transformStack.push( { ctm: this.ctm, ctmEnd: this.ctmEnd, activeTransform: this.activeTransform } ); break;
|
|
622
|
+
case 'TransformEnd': {
|
|
623
|
+
|
|
624
|
+
const t = this.transformStack.pop();
|
|
625
|
+
if ( t ) {
|
|
516
626
|
|
|
517
|
-
this.ctm =
|
|
518
|
-
this.
|
|
627
|
+
this.ctm = t.ctm;
|
|
628
|
+
this.ctmEnd = t.ctmEnd;
|
|
629
|
+
this.activeTransform = t.activeTransform;
|
|
519
630
|
|
|
520
631
|
}
|
|
521
632
|
|
|
@@ -523,19 +634,30 @@ export class PBRTParser {
|
|
|
523
634
|
|
|
524
635
|
}
|
|
525
636
|
|
|
526
|
-
case '
|
|
527
|
-
|
|
637
|
+
case 'ReverseOrientation': this.state.reverseOrientation = ! this.state.reverseOrientation; break;
|
|
638
|
+
|
|
639
|
+
// `Attribute "target" params` — v4 default-setting; ignored for MVP.
|
|
640
|
+
case 'Attribute': this._expectString( 'Attribute target' ); this._parseParams(); break;
|
|
641
|
+
case 'ActiveTransform': {
|
|
528
642
|
|
|
529
|
-
const
|
|
643
|
+
const t = this._next();
|
|
644
|
+
const which = t?.value;
|
|
645
|
+
if ( which === 'StartTime' ) this.activeTransform = START;
|
|
646
|
+
else if ( which === 'EndTime' ) this.activeTransform = END;
|
|
647
|
+
else if ( which === 'All' ) this.activeTransform = BOTH;
|
|
648
|
+
else this._warn( `ActiveTransform "${which}" not recognised` );
|
|
649
|
+
break;
|
|
530
650
|
|
|
531
651
|
}
|
|
532
652
|
|
|
533
|
-
case '
|
|
653
|
+
case 'TransformTimes': {
|
|
654
|
+
|
|
655
|
+
const [ start, end ] = this._readNumbers( 2 );
|
|
656
|
+
this.ir.transformTimes = { start, end };
|
|
657
|
+
break;
|
|
658
|
+
|
|
659
|
+
}
|
|
534
660
|
|
|
535
|
-
// `Attribute "target" params` — v4 default-setting; ignored for MVP.
|
|
536
|
-
case 'Attribute': this._expectString( 'Attribute target' ); this._parseParams(); break;
|
|
537
|
-
case 'ActiveTransform': this._next(); break; // StartTime|EndTime|All
|
|
538
|
-
case 'TransformTimes': this._readNumbers( 2 ); break;
|
|
539
661
|
case 'MediumInterface': {
|
|
540
662
|
|
|
541
663
|
// up to two strings (inside/outside)
|
|
@@ -602,6 +724,7 @@ export class PBRTParser {
|
|
|
602
724
|
|
|
603
725
|
const type = this._expectString( 'LightSource type' );
|
|
604
726
|
const params = this._parseParams();
|
|
727
|
+
if ( this.ctmEnd !== null && ! sameMatrix( this.ctmEnd, this.ctm ) ) this._warnOnce( 'animated-light', 'animated LightSource transforms are not supported — using the start transform' );
|
|
605
728
|
this.ir.lights.push( { type, params, ctm: this._ctmSnapshot() } );
|
|
606
729
|
break;
|
|
607
730
|
|
|
@@ -620,6 +743,14 @@ export class PBRTParser {
|
|
|
620
743
|
areaLight: this.state.areaLight,
|
|
621
744
|
reverseOrientation: this.state.reverseOrientation
|
|
622
745
|
};
|
|
746
|
+
const end = this.currentObject === null ? this._ctmEndSnapshot() : null;
|
|
747
|
+
if ( end ) shape.ctmEnd = end;
|
|
748
|
+
else if ( this.currentObject !== null && this.ctmEnd !== null && ! sameMatrix( this.ctmEnd, this.ctm ) ) {
|
|
749
|
+
|
|
750
|
+
this._warnOnce( 'animated-template', 'animated transforms inside ObjectBegin are not supported — using the start transform' );
|
|
751
|
+
|
|
752
|
+
}
|
|
753
|
+
|
|
623
754
|
this._emitShape( shape );
|
|
624
755
|
break;
|
|
625
756
|
|
|
@@ -630,12 +761,7 @@ export class PBRTParser {
|
|
|
630
761
|
|
|
631
762
|
const objName = this._expectString( 'ObjectBegin name' );
|
|
632
763
|
// pbrt implicitly pushes graphics state.
|
|
633
|
-
this.
|
|
634
|
-
ctm: this.ctm.slice(),
|
|
635
|
-
material: this.state.material,
|
|
636
|
-
areaLight: this.state.areaLight,
|
|
637
|
-
reverseOrientation: this.state.reverseOrientation
|
|
638
|
-
} );
|
|
764
|
+
this._pushGraphicsState();
|
|
639
765
|
this.currentObject = objName;
|
|
640
766
|
this.objectBeginCTM = this.ctm.slice();
|
|
641
767
|
if ( ! this.ir.objects.has( objName ) ) this.ir.objects.set( objName, [] );
|
|
@@ -647,14 +773,7 @@ export class PBRTParser {
|
|
|
647
773
|
|
|
648
774
|
this.currentObject = null;
|
|
649
775
|
this.objectBeginCTM = null;
|
|
650
|
-
|
|
651
|
-
if ( s ) {
|
|
652
|
-
|
|
653
|
-
this.ctm = s.ctm;
|
|
654
|
-
this.state = { material: s.material, areaLight: s.areaLight, reverseOrientation: s.reverseOrientation };
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
|
|
776
|
+
this._popGraphicsState();
|
|
658
777
|
break;
|
|
659
778
|
|
|
660
779
|
}
|
|
@@ -770,6 +889,14 @@ export class PBRTParser {
|
|
|
770
889
|
|
|
771
890
|
}
|
|
772
891
|
|
|
892
|
+
_warnOnce( key, msg ) {
|
|
893
|
+
|
|
894
|
+
if ( this._warnedUnknown.has( key ) ) return;
|
|
895
|
+
this._warnedUnknown.add( key );
|
|
896
|
+
this._warn( msg );
|
|
897
|
+
|
|
898
|
+
}
|
|
899
|
+
|
|
773
900
|
_warnUnknown( name ) {
|
|
774
901
|
|
|
775
902
|
if ( this._warnedUnknown.has( name ) ) return;
|