Răsfoiți Sursa

Some work on particles

Panagiotis Christopoulos Charitos 7 ani în urmă
părinte
comite
76273aa545

+ 53 - 32
samples/physics_playground/Main.cpp

@@ -12,14 +12,14 @@ static Error createDestructionEvent(SceneNode* node)
 {
 {
 	CString script = R"(
 	CString script = R"(
 function update(event, prevTime, crntTime)
 function update(event, prevTime, crntTime)
--- Do nothing
-return 1
+	-- Do nothing
+	return 1
 end
 end
 
 
 function onKilled(event, prevTime, crntTime)
 function onKilled(event, prevTime, crntTime)
-logi("onKilled")
-event:getAssociatedSceneNodes():getAt(0):setMarkedForDeletion()
-return 1
+	logi(string.format("Will kill %s", event:getAssociatedSceneNodes():getAt(0):getName()))
+	event:getAssociatedSceneNodes():getAt(0):setMarkedForDeletion()
+	return 1
 end
 end
 	)";
 	)";
 	ScriptEvent* event;
 	ScriptEvent* event;
@@ -32,6 +32,10 @@ end
 class RayCast : public PhysicsWorldRayCastCallback
 class RayCast : public PhysicsWorldRayCastCallback
 {
 {
 public:
 public:
+	Vec3 m_hitPosition = Vec3(MAX_F32);
+	Vec3 m_hitNormal;
+	Bool m_hit = false;
+
 	RayCast(Vec3 from, Vec3 to, PhysicsMaterialBit mtl)
 	RayCast(Vec3 from, Vec3 to, PhysicsMaterialBit mtl)
 		: PhysicsWorldRayCastCallback(from, to, mtl)
 		: PhysicsWorldRayCastCallback(from, to, mtl)
 	{
 	{
@@ -39,38 +43,19 @@ public:
 
 
 	void processResult(PhysicsFilteredObject& obj, const Vec3& worldNormal, const Vec3& worldPosition)
 	void processResult(PhysicsFilteredObject& obj, const Vec3& worldNormal, const Vec3& worldPosition)
 	{
 	{
-		SceneNode* node = static_cast<SceneNode*>(obj.getUserData());
-
 		if((m_from - m_to).dot(worldNormal) < 0.0f)
 		if((m_from - m_to).dot(worldNormal) < 0.0f)
 		{
 		{
 			return;
 			return;
 		}
 		}
 
 
-		ANKI_LOGI("Ray hits %s", node->getName().cstr());
-
-		// Create rotation
-		const Vec3& zAxis = worldNormal;
-		Vec3 yAxis = Vec3(0, 1, 0.5);
-		Vec3 xAxis = yAxis.cross(zAxis).getNormalized();
-		yAxis = zAxis.cross(xAxis);
-
-		Mat3x4 rot = Mat3x4::getIdentity();
-		rot.setXAxis(xAxis);
-		rot.setYAxis(yAxis);
-		rot.setZAxis(zAxis);
-
-		Transform trf(worldPosition.xyz0(), rot, 1.0f);
-
-		// Create an obj
-		static U id = 0;
-		ModelNode* monkey;
-		node->getSceneGraph().newSceneNode<ModelNode>(
-			StringAuto(node->getFrameAllocator()).sprintf("decal%u", id++).toCString(),
-			monkey,
-			"assets/Suzannedynamic-material.ankimdl");
-		monkey->getComponent<MoveComponent>().setLocalTransform(trf);
+		if((worldPosition - m_from).getLengthSquared() > (m_hitPosition - m_from).getLengthSquared())
+		{
+			return;
+		}
 
 
-		createDestructionEvent(monkey);
+		m_hitPosition = worldPosition;
+		m_hitNormal = worldNormal;
+		m_hit = true;
 	}
 	}
 };
 };
 
 
@@ -242,10 +227,46 @@ Error MyApp::userMainLoop(Bool& quit)
 		Vec3 from = camTrf.getOrigin().xyz();
 		Vec3 from = camTrf.getOrigin().xyz();
 		Vec3 to = from + -camTrf.getRotation().getZAxis() * 100.0f;
 		Vec3 to = from + -camTrf.getRotation().getZAxis() * 100.0f;
 
 
-		RayCast ray(from, to, PhysicsMaterialBit::ALL);
+		RayCast ray(from, to, PhysicsMaterialBit::ALL & (~PhysicsMaterialBit::PARTICLE));
 		ray.m_firstHit = true;
 		ray.m_firstHit = true;
 
 
 		getPhysicsWorld().rayCast(ray);
 		getPhysicsWorld().rayCast(ray);
+
+		if(ray.m_hit)
+		{
+			// Create rotation
+			const Vec3& zAxis = ray.m_hitNormal;
+			Vec3 yAxis = Vec3(0, 1, 0.5);
+			Vec3 xAxis = yAxis.cross(zAxis).getNormalized();
+			yAxis = zAxis.cross(xAxis);
+
+			Mat3x4 rot = Mat3x4::getIdentity();
+			rot.setXAxis(xAxis);
+			rot.setYAxis(yAxis);
+			rot.setZAxis(zAxis);
+
+			Transform trf(ray.m_hitPosition.xyz0(), rot, 1.0f);
+
+			// Create an obj
+			static U id = 0;
+			ModelNode* monkey;
+			ANKI_CHECK(getSceneGraph().newSceneNode(
+				StringAuto(getSceneGraph().getFrameAllocator()).sprintf("decal%u", id++).toCString(),
+				monkey,
+				"assets/Suzannedynamic-material.ankimdl"));
+			monkey->getComponent<MoveComponent>().setLocalTransform(trf);
+
+			createDestructionEvent(monkey);
+
+			// Create some particles
+			ParticleEmitterNode* particles;
+			ANKI_CHECK(getSceneGraph().newSceneNode(
+				StringAuto(getSceneGraph().getFrameAllocator()).sprintf("parts%u", id++).toCString(),
+				particles,
+				"assets/smoke.ankipart"));
+			particles->getComponent<MoveComponent>().setLocalTransform(trf);
+			createDestructionEvent(particles);
+		}
 	}
 	}
 
 
 	if(0)
 	if(0)

