rayzee 7.31.0 → 8.0.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.
Files changed (53) hide show
  1. package/README.md +164 -0
  2. package/dist/rayzee.es.js +6547 -4334
  3. package/dist/rayzee.umd.js +122 -73
  4. package/package.json +1 -1
  5. package/src/EngineDefaults.js +168 -13
  6. package/src/EngineIssues.js +6 -0
  7. package/src/PathTracerApp.js +128 -20
  8. package/src/Processor/ArchiveReader.js +538 -0
  9. package/src/Processor/AssetLoader.js +357 -22
  10. package/src/Processor/BVHBuilder.js +48 -32
  11. package/src/Processor/BVHRefitter.js +244 -99
  12. package/src/Processor/ChunkedRecords.js +302 -0
  13. package/src/Processor/EmissiveTriangleBuilder.js +48 -21
  14. package/src/Processor/GeometryExtractor.js +577 -92
  15. package/src/Processor/HostMemory.js +222 -0
  16. package/src/Processor/InstanceTable.js +548 -97
  17. package/src/Processor/PBRT/EqualAreaOctahedral.js +173 -0
  18. package/src/Processor/PBRT/LoopSubdivision.js +240 -0
  19. package/src/Processor/PBRT/PBRTCurves.js +249 -0
  20. package/src/Processor/PBRT/PBRTMath.js +28 -0
  21. package/src/Processor/PBRT/PBRTParser.js +187 -41
  22. package/src/Processor/PBRT/PBRTSceneBuilder.js +659 -34
  23. package/src/Processor/PBRT/PBRTTokenizer.js +205 -65
  24. package/src/Processor/PBRT/index.js +306 -45
  25. package/src/Processor/PackedRayBuffer.js +29 -18
  26. package/src/Processor/ParallelBVHBuilder.js +9 -8
  27. package/src/Processor/QueueManager.js +5 -3
  28. package/src/Processor/SceneProcessor.js +1063 -366
  29. package/src/Processor/TLASBuilder.js +546 -201
  30. package/src/Processor/TextureCreator.js +11 -9
  31. package/src/Processor/VRAMTracker.js +12 -3
  32. package/src/Processor/Workers/BVHRefitWorker.js +16 -11
  33. package/src/Processor/Workers/BVHWorker.js +12 -12
  34. package/src/Processor/Workers/TLASWorker.js +37 -0
  35. package/src/Stages/NormalDepth.js +6 -2
  36. package/src/Stages/PathTracer.js +5 -5
  37. package/src/Stages/PathTracerStage.js +163 -41
  38. package/src/TSL/BVHTraversal.js +208 -90
  39. package/src/TSL/Common.js +135 -2
  40. package/src/TSL/Displacement.js +21 -11
  41. package/src/TSL/EmissiveSampling.js +49 -25
  42. package/src/TSL/ExtendKernel.js +3 -2
  43. package/src/TSL/LightBVHSampling.js +7 -4
  44. package/src/TSL/LightsDirect.js +30 -29
  45. package/src/TSL/ShadeKernel.js +8 -6
  46. package/src/TSL/Struct.js +3 -0
  47. package/src/TSL/TextureSampling.js +24 -9
  48. package/src/TSL/patches.js +128 -3
  49. package/src/index.js +3 -0
  50. package/src/managers/AnimationManager.js +86 -76
  51. package/src/managers/CameraManager.js +11 -0
  52. package/src/managers/MaterialDataManager.js +29 -23
  53. package/src/managers/TransformManager.js +9 -192
@@ -9,6 +9,11 @@
9
9
  * - bare words: WorldBegin, Shape, true, false (directives / bools)
10
10
  * Comments run from '#' to end of line.
11
11
  *
12
+ * Scans BYTES, not a string, and hands the parser one token at a time. Scene files
13
+ * reach 2.4 GB — far past both the 512 MB a JavaScript string can hold and what an
14
+ * array of a hundred million token objects would cost — so neither the source text
15
+ * nor the token stream is ever materialised whole.
16
+ *
12
17
  * Pure JS, no Three.js — keeps it unit-testable in node.
13
18
  */
14
19
 
