Bläddra i källkod

Improving GPUEmitter mesh spawn api. Adding Camera space (not completely working yet). GPUEmitter use Storage buffers.

clementlandrin 1 år sedan
förälder
incheckning
fea9129a60

+ 8 - 3
hrt/prefab/fx/gpuemitter/BaseSimulation.hx

@@ -2,18 +2,20 @@ package hrt.prefab.fx.gpuemitter;
 
 class BaseSimulation extends ComputeUtils {
 	static var SRC = {
-		@const(4096) var MAX_INSTANCE_COUNT : Int;
 		@param var batchBuffer : RWPartialBuffer<{
 			modelView : Mat4, 
 			speed : Vec3,
 			lifeTime : Float
-		}, MAX_INSTANCE_COUNT>;
+		}, 33554432>;
 
 		@const var INFINITE : Bool = false;
 		@const var FACE_CAM : Bool = false;
+		@const var CAMERA_BOUNDS : Bool = false;
 
 		@param var dtParam : Float;
 		@param var cameraUp : Vec3;
+		@param var boundsPos : Vec3;
+		@param var boundsSize : Vec3;
 
 		var dt : Float;
 		var speed : Vec3;
@@ -39,7 +41,10 @@ class BaseSimulation extends ComputeUtils {
 				} else {
 					align = rotateMatrixZ(computeVar.globalInvocation.x * 0.35487) * alignMatrix(vec3(0.0, 0.0, 1.0), normalize(speed));
 				}
-				modelView = align * translationMatrix(prevPos) * translationMatrix(speed * dt);
+				var newPos = prevPos + speed * dt;
+				if ( CAMERA_BOUNDS )
+					newPos = ((newPos - boundsPos) % boundsSize) + boundsPos;
+				modelView = align * translationMatrix(newPos);
 			}
 			var idx = computeVar.globalInvocation.x;
 			if ( !INFINITE )

+ 1 - 2
hrt/prefab/fx/gpuemitter/BaseSpawn.hx