+ 17 - 0
samples/physics_playground/assets/smoke.ankimtl

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<material shaderProgram="shaders/ForwardShadingParticles.glslp" forwardShading="1" shadow="0">
+	<mutators>
+		<mutator name="ANIMATED_TEXTURE" value="0"/>
+		<mutator name="LIGHT" value="1"/>
+	</mutators>
+
+	<inputs>
+		<input shaderInput="mvp" builtin="MODEL_VIEW_PROJECTION_MATRIX"/>
+		<input shaderInput="cameraRotMat" builtin="CAMERA_ROTATION_MATRIX"/>
+
+		<input shaderInput="diffuseMap" value="assets/smoke.ankitex"/>
+		<input shaderInput="colorScale" value="1.0 1.0 1.0 1.0"/>
+		<input shaderInput="colorBias" value="0 0 0 0"/>
+	</inputs>
+</material>
+

+ 26 - 0
samples/physics_playground/assets/smoke.ankipart

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<particleEmitter>
+	<life>10.5</life>
+	<lifeDeviation>0.0</lifeDeviation>
+	<mass>2.0</mass>
+	<massDeviation>0.0</massDeviation>
+	<gravity>0.0 -9.5 0.0</gravity>
+	<alpha>1.0</alpha>
+	<alphaDeviation>0.1</alphaDeviation>
+	<alphaAnimationEnabled>0</alphaAnimationEnabled>
+	<emissionPeriod>0.001</emissionPeriod>
+	<particlesPerEmission>8</particlesPerEmission>
+	<startingPosition>0.0 0.0 0.8</startingPosition>
+	<startingPositionDeviation>1.5 1.5 0.0</startingPositionDeviation>
+	<material>assets/smoke.ankimtl</material>
+	<size>1.4</size>
+	<sizeDeviation>0.2</sizeDeviation>
+	<sizeAnimation>0.0</sizeAnimation>
+	<usePhysicsEngine>1</usePhysicsEngine>
+	<maxNumberOfParticles>8</maxNumberOfParticles>
+	<forceDirection>0 0 1</forceDirection>
+	<forceDirectionDeviation>1.0 1.0 0</forceDirectionDeviation>
+	<forceMagnitude>900</forceMagnitude>
+	<forceMagnitudeDeviation>200</forceMagnitudeDeviation>
+</particleEmitter>

BIN
samples/physics_playground/assets/smoke.ankitex


+ 2 - 2
src/anki/math/Transform.h

@@ -192,14 +192,14 @@ public:
 	}
 	}
 
 
 	/// Transform a TVec3
 	/// Transform a TVec3
