@aura3d/rendering 2.0.4 → 3.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.
- package/dist/AtmosphereWetness.d.ts +61 -0
- package/dist/AtmosphereWetness.d.ts.map +1 -0
- package/dist/AtmosphereWetness.js +99 -0
- package/dist/AtmosphereWetness.js.map +1 -0
- package/dist/ClusteredForwardLighting.d.ts +25 -1
- package/dist/ClusteredForwardLighting.d.ts.map +1 -1
- package/dist/ClusteredForwardLighting.js +80 -3
- package/dist/ClusteredForwardLighting.js.map +1 -1
- package/dist/DayNightSky.d.ts +63 -0
- package/dist/DayNightSky.d.ts.map +1 -0
- package/dist/DayNightSky.js +156 -0
- package/dist/DayNightSky.js.map +1 -0
- package/dist/EnvironmentPresetPack.d.ts +30 -0
- package/dist/EnvironmentPresetPack.d.ts.map +1 -0
- package/dist/EnvironmentPresetPack.js +49 -0
- package/dist/EnvironmentPresetPack.js.map +1 -0
- package/dist/ForwardPass.d.ts +65 -0
- package/dist/ForwardPass.d.ts.map +1 -1
- package/dist/ForwardPass.js +196 -2
- package/dist/ForwardPass.js.map +1 -1
- package/dist/InstancedPBRMaterial.d.ts +1 -0
- package/dist/InstancedPBRMaterial.d.ts.map +1 -1
- package/dist/InstancedPBRMaterial.js +3 -0
- package/dist/InstancedPBRMaterial.js.map +1 -1
- package/dist/InstancingDiagnostics.d.ts +69 -0
- package/dist/InstancingDiagnostics.d.ts.map +1 -0
- package/dist/InstancingDiagnostics.js +98 -0
- package/dist/InstancingDiagnostics.js.map +1 -0
- package/dist/LightingRig.d.ts +19 -1
- package/dist/LightingRig.d.ts.map +1 -1
- package/dist/LightingRig.js +61 -1
- package/dist/LightingRig.js.map +1 -1
- package/dist/MeshConsolidation.d.ts +12 -0
- package/dist/MeshConsolidation.d.ts.map +1 -1
- package/dist/MeshConsolidation.js +28 -0
- package/dist/MeshConsolidation.js.map +1 -1
- package/dist/MorphTargetPlan.d.ts +29 -2
- package/dist/MorphTargetPlan.d.ts.map +1 -1
- package/dist/MorphTargetPlan.js +23 -4
- package/dist/MorphTargetPlan.js.map +1 -1
- package/dist/NormalMappedPBRMaterial.d.ts.map +1 -1
- package/dist/NormalMappedPBRMaterial.js +22 -2
- package/dist/NormalMappedPBRMaterial.js.map +1 -1
- package/dist/OceanSurface.d.ts +75 -0
- package/dist/OceanSurface.d.ts.map +1 -1
- package/dist/OceanSurface.js +211 -0
- package/dist/OceanSurface.js.map +1 -1
- package/dist/PBRMaterial.d.ts +1 -0
- package/dist/PBRMaterial.d.ts.map +1 -1
- package/dist/PBRMaterial.js +23 -2
- package/dist/PBRMaterial.js.map +1 -1
- package/dist/PbrReference.d.ts +48 -0
- package/dist/PbrReference.d.ts.map +1 -1
- package/dist/PbrReference.js +126 -0
- package/dist/PbrReference.js.map +1 -1
- package/dist/PlanarReflection.d.ts +221 -0
- package/dist/PlanarReflection.d.ts.map +1 -0
- package/dist/PlanarReflection.js +541 -0
- package/dist/PlanarReflection.js.map +1 -0
- package/dist/PostProcessPass.d.ts +13 -0
- package/dist/PostProcessPass.d.ts.map +1 -1
- package/dist/PostProcessPass.js +15 -4
- package/dist/PostProcessPass.js.map +1 -1
- package/dist/ReflectionSurfaces.d.ts +14 -0
- package/dist/ReflectionSurfaces.d.ts.map +1 -1
- package/dist/ReflectionSurfaces.js +93 -0
- package/dist/ReflectionSurfaces.js.map +1 -1
- package/dist/RenderDevice.d.ts +87 -0
- package/dist/RenderDevice.d.ts.map +1 -1
- package/dist/RenderDevice.js +85 -0
- package/dist/RenderDevice.js.map +1 -1
- package/dist/Renderer.js +3 -1
- package/dist/Renderer.js.map +1 -1
- package/dist/RendererPostprocessPlan.d.ts +48 -0
- package/dist/RendererPostprocessPlan.d.ts.map +1 -1
- package/dist/RendererPostprocessPlan.js +101 -11
- package/dist/RendererPostprocessPlan.js.map +1 -1
- package/dist/RendererVisualPipelineReport.d.ts.map +1 -1
- package/dist/RendererVisualPipelineReport.js +3 -1
- package/dist/RendererVisualPipelineReport.js.map +1 -1
- package/dist/Sampler.d.ts +29 -0
- package/dist/Sampler.d.ts.map +1 -1
- package/dist/Sampler.js +47 -0
- package/dist/Sampler.js.map +1 -1
- package/dist/SdfText.d.ts +250 -0
- package/dist/SdfText.d.ts.map +1 -0
- package/dist/SdfText.js +551 -0
- package/dist/SdfText.js.map +1 -0
- package/dist/ShaderChunks.d.ts.map +1 -1
- package/dist/ShaderChunks.js +30 -12
- package/dist/ShaderChunks.js.map +1 -1
- package/dist/ShaderLibrary.d.ts.map +1 -1
- package/dist/ShaderLibrary.js +478 -34
- package/dist/ShaderLibrary.js.map +1 -1
- package/dist/ShaderLibraryCore.d.ts.map +1 -1
- package/dist/ShaderLibraryCore.js +70 -12
- package/dist/ShaderLibraryCore.js.map +1 -1
- package/dist/ShadowMap.d.ts +24 -0
- package/dist/ShadowMap.d.ts.map +1 -1
- package/dist/ShadowMap.js +43 -0
- package/dist/ShadowMap.js.map +1 -1
- package/dist/SkinnedLitMaterial.d.ts.map +1 -1
- package/dist/SkinnedLitMaterial.js +2 -0
- package/dist/SkinnedLitMaterial.js.map +1 -1
- package/dist/SkinningBounds.d.ts +10 -0
- package/dist/SkinningBounds.d.ts.map +1 -1
- package/dist/SkinningBounds.js +27 -0
- package/dist/SkinningBounds.js.map +1 -1
- package/dist/SpriteFlipbook.d.ts +60 -0
- package/dist/SpriteFlipbook.d.ts.map +1 -0
- package/dist/SpriteFlipbook.js +128 -0
- package/dist/SpriteFlipbook.js.map +1 -0
- package/dist/TerrainTiles.d.ts +120 -0
- package/dist/TerrainTiles.d.ts.map +1 -0
- package/dist/TerrainTiles.js +221 -0
- package/dist/TerrainTiles.js.map +1 -0
- package/dist/TexturedPBRMaterial.d.ts +1 -0
- package/dist/TexturedPBRMaterial.d.ts.map +1 -1
- package/dist/TexturedPBRMaterial.js +23 -2
- package/dist/TexturedPBRMaterial.js.map +1 -1
- package/dist/TexturedUnlitMaterial.d.ts.map +1 -1
- package/dist/TexturedUnlitMaterial.js +20 -2
- package/dist/TexturedUnlitMaterial.js.map +1 -1
- package/dist/UnlitMaterial.d.ts.map +1 -1
- package/dist/UnlitMaterial.js +20 -2
- package/dist/UnlitMaterial.js.map +1 -1
- package/dist/VertexFormat.d.ts +6 -0
- package/dist/VertexFormat.d.ts.map +1 -1
- package/dist/VertexFormat.js +12 -0
- package/dist/VertexFormat.js.map +1 -1
- package/dist/VolumetricFog.d.ts +55 -0
- package/dist/VolumetricFog.d.ts.map +1 -0
- package/dist/VolumetricFog.js +91 -0
- package/dist/VolumetricFog.js.map +1 -0
- package/dist/WaterSurface.d.ts +77 -0
- package/dist/WaterSurface.d.ts.map +1 -0
- package/dist/WaterSurface.js +133 -0
- package/dist/WaterSurface.js.map +1 -0
- package/dist/WebGL2Device.d.ts +58 -0
- package/dist/WebGL2Device.d.ts.map +1 -1
- package/dist/WebGL2Device.js +536 -17
- package/dist/WebGL2Device.js.map +1 -1
- package/dist/WebGPUDevice.d.ts +50 -0
- package/dist/WebGPUDevice.d.ts.map +1 -1
- package/dist/WebGPUDevice.js +504 -10
- package/dist/WebGPUDevice.js.map +1 -1
- package/dist/WebGPUSkinningLimits.d.ts +40 -0
- package/dist/WebGPUSkinningLimits.d.ts.map +1 -1
- package/dist/WebGPUSkinningLimits.js +32 -0
- package/dist/WebGPUSkinningLimits.js.map +1 -1
- package/dist/effects/CollisionModule.d.ts +9 -0
- package/dist/effects/CollisionModule.d.ts.map +1 -1
- package/dist/effects/CollisionModule.js +10 -0
- package/dist/effects/CollisionModule.js.map +1 -1
- package/dist/effects/ColorModule.d.ts +7 -0
- package/dist/effects/ColorModule.d.ts.map +1 -1
- package/dist/effects/ColorModule.js +23 -0
- package/dist/effects/ColorModule.js.map +1 -1
- package/dist/effects/ForceModule.d.ts +35 -0
- package/dist/effects/ForceModule.d.ts.map +1 -1
- package/dist/effects/ForceModule.js +49 -0
- package/dist/effects/ForceModule.js.map +1 -1
- package/dist/effects/GPUParticleBackend.d.ts +186 -0
- package/dist/effects/GPUParticleBackend.d.ts.map +1 -1
- package/dist/effects/GPUParticleBackend.js +692 -1
- package/dist/effects/GPUParticleBackend.js.map +1 -1
- package/dist/effects/HeightfieldModule.d.ts +58 -0
- package/dist/effects/HeightfieldModule.d.ts.map +1 -0
- package/dist/effects/HeightfieldModule.js +129 -0
- package/dist/effects/HeightfieldModule.js.map +1 -0
- package/dist/effects/LightingModule.d.ts +34 -0
- package/dist/effects/LightingModule.d.ts.map +1 -0
- package/dist/effects/LightingModule.js +54 -0
- package/dist/effects/LightingModule.js.map +1 -0
- package/dist/effects/ParticleDiagnostics.d.ts +8 -0
- package/dist/effects/ParticleDiagnostics.d.ts.map +1 -1
- package/dist/effects/ParticleDiagnostics.js +3 -0
- package/dist/effects/ParticleDiagnostics.js.map +1 -1
- package/dist/effects/ParticleModule.d.ts +8 -0
- package/dist/effects/ParticleModule.d.ts.map +1 -1
- package/dist/effects/ParticleModule.js.map +1 -1
- package/dist/effects/ParticleRenderer.d.ts +20 -0
- package/dist/effects/ParticleRenderer.d.ts.map +1 -1
- package/dist/effects/ParticleRenderer.js +57 -7
- package/dist/effects/ParticleRenderer.js.map +1 -1
- package/dist/effects/ParticleSystem.d.ts +45 -1
- package/dist/effects/ParticleSystem.d.ts.map +1 -1
- package/dist/effects/ParticleSystem.js +373 -4
- package/dist/effects/ParticleSystem.js.map +1 -1
- package/dist/effects/SizeModule.d.ts +4 -0
- package/dist/effects/SizeModule.d.ts.map +1 -1
- package/dist/effects/SizeModule.js +16 -0
- package/dist/effects/SizeModule.js.map +1 -1
- package/dist/effects/SubEmitterModule.d.ts +61 -0
- package/dist/effects/SubEmitterModule.d.ts.map +1 -0
- package/dist/effects/SubEmitterModule.js +119 -0
- package/dist/effects/SubEmitterModule.js.map +1 -0
- package/dist/effects/TrailModule.d.ts +38 -0
- package/dist/effects/TrailModule.d.ts.map +1 -1
- package/dist/effects/TrailModule.js +95 -2
- package/dist/effects/TrailModule.js.map +1 -1
- package/dist/effects/TurbulenceModule.d.ts +43 -0
- package/dist/effects/TurbulenceModule.d.ts.map +1 -0
- package/dist/effects/TurbulenceModule.js +130 -0
- package/dist/effects/TurbulenceModule.js.map +1 -0
- package/dist/effects/index.d.ts +4 -0
- package/dist/effects/index.d.ts.map +1 -1
- package/dist/effects/index.js +4 -0
- package/dist/effects/index.js.map +1 -1
- package/dist/index.d.ts +57 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -10
- package/dist/index.js.map +1 -1
- package/dist/materials/MaterialExtensions.js +3 -3
- package/dist/materials/MaterialExtensions.js.map +1 -1
- package/dist/postprocess/NativeBloomPyramid.d.ts +28 -0
- package/dist/postprocess/NativeBloomPyramid.d.ts.map +1 -0
- package/dist/postprocess/NativeBloomPyramid.js +54 -0
- package/dist/postprocess/NativeBloomPyramid.js.map +1 -0
- package/dist/postprocess/NativeLdrEffectLuts.d.ts +10 -1
- package/dist/postprocess/NativeLdrEffectLuts.d.ts.map +1 -1
- package/dist/postprocess/NativeLdrEffectLuts.js +19 -2
- package/dist/postprocess/NativeLdrEffectLuts.js.map +1 -1
- package/dist/postprocess/index.d.ts +2 -0
- package/dist/postprocess/index.d.ts.map +1 -1
- package/dist/postprocess/index.js +1 -0
- package/dist/postprocess/index.js.map +1 -1
- package/dist/production-runtime/PBRHDRPipeline.d.ts +22 -0
- package/dist/production-runtime/PBRHDRPipeline.d.ts.map +1 -1
- package/dist/production-runtime/PBRHDRPipeline.js +125 -0
- package/dist/production-runtime/PBRHDRPipeline.js.map +1 -1
- package/dist/production-runtime/ProductionWebGPURenderer.d.ts +52 -0
- package/dist/production-runtime/ProductionWebGPURenderer.d.ts.map +1 -1
- package/dist/production-runtime/ProductionWebGPURenderer.js +101 -0
- package/dist/production-runtime/ProductionWebGPURenderer.js.map +1 -1
- package/dist/production-runtime/index.d.ts +5 -4
- package/dist/production-runtime/index.d.ts.map +1 -1
- package/dist/production-runtime/index.js +3 -2
- package/dist/production-runtime/index.js.map +1 -1
- package/dist/production-runtime/passes/DepthPrepass.d.ts +26 -1
- package/dist/production-runtime/passes/DepthPrepass.d.ts.map +1 -1
- package/dist/production-runtime/passes/DepthPrepass.js +49 -2
- package/dist/production-runtime/passes/DepthPrepass.js.map +1 -1
- package/dist/production-runtime/passes/FramegraphTopology.d.ts +13 -0
- package/dist/production-runtime/passes/FramegraphTopology.d.ts.map +1 -0
- package/dist/production-runtime/passes/FramegraphTopology.js +59 -0
- package/dist/production-runtime/passes/FramegraphTopology.js.map +1 -0
- package/dist/production-runtime/passes/OpaquePass.d.ts +24 -1
- package/dist/production-runtime/passes/OpaquePass.d.ts.map +1 -1
- package/dist/production-runtime/passes/OpaquePass.js +41 -2
- package/dist/production-runtime/passes/OpaquePass.js.map +1 -1
- package/dist/production-runtime/passes/ShadowPass.d.ts +24 -1
- package/dist/production-runtime/passes/ShadowPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/ShadowPass.js +43 -2
- package/dist/production-runtime/passes/ShadowPass.js.map +1 -1
- package/dist/production-runtime/passes/SkyboxPass.d.ts +21 -1
- package/dist/production-runtime/passes/SkyboxPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/SkyboxPass.js +39 -2
- package/dist/production-runtime/passes/SkyboxPass.js.map +1 -1
- package/dist/production-runtime/passes/ToneMappingPass.d.ts +29 -1
- package/dist/production-runtime/passes/ToneMappingPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/ToneMappingPass.js +53 -2
- package/dist/production-runtime/passes/ToneMappingPass.js.map +1 -1
- package/dist/production-runtime/passes/TransparentPass.d.ts +24 -1
- package/dist/production-runtime/passes/TransparentPass.d.ts.map +1 -1
- package/dist/production-runtime/passes/TransparentPass.js +42 -2
- package/dist/production-runtime/passes/TransparentPass.js.map +1 -1
- package/dist/shadows/CascadeHysteresis.d.ts +67 -0
- package/dist/shadows/CascadeHysteresis.d.ts.map +1 -0
- package/dist/shadows/CascadeHysteresis.js +104 -0
- package/dist/shadows/CascadeHysteresis.js.map +1 -0
- package/dist/shadows/ContactShadows.d.ts +52 -0
- package/dist/shadows/ContactShadows.d.ts.map +1 -1
- package/dist/shadows/ContactShadows.js +122 -0
- package/dist/shadows/ContactShadows.js.map +1 -1
- package/dist/shadows/SpotShadowMaps.d.ts +77 -0
- package/dist/shadows/SpotShadowMaps.d.ts.map +1 -0
- package/dist/shadows/SpotShadowMaps.js +106 -0
- package/dist/shadows/SpotShadowMaps.js.map +1 -0
- package/dist/webgpu/WebGPUPostProcess.d.ts +2 -0
- package/dist/webgpu/WebGPUPostProcess.d.ts.map +1 -1
- package/dist/webgpu/WebGPUPostProcess.js +1 -0
- package/dist/webgpu/WebGPUPostProcess.js.map +1 -1
- package/dist/webgpu/WebGPUPostShaders.d.ts +78 -0
- package/dist/webgpu/WebGPUPostShaders.d.ts.map +1 -0
- package/dist/webgpu/WebGPUPostShaders.js +262 -0
- package/dist/webgpu/WebGPUPostShaders.js.map +1 -0
- package/package.json +3 -3
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Soft-particle depth fade: 0 when the particle is behind the scene surface,
|
|
3
|
+
* ramping to 1 one fadeDistance in front of it. Pure and shared so the probe
|
|
4
|
+
* can show the exact on/off delta the renderer applies.
|
|
5
|
+
*/
|
|
6
|
+
export function computeSoftParticleFade(sceneDepth, particleDepth, fadeDistance) {
|
|
7
|
+
if (!Number.isFinite(sceneDepth) || !Number.isFinite(particleDepth) || !Number.isFinite(fadeDistance)) {
|
|
8
|
+
throw new RangeError("Soft-particle fade inputs must be finite numbers.");
|
|
9
|
+
}
|
|
10
|
+
if (fadeDistance <= 0) {
|
|
11
|
+
throw new RangeError("Soft-particle fadeDistance must be positive.");
|
|
12
|
+
}
|
|
13
|
+
const t = (sceneDepth - particleDepth) / fadeDistance;
|
|
14
|
+
return Math.min(1, Math.max(0, t));
|
|
15
|
+
}
|
|
16
|
+
/** WGSL snippet consumers can drop into their own particle render shaders. */
|
|
17
|
+
export const SOFT_PARTICLE_WGSL = `
|
|
18
|
+
fn auraSoftParticleFade(sceneDepth: f32, particleDepth: f32, fadeDistance: f32) -> f32 {
|
|
19
|
+
return clamp((sceneDepth - particleDepth) / max(fadeDistance, 1e-6), 0.0, 1.0);
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
1
22
|
export class ParticleRenderer {
|
|
2
23
|
lastBatch = {
|
|
3
24
|
sprites: [],
|
|
@@ -6,15 +27,36 @@ export class ParticleRenderer {
|
|
|
6
27
|
bounds: null,
|
|
7
28
|
};
|
|
8
29
|
buildBatch(particles, options = {}) {
|
|
30
|
+
const soft = options.softParticles;
|
|
31
|
+
if (soft?.enabled) {
|
|
32
|
+
validateSoftParticles(soft);
|
|
33
|
+
}
|
|
9
34
|
const sprites = particles
|
|
10
35
|
.filter((particle) => particle.alive)
|
|
11
|
-
.map((particle) =>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
36
|
+
.map((particle) => {
|
|
37
|
+
let fade = 1;
|
|
38
|
+
let alpha = particle.color.a;
|
|
39
|
+
if (soft?.enabled) {
|
|
40
|
+
const sceneDepth = soft.sceneDepthAt(particle.position);
|
|
41
|
+
const particleDepth = soft.particleDepthAt(particle.position);
|
|
42
|
+
if (!Number.isFinite(particleDepth)) {
|
|
43
|
+
throw new RangeError("ParticleRenderer softParticles particleDepthAt must return a finite number.");
|
|
44
|
+
}
|
|
45
|
+
// Non-finite scene depth = the view ray hits no occluder: no attenuation.
|
|
46
|
+
fade = Number.isFinite(sceneDepth)
|
|
47
|
+
? computeSoftParticleFade(sceneDepth, particleDepth, soft.fadeDistance)
|
|
48
|
+
: 1;
|
|
49
|
+
alpha = particle.color.a * fade;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
id: particle.id,
|
|
53
|
+
position: { ...particle.position },
|
|
54
|
+
color: { ...particle.color, a: alpha },
|
|
55
|
+
size: particle.size,
|
|
56
|
+
rotation: particle.rotation,
|
|
57
|
+
fade,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
18
60
|
sortSprites(sprites, options);
|
|
19
61
|
this.lastBatch = {
|
|
20
62
|
sprites,
|
|
@@ -81,6 +123,14 @@ function computeBounds(sprites) {
|
|
|
81
123
|
}
|
|
82
124
|
return { min, max };
|
|
83
125
|
}
|
|
126
|
+
function validateSoftParticles(soft) {
|
|
127
|
+
if (!Number.isFinite(soft.fadeDistance) || soft.fadeDistance <= 0) {
|
|
128
|
+
throw new RangeError("ParticleRenderer softParticles fadeDistance must be a finite positive number.");
|
|
129
|
+
}
|
|
130
|
+
if (typeof soft.sceneDepthAt !== "function" || typeof soft.particleDepthAt !== "function") {
|
|
131
|
+
throw new RangeError("ParticleRenderer softParticles requires sceneDepthAt and particleDepthAt functions.");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
84
134
|
function distanceSquared(a, b) {
|
|
85
135
|
const x = a.x - b.x;
|
|
86
136
|
const y = a.y - b.y;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParticleRenderer.js","sourceRoot":"","sources":["../../../../../packages/rendering/src/effects/ParticleRenderer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ParticleRenderer.js","sourceRoot":"","sources":["../../../../../packages/rendering/src/effects/ParticleRenderer.ts"],"names":[],"mappings":"AAyCA;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,UAAkB,EAAE,aAAqB,EAAE,YAAoB;IACrG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACtG,MAAM,IAAI,UAAU,CAAC,mDAAmD,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,UAAU,CAAC,8CAA8C,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,UAAU,GAAG,aAAa,CAAC,GAAG,YAAY,CAAC;IACtD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACrC,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;CAIjC,CAAC;AAQF,MAAM,OAAO,gBAAgB;IACnB,SAAS,GAAwB;QACvC,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,CAAC;QACZ,aAAa,EAAE,CAAC;QAChB,MAAM,EAAE,IAAI;KACb,CAAC;IAEF,UAAU,CAAC,SAA8B,EAAE,UAAiC,EAAE;QAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC;QACnC,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC;YAClB,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,OAAO,GAAG,SAAS;aACtB,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;aACpC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE;YAChB,IAAI,IAAI,GAAG,CAAC,CAAC;YACb,IAAI,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7B,IAAI,IAAI,EAAE,OAAO,EAAE,CAAC;gBAClB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBACxD,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBAC9D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;oBACpC,MAAM,IAAI,UAAU,CAAC,6EAA6E,CAAC,CAAC;gBACtG,CAAC;gBACD,0EAA0E;gBAC1E,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;oBAChC,CAAC,CAAC,uBAAuB,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,CAAC,YAAY,CAAC;oBACvE,CAAC,CAAC,CAAC,CAAC;gBACN,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC;YAClC,CAAC;YACD,OAAO;gBACL,EAAE,EAAE,QAAQ,CAAC,EAAE;gBACf,QAAQ,EAAE,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE;gBAClC,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE;gBACtC,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,IAAI;aACL,CAAC;QACJ,CAAC,CAAC,CAAC;QACL,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAE9B,IAAI,CAAC,SAAS,GAAG;YACf,OAAO;YACP,SAAS,EAAE,OAAO,CAAC,MAAM;YACzB,aAAa,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,iBAAiB;YAChF,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC;SAC/B,CAAC;QAEF,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,MAAsB,EAAE,MAA0B,EAAE,UAAiC,EAAE;QAC5F,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACzD,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC5B,MAAM,CAAC,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,YAAY;QACV,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC/C,GAAG,MAAM;gBACT,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE;gBAChC,KAAK,EAAE,EAAE,GAAG,MAAM,CAAC,KAAK,EAAE;aAC3B,CAAC,CAAC;YACH,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS;YACnC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,aAAa;YAC3C,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC9B,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE;gBACrC,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE;aACtC,CAAC,CAAC,CAAC,IAAI;SACT,CAAC;IACJ,CAAC;CACF;AAED,SAAS,WAAW,CAAC,OAAyB,EAAE,OAA8B;IAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC;IACpC,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO;IAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IACtC,IAAI,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,UAAU,CAAC,+EAA+E,CAAC,CAAC;IACxG,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,KAAK,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACpB,MAAM,aAAa,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAChG,IAAI,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,IAAI;YAAE,OAAO,aAAa,GAAG,SAAS,CAAC;QACrE,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,aAAa,CAAC,OAAkC;IACvD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,MAAM,GAAG,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC;IACtG,MAAM,GAAG,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC,EAAE,MAAM,CAAC,iBAAiB,EAAE,CAAC;IACtG,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACzF,MAAM,IAAI,UAAU,CAAC,kFAAkF,CAAC,CAAC;QAC3G,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,GAAG,GAAG,CAAC;QACjC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QACpD,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QACpD,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QACpD,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QACpD,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QACpD,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAyB;IACtD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,YAAY,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,UAAU,CAAC,+EAA+E,CAAC,CAAC;IACxG,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,UAAU,IAAI,OAAO,IAAI,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QAC1F,MAAM,IAAI,UAAU,CAAC,qFAAqF,CAAC,CAAC;IAC9G,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,CAAc,EAAE,CAAc;IACrD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,cAAc,CAAC,KAAkB;IACxC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Particle } from "./Particle.js";
|
|
2
2
|
import { ParticleEmitter } from "./ParticleEmitter.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type GPUParticleBackend, type GPUParticleEffectsInput } from "./GPUParticleBackend.js";
|
|
4
4
|
import { type ParticleModule } from "./ParticleModule.js";
|
|
5
5
|
export interface ParticleSystemStats {
|
|
6
6
|
liveCount: number;
|
|
@@ -12,7 +12,25 @@ export interface ParticleSystemStats {
|
|
|
12
12
|
uploadedBytes: number;
|
|
13
13
|
gpuSpawns: number;
|
|
14
14
|
gpuUpdates: number;
|
|
15
|
+
/** True once any spawn request (emitter or sub-emitter) exceeded capacity. Never a silent clamp. */
|
|
16
|
+
overBudget: boolean;
|
|
15
17
|
}
|
|
18
|
+
export interface CollectedGPUParticleEffects {
|
|
19
|
+
readonly effects: GPUParticleEffectsInput;
|
|
20
|
+
/**
|
|
21
|
+
* Modules whose per-frame hooks the compute kernel implements. The system
|
|
22
|
+
* skips their CPU update/afterIntegrate hooks on the GPU path (zero CPU
|
|
23
|
+
* per-particle work); every other module keeps its CPU hooks on both paths.
|
|
24
|
+
*/
|
|
25
|
+
readonly gpuCovered: ReadonlySet<ParticleModule>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Aggregate GPU-capable modules into one compute effects descriptor.
|
|
29
|
+
* Returns undefined when no module has a GPU implementation (legacy path).
|
|
30
|
+
* Overflow policy: the first N instances of a capped effect (3 planes,
|
|
31
|
+
* 2 sub-emitters) run on GPU; extras keep their exact CPU hooks.
|
|
32
|
+
*/
|
|
33
|
+
export declare function collectGPUParticleEffects(modules: readonly ParticleModule[]): CollectedGPUParticleEffects | undefined;
|
|
16
34
|
export interface ParticleSystemOptions {
|
|
17
35
|
maxParticles?: number;
|
|
18
36
|
emitters?: readonly ParticleEmitter[];
|
|
@@ -30,6 +48,11 @@ export declare class ParticleSystem {
|
|
|
30
48
|
private stats;
|
|
31
49
|
private gpuBackend?;
|
|
32
50
|
private preferGPU;
|
|
51
|
+
private lastSpawnRequests;
|
|
52
|
+
private lastIntegratedParticles;
|
|
53
|
+
private lastEffectsCovered;
|
|
54
|
+
/** GPU trail ring rows by particle id, fed back so history accumulates across calls. */
|
|
55
|
+
private trailRingById;
|
|
33
56
|
constructor(options?: ParticleSystemOptions);
|
|
34
57
|
addEmitter(emitter: ParticleEmitter): this;
|
|
35
58
|
addModule(module: ParticleModule): this;
|
|
@@ -45,6 +68,27 @@ export declare class ParticleSystem {
|
|
|
45
68
|
private initializeSpawnedOnGPU;
|
|
46
69
|
private integrate;
|
|
47
70
|
private integrateOnGPU;
|
|
71
|
+
/**
|
|
72
|
+
* Assemble the previous trail ring in live-particle order from per-id rows
|
|
73
|
+
* so compute history survives spawn/death reordering. Unknown ids start
|
|
74
|
+
* fresh (head = live position/age, older slots expired).
|
|
75
|
+
*/
|
|
76
|
+
private assembleTrailHistory;
|
|
77
|
+
private storeTrailRings;
|
|
78
|
+
/** CPU path: turn queued sub-emitter requests into live child particles. */
|
|
79
|
+
private drainSubEmitterSpawns;
|
|
80
|
+
/**
|
|
81
|
+
* GPU path: covered sub-emitters report per-particle spawn requests from
|
|
82
|
+
* the compute kernel; uncovered ones queue CPU requests as usual. Children
|
|
83
|
+
* initialize through the GPU spawn pipeline when the backend offers one.
|
|
84
|
+
*/
|
|
85
|
+
private drainSubEmitterSpawnsOnGPU;
|
|
86
|
+
/**
|
|
87
|
+
* Decode one covered sub-emitter slot from the packed kernel flags
|
|
88
|
+
* (low 16 bits = slot 0, high 16 bits = slot 1).
|
|
89
|
+
*/
|
|
90
|
+
private requestsForSubEmitter;
|
|
91
|
+
private applyGPUTrailHistories;
|
|
48
92
|
private compactDeadParticles;
|
|
49
93
|
private createContext;
|
|
50
94
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ParticleSystem.d.ts","sourceRoot":"","sources":["../../../../../packages/rendering/src/effects/ParticleSystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ParticleSystem.d.ts","sourceRoot":"","sources":["../../../../../packages/rendering/src/effects/ParticleSystem.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,139 @@
|
|
|
1
1
|
import { addScaledVector3, normalizedParticleAge, setVector3, } from "./Particle.js";
|
|
2
2
|
import { ParticleEmitter } from "./ParticleEmitter.js";
|
|
3
|
+
import { GPU_PARTICLE_CURVE_STOPS, } from "./GPUParticleBackend.js";
|
|
3
4
|
import { applyParticleModules } from "./ParticleModule.js";
|
|
5
|
+
import { CollisionModule } from "./CollisionModule.js";
|
|
6
|
+
import { encodeColorGradientLUT, ColorModule } from "./ColorModule.js";
|
|
7
|
+
import { encodeSizeCurveLUT, SizeModule } from "./SizeModule.js";
|
|
8
|
+
import { WindModule } from "./ForceModule.js";
|
|
9
|
+
import { TurbulenceModule } from "./TurbulenceModule.js";
|
|
10
|
+
import { HeightfieldModule } from "./HeightfieldModule.js";
|
|
11
|
+
import { LightingModule } from "./LightingModule.js";
|
|
12
|
+
import { SubEmitterModule } from "./SubEmitterModule.js";
|
|
13
|
+
import { decodeTrailRingBuffer, TrailModule } from "./TrailModule.js";
|
|
14
|
+
/**
|
|
15
|
+
* Aggregate GPU-capable modules into one compute effects descriptor.
|
|
16
|
+
* Returns undefined when no module has a GPU implementation (legacy path).
|
|
17
|
+
* Overflow policy: the first N instances of a capped effect (3 planes,
|
|
18
|
+
* 2 sub-emitters) run on GPU; extras keep their exact CPU hooks.
|
|
19
|
+
*/
|
|
20
|
+
export function collectGPUParticleEffects(modules) {
|
|
21
|
+
const covered = new Set();
|
|
22
|
+
let wind;
|
|
23
|
+
let turbulence;
|
|
24
|
+
let heightfield;
|
|
25
|
+
let lighting;
|
|
26
|
+
let lifeCurves;
|
|
27
|
+
let trailPointsPerParticle;
|
|
28
|
+
const planes = [];
|
|
29
|
+
const subEmitters = [];
|
|
30
|
+
let colorModule;
|
|
31
|
+
let sizeModule;
|
|
32
|
+
for (const module of modules) {
|
|
33
|
+
if (module instanceof WindModule && !wind) {
|
|
34
|
+
const encoded = module.toGPUWind();
|
|
35
|
+
wind = {
|
|
36
|
+
direction: encoded.direction,
|
|
37
|
+
strength: encoded.strength,
|
|
38
|
+
gustAmplitude: encoded.gustAmplitude,
|
|
39
|
+
gustDirection: encoded.gustDirection,
|
|
40
|
+
gustFrequency: encoded.gustFrequency,
|
|
41
|
+
gustSpeed: encoded.gustSpeed,
|
|
42
|
+
};
|
|
43
|
+
covered.add(module);
|
|
44
|
+
}
|
|
45
|
+
else if (module instanceof TurbulenceModule && !turbulence) {
|
|
46
|
+
const encoded = module.toGPUTurbulence();
|
|
47
|
+
turbulence = {
|
|
48
|
+
strength: encoded.strength,
|
|
49
|
+
scale: encoded.scale,
|
|
50
|
+
flowSpeed: encoded.flowSpeed,
|
|
51
|
+
lut: module.getLUT(),
|
|
52
|
+
lutResolution: encoded.lutResolution,
|
|
53
|
+
};
|
|
54
|
+
covered.add(module);
|
|
55
|
+
}
|
|
56
|
+
else if (module instanceof CollisionModule && planes.length < 3) {
|
|
57
|
+
const encoded = module.toGPUPlane();
|
|
58
|
+
planes.push({
|
|
59
|
+
normal: encoded.normal,
|
|
60
|
+
constant: encoded.constant,
|
|
61
|
+
restitution: encoded.restitution,
|
|
62
|
+
killOnContact: encoded.killOnContact,
|
|
63
|
+
});
|
|
64
|
+
covered.add(module);
|
|
65
|
+
}
|
|
66
|
+
else if (module instanceof HeightfieldModule && !heightfield) {
|
|
67
|
+
const encoded = module.toGPUHeightfield();
|
|
68
|
+
heightfield = {
|
|
69
|
+
originX: encoded.originX,
|
|
70
|
+
originZ: encoded.originZ,
|
|
71
|
+
cellSize: encoded.cellSize,
|
|
72
|
+
columns: encoded.columns,
|
|
73
|
+
rows: encoded.rows,
|
|
74
|
+
heights: module.sampler.heights,
|
|
75
|
+
restitution: encoded.restitution,
|
|
76
|
+
killOnContact: encoded.killOnContact,
|
|
77
|
+
};
|
|
78
|
+
covered.add(module);
|
|
79
|
+
}
|
|
80
|
+
else if (module instanceof ColorModule && !colorModule) {
|
|
81
|
+
colorModule = module;
|
|
82
|
+
}
|
|
83
|
+
else if (module instanceof SizeModule && !sizeModule) {
|
|
84
|
+
sizeModule = module;
|
|
85
|
+
}
|
|
86
|
+
else if (module instanceof LightingModule && !lighting) {
|
|
87
|
+
const encoded = module.toGPULighting();
|
|
88
|
+
lighting = {
|
|
89
|
+
ambient: encoded.ambient,
|
|
90
|
+
keyDirection: encoded.keyDirection,
|
|
91
|
+
diffuseStrength: encoded.diffuseStrength,
|
|
92
|
+
};
|
|
93
|
+
covered.add(module);
|
|
94
|
+
}
|
|
95
|
+
else if (module instanceof SubEmitterModule && subEmitters.length < 2) {
|
|
96
|
+
const encoded = module.toGPUSubEmitter();
|
|
97
|
+
subEmitters.push({
|
|
98
|
+
triggerAge: encoded.triggerAge,
|
|
99
|
+
chance: encoded.chance,
|
|
100
|
+
childCount: encoded.childCount,
|
|
101
|
+
});
|
|
102
|
+
covered.add(module);
|
|
103
|
+
}
|
|
104
|
+
else if (module instanceof TrailModule && trailPointsPerParticle === undefined) {
|
|
105
|
+
trailPointsPerParticle = Math.min(module.maxPoints, 8);
|
|
106
|
+
covered.add(module);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (colorModule || sizeModule) {
|
|
110
|
+
lifeCurves = {
|
|
111
|
+
stops: GPU_PARTICLE_CURVE_STOPS,
|
|
112
|
+
...(colorModule ? { colors: encodeColorGradientLUT(colorModule.gradient, GPU_PARTICLE_CURVE_STOPS) } : {}),
|
|
113
|
+
...(sizeModule ? { sizes: encodeSizeCurveLUT(sizeModule.curve, GPU_PARTICLE_CURVE_STOPS) } : {}),
|
|
114
|
+
};
|
|
115
|
+
if (colorModule)
|
|
116
|
+
covered.add(colorModule);
|
|
117
|
+
if (sizeModule)
|
|
118
|
+
covered.add(sizeModule);
|
|
119
|
+
}
|
|
120
|
+
if (covered.size === 0) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
effects: {
|
|
125
|
+
wind,
|
|
126
|
+
turbulence,
|
|
127
|
+
planes,
|
|
128
|
+
heightfield,
|
|
129
|
+
subEmitters,
|
|
130
|
+
lifeCurves,
|
|
131
|
+
lighting,
|
|
132
|
+
trailPointsPerParticle,
|
|
133
|
+
},
|
|
134
|
+
gpuCovered: covered,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
4
137
|
export class ParticleSystem {
|
|
5
138
|
particles = [];
|
|
6
139
|
emitters = [];
|
|
@@ -11,6 +144,11 @@ export class ParticleSystem {
|
|
|
11
144
|
stats;
|
|
12
145
|
gpuBackend;
|
|
13
146
|
preferGPU;
|
|
147
|
+
lastSpawnRequests = [];
|
|
148
|
+
lastIntegratedParticles = [];
|
|
149
|
+
lastEffectsCovered;
|
|
150
|
+
/** GPU trail ring rows by particle id, fed back so history accumulates across calls. */
|
|
151
|
+
trailRingById = new Map();
|
|
14
152
|
constructor(options = {}) {
|
|
15
153
|
this.maxParticles = options.maxParticles ?? 1000;
|
|
16
154
|
if (!Number.isInteger(this.maxParticles) || this.maxParticles <= 0) {
|
|
@@ -30,6 +168,7 @@ export class ParticleSystem {
|
|
|
30
168
|
uploadedBytes: 0,
|
|
31
169
|
gpuSpawns: 0,
|
|
32
170
|
gpuUpdates: 0,
|
|
171
|
+
overBudget: false,
|
|
33
172
|
};
|
|
34
173
|
}
|
|
35
174
|
addEmitter(emitter) {
|
|
@@ -49,6 +188,10 @@ export class ParticleSystem {
|
|
|
49
188
|
this.stats.killedCount += this.particles.length;
|
|
50
189
|
this.particles.length = 0;
|
|
51
190
|
this.stats.liveCount = 0;
|
|
191
|
+
this.trailRingById.clear();
|
|
192
|
+
this.lastSpawnRequests = [];
|
|
193
|
+
this.lastIntegratedParticles = [];
|
|
194
|
+
this.lastEffectsCovered = undefined;
|
|
52
195
|
}
|
|
53
196
|
dispose() {
|
|
54
197
|
this.clear();
|
|
@@ -65,6 +208,7 @@ export class ParticleSystem {
|
|
|
65
208
|
}
|
|
66
209
|
this.spawn(deltaTime);
|
|
67
210
|
this.integrate(deltaTime);
|
|
211
|
+
this.drainSubEmitterSpawns();
|
|
68
212
|
this.compactDeadParticles();
|
|
69
213
|
this.elapsedTime += deltaTime;
|
|
70
214
|
this.stats.liveCount = this.particles.length;
|
|
@@ -79,6 +223,7 @@ export class ParticleSystem {
|
|
|
79
223
|
const spawnedParticles = this.spawn(deltaTime);
|
|
80
224
|
await this.initializeSpawnedOnGPU(spawnedParticles, backend);
|
|
81
225
|
await this.integrateOnGPU(deltaTime, backend);
|
|
226
|
+
await this.drainSubEmitterSpawnsOnGPU(backend);
|
|
82
227
|
this.compactDeadParticles();
|
|
83
228
|
this.elapsedTime += deltaTime;
|
|
84
229
|
this.stats.liveCount = this.particles.length;
|
|
@@ -106,7 +251,11 @@ export class ParticleSystem {
|
|
|
106
251
|
for (const emitter of this.emitters) {
|
|
107
252
|
const capacity = this.maxParticles - this.particles.length;
|
|
108
253
|
const result = emitter.emit(deltaTime, capacity);
|
|
109
|
-
|
|
254
|
+
const dropped = Math.max(0, result.requested - result.emitted);
|
|
255
|
+
this.stats.droppedCount += dropped;
|
|
256
|
+
if (dropped > 0) {
|
|
257
|
+
this.stats.overBudget = true;
|
|
258
|
+
}
|
|
110
259
|
this.stats.spawnedCount += result.emitted;
|
|
111
260
|
for (const particle of result.particles) {
|
|
112
261
|
const context = this.createContext(deltaTime, particle);
|
|
@@ -185,14 +334,26 @@ export class ParticleSystem {
|
|
|
185
334
|
if (liveParticles.length === 0) {
|
|
186
335
|
return;
|
|
187
336
|
}
|
|
337
|
+
const collected = collectGPUParticleEffects(this.modules);
|
|
338
|
+
const useEffects = collected !== undefined && backend.supportsEffects === true;
|
|
339
|
+
const covered = useEffects ? collected.gpuCovered : undefined;
|
|
188
340
|
for (const particle of liveParticles) {
|
|
189
341
|
const context = this.createContext(deltaTime, particle);
|
|
190
|
-
|
|
342
|
+
for (const module of this.modules) {
|
|
343
|
+
if (covered?.has(module)) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
module.update?.(particle, context);
|
|
347
|
+
}
|
|
191
348
|
setVector3(particle.previousPosition, particle.position);
|
|
192
349
|
}
|
|
193
350
|
const positions = new Float32Array(liveParticles.length * 4);
|
|
194
351
|
const velocities = new Float32Array(liveParticles.length * 4);
|
|
195
352
|
const accelerations = new Float32Array(liveParticles.length * 4);
|
|
353
|
+
const needsBaseAttributes = Boolean(useEffects && (collected.effects.lifeCurves || collected.effects.lighting));
|
|
354
|
+
const baseAttributes = needsBaseAttributes ? new Float32Array(liveParticles.length * 8) : undefined;
|
|
355
|
+
const trailDepth = useEffects ? (collected.effects.trailPointsPerParticle ?? 0) : 0;
|
|
356
|
+
const trailHistory = trailDepth > 0 ? this.assembleTrailHistory(liveParticles, trailDepth) : undefined;
|
|
196
357
|
for (let index = 0; index < liveParticles.length; index += 1) {
|
|
197
358
|
const particle = liveParticles[index];
|
|
198
359
|
const offset = index * 4;
|
|
@@ -203,9 +364,19 @@ export class ParticleSystem {
|
|
|
203
364
|
velocities[offset] = particle.velocity.x;
|
|
204
365
|
velocities[offset + 1] = particle.velocity.y;
|
|
205
366
|
velocities[offset + 2] = particle.velocity.z;
|
|
367
|
+
// velocities.w carries per-particle lifetime for the effects kernel.
|
|
368
|
+
velocities[offset + 3] = useEffects ? particle.lifetime : 0;
|
|
206
369
|
accelerations[offset] = particle.acceleration.x;
|
|
207
370
|
accelerations[offset + 1] = particle.acceleration.y;
|
|
208
371
|
accelerations[offset + 2] = particle.acceleration.z;
|
|
372
|
+
if (baseAttributes) {
|
|
373
|
+
const attributeOffset = index * 8;
|
|
374
|
+
baseAttributes[attributeOffset] = particle.color.r;
|
|
375
|
+
baseAttributes[attributeOffset + 1] = particle.color.g;
|
|
376
|
+
baseAttributes[attributeOffset + 2] = particle.color.b;
|
|
377
|
+
baseAttributes[attributeOffset + 3] = particle.color.a;
|
|
378
|
+
baseAttributes[attributeOffset + 4] = particle.size;
|
|
379
|
+
}
|
|
209
380
|
}
|
|
210
381
|
const result = await backend.update({
|
|
211
382
|
positions,
|
|
@@ -213,26 +384,224 @@ export class ParticleSystem {
|
|
|
213
384
|
accelerations,
|
|
214
385
|
deltaTime,
|
|
215
386
|
count: liveParticles.length,
|
|
387
|
+
...(useEffects
|
|
388
|
+
? {
|
|
389
|
+
effects: {
|
|
390
|
+
...collected.effects,
|
|
391
|
+
time: this.elapsedTime,
|
|
392
|
+
},
|
|
393
|
+
baseAttributes,
|
|
394
|
+
trailHistory,
|
|
395
|
+
}
|
|
396
|
+
: {}),
|
|
216
397
|
});
|
|
398
|
+
if (useEffects) {
|
|
399
|
+
this.lastSpawnRequests = result.spawnRequests ? Array.from(result.spawnRequests) : [];
|
|
400
|
+
this.lastIntegratedParticles = liveParticles;
|
|
401
|
+
this.lastEffectsCovered = covered;
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
this.lastSpawnRequests = [];
|
|
405
|
+
this.lastIntegratedParticles = [];
|
|
406
|
+
this.lastEffectsCovered = undefined;
|
|
407
|
+
}
|
|
217
408
|
for (let index = 0; index < liveParticles.length; index += 1) {
|
|
218
409
|
const particle = liveParticles[index];
|
|
219
410
|
const offset = index * 4;
|
|
220
411
|
particle.position.x = result.positions[offset];
|
|
221
412
|
particle.position.y = result.positions[offset + 1];
|
|
222
413
|
particle.position.z = result.positions[offset + 2];
|
|
223
|
-
|
|
414
|
+
const rawAge = result.positions[offset + 3];
|
|
415
|
+
if (useEffects && (!Number.isFinite(rawAge) || rawAge < 0)) {
|
|
416
|
+
// Compute-side kill (analytic plane or heightfield in kill mode).
|
|
417
|
+
particle.alive = false;
|
|
418
|
+
particle.age = particle.lifetime;
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
particle.age = rawAge;
|
|
224
422
|
particle.velocity.x = result.velocities[offset];
|
|
225
423
|
particle.velocity.y = result.velocities[offset + 1];
|
|
226
424
|
particle.velocity.z = result.velocities[offset + 2];
|
|
425
|
+
if (result.attributes) {
|
|
426
|
+
const attributeOffset = index * 8;
|
|
427
|
+
particle.color.r = result.attributes[attributeOffset] ?? particle.color.r;
|
|
428
|
+
particle.color.g = result.attributes[attributeOffset + 1] ?? particle.color.g;
|
|
429
|
+
particle.color.b = result.attributes[attributeOffset + 2] ?? particle.color.b;
|
|
430
|
+
particle.color.a = result.attributes[attributeOffset + 3] ?? particle.color.a;
|
|
431
|
+
particle.size = result.attributes[attributeOffset + 4] ?? particle.size;
|
|
432
|
+
}
|
|
227
433
|
particle.rotation += particle.angularVelocity * deltaTime;
|
|
228
|
-
|
|
434
|
+
const context = this.createContext(deltaTime, particle);
|
|
435
|
+
for (const module of this.modules) {
|
|
436
|
+
if (covered?.has(module)) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
module.afterIntegrate?.(particle, context);
|
|
440
|
+
}
|
|
229
441
|
if (particle.age >= particle.lifetime) {
|
|
230
442
|
particle.alive = false;
|
|
231
443
|
}
|
|
232
444
|
}
|
|
445
|
+
if (useEffects && result.trailPositions) {
|
|
446
|
+
this.applyGPUTrailHistories(liveParticles, collected.effects, result.trailPositions);
|
|
447
|
+
this.storeTrailRings(liveParticles, collected.effects, result.trailPositions);
|
|
448
|
+
}
|
|
233
449
|
this.stats.gpuUpdates += 1;
|
|
234
450
|
this.recordBufferUpload(positions.byteLength + velocities.byteLength + accelerations.byteLength + 16);
|
|
235
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Assemble the previous trail ring in live-particle order from per-id rows
|
|
454
|
+
* so compute history survives spawn/death reordering. Unknown ids start
|
|
455
|
+
* fresh (head = live position/age, older slots expired).
|
|
456
|
+
*/
|
|
457
|
+
assembleTrailHistory(liveParticles, depth) {
|
|
458
|
+
const history = new Float32Array(liveParticles.length * depth * 4);
|
|
459
|
+
for (let index = 0; index < liveParticles.length; index += 1) {
|
|
460
|
+
const particle = liveParticles[index];
|
|
461
|
+
const target = index * depth * 4;
|
|
462
|
+
const row = this.trailRingById.get(particle.id);
|
|
463
|
+
if (row && row.length >= depth * 4) {
|
|
464
|
+
history.set(row.subarray(0, depth * 4), target);
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
history[target] = particle.position.x;
|
|
468
|
+
history[target + 1] = particle.position.y;
|
|
469
|
+
history[target + 2] = particle.position.z;
|
|
470
|
+
history[target + 3] = particle.age;
|
|
471
|
+
for (let slot = 1; slot < depth; slot += 1) {
|
|
472
|
+
const slotOffset = target + slot * 4;
|
|
473
|
+
history[slotOffset] = particle.position.x;
|
|
474
|
+
history[slotOffset + 1] = particle.position.y;
|
|
475
|
+
history[slotOffset + 2] = particle.position.z;
|
|
476
|
+
history[slotOffset + 3] = Number.POSITIVE_INFINITY;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return history;
|
|
480
|
+
}
|
|
481
|
+
storeTrailRings(liveParticles, effects, trailPositions) {
|
|
482
|
+
const depth = effects.trailPointsPerParticle ?? 1;
|
|
483
|
+
for (let index = 0; index < liveParticles.length; index += 1) {
|
|
484
|
+
const particle = liveParticles[index];
|
|
485
|
+
if (!particle) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
this.trailRingById.set(particle.id, trailPositions.slice(index * depth * 4, (index + 1) * depth * 4));
|
|
489
|
+
}
|
|
490
|
+
if (this.trailRingById.size > this.particles.length * 2 + 16) {
|
|
491
|
+
const liveIds = new Set(this.particles.map((particle) => particle.id));
|
|
492
|
+
for (const id of this.trailRingById.keys()) {
|
|
493
|
+
if (!liveIds.has(id)) {
|
|
494
|
+
this.trailRingById.delete(id);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/** CPU path: turn queued sub-emitter requests into live child particles. */
|
|
500
|
+
drainSubEmitterSpawns() {
|
|
501
|
+
for (const module of this.modules) {
|
|
502
|
+
if (!(module instanceof SubEmitterModule)) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const capacity = this.maxParticles - this.particles.length;
|
|
506
|
+
const { children, dropped } = module.drainPendingSpawns(capacity);
|
|
507
|
+
this.stats.droppedCount += dropped;
|
|
508
|
+
if (dropped > 0) {
|
|
509
|
+
this.stats.overBudget = true;
|
|
510
|
+
}
|
|
511
|
+
for (const child of children) {
|
|
512
|
+
const context = this.createContext(0, child);
|
|
513
|
+
applyParticleModules(this.modules, "onSpawn", child, context);
|
|
514
|
+
this.particles.push(child);
|
|
515
|
+
this.stats.spawnedCount += 1;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* GPU path: covered sub-emitters report per-particle spawn requests from
|
|
521
|
+
* the compute kernel; uncovered ones queue CPU requests as usual. Children
|
|
522
|
+
* initialize through the GPU spawn pipeline when the backend offers one.
|
|
523
|
+
*/
|
|
524
|
+
async drainSubEmitterSpawnsOnGPU(backend) {
|
|
525
|
+
const spawned = [];
|
|
526
|
+
let slot = 0;
|
|
527
|
+
for (const module of this.modules) {
|
|
528
|
+
if (!(module instanceof SubEmitterModule)) {
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
const capacity = this.maxParticles - this.particles.length - spawned.length;
|
|
532
|
+
if (this.lastEffectsCovered?.has(module) ?? false) {
|
|
533
|
+
const { children, dropped } = module.createChildren(this.requestsForSubEmitter(slot), capacity);
|
|
534
|
+
slot += 1;
|
|
535
|
+
this.stats.droppedCount += dropped;
|
|
536
|
+
if (dropped > 0) {
|
|
537
|
+
this.stats.overBudget = true;
|
|
538
|
+
}
|
|
539
|
+
spawned.push(...children);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
const { children, dropped } = module.drainPendingSpawns(capacity);
|
|
543
|
+
this.stats.droppedCount += dropped;
|
|
544
|
+
if (dropped > 0) {
|
|
545
|
+
this.stats.overBudget = true;
|
|
546
|
+
}
|
|
547
|
+
spawned.push(...children);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (spawned.length === 0) {
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
for (const child of spawned) {
|
|
554
|
+
const context = this.createContext(0, child);
|
|
555
|
+
applyParticleModules(this.modules, "onSpawn", child, context);
|
|
556
|
+
this.particles.push(child);
|
|
557
|
+
this.stats.spawnedCount += 1;
|
|
558
|
+
}
|
|
559
|
+
await this.initializeSpawnedOnGPU(spawned, backend);
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Decode one covered sub-emitter slot from the packed kernel flags
|
|
563
|
+
* (low 16 bits = slot 0, high 16 bits = slot 1).
|
|
564
|
+
*/
|
|
565
|
+
requestsForSubEmitter(slot) {
|
|
566
|
+
const requests = [];
|
|
567
|
+
if (this.lastSpawnRequests.length === 0) {
|
|
568
|
+
return requests;
|
|
569
|
+
}
|
|
570
|
+
for (let index = 0; index < this.lastSpawnRequests.length; index += 1) {
|
|
571
|
+
const flag = this.lastSpawnRequests[index] ?? 0;
|
|
572
|
+
const count = slot === 0 ? flag & 0xffff : flag >>> 16;
|
|
573
|
+
if (count <= 0) {
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
// Death triggers intentionally reference particles that just died; the
|
|
577
|
+
// kernel only records requests for triggers crossed while alive.
|
|
578
|
+
const particle = this.lastIntegratedParticles[index];
|
|
579
|
+
if (!particle) {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
requests.push({
|
|
583
|
+
position: { ...particle.position },
|
|
584
|
+
velocity: { ...particle.velocity },
|
|
585
|
+
count,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
return requests;
|
|
589
|
+
}
|
|
590
|
+
applyGPUTrailHistories(liveParticles, effects, trailPositions) {
|
|
591
|
+
const trailModule = this.modules.find((module) => module instanceof TrailModule);
|
|
592
|
+
if (!(trailModule instanceof TrailModule)) {
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
const depth = effects.trailPointsPerParticle ?? 1;
|
|
596
|
+
const histories = decodeTrailRingBuffer(trailPositions, liveParticles.length, depth, trailModule.lifetime);
|
|
597
|
+
for (let index = 0; index < liveParticles.length; index += 1) {
|
|
598
|
+
const particle = liveParticles[index];
|
|
599
|
+
if (!particle || !particle.alive) {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
particle.userData.trail = histories[index] ?? [];
|
|
603
|
+
}
|
|
604
|
+
}
|
|
236
605
|
compactDeadParticles() {
|
|
237
606
|
let write = 0;
|
|
238
607
|
let killed = 0;
|