Setting the file. One moment.
Assembly Field · Frost Sequence Camera Orbit · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page Three Mesh BVH LICENSE
source/src/erosion/AssemblyField.ts
source/src/erosion/ AssemblyField.ts
TypeScript · 174 lines · 6 KB
10 uint ,
11 int ,
12 float ,
13 ivec3 ,
14 vec3 ,
15 vec4 ,
16 If ,
17 Loop ,
18 atomicStore ,
19 atomicLoad ,
20 atomicMin ,
21 clamp ,
22 floor ,
23 length ,
24 dot ,
25 select ,
26 texture3D ,
27 textureStore ,
28 } = tsl;
29
30 export const ASSEMBLY_GRID = 32 ;
31 export const assemblyReach = ( bound : number ) => ( 4 * bound) / ASSEMBLY_GRID ;
32 const NONE = 0xffffffff ;
33
34 export class AssemblyField {
35 private readonly seeds = instancedArray ( ASSEMBLY_GRID ** 3 , "uint" ). toAtomic ();
36 private readonly owners = [
37 instancedArray ( ASSEMBLY_GRID ** 3 , "uint" ),
38 instancedArray ( ASSEMBLY_GRID ** 3 , "uint" ),
39 ];
40 private readonly prepareNodes : any [];
41 private readonly updateNode : any ;
42
43 constructor (
44 private readonly erosion : ErosionField ,
45 count : number ,
46 buffers : Record < string , any >,
47 u : any ,
48 ) {
49 const G = ASSEMBLY_GRID ,
50 n = G ** 3 ,
51 i = instanceIndex,
52 bound = erosion.bound;
53 const coord = ( idx : any , res : number ) =>
54 ivec3 (idx. mod ( uint (res)), idx. div ( uint (res)). mod ( uint (res)), idx. div ( uint (res * res)));
55 const index = ( c : any ) =>
56 uint (c.x)
57 . add ( uint (c.y). mul ( uint ( G )))
58 . add ( uint (c.z). mul ( uint ( G * G )));
59 const grid = ( p : any ) =>
60 clamp (
61 ivec3 (
62 floor (
63 p
64 . div ( 2 * bound)
65 . add ( 0.5 )
66 . mul ( G ),
67 ),
68 ),
69 ivec3 ( 0 ),
70 ivec3 ( G - 1 ),
71 );
72 const at = ( c : any ) =>
73 vec3 (c)
74 . add ( 0.5 )
75 . div ( G )
76 . sub ( 0.5 )
77 . mul ( 2 * bound);
78 const clear = Fn (() => {
79 atomicStore ( this .seeds. element (i), uint ( NONE ));
80 })(). compute (n, [ 64 ]);
81 const seed = Fn (() => {
82 const state = buffers.meta. element (i).x;
83 // Only shards genuinely in flight may own target volume. No invisible ghost arrivals.
84 If (state. greaterThan ( 0.5 ). and (state. lessThan ( 2.5 )), () => {
85 atomicMin ( this .seeds. element ( index ( grid (buffers.rest. element (i).xyz))), uint (i));
86 });
87 })(). compute (count, [ 64 ]);
88 const copy = Fn (() => {
89 this .owners[ 0 ]. element (i). assign ( atomicLoad ( this .seeds. element (i)));
90 })(). compute (n, [ 64 ]);
91 this .prepareNodes = [clear, seed, copy];
92 let source = 0 ;
93 // Fixed, small ownership grid: six ping-pong passes on retarget only, independent of
94 // erosion resolution and particle count. Extra unit pass closes jump-flood edge cases.
95 for ( const jump of [ 16 , 8 , 4 , 2 , 1 , 1 ]) {
96 const read = this .owners[source],
97 write = this .owners[ 1 - source];
98 this .prepareNodes. push (
99 Fn (() => {
100 const c = coord (i, G ). toVar (),
101 p = at (c). toVar ();
102 const best = uint ( NONE ). toVar (),
103 distance = float ( 1e30 ). toVar ();
104 const range = { start: int ( - 1 ), end: int ( 1 ), condition: "<=" };
105 Loop (range, range, range, ({ i : x , j : y , k : z } : any ) => {
106 const cc = clamp (c. add ( ivec3 (x, y, z). mul (jump)), ivec3 ( 0 ), ivec3 ( G - 1 ));
107 const candidate = read. element ( index (cc)). toVar ();
108 If (candidate. notEqual ( uint ( NONE )), () => {
109 const delta = buffers.rest. element (candidate).xyz. sub (p);
110 const d = dot (delta, delta). toVar ();
111 If (d. lessThan (distance). or (d. equal (distance). and (candidate. lessThan (best))), () => {
112 best. assign (candidate);
113 distance. assign (d);
114 });
115 });
116 });
117 write. element (i). assign (best);
118 })(). compute (n, [ 64 ]),
119 );
120 source = 1 - source;
121 }
122 const owner = this .owners[source],
123 R = erosion.res;
124 const previous = texture3D (erosion.tex);
125 this .updateNode = Fn (() => {
126 const c = coord (i, R ). toVar ();
127 const p = vec3 (c)
128 . add ( 0.5 )
129 . div ( R )
130 . sub ( 0.5 )
131 . mul ( 2 * bound)
132 . toVar ();
133 const id = owner. element ( index ( grid (p))). toVar ();
134 const old = previous. load (c). level ( 0 ). toVar ();
135 const e = old.r. toVar ();
136 If (id. notEqual ( uint ( NONE )), () => {
137 const rest = buffers.rest. element (id).xyz. toVar ();
138 const target = u.model. mul ( vec4 (rest, 1 )).xyz;
139 const distance = length (buffers.pos. element (id).xyz. sub (target));
140 const state = buffers.meta. element (id).x;
141 // A spatial contact front expands around this shard's own home as it approaches.
142 // No elapsed-time fade, cell-wide wait, stochastic refill, or neighbour propagation.
143 const localDistance = length (p. sub (rest));
144 const contact = u.landRadius. add (
145 float ( assemblyReach (bound)). div ( float ( 1 ). add (localDistance. div (( 2 * bound) / G ))),
146 );
147 const returning = state
148 . greaterThan ( 1.5 )
149 . and (state. lessThan ( 2.5 ))
150 . or (state. greaterThan ( 4.5 ))
151 . or (state. lessThan ( 0.5 ));
152 If (returning. and (distance. lessThanEqual (contact)), () => {
153 e. assign ( 0 );
154 });
155 });
156 textureStore (
157 erosion.scratch,
158 c,
159 vec4 (e, 0 , select (e. lessThan ( 0.5 ), float ( 1 ), float ( 0 )), 0 ),
160 ). toWriteOnly ();
161 })(). compute ( R ** 3 , [ 64 ]);
162 }
163
164 prepare ( renderer : THREE . WebGPURenderer ) {
165 for ( const node of this .prepareNodes) renderer. compute (node);
166 }
167 update ( renderer : THREE . WebGPURenderer ) {
168 renderer. compute ( this .updateNode);
169 this .erosion. commitAssembly (renderer);
170 }
171 dispose () {
172 for ( const b of [ this .seeds, ... this .owners]) b.value. dispose ?.();
173 }
174 }