-	TVec3<T> transform(const TVec3<T>& b) const
+	ANKI_USE_RESULT TVec3<T> transform(const TVec3<T>& b) const
 	{
 	{
 		checkW();
 		checkW();
 		return (m_rotation.getRotationPart() * (b * m_scale)) + m_origin.xyz();
 		return (m_rotation.getRotationPart() * (b * m_scale)) + m_origin.xyz();
 	}
 	}
 
 
 	/// Transform a TVec4. SIMD optimized
 	/// Transform a TVec4. SIMD optimized
-	TVec4<T> transform(const TVec4<T>& b) const
+	ANKI_USE_RESULT TVec4<T> transform(const TVec4<T>& b) const
 	{
 	{
 		checkW();
 		checkW();
 		TVec4<T> out = TVec4<T>(m_rotation * (b * m_scale), 0.0) + m_origin;
 		TVec4<T> out = TVec4<T>(m_rotation * (b * m_scale), 0.0) + m_origin;

+ 1 - 0
src/anki/physics/Common.h

@@ -68,6 +68,7 @@ enum class PhysicsMaterialBit : U64
 	DYNAMIC_GEOMETRY = 1 << 1,
 	DYNAMIC_GEOMETRY = 1 << 1,
 	TRIGGER = 1 << 2,
 	TRIGGER = 1 << 2,
 	PLAYER = 1 << 3,
 	PLAYER = 1 << 3,
+	PARTICLE = 1 << 4,
 
 
 	ALL = MAX_U64
 	ALL = MAX_U64
 };
 };

+ 34 - 0
src/anki/physics/PhysicsBody.h

@@ -54,6 +54,40 @@ public:
 		return m_mass;
 		return m_mass;
 	}
 	}
 
 
+	void activate(Bool activate)
+	{
+		getBtBody()->forceActivationState((activate) ? ACTIVE_TAG : DISABLE_SIMULATION);
+		if(activate)
+		{
+			getBtBody()->activate(true);
+		}
+	}
+
+	void clearForces()
+	{
+		getBtBody()->clearForces();
+	}
+
+	void setLinearVelocity(const Vec3& velocity)
+	{
+		getBtBody()->setLinearVelocity(toBt(velocity));
+	}
+
+	void setAngularVelocity(const Vec3& velocity)
+	{
+		getBtBody()->setAngularVelocity(toBt(velocity));
+	}
+
+	void setGravity(const Vec3& gravity)
+	{
+		getBtBody()->setGravity(toBt(gravity));
+	}
+
+	void setAngularFactor(const Vec3& factor)
+	{
+		getBtBody()->setAngularFactor(toBt(factor));
+	}
+
 anki_internal:
 anki_internal:
 	const btRigidBody* getBtBody() const
 	const btRigidBody* getBtBody() const
 	{
 	{

+ 2 - 2
src/anki/resource/RenderingKey.h

@@ -15,8 +15,8 @@ namespace anki
 enum class Pass : U8
 enum class Pass : U8
 {
 {
 	GB_FS, ///< GBuffer or forward shading.
 	GB_FS, ///< GBuffer or forward shading.
-	SM,
-	EZ,
+	SM, ///< Shadow mapping.
+	EZ, ///< Early Z.
 	COUNT
 	COUNT
 };
 };
 
 

+ 156 - 116
src/anki/scene/ParticleEmitterNode.cpp

@@ -11,7 +11,9 @@
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ModelResource.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/resource/ResourceManager.h>
 #include <anki/util/Functions.h>
 #include <anki/util/Functions.h>
+#include <anki/physics/PhysicsBody.h>
 #include <anki/physics/PhysicsWorld.h>
 #include <anki/physics/PhysicsWorld.h>
+#include <anki/physics/PhysicsCollisionShape.h>
 #include <anki/Gr.h>
 #include <anki/Gr.h>
 
 
 namespace anki
 namespace anki
@@ -39,132 +41,178 @@ static Vec3 getRandom(const Vec3& initial, const Vec3& deviation)
 	}
 	}
 }
 }
 
 
