Skip to content

Commit

Permalink
Fix warm up with zero scale particles, hide template object. Emitter …
Browse files Browse the repository at this point in the history
…shaders compatibility with GPUEmitters
  • Loading branch information
clementlandrin committed Jan 13, 2025
1 parent 0e69886 commit 7fb2f15
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 123 deletions.
3 changes: 1 addition & 2 deletions hrt/prefab/fx/gpuemitter/BaseSimulation.hx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ class BaseSimulation extends ComputeUtils {
var life : Float;
var particleRandom : Float;
var modelView : Mat4;
var instanceID : Int;
var prevModelView : Mat4;
var prevSpeed : Vec3;
var relativeTransform : Mat4;
Expand All @@ -36,7 +35,7 @@ class BaseSimulation extends ComputeUtils {
life = particleBuffer[computeVar.globalInvocation.x].life;
prevModelView = batchBuffer[computeVar.globalInvocation.x].modelView;
particleRandom = particleBuffer[computeVar.globalInvocation.x].random;
relativeTransform = scaleMatrix(vec3(particleRandom * (maxSize - minSize) + minSize));
relativeTransform = scaleMatrix((life > 0.0 ? 1.0 : 0.0) * vec3(particleRandom * (maxSize - minSize) + minSize));
}

function main() {
Expand Down
219 changes: 113 additions & 106 deletions hrt/prefab/fx/gpuemitter/GPUEmitter.hx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,24 @@ class ParticleShader extends hxsl.Shader {
@param var localTransform : Mat4;
@param var absPos : Mat4;

@param var particleBuffer : RWPartialBuffer<{
life : Float,
lifeTime : Float,
random : Float,
}>;

var particleLife : Float;
var particleLifeTime : Float;
var particleRandom : Float;

var relativePosition : Vec3;
var transformedPosition : Vec3;
function __init__vertex() {
{
particleLife = particleBuffer[instanceID].life;
particleLifeTime = particleBuffer[instanceID].lifeTime;
particleRandom = particleBuffer[instanceID].random;
}
transformedPosition = transformedPosition * absPos.mat3x4();
}

Expand Down Expand Up @@ -56,7 +71,6 @@ typedef Data = {
typedef ParticleBuffer = {
var buffer : h3d.Buffer;
var atomic : h3d.Buffer;
var next : ParticleBuffer;
}

@:allow(hrt.prefab.fx.GPUEmitter)
Expand All @@ -71,7 +85,7 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
var shaderParams : Array<{ param : hrt.prefab.fx.BaseFX.ShaderParam, shader : UpdateParamShader }> = [];
var paramTexture : h3d.mat.Texture;

var particleBuffers : ParticleBuffer;
var particleBuffer : ParticleBuffer;
var particleShader : ParticleShader;

var rateAccumulation : Float = 0.0;
Expand Down Expand Up @@ -106,50 +120,6 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
simulationPass.addShader(new BaseSimulation());
}

override function flush() {
super.flush();

var alloc = hxd.impl.Allocator.get();
if ( particleBuffers == null )
particleBuffers = { buffer : null, atomic : null, next : null};
var particleBuffer = particleBuffers;
var p = dataPasses;
var particleBufferFormat = hxd.BufferFormat.make([
{ name : "speed", type : DVec3 },
{ name : "life", type : DFloat },
{ name : "lifeTime", type : DFloat },
{ name : "random", type : DFloat },
{ name : "padding", type : DVec2 },
]);
while ( p != null ) {
if ( particleBuffer.buffer == null ) {
var stride = 4 * 2;
var floats = alloc.allocFloats(instanceCount * stride);
for ( i in 0...instanceCount ) {
// speed
// floats[i * stride] = 0.0;
// floats[i * stride + 1] = 0.0;
// floats[i * stride + 2] = 0.0;
floats[i * stride + 3] = -1000.0; // life warmup
var l = hxd.Math.random() * (data.maxLifeTime - data.minLifeTime) + data.minLifeTime;
floats[i * stride + 4] = l; // lifeTime
floats[i * stride + 5] = hxd.Math.random(); // random
// padding
// floats[i * stride + 6] = 0.0;
// floats[i * stride + 7] = 0.0;
}
particleBuffer.buffer = alloc.ofFloats(floats, particleBufferFormat, UniformReadWrite);
particleBuffer.atomic = alloc.allocBuffer( 1, hxd.BufferFormat.VEC4_DATA, UniformReadWrite );
}
p = p.next;
if ( p != null && particleBuffer.next == null ) {
particleBuffer.next = { buffer : null, atomic : null, next : null};
}

particleBuffer = particleBuffer.next;
}
}

public function bakeAnimations() {
if ( paramTexture != null )
paramTexture.dispose();
Expand Down Expand Up @@ -193,9 +163,42 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
}
paramTexture.uploadPixels(pxls);
}
}

function init() {
var alloc = hxd.impl.Allocator.get();
var particleBufferFormat = hxd.BufferFormat.make([
{ name : "speed", type : DVec3 },
{ name : "life", type : DFloat },
{ name : "lifeTime", type : DFloat },
{ name : "random", type : DFloat },
{ name : "padding", type : DVec2 },
]);

if ( particleBuffer == null ) {
particleBuffer = { buffer : null, atomic : null };
var stride = particleBufferFormat.stride;
var floats = alloc.allocFloats(data.maxCount * stride);
for ( i in 0...data.maxCount ) {
// speed
// floats[i * stride] = 0.0;
// floats[i * stride + 1] = 0.0;
// floats[i * stride + 2] = 0.0;
floats[i * stride + 3] = 0.0; // life warmup
var l = hxd.Math.random() * (data.maxLifeTime - data.minLifeTime) + data.minLifeTime;
floats[i * stride + 4] = l; // lifeTime
floats[i * stride + 5] = hxd.Math.random(); // random
// padding
// floats[i * stride + 6] = 0.0;
// floats[i * stride + 7] = 0.0;
}
particleBuffer.buffer = alloc.ofFloats(floats, particleBufferFormat, UniformReadWrite);
particleBuffer.atomic = alloc.allocBuffer( 1, hxd.BufferFormat.VEC4_DATA, UniformReadWrite );
particleShader.particleBuffer = particleBuffer.buffer;
}

begin();
for ( _ in 0...this.data.maxCount )
for ( _ in 0...data.maxCount )
emitInstance();
}

Expand All @@ -209,17 +212,16 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
#end

var p = dataPasses;
var particleBuffer = particleBuffers;
while ( p != null ) {
if ( countBytes == null ) {
countBytes = haxe.io.Bytes.alloc(4*4);
countBytes.setInt32(0, 0);
countBytes.setInt32(1, 0);
countBytes.setInt32(2, 0);
countBytes.setInt32(3, 0);
}
particleBuffer.atomic.uploadBytes(countBytes, 0, 1);
if ( countBytes == null ) {
countBytes = haxe.io.Bytes.alloc(4*4);
countBytes.setInt32(0, 0);
countBytes.setInt32(1, 0);
countBytes.setInt32(2, 0);
countBytes.setInt32(3, 0);
}
particleBuffer.atomic.uploadBytes(countBytes, 0, 1);

while ( p != null ) {
var baseSpawn = spawnPass.getShader(BaseSpawn);
baseSpawn.maxLifeTime = data.maxLifeTime;
baseSpawn.minLifeTime = data.minLifeTime;
Expand Down Expand Up @@ -349,7 +351,6 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
i += p.maxInstance;
}
p = p.next;
particleBuffer = particleBuffer.next;
}
firstDispatch = false;
}
Expand All @@ -360,20 +361,15 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
super.emit(ctx);
}

