@@ -45,7 +45,7 @@ const CUBES_Y = 24;
4545const CUBES_Z = 24 ;
4646const CUBES_COUNT = ( CUBES_X * CUBES_Y * CUBES_Z ) ;
4747
48- const getGridLength = ( x : number ) => ( Cube . INTERVAL * ( x ) - Cube . PADDING ) ;
48+ const getGridLength = ( x : number ) => ( Cube . INTERVAL * x - Cube . PADDING ) ;
4949const SIZE_X = getGridLength ( CUBES_X ) ;
5050const SIZE_Y = getGridLength ( CUBES_Y ) ;
5151const SIZE_Z = getGridLength ( CUBES_Z ) ;
@@ -65,13 +65,15 @@ const POINT_COUNT = 10;
6565
6666const cubeIdx = ( x : number , y : number , z : number ) => CUBES_X * CUBES_Y * z + CUBES_X * y + x ;
6767
68- const v3scratch : [ x : number , y : number , z : number ] = [ 0 , 0 , 0 ] ;
68+ // benchmarked various methods of doing this function, it made little difference
69+ // turns out it makes a copy anyways regardless of const, oh well.
70+ const v3Scratch : [ x : number , y : number , z : number ] = [ 0 , 0 , 0 ] ;
6971const getVec3Idx = ( idx : number ) => {
7072 const base = idx * 3 ;
71- v3scratch [ 0 ] = base ;
72- v3scratch [ 1 ] = base + 1 ;
73- v3scratch [ 2 ] = base + 2 ;
74- return v3scratch ;
73+ v3Scratch [ 0 ] = base ;
74+ v3Scratch [ 1 ] = base + 1 ;
75+ v3Scratch [ 2 ] = base + 2 ;
76+ return v3Scratch ;
7577} ;
7678
7779// -------------------- main --------------------
@@ -129,7 +131,7 @@ export const main = async () => {
129131 }
130132 } ;
131133
132- const gridPositions = new Float32Array ( CUBES_COUNT * 3 ) ;
134+ const gridPositions = new Float32Array ( CUBES_COUNT * 3 ) ; // float vec3
133135 const refPos = twgl . v3 . create ( X_MIN_CUBE_CENTER , Y_MIN_CUBE_CENTER , Z_MIN_CUBE_CENTER ) ;
134136 for ( let z = 0 ; z < CUBES_Z ; z ++ ) {
135137 refPos [ 1 ] = Y_MIN_CUBE_CENTER ;
@@ -157,7 +159,7 @@ export const main = async () => {
157159 gl . blendFunc ( gl . SRC_ALPHA , gl . ONE_MINUS_SRC_ALPHA ) ;
158160 gl . disable ( gl . DEPTH_TEST ) ;
159161 twgl . setUniforms ( progInfo , {
160- u_view : twgl . m4 . inverse ( twgl . m4 . lookAt ( [ 0 , 0 , maxGridSize * fieldOfView ] , [ 0 , 0 , 0 ] , [ 0 , 1 , 0 ] ) )
162+ u_view : twgl . m4 . inverse ( twgl . m4 . lookAt ( [ 0 , 0 , maxGridSize * fieldOfView ] , CENTER , [ 0 , 1 , 0 ] ) )
161163 } ) ;
162164
163165 let dt : number ;
@@ -166,6 +168,7 @@ export const main = async () => {
166168 const points = new PointsArray ( POINT_COUNT ) ;
167169 let spawnTimer = PRNG . nextRange ( Spawn . TIME . MIN , Spawn . TIME . MAX ) ;
168170 const render = ( time : number ) => {
171+ // --- update ---
169172 dt = ( time - lastTime ) / 1000 ;
170173 lastTime = time ;
171174 cubeInstanceArray . reset ( ) ;
@@ -190,14 +193,17 @@ export const main = async () => {
190193 for ( let z = bbox . min . z ; z < bbox . max . z ; z ++ ) {
191194 for ( let y = bbox . min . y ; y < bbox . max . y ; y ++ ) {
192195 for ( let x = bbox . min . x ; x < bbox . max . x ; x ++ ) {
193- const [ cx , cy , cz ] = getVec3Idx ( cubeIdx ( x , y , z ) ) ;
196+ // inlined: getVec3Idx(cubeIdx(x, y, z))
197+ const cx = ( CUBES_X * CUBES_Y * z + CUBES_X * y + x ) * 3 ;
198+ const cy = cx + 1 ;
199+ const cz = cy + 1 ;
194200 const dx = Math . abs ( ( points . positions [ px ] - gridPositions [ cx ] ) / points . scales [ px ] ) ;
195201 const dy = Math . abs ( ( points . positions [ py ] - gridPositions [ cy ] ) / points . scales [ py ] ) ;
196202 const dz = Math . abs ( ( points . positions [ pz ] - gridPositions [ cz ] ) / points . scales [ pz ] ) ;
197203 const d = dx + dy + dz ;
198204
199205 // octahedral shape with a linear falloff
200- const sideLen = Math . max ( 0 , Cube . SIZE * ( 1 - d ) ) ;
206+ const sideLen = Cube . SIZE * ( 1 - d ) ;
201207 if ( sideLen <= EPSILON )
202208 continue ;
203209
@@ -211,6 +217,7 @@ export const main = async () => {
211217 }
212218 }
213219
220+ // --- render ---
214221 resized = twgl . resizeCanvasToDisplaySize ( gl . canvas as HTMLCanvasElement ) ;
215222 if ( resized ) {
216223 twgl . setUniforms ( progInfo , {
@@ -289,23 +296,33 @@ class CubeInstanceArray {
289296 }
290297
291298 public setPointData ( x : number , y : number , z : number , r : number , g : number , b : number , size : number ) {
292- // indexing directly to avoid constructing an array in hot loop.
293- const [ ix , iy , iz ] = getVec3Idx ( this . count ) ;
299+ // This is in the hottest loop in the program, must be as fast as possible.
300+ const ix = this . count * 3 ;
301+ const iy = ix + 1 ;
302+ const iz = iy + 1 ;
294303 this . instanceCenters [ ix ] = x ;
295304 this . instanceCenters [ iy ] = y ;
296305 this . instanceCenters [ iz ] = z ;
297306
298- const [ ir , ig , ib ] = getVec3Idx ( this . count ) ;
299- this . instanceColors [ ir ] = r ;
300- this . instanceColors [ ig ] = g ;
301- this . instanceColors [ ib ] = b ;
307+ this . instanceColors [ ix ] = r ;
308+ this . instanceColors [ iy ] = g ;
309+ this . instanceColors [ iz ] = b ;
302310 this . instanceSizes [ this . count ] = size ;
303311 this . count ++ ;
304312 }
305313
306314 public reset ( ) { this . count = 0 ; }
307315}
308316
317+ // constants used for freelist, internal to PointsArray
318+ // hoisted out for readability
319+ const FL = Object . freeze ( {
320+ HEAD : 0 ,
321+ TAIL : 1 ,
322+ END : - 1 ,
323+ SPAWNED : - 2
324+ } ) ;
325+
309326class PointsArray {
310327 private data : ArrayBuffer ;
311328 public readonly count : number ;
@@ -318,12 +335,7 @@ class PointsArray {
318335 public activeIdx : Int32Array ; // int32
319336 public directions : Int8Array ; // char
320337
321- // freelist constants, if name collisions become an issue wrap these in a frozen object
322- private static readonly HEAD_IDX = 0 ;
323- private static readonly TAIL_IDX = 1 ;
324- private static readonly LIST_END = - 1 ;
325- private static readonly IS_SPAWNED = - 2 ;
326- private freelist = new Int32Array ( [ PointsArray . LIST_END , PointsArray . LIST_END ] ) ;
338+ private freelist = new Int32Array ( [ FL . END , FL . END ] ) ;
327339
328340 constructor ( poolSize : number ) {
329341 this . count = poolSize ;
@@ -356,22 +368,22 @@ class PointsArray {
356368 // initialize freelist
357369 for ( let i = 0 ; i < poolSize - 1 ; i ++ )
358370 this . nextFreeOrSpawned [ i ] = i + 1 ;
359- this . nextFreeOrSpawned [ poolSize - 1 ] = PointsArray . LIST_END ;
360- this . freelist [ PointsArray . HEAD_IDX ] = 0 ;
361- this . freelist [ 1 ] = POINT_COUNT - 1 ;
371+ this . nextFreeOrSpawned [ poolSize - 1 ] = FL . END ;
372+ this . freelist [ FL . HEAD ] = 0 ;
373+ this . freelist [ FL . TAIL ] = POINT_COUNT - 1 ;
362374 }
363375
364376 public spawn ( ) {
365377 // pop from freelist, return if none available
366- if ( this . freelist [ PointsArray . HEAD_IDX ] == PointsArray . LIST_END )
367- return PointsArray . LIST_END ;
368-
369- const idx = this . freelist [ PointsArray . HEAD_IDX ] ;
370-
371- this . freelist [ PointsArray . HEAD_IDX ] = this . nextFreeOrSpawned [ idx ] ;
372- if ( this . freelist [ PointsArray . HEAD_IDX ] == PointsArray . LIST_END )
373- this . freelist [ 1 ] = PointsArray . LIST_END ;
374- this . nextFreeOrSpawned [ idx ] = PointsArray . IS_SPAWNED ;
378+ if ( this . freelist [ FL . HEAD ] == FL . END )
379+ return FL . END ;
380+
381+ const idx = this . freelist [ FL . HEAD ] ;
382+
383+ this . freelist [ FL . HEAD ] = this . nextFreeOrSpawned [ idx ] ;
384+ if ( this . freelist [ FL . HEAD ] == FL . END )
385+ this . freelist [ FL . TAIL ] = FL . END ;
386+ this . nextFreeOrSpawned [ idx ] = FL . SPAWNED ;
375387
376388 // got the next available point, initialize
377389 const lerpVal = PRNG . nextRange ( 0 , 1 ) ;
@@ -405,15 +417,15 @@ class PointsArray {
405417
406418 public free ( idx : number ) {
407419 const [ vx , vy , vz ] = getVec3Idx ( idx ) ;
408- this . positions [ vx ] = Number . MAX_VALUE ;
420+ this . positions [ vx ] = Number . MAX_VALUE ; // prevent small chance of flickering upon ege of free/respawn
409421 this . positions [ vy ] = Number . MAX_VALUE ;
410422 this . positions [ vz ] = Number . MAX_VALUE ;
411- this . nextFreeOrSpawned [ idx ] = PointsArray . LIST_END ;
412- if ( this . freelist [ PointsArray . TAIL_IDX ] != PointsArray . LIST_END )
413- this . nextFreeOrSpawned [ this . freelist [ PointsArray . TAIL_IDX ] ] = idx ;
423+ this . nextFreeOrSpawned [ idx ] = FL . END ;
424+ if ( this . freelist [ FL . TAIL ] != FL . END )
425+ this . nextFreeOrSpawned [ this . freelist [ FL . TAIL ] ] = idx ;
414426 else
415- this . freelist [ PointsArray . HEAD_IDX ] = idx ;
416- this . freelist [ PointsArray . TAIL_IDX ] = idx ;
427+ this . freelist [ FL . HEAD ] = idx ;
428+ this . freelist [ FL . TAIL ] = idx ;
417429 }
418430
419431 public getBoundingBox ( idx : number ) {
@@ -453,7 +465,7 @@ class PointsArray {
453465 }
454466
455467 public isNotSpawned ( idx : number ) {
456- return this . nextFreeOrSpawned [ idx ] != PointsArray . IS_SPAWNED ;
468+ return this . nextFreeOrSpawned [ idx ] != FL . SPAWNED ;
457469 }
458470
459471 private getStartPos ( idx : number ) {
0 commit comments