-void ParticleBase::revive(
-	const ParticleEmitterNode& pe, const Transform& trf, Second /*prevUpdateTime*/, Second crntTime)
+/// Particle base
+class ParticleEmitterNode::ParticleBase
 {
 {
-	ANKI_ASSERT(isDead());
-	const ParticleEmitterProperties& props = pe;
+public:
+	Second m_timeOfBirth; ///< Keep the time of birth for nice effects
+	Second m_timeOfDeath = -1.0; ///< Time of death. If < 0.0 then dead
+	Second m_size = 1.0;
+	Second m_alpha = 1.0;
 
 
-	// life
-	m_timeOfDeath = getRandom(crntTime + props.m_particle.m_life, props.m_particle.m_lifeDeviation);
-	m_timeOfBirth = crntTime;
-}
+	virtual ~ParticleBase()
+	{
+	}
 
 
-void ParticleSimple::simulate(const ParticleEmitterNode& pe, Second prevUpdateTime, Second crntTime)
-{
-	Second dt = crntTime - prevUpdateTime;
+	Bool isDead() const
+	{
+		return m_timeOfDeath < 0.0;
+	}
 
 
-	Vec4 xp = m_position;
-	Vec4 xc = m_acceleration * (dt * dt) + m_velocity * dt + xp;
+	/// Kill the particle
+	virtual void kill()
+	{
+		ANKI_ASSERT(m_timeOfDeath > 0.0);
+		m_timeOfDeath = -1.0;
+	}
 
 
-	m_position = xc;
+	/// Revive the particle
+	virtual void revive(const ParticleEmitterNode& pe, const Transform& trf, Second prevUpdateTime, Second crntTime)
+	{
+		ANKI_ASSERT(isDead());
+		const ParticleEmitterProperties& props = pe;
 
 
-	m_velocity += m_acceleration * dt;
-}
+		// life
+		m_timeOfDeath = getRandom(crntTime + props.m_particle.m_life, props.m_particle.m_lifeDeviation);
+		m_timeOfBirth = crntTime;
+	}
+
+	/// Only relevant for non-bullet simulations
+	virtual void simulate(const ParticleEmitterNode& pe, Second prevUpdateTime, Second crntTime)
+	{
+		(void)pe;
+		(void)prevUpdateTime;
+		(void)crntTime;
+	}
+
+	virtual const Vec4& getPosition() const = 0;
+};
 
 
-void ParticleSimple::revive(const ParticleEmitterNode& pe, const Transform& trf, Second prevUpdateTime, Second crntTime)
+/// Simple particle for simple simulation
+class ParticleEmitterNode::ParticleSimple : public ParticleEmitterNode::ParticleBase
 {
 {
-	ParticleBase::revive(pe, trf, prevUpdateTime, crntTime);
-	m_velocity = Vec4(0.0);
+public:
+	Vec4 m_velocity = Vec4(0.0);
+	Vec4 m_acceleration = Vec4(0.0);
+	Vec4 m_position;
 
 
-	const ParticleEmitterProperties& props = pe;
+	void revive(const ParticleEmitterNode& pe, const Transform& trf, Second prevUpdateTime, Second crntTime) override
+	{
+		ParticleBase::revive(pe, trf, prevUpdateTime, crntTime);
+		m_velocity = Vec4(0.0);
 
 
-	m_acceleration = getRandom(props.m_particle.m_gravity, props.m_particle.m_gravityDeviation).xyz0();
+		const ParticleEmitterProperties& props = pe;
 
 
-	// Set the initial position
-	m_position = getRandom(props.m_particle.m_startingPos, props.m_particle.m_startingPosDeviation).xyz0();
+		m_acceleration = getRandom(props.m_particle.m_gravity, props.m_particle.m_gravityDeviation).xyz0();
 
 
-	m_position += trf.getOrigin();
-}
+		// Set the initial position
+		m_position = getRandom(props.m_particle.m_startingPos, props.m_particle.m_startingPosDeviation).xyz0();
 
 
-#if 0
+		m_position += trf.getOrigin();
+	}
 
 
+	void simulate(const ParticleEmitterNode& pe, Second prevUpdateTime, Second crntTime) override
+	{
+		Second dt = crntTime - prevUpdateTime;
 
 
-Particle::Particle(
-	const char* name, SceneGraph* scene, // SceneNode
-	// RigidBody
-	PhysicsWorld* masterContainer, const RigidBody::Initializer& init_)
-	:	ParticleBase(name, scene, PT_PHYSICS)
-{
-	RigidBody::Initializer init = init_;
+		Vec4 xp = m_position;
+		Vec4 xc = m_acceleration * (dt * dt) + m_velocity * dt + xp;
 
 
-	getSceneGraph().getPhysics().newPhysicsObject<RigidBody>(body, init);
+		m_position = xc;
 
 
-	sceneNodeProtected.rigidBodyC = body;
-}
+		m_velocity += m_acceleration * dt;
+	}
 
 
+	const Vec4& getPosition() const override
+	{
+		return m_position;
+	}
+};
 
 
-Particle::~Particle()
+/// Particle for bullet simulations
+class ParticleEmitterNode::PhysParticle : public ParticleEmitterNode::ParticleBase
 {
 {
-	getSceneGraph().getPhysics().deletePhysicsObject(body);
-}
+public:
+	PhysicsBodyPtr m_body;
 
 
+	PhysParticle(const PhysicsBodyInitInfo& init, SceneNode* node)
+	{
+		m_body = node->getSceneGraph().getPhysicsWorld().newInstance<PhysicsBody>(init);
+		m_body->setUserData(node);
+		m_body->activate(false);
+		m_body->setMaterialGroup(PhysicsMaterialBit::PARTICLE);
+		m_body->setMaterialMask(PhysicsMaterialBit::STATIC_GEOMETRY);
+		m_body->setAngularFactor(Vec3(0.0f, 0.0f, 0.0f));
+	}
 
 
-void Particle::revive(const ParticleEmitterNode& pe,
-	F32 prevUpdateTime, F32 crntTime)
-{
-	ParticleBase::revive(pe, prevUpdateTime, crntTime);
+	void kill() override
+	{
+		ParticleBase::kill();
+		m_body->activate(false);
+	}
 
 
-	const ParticleEmitterProperties& props = pe;
+	void revive(const ParticleEmitterNode& pe, const Transform& trf, Second prevUpdateTime, Second crntTime) override
+	{
+		ParticleBase::revive(pe, trf, prevUpdateTime, crntTime);
 
 
-	// pre calculate
-	Bool forceFlag = props.forceEnabled;
-	Bool worldGravFlag = props.wordGravityEnabled;
+		const ParticleEmitterProperties& props = pe;
 
 
-	// activate it (Bullet stuff)
-	body->forceActivationState(ACTIVE_TAG);
-	body->activate();
-	body->clearForces();
-	body->setLinearVelocity(btVector3(0.0, 0.0, 0.0));
-	body->setAngularVelocity(btVector3(0.0, 0.0, 0.0));
+		// pre calculate
+		const Bool forceFlag = props.m_forceEnabled;
+		const Bool worldGravFlag = props.m_wordGravityEnabled;
 
 
-	// force
-	if(forceFlag)
-	{
-		Vec3 forceDir = getRandom(props.particle.forceDirection,
-			props.particle.forceDirectionDeviation);
-		forceDir.normalize();
+		// Activate it
+		m_body->activate(true);
+		m_body->setLinearVelocity(Vec3(0.0f));
+		m_body->setAngularVelocity(Vec3(0.0f));
+		m_body->clearForces();
 
 
-		if(!pe.identityRotation)
+		// force
+		if(forceFlag)
 		{
 		{
-			// the forceDir depends on the particle emitter rotation
-			forceDir = pe.getWorldTransform().getRotation() * forceDir;
-		}
+			Vec3 forceDir = getRandom(props.m_particle.m_forceDirection, props.m_particle.m_forceDirectionDeviation);
+			forceDir.normalize();
 
 
-		F32 forceMag = getRandom(props.particle.forceMagnitude,
-			props.particle.forceMagnitudeDeviation);
+			if(!pe.m_identityRotation)
+			{
+				// the forceDir depends on the particle emitter rotation
+				forceDir = trf.getRotation().getRotationPart() * forceDir;
+			}
 
 
-		body->applyCentralForce(toBt(forceDir * forceMag));
-	}
+			const F32 forceMag =
+				getRandom(props.m_particle.m_forceMagnitude, props.m_particle.m_forceMagnitudeDeviation);
+			m_body->applyForce(forceDir * forceMag, Vec3(0.0f));
+		}
 
 
-	// gravity
-	if(!worldGravFlag)
-	{
-		body->setGravity(toBt(getRandom(props.particle.gravity,
-			props.particle.gravityDeviation)));
-	}
+		// gravity
+		if(!worldGravFlag)
+		{
+			m_body->setGravity(getRandom(props.m_particle.m_gravity, props.m_particle.m_gravityDeviation));
+		}
 
 
-	// Starting pos. In local space
-	Vec3 pos = getRandom(props.particle.startingPos,
-		props.particle.startingPosDeviation);
+		// Starting pos. In local space
+		Vec3 pos = getRandom(props.m_particle.m_startingPos, props.m_particle.m_startingPosDeviation);
 
 
-	if(pe.identityRotation)
-	{
-		pos += pe.getWorldTransform().getOrigin();
+		if(pe.m_identityRotation)
+		{
+			pos += trf.getOrigin().xyz();
+		}
+		else
+		{
+			pos = trf.transform(pos);
+		}
+
+		m_body->setTransform(Transform(pos.xyz0(), trf.getRotation(), 1.0f));
 	}
 	}
-	else
+
+	const Vec4& getPosition() const override
 	{
 	{
-		pos.transform(pe.getWorldTransform());
+		return m_body->getTransform().getOrigin();
 	}
 	}
-
-	btTransform trf(
-		toBt(Transform(pos, pe.getWorldTransform().getRotation(), 1.0)));
-	body->setWorldTransform(trf);
-}
-#endif
+};
 
 
 /// The derived render component for particle emitters.
 /// The derived render component for particle emitters.