override function cleanPasses() {
super.cleanPasses();
var b = particleBuffers;
while ( b != null ) {
b.buffer.dispose();
b.atomic.dispose();
b = b.next;
}
particleBuffers = null;
}

override function onRemove() {
super.onRemove();

if ( particleBuffer != null ) {
particleBuffer.buffer.dispose();
particleBuffer.atomic.dispose();
}
particleBuffer = null;

if ( paramTexture != null )
paramTexture.dispose();
}
Expand Down Expand Up @@ -405,24 +401,24 @@ class GPUEmitter extends Object3D {
return new h3d.scene.Object(parent3d);
}

function updateEmitters() {
function updateEmitters() : Array<{meshes : Array<h3d.scene.Mesh>, prefab : hrt.prefab.Prefab, emitters : Array<GPUEmitterObject>}> {
#if editor
return;
return [];
#end
for ( emitter in local3d.findAll(o -> Std.downcast(o, GPUEmitterObject)) )
emitter.remove();

var meshes = [];
meshes = local3d.findAll(o -> Std.downcast(o, h3d.scene.Mesh));
var templates = [];
for ( c in children ) {
if ( !Std.isOfType(c, Object3D) )
if ( Std.isOfType(c, hrt.prefab.Shader) )
continue;
var obj = new h3d.scene.Object();
c.make(new ContextShared(obj));
meshes = meshes.concat(obj.findAll(o -> Std.downcast(o, h3d.scene.Mesh)));
var cloned = c.make(new ContextShared(obj));
var clonedMeshes = obj.findAll(o -> Std.downcast(o, h3d.scene.Mesh));
templates.push({meshes : clonedMeshes, prefab : cloned, emitters : []});
}
inline function createEmitter(data, prim, materials) {
new GPUEmitterObject(data, prim, materials, local3d);
return new GPUEmitterObject(data, prim, materials, local3d);
}
inline function getData(trs : h3d.Matrix) {
return {
Expand All @@ -443,46 +439,57 @@ class GPUEmitter extends Object3D {
maxStartSpeed : maxStartSpeed,
}
}
for ( mesh in meshes ) {
var data = getData(mesh.getAbsPos().clone());
var multimat = Std.downcast(mesh, h3d.scene.MultiMaterial);
var materials : Array<h3d.mat.Material>;
if ( multimat == null )
materials = [mesh.material];
else
materials = multimat.materials;
createEmitter(getData(mesh.getAbsPos().clone()), cast(mesh.primitive, h3d.prim.MeshPrimitive), materials);
mesh.visible = false;
mesh.ignoreCollide = true;
}
if ( meshes.length == 0 ) {
var data = getData(h3d.Matrix.I());
createEmitter(data, getDefaultPrimitive(), null);

for ( t in templates ) {
for ( mesh in t.meshes ) {
var data = getData(mesh.getAbsPos().clone());
var multimat = Std.downcast(mesh, h3d.scene.MultiMaterial);
var materials : Array<h3d.mat.Material>;
if ( multimat == null )
materials = [mesh.material];
else
materials = multimat.materials;
var emitter = createEmitter(getData(mesh.getAbsPos().clone()), cast(mesh.primitive, h3d.prim.MeshPrimitive), materials);
t.emitters.push(emitter);
mesh.visible = false;
mesh.ignoreCollide = true;
}
}

// if ( templates.length == 0 ) {
// templates.push({})
// }

return templates;
}

function bakeAnimations() {
var obj = local3d.find(o -> Std.downcast(o, GPUEmitterObject));
if ( obj != null ) {
obj.customAnimations = [];
var shaders = findAll(hrt.prefab.Shader);
for ( shader in shaders ) {
if( !shader.enabled ) continue;
hrt.prefab.fx.BaseFX.BaseFXTools.getCustomAnimations(shader, obj.customAnimations, obj.find(o -> Std.downcast(o, h3d.scene.MeshBatch)));
function init() {
var templates = updateEmitters();

for ( t in templates ) {
for ( emitter in t.emitters ) {
emitter.customAnimations = [];
var shaders = t.prefab.findAll(hrt.prefab.Shader);
for ( shader in shaders ) {
if( !shader.enabled ) continue;
hrt.prefab.fx.BaseFX.BaseFXTools.getCustomAnimations(shader, emitter.customAnimations, emitter.find(o -> Std.downcast(o, h3d.scene.MeshBatch)));
}
emitter.init();
emitter.bakeAnimations();
}
obj.bakeAnimations();
}
}

override function postMakeInstance() {
super.postMakeInstance();

bakeAnimations();
override function makeChild(c : hrt.prefab.Prefab) {
if ( !Std.isOfType(c, hrt.prefab.Shader) )
return;
super.makeChild(c);
}

override function updateInstance(?propName : String) {
super.updateInstance(propName);
updateEmitters();

init();
}

#if editor
Expand Down
Loading

0 comments on commit 7fb2f15

Please sign in to comment.