@@ -2,12 +2,11 @@ package hrt.prefab.fx.gpuemitter;
 
 class BaseSpawn extends ComputeUtils {
 	static var SRC = {
-		@const(4096) var MAX_INSTANCE_COUNT : Int;
 		@param var batchBuffer : RWPartialBuffer<{
 			modelView : Mat4, 
 			speed : Vec3,
 			lifeTime : Float
-		}, MAX_INSTANCE_COUNT>;
+		}, 33554432>;
 		@const var SPEED_NORMAL : Bool;
 		@param var minLifeTime : Float;
 		@param var maxLifeTime : Float;

+ 53 - 0
hrt/prefab/fx/gpuemitter/CubeSpawn.hx

@@ -0,0 +1,53 @@
+package hrt.prefab.fx.gpuemitter;
+
+class CubeSpawnShader extends ComputeUtils {
+	override function onUpdate(emitter : GPUEmitter.GPUEmitterObject, buffer : h3d.Buffer, index : Int) {
+		super.onUpdate(emitter, buffer, index);
+		randOffset = index;
+	}
+
+	static var SRC = {
+		@param var boundsSize : Vec3;
+		@param var randOffset : Int;
+
+		var emitNormal : Vec3;
+		var lifeTime : Float;
+		var relativeTransform : Mat4;
+		function main() {
+			var rnd = random3d(vec2(global.time, computeVar.globalInvocation.x + randOffset));
+			rnd = rnd - 0.5;
+			relativeTransform = translationMatrix(rnd * boundsSize);
+		}
+	}
+}
+
+class CubeSpawn extends SpawnShader {
+	@:s var cubeEdge : Float = 1.0;
+
+	override function makeShader() {
+		return new CubeSpawnShader();
+	}
+
+	override function updateInstance(?propName) {
+		super.updateInstance(propName);
+
+		var sh = cast(shader, CubeSpawnShader);
+		sh.boundsSize.set(cubeEdge, cubeEdge, cubeEdge);
+	}
+
+	#if editor
+	override function edit( ctx : hide.prefab.EditContext ) {
+		ctx.properties.add(new hide.Element('
+			<div class="group" name="Spawn">
+				<dl>
+					<dt>Cube edge</dt><dd><input type="range" min="0.1" max="10" field="cubeEdge"/></dd>
+				</dl>
+			</div>
+			'), this, function(pname) {
+				ctx.onChange(this, pname);
+		});
+	}
+	#end
+
+	static var _ = Prefab.register("cubeSpawn", CubeSpawn);
+}

+ 33 - 16
hrt/prefab/fx/gpuemitter/GPUEmitter.hx

@@ -1,8 +1,11 @@
 package hrt.prefab.fx.gpuemitter;
 
+import hrt.prefab.fx.gpuemitter.CubeSpawn.CubeSpawnShader;
+
 enum Mode {
 	World;
 	Local;
+	Camera;
 }
 
 enum Align {
@@ -50,6 +53,7 @@ typedef Data = {
 	var startSpeed : Float;
 	var trs : h3d.Matrix;
 	var mode : Mode;
+	var cameraModeDistance : Float;
 	var align : Align;
 	var speedMode : SpeedMode;
 	var maxStartSpeed : Float;
@@ -67,6 +71,7 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
 	public function new(data, primitive, materials, ?parent) {
 		super(primitive, null, parent);
 		this.meshBatchFlags.set(EnableGpuUpdate);
+		this.meshBatchFlags.set(EnableStorageBuffer);
 		if ( materials != null )
 			this.materials = materials;
 		particleShader = new ParticleShader();
@@ -89,13 +94,6 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
 		begin();
 		for ( i in 0...data.maxCount )
 			emitInstance();
-		var p = dataPasses;
-		trace("Max count is ", data.maxCount);
-		while ( p != null ) {
-			trace("Max instance is ", p.maxInstance);
-			trace(p.pass.name + " pass has estimated " + Math.ceil(data.maxCount / p.maxInstance) + " buffers");
-			p = p.next;
-		}
 	}
 
 	function dispatch(ctx : h3d.scene.RenderContext) {
@@ -105,7 +103,6 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
 		var p = dataPasses;
 		while ( p != null ) {
 			var baseSpawn = spawnPass.getShader(BaseSpawn);
-			baseSpawn.MAX_INSTANCE_COUNT = p.maxInstance;
 			baseSpawn.maxLifeTime = data.maxLifeTime;
 			baseSpawn.minLifeTime = data.minLifeTime;
 			baseSpawn.maxStartSpeed = data.maxStartSpeed;
@@ -120,7 +117,6 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
 			var baseSimulation = simulationPass.getShader(BaseSimulation);
 			baseSimulation.INFINITE = data.infinite;
 			baseSimulation.dtParam = ctx.elapsedTime;
-			baseSimulation.MAX_INSTANCE_COUNT = p.maxInstance;
 			switch ( data.align ) {
 			case FaceCam:
 				baseSimulation.FACE_CAM = true;
@@ -136,7 +132,31 @@ class GPUEmitterObject extends h3d.scene.MeshBatch {
 			case Local:
 				baseSpawn.absPos.identity();
 				particleShader.absPos.load(getAbsPos());
+			case Camera:
+				baseSpawn.absPos.identity();
+				particleShader.absPos.identity();
+				
+				var cam = ctx.camera.clone();
+				cam.zFar = data.cameraModeDistance;
+				cam.update();
+				var bounds = new h3d.col.Bounds();
+				bounds.addPoint(cam.unproject(-1, -1, 0.0).toPoint());
+				bounds.addPoint(cam.unproject(1, 1, 0.0).toPoint());
+				bounds.addPoint(cam.unproject(-1, -1, 1.0).toPoint());
+				bounds.addPoint(cam.unproject(1, 1, 1.0).toPoint());
+				baseSimulation.boundsPos.set(bounds.xMin, bounds.yMin, bounds.zMin);
+				baseSimulation.boundsSize.set(bounds.xSize, bounds.ySize, bounds.zSize);
+				particleShader.absPos.load(getAbsPos());
+
+				var cubeSpawn = spawnPass.getShader(CubeSpawnShader);
+				if ( cubeSpawn == null ) {
+					cubeSpawn = new CubeSpawnShader();
+					spawnPass.addShader(cubeSpawn);
+				}
+				cubeSpawn.boundsSize.set(bounds.xSize, bounds.ySize, bounds.zSize);
+				cubeSpawn.boundsSize.set(bounds.getCenter().x, bounds.getCenter().y, bounds.getCenter().z);
 			}
+			baseSimulation.CAMERA_BOUNDS = data.mode == Camera;
 
 			var i = 0;
 			for ( b in p.buffers ) {
@@ -198,6 +218,7 @@ class GPUEmitter extends Object3D {
 	@:s var infinite : Bool = false; 
 	@:s var faceCam : Bool = false;
 	@:s var mode : Mode = World;
+	@:s var cameraModeDistance : Float = 50.0;
 	@:s var align : Align = FaceCam;
 	@:s var speedMode : SpeedMode = Normal;
 
@@ -235,6 +256,7 @@ class GPUEmitter extends Object3D {
 				infinite : infinite,
 				faceCam : faceCam,
 				mode : mode,
+				cameraModeDistance : cameraModeDistance,
 				align : align,
 				speedMode : speedMode,
 				minStartSpeed : minStartSpeed,
@@ -259,13 +281,6 @@ class GPUEmitter extends Object3D {
 		}
 	}
 
-	override function makeChild(c : hrt.prefab.Prefab) {
-		#if !editor
-		if ( Std.isOfType(c, hrt.prefab.Object3D) )
-			return;
-		#end
-		super.makeChild(c);
-	}
 
 	override function updateInstance(?propName : String) {
 		super.updateInstance(propName);
@@ -300,8 +315,10 @@ class GPUEmitter extends Object3D {
 						<select field="mode">
 							<option value="World">World</option>
 							<option value="Local">Local</option>
+							<option value="Camera">Camera</option>
 						</select>
 					</dd>
+					<dt>Camera distance</dt><dd><input type="range" min="0" field="cameraModeDistance"/></dd>
 					<dt>Align</dt>
 					<dd>
 						<select field="align">

+ 33 - 11
hrt/prefab/fx/gpuemitter/MeshSpawn.hx

@@ -17,7 +17,13 @@ class ComputeSkin extends hxsl.Shader {
 }
 
 class MeshSpawnShader extends ComputeUtils {
-	public var mesh : h3d.scene.Mesh;
+	public var mesh(default, null) : h3d.scene.Mesh;
+	
+	public function attachTo(m : h3d.scene.Mesh) {
+		mesh = m;
+		vbuf = mesh.primitive.buffer;
+		SIZE = mesh.primitive.buffer.vertices;
+	}
 
 	override function onUpdate(emitter : GPUEmitter.GPUEmitterObject, buffer : h3d.Buffer, index : Int) {
 		super.onUpdate(emitter, buffer, index);
@@ -77,20 +83,36 @@ class MeshSpawn extends SpawnShader {
 	// 	}
 	// }
 
+	var mesh : h3d.scene.Mesh;
+	override function makeChild(c) {
+		if ( mesh != null )
+			return;
+		var object3D = Std.downcast(c, hrt.prefab.Object3D);
+		if ( object3D == null )
+			super.makeChild(c);
+		else
+			mesh = object3D.make().local3d.find(o -> Std.downcast(o, h3d.scene.Mesh));
+	}
+
+	override function postMakeInstance() {
+		var sh = cast(shader, MeshSpawnShader);
+		// var mesh = findFirstLocal3d().find(o -> Std.isOfType(o, GPUEmitter.GPUEmitterObject) ? null : Std.downcast(o, h3d.scene.Mesh));
+		if ( mesh == null ) {
+			var defaultPrim = new h3d.prim.Sphere();
+			defaultPrim.addUVs();
+			defaultPrim.addNormals();
+			mesh = new h3d.scene.Mesh(defaultPrim);
+		}
+		// Force primitive alloc
+		if ( mesh.primitive.buffer == null )
+			mesh.primitive.alloc(h3d.Engine.getCurrent());
+		sh.attachTo(mesh);
+	}
+
 	override function updateInstance(?propName) {
 		super.updateInstance(propName);
 
-		#if !editor
 		var sh = cast(shader, MeshSpawnShader);
-		var mesh = findFirstLocal3d().find(o -> Std.downcast(o, h3d.scene.Mesh));
-		if ( mesh != null )
-			sh.mesh = mesh;
-		// Force primitive alloc
-		if ( sh.mesh.primitive.buffer == null )
-			sh.mesh.primitive.alloc(h3d.Engine.getCurrent());
-		sh.vbuf = sh.mesh.primitive.buffer;
-		sh.SIZE = sh.mesh.primitive.buffer.vertices;
-		#end
 	}
 
 	#if editor