-class ParticleEmitterRenderComponent : public MaterialRenderComponent
+class ParticleEmitterNode::MyRenderComponent : public MaterialRenderComponent
 {
 {
 public:
 public:
 	const ParticleEmitterNode& getNode() const
 	const ParticleEmitterNode& getNode() const
@@ -172,7 +220,7 @@ public:
 		return static_cast<const ParticleEmitterNode&>(getSceneNode());
 		return static_cast<const ParticleEmitterNode&>(getSceneNode());
 	}
 	}
 
 
-	ParticleEmitterRenderComponent(SceneNode* node)
+	MyRenderComponent(SceneNode* node)
 		: MaterialRenderComponent(
 		: MaterialRenderComponent(
 			  node, static_cast<ParticleEmitterNode*>(node)->m_particleEmitterResource->getMaterial())
 			  node, static_cast<ParticleEmitterNode*>(node)->m_particleEmitterResource->getMaterial())
 	{
 	{
@@ -185,7 +233,7 @@ public:
 };
 };
 
 
 /// Feedback component
 /// Feedback component
-class MoveFeedbackComponent : public SceneComponent
+class ParticleEmitterNode::MoveFeedbackComponent : public SceneComponent
 {
 {
 public:
 public:
 	MoveFeedbackComponent(SceneNode* node)
 	MoveFeedbackComponent(SceneNode* node)
@@ -215,12 +263,9 @@ ParticleEmitterNode::ParticleEmitterNode(SceneGraph* scene, CString name)
 ParticleEmitterNode::~ParticleEmitterNode()
 ParticleEmitterNode::~ParticleEmitterNode()
 {
 {
 	// Delete simple particles
 	// Delete simple particles
-	if(m_simulationType == SimulationType::SIMPLE)
+	for(ParticleBase* part : m_particles)
 	{
 	{
-		for(ParticleBase* part : m_particles)
-		{
-			getSceneAllocator().deleteInstance(part);
-		}
+		getSceneAllocator().deleteInstance(part);
 	}
 	}
 
 
 	m_particles.destroy(getSceneAllocator());
 	m_particles.destroy(getSceneAllocator());
@@ -241,7 +286,7 @@ Error ParticleEmitterNode::init(const CString& filename)
 	newComponent<SpatialComponent>(&m_obb);
 	newComponent<SpatialComponent>(&m_obb);
 
 
 	// Render component
 	// Render component
-	newComponent<ParticleEmitterRenderComponent>();
+	newComponent<MyRenderComponent>();
 
 
 	// Other
 	// Other
 	m_obb.setCenter(Vec4(0.0));
 	m_obb.setCenter(Vec4(0.0));
@@ -255,7 +300,7 @@ Error ParticleEmitterNode::init(const CString& filename)
 
 
 	if(m_usePhysicsEngine)
 	if(m_usePhysicsEngine)
 	{
 	{
-		createParticlesSimulation(&getSceneGraph());
+		createParticlesPhysicsSimulation(&getSceneGraph());
 		m_simulationType = SimulationType::PHYSICS_ENGINE;
 		m_simulationType = SimulationType::PHYSICS_ENGINE;
 	}
 	}
 	else
 	else
@@ -332,32 +377,27 @@ void ParticleEmitterNode::onMoveComponentUpdate(MoveComponent& move)
 	sp.markForUpdate();
 	sp.markForUpdate();
 }
 }
 
 
-void ParticleEmitterNode::createParticlesSimulation(SceneGraph* scene)
+void ParticleEmitterNode::createParticlesPhysicsSimulation(SceneGraph* scene)
 {
 {
-#if 0
-	collShape = getSceneAllocator().newInstance<btSphereShape>(particle.size);
+	PhysicsCollisionShapePtr collisionShape =
+		getSceneGraph().getPhysicsWorld().newInstance<PhysicsSphere>(m_particle.m_size / 2.0f);
 
 
-	RigidBody::Initializer binit;
-	binit.shape = collShape;
-	binit.group = PhysicsWorld::CG_PARTICLE;
-	binit.mask = PhysicsWorld::CG_MAP;
+	PhysicsBodyInitInfo binit;
+	binit.m_shape = collisionShape;
 
 
-	particles.reserve(maxNumOfParticles);
+	m_particles.create(getSceneAllocator(), m_maxNumOfParticles);
 
 
-	for(U i = 0; i < maxNumOfParticles; i++)
+	for(U i = 0; i < m_maxNumOfParticles; i++)
 	{
 	{
-		binit.mass = getRandom(particle.mass, particle.massDeviation);
+		binit.m_mass = getRandom(m_particle.m_mass, m_particle.m_massDeviation);
 
 
-		Particle* part = getSceneGraph().newSceneNode<Particle>(
-			nullptr, &scene->getPhysics(), binit);
+		PhysParticle* part = getSceneAllocator().newInstance<PhysParticle>(binit, this);
 
 
-		part->size = getRandom(particle.size, particle.sizeDeviation);
-		part->alpha = getRandom(particle.alpha, particle.alphaDeviation);
-		part->getRigidBody()->forceActivationState(DISABLE_SIMULATION);
+		part->m_size = getRandom(m_particle.m_size, m_particle.m_sizeDeviation);
+		part->m_alpha = getRandom(m_particle.m_alpha, m_particle.m_alphaDeviation);
 
 
-		particles.push_back(part);
+		m_particles[i] = part;
 	}
 	}
-#endif
 }
 }
 
 
 void ParticleEmitterNode::createParticlesSimpleSimulation()
 void ParticleEmitterNode::createParticlesSimpleSimulation()
@@ -399,7 +439,7 @@ Error ParticleEmitterNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 			continue;
 			continue;
 		}
 		}
 
 
