core-vfx 0.0.3 → 0.0.4
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 +68 -68
- package/dist/index.d.ts +15 -6
- package/dist/index.js +288 -213
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,16 +27,16 @@ npm install three @react-three/fiber react
|
|
|
27
27
|
## Quick Start
|
|
28
28
|
|
|
29
29
|
```tsx
|
|
30
|
-
import { Canvas } from '@react-three/fiber'
|
|
31
|
-
import { VFXParticles, Appearance, EmitterShape } from 'r3f-vfx'
|
|
32
|
-
import * as THREE from 'three/webgpu'
|
|
30
|
+
import { Canvas } from '@react-three/fiber'
|
|
31
|
+
import { VFXParticles, Appearance, EmitterShape } from 'r3f-vfx'
|
|
32
|
+
import * as THREE from 'three/webgpu'
|
|
33
33
|
|
|
34
34
|
function App() {
|
|
35
35
|
return (
|
|
36
36
|
<Canvas>
|
|
37
37
|
<VFXParticles debug />
|
|
38
38
|
</Canvas>
|
|
39
|
-
)
|
|
39
|
+
)
|
|
40
40
|
}
|
|
41
41
|
```
|
|
42
42
|
|
|
@@ -114,8 +114,8 @@ The main particle system component.
|
|
|
114
114
|
|
|
115
115
|
```ts
|
|
116
116
|
interface StretchConfig {
|
|
117
|
-
factor: number
|
|
118
|
-
maxStretch: number
|
|
117
|
+
factor: number // Stretch multiplier
|
|
118
|
+
maxStretch: number // Maximum stretch amount
|
|
119
119
|
}
|
|
120
120
|
```
|
|
121
121
|
|
|
@@ -127,9 +127,9 @@ interface StretchConfig {
|
|
|
127
127
|
|
|
128
128
|
```ts
|
|
129
129
|
interface TurbulenceConfig {
|
|
130
|
-
intensity: number
|
|
131
|
-
frequency: number
|
|
132
|
-
speed: number
|
|
130
|
+
intensity: number // Turbulence strength
|
|
131
|
+
frequency: number // Noise scale
|
|
132
|
+
speed: number // Animation speed
|
|
133
133
|
}
|
|
134
134
|
```
|
|
135
135
|
|
|
@@ -142,11 +142,11 @@ interface TurbulenceConfig {
|
|
|
142
142
|
|
|
143
143
|
```ts
|
|
144
144
|
interface AttractorConfig {
|
|
145
|
-
position: [x, y, z]
|
|
146
|
-
strength: number
|
|
147
|
-
radius?: number
|
|
148
|
-
type?: 'point' | 'vortex'
|
|
149
|
-
axis?: [x, y, z]
|
|
145
|
+
position: [x, y, z]
|
|
146
|
+
strength: number // Positive = attract, negative = repel
|
|
147
|
+
radius?: number // 0 = infinite range
|
|
148
|
+
type?: 'point' | 'vortex'
|
|
149
|
+
axis?: [x, y, z] // Vortex rotation axis
|
|
150
150
|
}
|
|
151
151
|
```
|
|
152
152
|
|
|
@@ -158,11 +158,11 @@ interface AttractorConfig {
|
|
|
158
158
|
|
|
159
159
|
```ts
|
|
160
160
|
interface CollisionConfig {
|
|
161
|
-
plane: { y: number }
|
|
162
|
-
bounce?: number
|
|
163
|
-
friction?: number
|
|
164
|
-
die?: boolean
|
|
165
|
-
sizeBasedGravity?: number
|
|
161
|
+
plane: { y: number } // Plane Y position
|
|
162
|
+
bounce?: number // Bounce factor (0-1)
|
|
163
|
+
friction?: number // Horizontal friction
|
|
164
|
+
die?: boolean // Kill on collision
|
|
165
|
+
sizeBasedGravity?: number // Gravity multiplier by size
|
|
166
166
|
}
|
|
167
167
|
```
|
|
168
168
|
|
|
@@ -180,10 +180,10 @@ All curves use Bezier spline format:
|
|
|
180
180
|
```ts
|
|
181
181
|
interface CurveData {
|
|
182
182
|
points: Array<{
|
|
183
|
-
pos: [x, y]
|
|
184
|
-
handleIn?: [x, y]
|
|
185
|
-
handleOut?: [x, y]
|
|
186
|
-
}
|
|
183
|
+
pos: [x, y] // Position (x: 0-1 progress, y: value)
|
|
184
|
+
handleIn?: [x, y] // Bezier handle in (offset)
|
|
185
|
+
handleOut?: [x, y] // Bezier handle out (offset)
|
|
186
|
+
}>
|
|
187
187
|
}
|
|
188
188
|
```
|
|
189
189
|
|
|
@@ -205,21 +205,21 @@ interface CurveData {
|
|
|
205
205
|
| `alphaTestNode` | `NodeFunction` | Alpha test/discard |
|
|
206
206
|
|
|
207
207
|
```ts
|
|
208
|
-
type NodeFunction = (data: ParticleData, defaultColor?: Node) => Node
|
|
208
|
+
type NodeFunction = (data: ParticleData, defaultColor?: Node) => Node
|
|
209
209
|
|
|
210
210
|
interface ParticleData {
|
|
211
|
-
progress: Node
|
|
212
|
-
lifetime: Node
|
|
213
|
-
position: Node
|
|
214
|
-
velocity: Node
|
|
215
|
-
size: Node
|
|
216
|
-
rotation: Node
|
|
217
|
-
colorStart: Node
|
|
218
|
-
colorEnd: Node
|
|
219
|
-
color: Node
|
|
220
|
-
intensifiedColor: Node
|
|
221
|
-
shapeMask: Node
|
|
222
|
-
index: Node
|
|
211
|
+
progress: Node // 0 → 1 over lifetime
|
|
212
|
+
lifetime: Node // 1 → 0 over lifetime
|
|
213
|
+
position: Node // vec3 world position
|
|
214
|
+
velocity: Node // vec3 velocity
|
|
215
|
+
size: Node // float size
|
|
216
|
+
rotation: Node // vec3 rotation
|
|
217
|
+
colorStart: Node // vec3 start color
|
|
218
|
+
colorEnd: Node // vec3 end color
|
|
219
|
+
color: Node // vec3 interpolated color
|
|
220
|
+
intensifiedColor: Node // color × intensity
|
|
221
|
+
shapeMask: Node // float alpha mask
|
|
222
|
+
index: Node // particle index
|
|
223
223
|
}
|
|
224
224
|
```
|
|
225
225
|
|
|
@@ -232,8 +232,8 @@ interface ParticleData {
|
|
|
232
232
|
|
|
233
233
|
```ts
|
|
234
234
|
interface FlipbookConfig {
|
|
235
|
-
rows: number
|
|
236
|
-
columns: number
|
|
235
|
+
rows: number
|
|
236
|
+
columns: number
|
|
237
237
|
}
|
|
238
238
|
```
|
|
239
239
|
|
|
@@ -276,13 +276,13 @@ Decoupled emitter component that links to a VFXParticles system.
|
|
|
276
276
|
|
|
277
277
|
```ts
|
|
278
278
|
interface VFXEmitterAPI {
|
|
279
|
-
emit(): boolean
|
|
280
|
-
burst(count?: number): boolean
|
|
281
|
-
start(): void
|
|
282
|
-
stop(): void
|
|
283
|
-
isEmitting: boolean
|
|
284
|
-
getParticleSystem(): ParticleAPI
|
|
285
|
-
group: THREE.Group
|
|
279
|
+
emit(): boolean // Emit at current position
|
|
280
|
+
burst(count?: number): boolean // Burst emit
|
|
281
|
+
start(): void // Start auto-emission
|
|
282
|
+
stop(): void // Stop auto-emission
|
|
283
|
+
isEmitting: boolean // Current state
|
|
284
|
+
getParticleSystem(): ParticleAPI
|
|
285
|
+
group: THREE.Group // The group element
|
|
286
286
|
}
|
|
287
287
|
```
|
|
288
288
|
|
|
@@ -292,13 +292,13 @@ Programmatic emitter control.
|
|
|
292
292
|
|
|
293
293
|
```tsx
|
|
294
294
|
function MyComponent() {
|
|
295
|
-
const { emit, burst, start, stop } = useVFXEmitter('sparks')
|
|
295
|
+
const { emit, burst, start, stop } = useVFXEmitter('sparks')
|
|
296
296
|
|
|
297
297
|
const handleClick = () => {
|
|
298
|
-
burst([0, 1, 0], 100, { colorStart: ['#ff0000'] })
|
|
299
|
-
}
|
|
298
|
+
burst([0, 1, 0], 100, { colorStart: ['#ff0000'] })
|
|
299
|
+
}
|
|
300
300
|
|
|
301
|
-
return <mesh onClick={handleClick}>...</mesh
|
|
301
|
+
return <mesh onClick={handleClick}>...</mesh>
|
|
302
302
|
}
|
|
303
303
|
```
|
|
304
304
|
|
|
@@ -310,18 +310,18 @@ interface UseVFXEmitterResult {
|
|
|
310
310
|
position?: [x, y, z],
|
|
311
311
|
count?: number,
|
|
312
312
|
overrides?: SpawnOverrides
|
|
313
|
-
): boolean
|
|
313
|
+
): boolean
|
|
314
314
|
burst(
|
|
315
315
|
position?: [x, y, z],
|
|
316
316
|
count?: number,
|
|
317
317
|
overrides?: SpawnOverrides
|
|
318
|
-
): boolean
|
|
319
|
-
start(): boolean
|
|
320
|
-
stop(): boolean
|
|
321
|
-
clear(): boolean
|
|
322
|
-
isEmitting(): boolean
|
|
323
|
-
getUniforms(): Record<string, { value: unknown }
|
|
324
|
-
getParticles(): ParticleAPI
|
|
318
|
+
): boolean
|
|
319
|
+
start(): boolean
|
|
320
|
+
stop(): boolean
|
|
321
|
+
clear(): boolean
|
|
322
|
+
isEmitting(): boolean
|
|
323
|
+
getUniforms(): Record<string, { value: unknown }>
|
|
324
|
+
getParticles(): ParticleAPI
|
|
325
325
|
}
|
|
326
326
|
```
|
|
327
327
|
|
|
@@ -330,17 +330,17 @@ interface UseVFXEmitterResult {
|
|
|
330
330
|
Zustand store for managing particle systems.
|
|
331
331
|
|
|
332
332
|
```ts
|
|
333
|
-
const store = useVFXStore()
|
|
333
|
+
const store = useVFXStore()
|
|
334
334
|
|
|
335
335
|
// Access registered particle systems
|
|
336
|
-
const sparks = store.getParticles('sparks')
|
|
337
|
-
sparks?.spawn(0, 0, 0, 50)
|
|
336
|
+
const sparks = store.getParticles('sparks')
|
|
337
|
+
sparks?.spawn(0, 0, 0, 50)
|
|
338
338
|
|
|
339
339
|
// Store methods
|
|
340
|
-
store.emit('sparks', { x: 0, y: 0, z: 0, count: 20 })
|
|
341
|
-
store.start('sparks')
|
|
342
|
-
store.stop('sparks')
|
|
343
|
-
store.clear('sparks')
|
|
340
|
+
store.emit('sparks', { x: 0, y: 0, z: 0, count: 20 })
|
|
341
|
+
store.start('sparks')
|
|
342
|
+
store.stop('sparks')
|
|
343
|
+
store.clear('sparks')
|
|
344
344
|
```
|
|
345
345
|
|
|
346
346
|
## Examples
|
|
@@ -388,9 +388,9 @@ store.clear('sparks');
|
|
|
388
388
|
### 3D Geometry Particles
|
|
389
389
|
|
|
390
390
|
```tsx
|
|
391
|
-
import { BoxGeometry } from 'three/webgpu'
|
|
391
|
+
import { BoxGeometry } from 'three/webgpu'
|
|
392
392
|
|
|
393
|
-
|
|
393
|
+
;<VFXParticles
|
|
394
394
|
geometry={new BoxGeometry(1, 1, 1)}
|
|
395
395
|
maxParticles={500}
|
|
396
396
|
size={[0.1, 0.2]}
|
|
@@ -404,7 +404,7 @@ import { BoxGeometry } from 'three/webgpu';
|
|
|
404
404
|
]}
|
|
405
405
|
shadow={true}
|
|
406
406
|
lighting={Lighting.STANDARD}
|
|
407
|
-
|
|
407
|
+
/>
|
|
408
408
|
```
|
|
409
409
|
|
|
410
410
|
### Turbulent Smoke
|
|
@@ -464,7 +464,7 @@ import type {
|
|
|
464
464
|
TurbulenceConfig,
|
|
465
465
|
CollisionConfig,
|
|
466
466
|
AttractorConfig,
|
|
467
|
-
} from 'r3f-vfx'
|
|
467
|
+
} from 'r3f-vfx'
|
|
468
468
|
```
|
|
469
469
|
|
|
470
470
|
## License
|
package/dist/index.d.ts
CHANGED
|
@@ -250,6 +250,8 @@ declare const DEFAULT_LINEAR_CURVE: {
|
|
|
250
250
|
handleOut?: undefined;
|
|
251
251
|
})[];
|
|
252
252
|
};
|
|
253
|
+
declare const createDefaultCurveTexture: () => THREE.DataTexture;
|
|
254
|
+
declare const loadCurveTextureFromPath: (path: string, existingTexture?: THREE.DataTexture) => Promise<THREE.DataTexture>;
|
|
253
255
|
|
|
254
256
|
type ParticleStorageArrays = {
|
|
255
257
|
positions: StorageBufferNode;
|
|
@@ -257,11 +259,18 @@ type ParticleStorageArrays = {
|
|
|
257
259
|
lifetimes: StorageBufferNode;
|
|
258
260
|
fadeRates: StorageBufferNode;
|
|
259
261
|
particleSizes: StorageBufferNode;
|
|
260
|
-
particleRotations: StorageBufferNode;
|
|
261
|
-
particleColorStarts: StorageBufferNode;
|
|
262
|
-
particleColorEnds: StorageBufferNode;
|
|
262
|
+
particleRotations: StorageBufferNode | null;
|
|
263
|
+
particleColorStarts: StorageBufferNode | null;
|
|
264
|
+
particleColorEnds: StorageBufferNode | null;
|
|
263
265
|
};
|
|
264
266
|
type ParticleUniforms = Record<string, Node>;
|
|
267
|
+
type ShaderFeatures = {
|
|
268
|
+
turbulence: boolean;
|
|
269
|
+
attractors: boolean;
|
|
270
|
+
collision: boolean;
|
|
271
|
+
rotation: boolean;
|
|
272
|
+
perParticleColor: boolean;
|
|
273
|
+
};
|
|
265
274
|
type MaterialOptions = {
|
|
266
275
|
alphaMap: THREE.Texture | null;
|
|
267
276
|
flipbook: {
|
|
@@ -299,9 +308,9 @@ declare const createSpawnCompute: (storage: ParticleStorageArrays, uniforms: Par
|
|
|
299
308
|
|
|
300
309
|
/**
|
|
301
310
|
* Creates the update compute shader that simulates particle physics each frame.
|
|
302
|
-
*
|
|
311
|
+
* Features can be disabled to generate a simpler/faster shader.
|
|
303
312
|
*/
|
|
304
|
-
declare const createUpdateCompute: (storage: ParticleStorageArrays, uniforms: ParticleUniforms, curveTexture: THREE.DataTexture, maxParticles: number) => THREE.ComputeNode;
|
|
313
|
+
declare const createUpdateCompute: (storage: ParticleStorageArrays, uniforms: ParticleUniforms, curveTexture: THREE.DataTexture, maxParticles: number, features?: Partial<ShaderFeatures>) => THREE.ComputeNode;
|
|
305
314
|
|
|
306
315
|
/**
|
|
307
316
|
* Creates the particle material (either SpriteNodeMaterial or MeshNodeMaterial).
|
|
@@ -309,4 +318,4 @@ declare const createUpdateCompute: (storage: ParticleStorageArrays, uniforms: Pa
|
|
|
309
318
|
*/
|
|
310
319
|
declare const createParticleMaterial: (storage: ParticleStorageArrays, uniforms: ParticleUniforms, curveTexture: THREE.DataTexture, options: MaterialOptions) => THREE.SpriteNodeMaterial | THREE.MeshBasicNodeMaterial | THREE.MeshStandardNodeMaterial | THREE.MeshPhysicalNodeMaterial;
|
|
311
320
|
|
|
312
|
-
export { Appearance, type AttractorConfig, AttractorType, type BaseParticleProps, Blending, CURVE_RESOLUTION, type CollisionConfig, type CoreState, type CurveData, type CurvePoint, DEFAULT_LINEAR_CURVE, Easing, EmitterShape, type FlipbookConfig, type FrictionConfig, Lighting, MAX_ATTRACTORS, type MaterialOptions, type ParticleData, type ParticleStorageArrays, type ParticleUniforms, type Rotation3DInput, type StretchConfig, type TurbulenceConfig, axisToNumber, bakeCurveToArray, coreStore, createCombinedCurveTexture, createInitCompute, createParticleMaterial, createSpawnCompute, createUpdateCompute, easingToType, evaluateBezierSegment, hexToRgb, lifetimeToFadeRate, sampleCurveAtX, selectColor, toRange, toRotation3D };
|
|
321
|
+
export { Appearance, type AttractorConfig, AttractorType, type BaseParticleProps, Blending, CURVE_RESOLUTION, type CollisionConfig, type CoreState, type CurveData, type CurvePoint, DEFAULT_LINEAR_CURVE, Easing, EmitterShape, type FlipbookConfig, type FrictionConfig, Lighting, MAX_ATTRACTORS, type MaterialOptions, type ParticleData, type ParticleStorageArrays, type ParticleUniforms, type Rotation3DInput, type ShaderFeatures, type StretchConfig, type TurbulenceConfig, axisToNumber, bakeCurveToArray, coreStore, createCombinedCurveTexture, createDefaultCurveTexture, createInitCompute, createParticleMaterial, createSpawnCompute, createUpdateCompute, easingToType, evaluateBezierSegment, hexToRgb, lifetimeToFadeRate, loadCurveTextureFromPath, sampleCurveAtX, selectColor, toRange, toRotation3D };
|
package/dist/index.js
CHANGED
|
@@ -301,8 +301,14 @@ var lifetimeToFadeRate = (seconds) => 1 / seconds;
|
|
|
301
301
|
import * as THREE2 from "three/webgpu";
|
|
302
302
|
var evaluateBezierSegment = (t, p0, p1, h0Out, h1In) => {
|
|
303
303
|
const cp0 = p0;
|
|
304
|
-
const cp1 = [
|
|
305
|
-
|
|
304
|
+
const cp1 = [
|
|
305
|
+
p0[0] + ((h0Out == null ? void 0 : h0Out[0]) || 0),
|
|
306
|
+
p0[1] + ((h0Out == null ? void 0 : h0Out[1]) || 0)
|
|
307
|
+
];
|
|
308
|
+
const cp2 = [
|
|
309
|
+
p1[0] + ((h1In == null ? void 0 : h1In[0]) || 0),
|
|
310
|
+
p1[1] + ((h1In == null ? void 0 : h1In[1]) || 0)
|
|
311
|
+
];
|
|
306
312
|
const cp3 = p1;
|
|
307
313
|
const mt = 1 - t;
|
|
308
314
|
const mt2 = mt * mt;
|
|
@@ -403,10 +409,68 @@ var createCombinedCurveTexture = (sizeCurve, opacityCurve, velocityCurve, rotati
|
|
|
403
409
|
};
|
|
404
410
|
var DEFAULT_LINEAR_CURVE = {
|
|
405
411
|
points: [
|
|
406
|
-
{
|
|
407
|
-
|
|
412
|
+
{
|
|
413
|
+
pos: [0, 1],
|
|
414
|
+
handleOut: [0.33, 0]
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
pos: [1, 0],
|
|
418
|
+
handleIn: [-0.33, 0]
|
|
419
|
+
}
|
|
408
420
|
]
|
|
409
421
|
};
|
|
422
|
+
var createDefaultCurveTexture = () => {
|
|
423
|
+
const rgba = new Float32Array(CURVE_RESOLUTION * 4);
|
|
424
|
+
for (let i = 0; i < CURVE_RESOLUTION; i++) {
|
|
425
|
+
const value = 1 - i / (CURVE_RESOLUTION - 1);
|
|
426
|
+
rgba[i * 4] = value;
|
|
427
|
+
rgba[i * 4 + 1] = value;
|
|
428
|
+
rgba[i * 4 + 2] = value;
|
|
429
|
+
rgba[i * 4 + 3] = value;
|
|
430
|
+
}
|
|
431
|
+
const tex = new THREE2.DataTexture(
|
|
432
|
+
rgba,
|
|
433
|
+
CURVE_RESOLUTION,
|
|
434
|
+
1,
|
|
435
|
+
THREE2.RGBAFormat,
|
|
436
|
+
THREE2.FloatType
|
|
437
|
+
);
|
|
438
|
+
tex.minFilter = THREE2.LinearFilter;
|
|
439
|
+
tex.magFilter = THREE2.LinearFilter;
|
|
440
|
+
tex.wrapS = THREE2.ClampToEdgeWrapping;
|
|
441
|
+
tex.needsUpdate = true;
|
|
442
|
+
return tex;
|
|
443
|
+
};
|
|
444
|
+
var loadCurveTextureFromPath = async (path, existingTexture) => {
|
|
445
|
+
const response = await fetch(path);
|
|
446
|
+
if (!response.ok) {
|
|
447
|
+
throw new Error(`Failed to load curve texture: HTTP ${response.status}`);
|
|
448
|
+
}
|
|
449
|
+
const buffer = await response.arrayBuffer();
|
|
450
|
+
const rgba = new Float32Array(buffer);
|
|
451
|
+
if (rgba.length !== CURVE_RESOLUTION * 4) {
|
|
452
|
+
throw new Error(
|
|
453
|
+
`Invalid curve texture size: expected ${CURVE_RESOLUTION * 4}, got ${rgba.length}`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
if (existingTexture && existingTexture.image.data) {
|
|
457
|
+
existingTexture.image.data.set(rgba);
|
|
458
|
+
existingTexture.needsUpdate = true;
|
|
459
|
+
return existingTexture;
|
|
460
|
+
}
|
|
461
|
+
const tex = new THREE2.DataTexture(
|
|
462
|
+
rgba,
|
|
463
|
+
CURVE_RESOLUTION,
|
|
464
|
+
1,
|
|
465
|
+
THREE2.RGBAFormat,
|
|
466
|
+
THREE2.FloatType
|
|
467
|
+
);
|
|
468
|
+
tex.minFilter = THREE2.LinearFilter;
|
|
469
|
+
tex.magFilter = THREE2.LinearFilter;
|
|
470
|
+
tex.wrapS = THREE2.ClampToEdgeWrapping;
|
|
471
|
+
tex.needsUpdate = true;
|
|
472
|
+
return tex;
|
|
473
|
+
};
|
|
410
474
|
|
|
411
475
|
// src/shaders/helpers.ts
|
|
412
476
|
var selectColor = (idx, c0, c1, c2, c3, c4, c5, c6, c7) => {
|
|
@@ -432,22 +496,27 @@ var selectColor = (idx, c0, c1, c2, c3, c4, c5, c6, c7) => {
|
|
|
432
496
|
import { Fn, float, vec3, instanceIndex } from "three/tsl";
|
|
433
497
|
var createInitCompute = (storage, maxParticles) => {
|
|
434
498
|
return Fn(() => {
|
|
499
|
+
var _a, _b, _c;
|
|
435
500
|
const position = storage.positions.element(instanceIndex);
|
|
436
501
|
const velocity = storage.velocities.element(instanceIndex);
|
|
437
502
|
const lifetime = storage.lifetimes.element(instanceIndex);
|
|
438
503
|
const fadeRate = storage.fadeRates.element(instanceIndex);
|
|
439
504
|
const particleSize = storage.particleSizes.element(instanceIndex);
|
|
440
|
-
const particleRotation = storage.particleRotations.element(instanceIndex);
|
|
441
|
-
const colorStart = storage.particleColorStarts.element(instanceIndex);
|
|
442
|
-
const colorEnd = storage.particleColorEnds.element(instanceIndex);
|
|
505
|
+
const particleRotation = (_a = storage.particleRotations) == null ? void 0 : _a.element(instanceIndex);
|
|
506
|
+
const colorStart = (_b = storage.particleColorStarts) == null ? void 0 : _b.element(instanceIndex);
|
|
507
|
+
const colorEnd = (_c = storage.particleColorEnds) == null ? void 0 : _c.element(instanceIndex);
|
|
443
508
|
position.assign(vec3(0, -1e3, 0));
|
|
444
509
|
velocity.assign(vec3(0, 0, 0));
|
|
445
510
|
lifetime.assign(float(0));
|
|
446
511
|
fadeRate.assign(float(0));
|
|
447
512
|
particleSize.assign(float(0));
|
|
448
|
-
particleRotation
|
|
449
|
-
|
|
450
|
-
|
|
513
|
+
if (particleRotation) {
|
|
514
|
+
particleRotation.assign(vec3(0, 0, 0));
|
|
515
|
+
}
|
|
516
|
+
if (colorStart && colorEnd) {
|
|
517
|
+
colorStart.assign(vec3(1, 1, 1));
|
|
518
|
+
colorEnd.assign(vec3(1, 1, 1));
|
|
519
|
+
}
|
|
451
520
|
})().compute(maxParticles);
|
|
452
521
|
};
|
|
453
522
|
|
|
@@ -478,14 +547,15 @@ var createSpawnCompute = (storage, uniforms, maxParticles) => {
|
|
|
478
547
|
idx.greaterThanEqual(startIdx).or(idx.lessThan(endIdx))
|
|
479
548
|
);
|
|
480
549
|
If(inRange, () => {
|
|
550
|
+
var _a, _b, _c;
|
|
481
551
|
const position = storage.positions.element(instanceIndex2);
|
|
482
552
|
const velocity = storage.velocities.element(instanceIndex2);
|
|
483
553
|
const lifetime = storage.lifetimes.element(instanceIndex2);
|
|
484
554
|
const fadeRate = storage.fadeRates.element(instanceIndex2);
|
|
485
555
|
const particleSize = storage.particleSizes.element(instanceIndex2);
|
|
486
|
-
const particleRotation = storage.particleRotations.element(instanceIndex2);
|
|
487
|
-
const pColorStart = storage.particleColorStarts.element(instanceIndex2);
|
|
488
|
-
const pColorEnd = storage.particleColorEnds.element(instanceIndex2);
|
|
556
|
+
const particleRotation = (_a = storage.particleRotations) == null ? void 0 : _a.element(instanceIndex2);
|
|
557
|
+
const pColorStart = (_b = storage.particleColorStarts) == null ? void 0 : _b.element(instanceIndex2);
|
|
558
|
+
const pColorEnd = (_c = storage.particleColorEnds) == null ? void 0 : _c.element(instanceIndex2);
|
|
489
559
|
const particleSeed = idx.add(seed);
|
|
490
560
|
const randDirX = hash(particleSeed.add(333));
|
|
491
561
|
const randDirY = hash(particleSeed.add(444));
|
|
@@ -571,9 +641,7 @@ var createSpawnCompute = (storage, uniforms, maxParticles) => {
|
|
|
571
641
|
const coneLocalX = coneR.mul(cos(theta));
|
|
572
642
|
const coneLocalY = coneH.mul(cos(coneAngle));
|
|
573
643
|
const coneLocalZ = coneR.mul(sin(theta));
|
|
574
|
-
const conePos = rotateToEmitDir(
|
|
575
|
-
vec32(coneLocalX, coneLocalY, coneLocalZ)
|
|
576
|
-
);
|
|
644
|
+
const conePos = rotateToEmitDir(vec32(coneLocalX, coneLocalY, coneLocalZ));
|
|
577
645
|
const diskR = surfaceOnly.greaterThan(0.5).select(
|
|
578
646
|
radiusOuter,
|
|
579
647
|
mix(radiusInner, radiusOuter, sqrt(randRadius))
|
|
@@ -630,61 +698,61 @@ var createSpawnCompute = (storage, uniforms, maxParticles) => {
|
|
|
630
698
|
const startPosLength = shapeOffset.length();
|
|
631
699
|
const startPosDir = startPosLength.greaterThan(1e-3).select(shapeOffset.div(startPosLength), vec32(0, 0, 0));
|
|
632
700
|
const dir = useStartPosAsDir.select(startPosDir, randomDir);
|
|
633
|
-
const randomSpeed = mix(
|
|
634
|
-
uniforms.speedMin,
|
|
635
|
-
uniforms.speedMax,
|
|
636
|
-
randSpeed
|
|
637
|
-
);
|
|
701
|
+
const randomSpeed = mix(uniforms.speedMin, uniforms.speedMax, randSpeed);
|
|
638
702
|
const normalVelocity = dir.mul(randomSpeed);
|
|
639
703
|
velocity.assign(
|
|
640
704
|
useAttractToCenter.select(attractVelocity, normalVelocity)
|
|
641
705
|
);
|
|
642
706
|
const randomSize = mix(uniforms.sizeMin, uniforms.sizeMax, randSize);
|
|
643
707
|
particleSize.assign(randomSize);
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
)
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
uniforms.
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
708
|
+
if (particleRotation) {
|
|
709
|
+
const rotX = mix(
|
|
710
|
+
uniforms.rotationMinX,
|
|
711
|
+
uniforms.rotationMaxX,
|
|
712
|
+
randRotationX
|
|
713
|
+
);
|
|
714
|
+
const rotY = mix(
|
|
715
|
+
uniforms.rotationMinY,
|
|
716
|
+
uniforms.rotationMaxY,
|
|
717
|
+
randRotationY
|
|
718
|
+
);
|
|
719
|
+
const rotZ = mix(
|
|
720
|
+
uniforms.rotationMinZ,
|
|
721
|
+
uniforms.rotationMaxZ,
|
|
722
|
+
randRotationZ
|
|
723
|
+
);
|
|
724
|
+
particleRotation.assign(vec32(rotX, rotY, rotZ));
|
|
725
|
+
}
|
|
726
|
+
if (pColorStart && pColorEnd) {
|
|
727
|
+
const startColorIdx = floor(
|
|
728
|
+
randColorStart.mul(uniforms.colorStartCount)
|
|
729
|
+
);
|
|
730
|
+
const selectedStartColor = selectColor(
|
|
731
|
+
startColorIdx,
|
|
732
|
+
uniforms.colorStart0,
|
|
733
|
+
uniforms.colorStart1,
|
|
734
|
+
uniforms.colorStart2,
|
|
735
|
+
uniforms.colorStart3,
|
|
736
|
+
uniforms.colorStart4,
|
|
737
|
+
uniforms.colorStart5,
|
|
738
|
+
uniforms.colorStart6,
|
|
739
|
+
uniforms.colorStart7
|
|
740
|
+
);
|
|
741
|
+
pColorStart.assign(selectedStartColor);
|
|
742
|
+
const endColorIdx = floor(randColorEnd.mul(uniforms.colorEndCount));
|
|
743
|
+
const selectedEndColor = selectColor(
|
|
744
|
+
endColorIdx,
|
|
745
|
+
uniforms.colorEnd0,
|
|
746
|
+
uniforms.colorEnd1,
|
|
747
|
+
uniforms.colorEnd2,
|
|
748
|
+
uniforms.colorEnd3,
|
|
749
|
+
uniforms.colorEnd4,
|
|
750
|
+
uniforms.colorEnd5,
|
|
751
|
+
uniforms.colorEnd6,
|
|
752
|
+
uniforms.colorEnd7
|
|
753
|
+
);
|
|
754
|
+
pColorEnd.assign(selectedEndColor);
|
|
755
|
+
}
|
|
688
756
|
lifetime.assign(float2(1));
|
|
689
757
|
});
|
|
690
758
|
})().compute(maxParticles);
|
|
@@ -703,13 +771,22 @@ import {
|
|
|
703
771
|
instanceIndex as instanceIndex3,
|
|
704
772
|
mx_noise_vec3
|
|
705
773
|
} from "three/tsl";
|
|
706
|
-
var
|
|
774
|
+
var DEFAULT_FEATURES = {
|
|
775
|
+
turbulence: true,
|
|
776
|
+
attractors: true,
|
|
777
|
+
collision: true,
|
|
778
|
+
rotation: true,
|
|
779
|
+
perParticleColor: true
|
|
780
|
+
};
|
|
781
|
+
var createUpdateCompute = (storage, uniforms, curveTexture, maxParticles, features = {}) => {
|
|
782
|
+
const f = __spreadValues(__spreadValues({}, DEFAULT_FEATURES), features);
|
|
707
783
|
return Fn3(() => {
|
|
784
|
+
var _a;
|
|
708
785
|
const position = storage.positions.element(instanceIndex3);
|
|
709
786
|
const velocity = storage.velocities.element(instanceIndex3);
|
|
710
787
|
const lifetime = storage.lifetimes.element(instanceIndex3);
|
|
711
788
|
const fadeRate = storage.fadeRates.element(instanceIndex3);
|
|
712
|
-
const particleRotation = storage.particleRotations.element(instanceIndex3);
|
|
789
|
+
const particleRotation = f.rotation ? (_a = storage.particleRotations) == null ? void 0 : _a.element(instanceIndex3) : null;
|
|
713
790
|
const particleSize = storage.particleSizes.element(instanceIndex3);
|
|
714
791
|
const dt = uniforms.deltaTime;
|
|
715
792
|
If2(lifetime.greaterThan(0), () => {
|
|
@@ -753,139 +830,143 @@ var createUpdateCompute = (storage, uniforms, curveTexture, maxParticles) => {
|
|
|
753
830
|
return float3(1).sub(currentIntensity.mul(0.9));
|
|
754
831
|
})()
|
|
755
832
|
);
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
833
|
+
if (f.turbulence) {
|
|
834
|
+
const turbIntensity = uniforms.turbulenceIntensity;
|
|
835
|
+
const turbFreq = uniforms.turbulenceFrequency;
|
|
836
|
+
const turbTime = uniforms.turbulenceTime;
|
|
837
|
+
If2(turbIntensity.greaterThan(1e-3), () => {
|
|
838
|
+
const noisePos = position.mul(turbFreq).add(vec33(turbTime, turbTime.mul(0.7), turbTime.mul(1.3)));
|
|
839
|
+
const eps = float3(0.01);
|
|
840
|
+
const nPosX = mx_noise_vec3(noisePos.add(vec33(eps, 0, 0)));
|
|
841
|
+
const nNegX = mx_noise_vec3(noisePos.sub(vec33(eps, 0, 0)));
|
|
842
|
+
const nPosY = mx_noise_vec3(noisePos.add(vec33(0, eps, 0)));
|
|
843
|
+
const nNegY = mx_noise_vec3(noisePos.sub(vec33(0, eps, 0)));
|
|
844
|
+
const nPosZ = mx_noise_vec3(noisePos.add(vec33(0, 0, eps)));
|
|
845
|
+
const nNegZ = mx_noise_vec3(noisePos.sub(vec33(0, 0, eps)));
|
|
846
|
+
const dFx_dy = nPosY.x.sub(nNegY.x).div(eps.mul(2));
|
|
847
|
+
const dFx_dz = nPosZ.x.sub(nNegZ.x).div(eps.mul(2));
|
|
848
|
+
const dFy_dx = nPosX.y.sub(nNegX.y).div(eps.mul(2));
|
|
849
|
+
const dFy_dz = nPosZ.y.sub(nNegZ.y).div(eps.mul(2));
|
|
850
|
+
const dFz_dx = nPosX.z.sub(nNegX.z).div(eps.mul(2));
|
|
851
|
+
const dFz_dy = nPosY.z.sub(nNegY.z).div(eps.mul(2));
|
|
852
|
+
const curl = vec33(
|
|
853
|
+
dFz_dy.sub(dFy_dz),
|
|
854
|
+
dFx_dz.sub(dFz_dx),
|
|
855
|
+
dFy_dx.sub(dFx_dy)
|
|
856
|
+
);
|
|
857
|
+
velocity.addAssign(curl.mul(turbIntensity).mul(uniforms.deltaTime));
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
if (f.attractors) {
|
|
861
|
+
const attractorCount = uniforms.attractorCount;
|
|
862
|
+
const applyAttractor = (aPos, aStrength, aRadius, aType, aAxis) => {
|
|
863
|
+
If2(aStrength.abs().greaterThan(1e-3), () => {
|
|
864
|
+
const toAttractor = aPos.sub(position);
|
|
865
|
+
const dist = toAttractor.length();
|
|
866
|
+
const safeDist = dist.max(0.01);
|
|
867
|
+
const direction = toAttractor.div(safeDist);
|
|
868
|
+
const falloff = aRadius.greaterThan(1e-3).select(
|
|
869
|
+
float3(1).sub(dist.div(aRadius)).max(0),
|
|
870
|
+
float3(1).div(safeDist.mul(safeDist).add(1))
|
|
871
|
+
);
|
|
872
|
+
const force = aType.lessThan(0.5).select(
|
|
873
|
+
direction.mul(aStrength).mul(falloff),
|
|
874
|
+
(() => {
|
|
875
|
+
const tangent = vec33(
|
|
876
|
+
aAxis.y.mul(toAttractor.z).sub(aAxis.z.mul(toAttractor.y)),
|
|
877
|
+
aAxis.z.mul(toAttractor.x).sub(aAxis.x.mul(toAttractor.z)),
|
|
878
|
+
aAxis.x.mul(toAttractor.y).sub(aAxis.y.mul(toAttractor.x))
|
|
879
|
+
);
|
|
880
|
+
const tangentLen = tangent.length().max(1e-3);
|
|
881
|
+
return tangent.div(tangentLen).mul(aStrength).mul(falloff);
|
|
882
|
+
})()
|
|
883
|
+
);
|
|
884
|
+
velocity.addAssign(force.mul(uniforms.deltaTime));
|
|
885
|
+
});
|
|
886
|
+
};
|
|
887
|
+
If2(attractorCount.greaterThan(0), () => {
|
|
888
|
+
applyAttractor(
|
|
889
|
+
uniforms.attractor0Pos,
|
|
890
|
+
uniforms.attractor0Strength,
|
|
891
|
+
uniforms.attractor0Radius,
|
|
892
|
+
uniforms.attractor0Type,
|
|
893
|
+
uniforms.attractor0Axis
|
|
792
894
|
);
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
aAxis.z.mul(toAttractor.x).sub(aAxis.x.mul(toAttractor.z)),
|
|
802
|
-
aAxis.x.mul(toAttractor.y).sub(aAxis.y.mul(toAttractor.x))
|
|
803
|
-
);
|
|
804
|
-
const tangentLen = tangent.length().max(1e-3);
|
|
805
|
-
return tangent.div(tangentLen).mul(aStrength).mul(falloff);
|
|
806
|
-
})()
|
|
895
|
+
});
|
|
896
|
+
If2(attractorCount.greaterThan(1), () => {
|
|
897
|
+
applyAttractor(
|
|
898
|
+
uniforms.attractor1Pos,
|
|
899
|
+
uniforms.attractor1Strength,
|
|
900
|
+
uniforms.attractor1Radius,
|
|
901
|
+
uniforms.attractor1Type,
|
|
902
|
+
uniforms.attractor1Axis
|
|
807
903
|
);
|
|
808
|
-
velocity.addAssign(force.mul(uniforms.deltaTime));
|
|
809
904
|
});
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
905
|
+
If2(attractorCount.greaterThan(2), () => {
|
|
906
|
+
applyAttractor(
|
|
907
|
+
uniforms.attractor2Pos,
|
|
908
|
+
uniforms.attractor2Strength,
|
|
909
|
+
uniforms.attractor2Radius,
|
|
910
|
+
uniforms.attractor2Type,
|
|
911
|
+
uniforms.attractor2Axis
|
|
912
|
+
);
|
|
913
|
+
});
|
|
914
|
+
If2(attractorCount.greaterThan(3), () => {
|
|
915
|
+
applyAttractor(
|
|
916
|
+
uniforms.attractor3Pos,
|
|
917
|
+
uniforms.attractor3Strength,
|
|
918
|
+
uniforms.attractor3Radius,
|
|
919
|
+
uniforms.attractor3Type,
|
|
920
|
+
uniforms.attractor3Axis
|
|
921
|
+
);
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
position.addAssign(velocity.mul(dt).mul(speedScale));
|
|
925
|
+
if (f.collision) {
|
|
926
|
+
If2(uniforms.collisionEnabled.greaterThan(0.5), () => {
|
|
927
|
+
const planeY = uniforms.collisionPlaneY;
|
|
928
|
+
const bounce = uniforms.collisionBounce;
|
|
929
|
+
const friction = uniforms.collisionFriction;
|
|
930
|
+
const shouldDie = uniforms.collisionDie;
|
|
931
|
+
If2(position.y.lessThan(planeY), () => {
|
|
932
|
+
If2(shouldDie.greaterThan(0.5), () => {
|
|
933
|
+
lifetime.assign(float3(0));
|
|
934
|
+
position.y.assign(float3(-1e3));
|
|
935
|
+
}).Else(() => {
|
|
936
|
+
position.y.assign(planeY);
|
|
937
|
+
velocity.y.assign(velocity.y.abs().mul(bounce));
|
|
938
|
+
velocity.x.mulAssign(friction);
|
|
939
|
+
velocity.z.mulAssign(friction);
|
|
940
|
+
});
|
|
941
|
+
});
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
if (particleRotation) {
|
|
945
|
+
const idx = float3(instanceIndex3);
|
|
946
|
+
const rotSpeedX = mix2(
|
|
947
|
+
uniforms.rotationSpeedMinX,
|
|
948
|
+
uniforms.rotationSpeedMaxX,
|
|
949
|
+
hash2(idx.add(8888))
|
|
818
950
|
);
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
uniforms.attractor1Strength,
|
|
824
|
-
uniforms.attractor1Radius,
|
|
825
|
-
uniforms.attractor1Type,
|
|
826
|
-
uniforms.attractor1Axis
|
|
951
|
+
const rotSpeedY = mix2(
|
|
952
|
+
uniforms.rotationSpeedMinY,
|
|
953
|
+
uniforms.rotationSpeedMaxY,
|
|
954
|
+
hash2(idx.add(9999))
|
|
827
955
|
);
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
uniforms.attractor2Strength,
|
|
833
|
-
uniforms.attractor2Radius,
|
|
834
|
-
uniforms.attractor2Type,
|
|
835
|
-
uniforms.attractor2Axis
|
|
956
|
+
const rotSpeedZ = mix2(
|
|
957
|
+
uniforms.rotationSpeedMinZ,
|
|
958
|
+
uniforms.rotationSpeedMaxZ,
|
|
959
|
+
hash2(idx.add(10101))
|
|
836
960
|
);
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
uniforms.
|
|
844
|
-
uniforms.attractor3Axis
|
|
961
|
+
const rotSpeedCurveSample = texture(
|
|
962
|
+
curveTexture,
|
|
963
|
+
vec2(progress, float3(0.5))
|
|
964
|
+
).w;
|
|
965
|
+
const rotSpeedMultiplier = uniforms.rotationSpeedCurveEnabled.greaterThan(0.5).select(rotSpeedCurveSample, float3(1));
|
|
966
|
+
particleRotation.addAssign(
|
|
967
|
+
vec33(rotSpeedX, rotSpeedY, rotSpeedZ).mul(uniforms.deltaTime).mul(rotSpeedMultiplier)
|
|
845
968
|
);
|
|
846
|
-
}
|
|
847
|
-
position.addAssign(velocity.mul(dt).mul(speedScale));
|
|
848
|
-
If2(uniforms.collisionEnabled.greaterThan(0.5), () => {
|
|
849
|
-
const planeY = uniforms.collisionPlaneY;
|
|
850
|
-
const bounce = uniforms.collisionBounce;
|
|
851
|
-
const friction = uniforms.collisionFriction;
|
|
852
|
-
const shouldDie = uniforms.collisionDie;
|
|
853
|
-
If2(position.y.lessThan(planeY), () => {
|
|
854
|
-
If2(shouldDie.greaterThan(0.5), () => {
|
|
855
|
-
lifetime.assign(float3(0));
|
|
856
|
-
position.y.assign(float3(-1e3));
|
|
857
|
-
}).Else(() => {
|
|
858
|
-
position.y.assign(planeY);
|
|
859
|
-
velocity.y.assign(velocity.y.abs().mul(bounce));
|
|
860
|
-
velocity.x.mulAssign(friction);
|
|
861
|
-
velocity.z.mulAssign(friction);
|
|
862
|
-
});
|
|
863
|
-
});
|
|
864
|
-
});
|
|
865
|
-
const idx = float3(instanceIndex3);
|
|
866
|
-
const rotSpeedX = mix2(
|
|
867
|
-
uniforms.rotationSpeedMinX,
|
|
868
|
-
uniforms.rotationSpeedMaxX,
|
|
869
|
-
hash2(idx.add(8888))
|
|
870
|
-
);
|
|
871
|
-
const rotSpeedY = mix2(
|
|
872
|
-
uniforms.rotationSpeedMinY,
|
|
873
|
-
uniforms.rotationSpeedMaxY,
|
|
874
|
-
hash2(idx.add(9999))
|
|
875
|
-
);
|
|
876
|
-
const rotSpeedZ = mix2(
|
|
877
|
-
uniforms.rotationSpeedMinZ,
|
|
878
|
-
uniforms.rotationSpeedMaxZ,
|
|
879
|
-
hash2(idx.add(10101))
|
|
880
|
-
);
|
|
881
|
-
const rotSpeedCurveSample = texture(
|
|
882
|
-
curveTexture,
|
|
883
|
-
vec2(progress, float3(0.5))
|
|
884
|
-
).w;
|
|
885
|
-
const rotSpeedMultiplier = uniforms.rotationSpeedCurveEnabled.greaterThan(0.5).select(rotSpeedCurveSample, float3(1));
|
|
886
|
-
particleRotation.addAssign(
|
|
887
|
-
vec33(rotSpeedX, rotSpeedY, rotSpeedZ).mul(uniforms.deltaTime).mul(rotSpeedMultiplier)
|
|
888
|
-
);
|
|
969
|
+
}
|
|
889
970
|
lifetime.subAssign(fadeRate.mul(uniforms.deltaTime));
|
|
890
971
|
If2(lifetime.lessThanEqual(0), () => {
|
|
891
972
|
lifetime.assign(float3(0));
|
|
@@ -920,6 +1001,7 @@ import {
|
|
|
920
1001
|
clamp
|
|
921
1002
|
} from "three/tsl";
|
|
922
1003
|
var createParticleMaterial = (storage, uniforms, curveTexture, options) => {
|
|
1004
|
+
var _a, _b, _c, _d;
|
|
923
1005
|
const {
|
|
924
1006
|
alphaMap,
|
|
925
1007
|
flipbook,
|
|
@@ -937,13 +1019,13 @@ var createParticleMaterial = (storage, uniforms, curveTexture, options) => {
|
|
|
937
1019
|
} = options;
|
|
938
1020
|
const lifetime = storage.lifetimes.element(instanceIndex4);
|
|
939
1021
|
const particleSize = storage.particleSizes.element(instanceIndex4);
|
|
940
|
-
const particleRotation = storage.particleRotations.element(instanceIndex4);
|
|
941
|
-
const pColorStart = storage.particleColorStarts.element(instanceIndex4);
|
|
942
|
-
const pColorEnd = storage.particleColorEnds.element(instanceIndex4);
|
|
1022
|
+
const particleRotation = (_b = (_a = storage.particleRotations) == null ? void 0 : _a.element(instanceIndex4)) != null ? _b : vec34(0, 0, 0);
|
|
1023
|
+
const pColorStart = (_c = storage.particleColorStarts) == null ? void 0 : _c.element(instanceIndex4);
|
|
1024
|
+
const pColorEnd = (_d = storage.particleColorEnds) == null ? void 0 : _d.element(instanceIndex4);
|
|
943
1025
|
const particlePos = storage.positions.element(instanceIndex4);
|
|
944
1026
|
const particleVel = storage.velocities.element(instanceIndex4);
|
|
945
1027
|
const progress = float4(1).sub(lifetime);
|
|
946
|
-
const currentColor = mix3(pColorStart, pColorEnd, progress);
|
|
1028
|
+
const currentColor = pColorStart && pColorEnd ? mix3(pColorStart, pColorEnd, progress) : mix3(uniforms.colorStart0, uniforms.colorEnd0, progress);
|
|
947
1029
|
const intensifiedColor = currentColor.mul(uniforms.intensity);
|
|
948
1030
|
const curveSample = texture2(curveTexture, vec22(progress, float4(0.5)));
|
|
949
1031
|
const sizeMultiplier = uniforms.fadeSizeCurveEnabled.greaterThan(0.5).select(
|
|
@@ -959,9 +1041,7 @@ var createParticleMaterial = (storage, uniforms, curveTexture, options) => {
|
|
|
959
1041
|
const rows = float4(flipbook.rows || 1);
|
|
960
1042
|
const columns = float4(flipbook.columns || 1);
|
|
961
1043
|
const totalFrames = rows.mul(columns);
|
|
962
|
-
const frameIndex = floor2(
|
|
963
|
-
progress.mul(totalFrames).min(totalFrames.sub(1))
|
|
964
|
-
);
|
|
1044
|
+
const frameIndex = floor2(progress.mul(totalFrames).min(totalFrames.sub(1)));
|
|
965
1045
|
const col = mod(frameIndex, columns);
|
|
966
1046
|
const row = floor2(frameIndex.div(columns));
|
|
967
1047
|
const scaledUV = uv().div(vec22(columns, rows));
|
|
@@ -991,20 +1071,19 @@ var createParticleMaterial = (storage, uniforms, curveTexture, options) => {
|
|
|
991
1071
|
}
|
|
992
1072
|
}
|
|
993
1073
|
const baseOpacity = opacityMultiplier.mul(shapeMask).mul(lifetime.greaterThan(1e-3).select(float4(1), float4(0)));
|
|
994
|
-
const particleData = {
|
|
1074
|
+
const particleData = __spreadProps(__spreadValues(__spreadValues({
|
|
995
1075
|
progress,
|
|
996
1076
|
lifetime,
|
|
997
1077
|
position: particlePos,
|
|
998
1078
|
velocity: particleVel,
|
|
999
1079
|
size: particleSize,
|
|
1000
|
-
rotation: particleRotation
|
|
1001
|
-
|
|
1002
|
-
colorEnd: pColorEnd,
|
|
1080
|
+
rotation: particleRotation
|
|
1081
|
+
}, pColorStart && { colorStart: pColorStart }), pColorEnd && { colorEnd: pColorEnd }), {
|
|
1003
1082
|
color: currentColor,
|
|
1004
1083
|
intensifiedColor,
|
|
1005
1084
|
shapeMask,
|
|
1006
1085
|
index: instanceIndex4
|
|
1007
|
-
};
|
|
1086
|
+
});
|
|
1008
1087
|
let finalOpacity = opacityNode ? baseOpacity.mul(
|
|
1009
1088
|
typeof opacityNode === "function" ? opacityNode(particleData) : opacityNode
|
|
1010
1089
|
) : baseOpacity;
|
|
@@ -1071,10 +1150,7 @@ var createParticleMaterial = (storage, uniforms, curveTexture, options) => {
|
|
|
1071
1150
|
const velDir = particleVel.div(velLen).mul(axisSign);
|
|
1072
1151
|
const localAxis = axisIndex.lessThan(0.5).select(
|
|
1073
1152
|
vec34(1, 0, 0),
|
|
1074
|
-
axisIndex.lessThan(1.5).select(
|
|
1075
|
-
vec34(0, 1, 0),
|
|
1076
|
-
vec34(0, 0, 1)
|
|
1077
|
-
)
|
|
1153
|
+
axisIndex.lessThan(1.5).select(vec34(0, 1, 0), vec34(0, 0, 1))
|
|
1078
1154
|
);
|
|
1079
1155
|
const dotProduct = localAxis.dot(velDir).clamp(-1, 1);
|
|
1080
1156
|
const crossProduct = localAxis.cross(velDir);
|
|
@@ -1092,10 +1168,7 @@ var createParticleMaterial = (storage, uniforms, curveTexture, options) => {
|
|
|
1092
1168
|
const kCrossV = rotAxis.cross(v);
|
|
1093
1169
|
const rotatedByAxis = needsRotation.select(
|
|
1094
1170
|
v.mul(cosAngleVal).add(kCrossV.mul(sinAngleVal)).add(rotAxis.mul(kDotV.mul(oneMinusCos))),
|
|
1095
|
-
dotProduct.lessThan(-0.99).select(
|
|
1096
|
-
v.negate(),
|
|
1097
|
-
v
|
|
1098
|
-
)
|
|
1171
|
+
dotProduct.lessThan(-0.99).select(v.negate(), v)
|
|
1099
1172
|
);
|
|
1100
1173
|
rotatedPos = rotatedByAxis;
|
|
1101
1174
|
} else {
|
|
@@ -1178,6 +1251,7 @@ export {
|
|
|
1178
1251
|
bakeCurveToArray,
|
|
1179
1252
|
coreStore,
|
|
1180
1253
|
createCombinedCurveTexture,
|
|
1254
|
+
createDefaultCurveTexture,
|
|
1181
1255
|
createInitCompute,
|
|
1182
1256
|
createParticleMaterial,
|
|
1183
1257
|
createSpawnCompute,
|
|
@@ -1186,6 +1260,7 @@ export {
|
|
|
1186
1260
|
evaluateBezierSegment,
|
|
1187
1261
|
hexToRgb,
|
|
1188
1262
|
lifetimeToFadeRate,
|
|
1263
|
+
loadCurveTextureFromPath,
|
|
1189
1264
|
sampleCurveAtX,
|
|
1190
1265
|
selectColor,
|
|
1191
1266
|
toRange,
|