@@ -20,122 +25,257 @@ export const TokenType = {
20
25
  RBRACKET: ']'
21
26
  };
22
27
 
23
- const isWhitespace = c => c === ' ' || c === '\t' || c === '\n' || c === '\r' || c === '\f';
24
- // A number can start with: digit / ".5" / "-1" / "-.5" / "+.5".
25
- const isDigit = c => c >= '0' && c <= '9';
26
- const isNumberStart = ( c, next, next2 ) => {
28
+ const TAB = 9, LF = 10, FF = 12, CR = 13, SPACE = 32;
29
+ const QUOTE = 34, HASH = 35, PLUS = 43, MINUS = 45, DOT = 46;
30
+ const ZERO = 48, NINE = 57, LBRACKET = 91, RBRACKET = 93;
31
+ const UPPER_E = 69, LOWER_E = 101;
32
+
33
+ const isSpace = b => b === SPACE || b === TAB || b === LF || b === CR || b === FF;
34
+ const isDigit = b => b >= ZERO && b <= NINE;
35
+
36
+ // Beyond 15 significant digits the scaled-integer shortcut loses the last bits, so
37
+ // those rare tokens go through Number() instead. 10^0..10^22 are the powers of ten a
38
+ // double holds exactly.
39
+ const MAX_EXACT_DIGITS = 15;
40
+ const MAX_EXACT_POW10 = 22;
41
+ const POW10 = ( () => {
42
+
43
+ const t = new Float64Array( MAX_EXACT_POW10 + 1 );
44
+ for ( let i = 0; i < t.length; i ++ ) t[ i ] = Math.pow( 10, i );
45
+ return t;
46
+
47
+ } )();
27
48
 
28
- if ( isDigit( c ) ) return true;
29
- if ( c === '.' && isDigit( next ) ) return true;
30
- if ( c === '-' || c === '+' ) {
49
+ const utf8 = new TextDecoder();
31
50
 
32
- if ( isDigit( next ) ) return true;
33
- if ( next === '.' && isDigit( next2 ) ) return true;
51
+ function decodeRange( bytes, start, end ) {
52
+
53
+ const len = end - start;
54
+ if ( len === 0 ) return '';
55
+
56
+ let ascii = true;
57
+ for ( let i = start; i < end; i ++ ) if ( bytes[ i ] > 127 ) {
58
+
59
+ ascii = false; break;
34
60
 
35
61
  }
36
62
 
37
- return false;
63
+ if ( ! ascii ) return utf8.decode( bytes.subarray( start, end ) );
38
64
 
39
- };
65
+ let out = '';
66
+ for ( let i = start; i < end; i ++ ) out += String.fromCharCode( bytes[ i ] );
67
+ return out;
68
+
69
+ }
40
70
 
41
71
  /**
42
- * Tokenize a pbrt source string.
43
- * @param {string} src
44
- * @returns {Array<{type: string, value?: string|number}>}
72
+ * Pull-based lexer over a byte buffer.
73
+ *
74
+ * `peek()` returns the next token without consuming it, `next()` consumes it, and both
75
+ * return null at end of input.
45
76
  */
46
- export function tokenize( src ) {
77
+ export class TokenStream {
47
78
 
48
- const tokens = [];
49
- const n = src.length;
50
- let i = 0;
79
+ /** @param {Uint8Array|string} source */
80
+ constructor( source ) {
51
81
 
52
- while ( i < n ) {
82
+ this.bytes = typeof source === 'string' ? new TextEncoder().encode( source ) : source;
83
+ this.i = 0;
84
+ this.n = this.bytes.length;
85
+ this._ahead = null;
53
86
 
54
- const c = src[ i ];
87
+ }
55
88
 
56
- // Whitespace
57
- if ( isWhitespace( c ) ) {
89
+ peek() {
58
90
 
59
- i ++;
60
- continue;
91
+ if ( this._ahead === null ) this._ahead = this._scan();
92
+ return this._ahead;
93
+
94
+ }
95
+
96
+ next() {
97
+
98
+ const t = this.peek();
99
+ this._ahead = null;
100
+ return t;
101
+
102
+ }
103
+
104
+ _scan() {
105
+
106
+ const b = this.bytes;
107
+ let i = this.i;
108
+ const n = this.n;
109
+
110
+ for ( ;; ) {
111
+
112
+ while ( i < n && isSpace( b[ i ] ) ) i ++;
113
+ if ( i < n && b[ i ] === HASH ) {
114
+
115
+ while ( i < n && b[ i ] !== LF ) i ++;
116
+ continue;
117
+
118
+ }
119
+
120
+ break;
61
121
 
62
122
  }
63
123
 
64
- // Comment to end of line
65
- if ( c === '#' ) {
124
+ if ( i >= n ) {
66
125
 
67
- while ( i < n && src[ i ] !== '\n' ) i ++;
68
- continue;
126
+ this.i = i;
127
+ return null;
69
128
 
70
129
  }
71
130
 
72
- // Brackets
73
- if ( c === '[' ) {
131
+ const c = b[ i ];
74
132
 
75
- tokens.push( { type: TokenType.LBRACKET } );
76
- i ++;
77
- continue;
133
+ if ( c === LBRACKET ) {
134
+
135
+ this.i = i + 1;
136
+ return { type: TokenType.LBRACKET };
78
137
 
79
138
  }
80
139
 
81
- if ( c === ']' ) {
140
+ if ( c === RBRACKET ) {
82
141
 
83
- tokens.push( { type: TokenType.RBRACKET } );
84
- i ++;
85
- continue;
142
+ this.i = i + 1;
143
+ return { type: TokenType.RBRACKET };
86
144
 
87
145
  }
88
146
 
89
- // Quoted string
90
- if ( c === '"' ) {
147
+ if ( c === QUOTE ) {
91
148
 
92
- i ++; // skip opening quote
93
- let start = i;
94
- while ( i < n && src[ i ] !== '"' ) i ++;
149
+ const start = ++ i;
150
+ while ( i < n && b[ i ] !== QUOTE ) i ++;
95
151
  if ( i >= n ) throw new Error( 'PBRT tokenizer: unterminated string literal' );
96
- tokens.push( { type: TokenType.STRING, value: src.slice( start, i ) } );
97
- i ++; // skip closing quote
98
- continue;
152
+ this.i = i + 1;
153
+ return { type: TokenType.STRING, value: decodeRange( b, start, i ) };
154
+
155
+ }
156
+
157
+ if ( isDigit( c ) || ( c === DOT && isDigit( b[ i + 1 ] ) )
158
+ || ( ( c === MINUS || c === PLUS ) && ( isDigit( b[ i + 1 ] ) || ( b[ i + 1 ] === DOT && isDigit( b[ i + 2 ] ) ) ) ) ) {
159
+
160
+ return this._scanNumber( i );
99
161
 
100
162
  }
101
163
 
102
- // Number
103
- if ( isNumberStart( c, src[ i + 1 ], src[ i + 2 ] ) ) {
164
+ const start = i;
165
+ while ( i < n ) {
104
166
 
105
- let start = i;
167
+ const ch = b[ i ];
168
+ if ( isSpace( ch ) || ch === QUOTE || ch === LBRACKET || ch === RBRACKET || ch === HASH ) break;
106
169
  i ++;
107
- while ( i < n ) {
108
170
 
109
- const ch = src[ i ];
110
- if ( ( ch >= '0' && ch <= '9' ) || ch === '.' || ch === 'e' || ch === 'E' ||
111
- ch === '-' || ch === '+' ) {
171
+ }
112
172
 
113
- i ++;
173
+ this.i = i;
174
+ return { type: TokenType.WORD, value: decodeRange( b, start, i ) };
114
175
 
115
- } else break;
176
+ }
177
+
178
+ _scanNumber( start ) {
179
+
180
+ const b = this.bytes;
181
+ const n = this.n;
182
+ let i = start;
183
+
184
+ let negative = false;
185
+ if ( b[ i ] === MINUS ) {
186
+
187
+ negative = true; i ++;
188
+
189
+ } else if ( b[ i ] === PLUS ) i ++;
190
+
191
+ let mantissa = 0;
192
+ let digits = 0;
193
+ let fracDigits = 0;
194
+
195
+ while ( i < n && isDigit( b[ i ] ) ) {
196
+
197
+ mantissa = mantissa * 10 + ( b[ i ] - ZERO );
198
+ digits ++; i ++;
199
+
200
+ }
201
+
202
+ if ( i < n && b[ i ] === DOT ) {
203
+
204
+ i ++;
205
+ while ( i < n && isDigit( b[ i ] ) ) {
206
+
207
+ mantissa = mantissa * 10 + ( b[ i ] - ZERO );
208
+ digits ++; fracDigits ++; i ++;
116
209
 
117
210
  }
118
211
 
119
- const text = src.slice( start, i );
120
- const num = Number( text );
121
- if ( Number.isNaN( num ) ) throw new Error( `PBRT tokenizer: invalid number "${text}"` );
122
- tokens.push( { type: TokenType.NUMBER, value: num } );
123
- continue;
212
+ }
213
+
214
+ let exponent = 0;
215
+ if ( i < n && ( b[ i ] === LOWER_E || b[ i ] === UPPER_E ) ) {
216
+
217
+ let j = i + 1;
218
+ let expNeg = false;
219
+ if ( j < n && ( b[ j ] === MINUS || b[ j ] === PLUS ) ) {
220
+
221
+ expNeg = b[ j ] === MINUS; j ++;
222
+
223
+ }
224
+
225
+ if ( j < n && isDigit( b[ j ] ) ) {
226
+
227
+ let e = 0;
228
+ while ( j < n && isDigit( b[ j ] ) ) {
229
+
230
+ e = e * 10 + ( b[ j ] - ZERO ); j ++;
231
+
232
+ }
233
+
234
+ exponent = expNeg ? - e : e;
235
+ i = j;
236
+
237
+ }
124
238
 
125
239
  }
126
240
 
127
- // Bare word (directive, true/false, etc.)
128
- {
241
+ this.i = i;
242
+
243
+ if ( digits === 0 ) throw new Error( `PBRT tokenizer: invalid number "${decodeRange( b, start, i )}"` );
244
+
245
+ const scale = exponent - fracDigits;
246
+ const exact = digits <= MAX_EXACT_DIGITS && scale >= - MAX_EXACT_POW10 && scale <= MAX_EXACT_POW10;
247
+
248
+ if ( ! exact ) {
129
249
 
130
- let start = i;
131
- while ( i < n && ! isWhitespace( src[ i ] ) && src[ i ] !== '"' &&
132
- src[ i ] !== '[' && src[ i ] !== ']' && src[ i ] !== '#' ) i ++;
133
- tokens.push( { type: TokenType.WORD, value: src.slice( start, i ) } );
250
+ const value = Number( decodeRange( b, start, i ) );
251
+ if ( Number.isNaN( value ) ) throw new Error( `PBRT tokenizer: invalid number "${decodeRange( b, start, i )}"` );
252
+ return { type: TokenType.NUMBER, value };
134
253
 
135
254
  }
136
255
 
256
+ // Scale by dividing rather than multiplying by a negative power: 10^-4 is not exact,
257
+ // and 654435761 * 1e-4 lands a bit below -65443.5761.
258
+ let value = scale === 0 ? mantissa
259
+ : scale > 0 ? mantissa * POW10[ scale ]
260
+ : mantissa / POW10[ - scale ];
261
+ if ( negative ) value = - value;
262
+ return { type: TokenType.NUMBER, value };
263
+
137
264
  }
138
265
 
266
+ }
267
+
268
+ /**
269
+ * Tokenize a whole pbrt source into an array.
270
+ * Convenience for tests and small files; the loader streams instead.
271
+ * @param {string|Uint8Array} src
272
+ * @returns {Array<{type: string, value?: string|number}>}
273
+ */
274
+ export function tokenize( src ) {
275
+
276
+ const stream = new TokenStream( src );
277
+ const tokens = [];
278
+ for ( let t = stream.next(); t !== null; t = stream.next() ) tokens.push( t );
139
279
  return tokens;
140
280
 
141
281
  }