-		if(p->getTimeOfDeath() < crntTime)
+		if(p->m_timeOfDeath < crntTime)
 		{
 		{
 			// Just died
 			// Just died
 			p->kill();
 			p->kill();
@@ -419,7 +459,7 @@ Error ParticleEmitterNode::frameUpdate(Second prevUpdateTime, Second crntTime)
 			aabbmin = aabbmin.min(origin);
 			aabbmin = aabbmin.min(origin);
 			aabbmax = aabbmax.max(origin);
 			aabbmax = aabbmax.max(origin);
 
 
-			F32 lifePercent = (crntTime - p->getTimeOfBirth()) / (p->getTimeOfDeath() - p->getTimeOfBirth());
+			F32 lifePercent = (crntTime - p->m_timeOfBirth) / (p->m_timeOfDeath - p->m_timeOfBirth);
 
 
 			verts[0] = origin.x();
 			verts[0] = origin.x();
 			verts[1] = origin.y();
 			verts[1] = origin.y();

+ 8 - 133
src/anki/scene/ParticleEmitterNode.h

@@ -9,6 +9,7 @@
 #include <anki/resource/ParticleEmitterResource.h>
 #include <anki/resource/ParticleEmitterResource.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/renderer/RenderQueue.h>
 #include <anki/collision/Obb.h>
 #include <anki/collision/Obb.h>
+#include <anki/physics/Forward.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -18,141 +19,9 @@ class ParticleEmitterNode;
 /// @addtogroup scene
 /// @addtogroup scene
 /// @{
 /// @{
 
 
-/// Particle base
-class ParticleBase
-{
-	friend class ParticleEmitterNode;
-
-public:
-	ParticleBase()
-	{
-	}
-
-	virtual ~ParticleBase()
-	{
-	}
-
-	Second getTimeOfBirth() const
-	{
-		return m_timeOfBirth;
-	}
-
-	Second& getTimeOfBirth()
-	{
-		return m_timeOfBirth;
-	}
-
-	void setTimeOfBirth(const Second x)
-	{
-		m_timeOfBirth = x;
-	}
-
-	Second getTimeOfDeath() const
-	{
-		return m_timeOfDeath;
-	}
-
-	Second& getTimeOfDeath()
-	{
-		return m_timeOfDeath;
-	}
-
-	void setTimeOfDeath(const Second x)
-	{
-		m_timeOfDeath = x;
-	}
-
-	Bool isDead() const
-	{
-		return m_timeOfDeath < 0.0;
-	}
-
-	/// Kill the particle
-	virtual void kill()
-	{
-		ANKI_ASSERT(m_timeOfDeath > 0.0);
-		m_timeOfDeath = -1.0;
-	}
-
-	/// Revive the particle
-	virtual void revive(const ParticleEmitterNode& pe, const Transform& trf, Second prevUpdateTime, Second crntTime);
-
-	/// Only relevant for non-bullet simulations
-	virtual void simulate(const ParticleEmitterNode& pe, Second prevUpdateTime, Second crntTime)
-	{
-		(void)pe;
-		(void)prevUpdateTime;
-		(void)crntTime;
-	}
-
-	virtual const Vec4& getPosition() const = 0;
-
-protected:
-	Second m_timeOfBirth; ///< Keep the time of birth for nice effects
-	Second m_timeOfDeath = -1.0; ///< Time of death. If < 0.0 then dead
-	Second m_size = 1.0;
-	Second m_alpha = 1.0;
-};
-
-/// Simple particle for simple simulation
-class ParticleSimple : public ParticleBase
-{
-public:
-	ParticleSimple()
-	{
-	}
-
-	void revive(const ParticleEmitterNode& pe, const Transform& trf, Second prevUpdateTime, Second crntTime) override;
-
-	void simulate(const ParticleEmitterNode& pe, Second prevUpdateTime, Second crntTime) override;
-
-	const Vec4& getPosition() const override
-	{
-		return m_position;
-	}
-
-private:
-	/// The velocity
-	Vec4 m_velocity = Vec4(0.0);
-	Vec4 m_acceleration = Vec4(0.0);
-	Vec4 m_position;
-};
-
-#if 0
-/// Particle for bullet simulations
-class Particle: public ParticleBase
-{
-public:
-	Particle(
-		const char* name, SceneGraph* scene, // SceneNode
-		// RigidBody
-		PhysicsWorld* masterContainer, const RigidBody::Initializer& init);
-
-	~Particle();
-
-	void kill()
-	{
-		ParticleBase::kill();
-		body->setActivationState(DISABLE_SIMULATION);
-	}
-
-	void revive(const ParticleEmitterNode& pe,
-		F32 prevUpdateTime, F32 crntTime);
-
-private:
-	RigidBody* m_body;
-};
-#endif
-
 /// The particle emitter scene node. This scene node emitts
 /// The particle emitter scene node. This scene node emitts
 class ParticleEmitterNode : public SceneNode, private ParticleEmitterProperties
 class ParticleEmitterNode : public SceneNode, private ParticleEmitterProperties
 {
 {
-	friend class ParticleBase;
-	friend class Particle;
-	friend class ParticleSimple;
-	friend class ParticleEmitterRenderComponent;
-	friend class MoveFeedbackComponent;
-
 public:
 public:
 	ParticleEmitterNode(SceneGraph* scene, CString name);
 	ParticleEmitterNode(SceneGraph* scene, CString name);
 
 
@@ -166,6 +35,12 @@ public:
 	/// @}
 	/// @}
 
 
 private:
 private:
+	class MyRenderComponent;
+	class MoveFeedbackComponent;
+	class ParticleBase;
+	class ParticleSimple;
+	class PhysParticle;
+
 	enum class SimulationType : U8
 	enum class SimulationType : U8
 	{
 	{
 		UNDEFINED,
 		UNDEFINED,
@@ -194,7 +69,7 @@ private:
 
 
 	SimulationType m_simulationType = SimulationType::UNDEFINED;
 	SimulationType m_simulationType = SimulationType::UNDEFINED;
 
 
-	void createParticlesSimulation(SceneGraph* scene);
+	void createParticlesPhysicsSimulation(SceneGraph* scene);
 	void createParticlesSimpleSimulation();
 	void createParticlesSimpleSimulation();
 
 
 	void onMoveComponentUpdate(MoveComponent& move);
 	void onMoveComponentUpdate(MoveComponent& move);