Browse Source

Particles

Panagiotis Christopoulos Charitos 13 years ago
parent
commit
6ee659b19d

+ 2 - 0
include/anki/gl/GlState.h

@@ -86,6 +86,8 @@ public:
 	void setClearStencilValue(const GLint s);
 	void setClearStencilValue(const GLint s);
 
 
 	void setBlendFunctions(const GLenum sFactor, const GLenum dFactor);
 	void setBlendFunctions(const GLenum sFactor, const GLenum dFactor);
+
+	void setDepthMaskEnabled(const Bool enable);
 	/// @}
 	/// @}
 
 
 private:
 private:

+ 2 - 2
include/anki/math/Mat3.h

@@ -9,7 +9,7 @@ namespace anki {
 /// @{
 /// @{
 
 
 /// 3x3 Matrix. Mainly used for rotations. It includes many helpful member
 /// 3x3 Matrix. Mainly used for rotations. It includes many helpful member
-/// functions. Its row major
+/// functions. Its row major. The columns are the x,y,z axis
 class Mat3
 class Mat3
 {
 {
 public:
 public:
@@ -129,7 +129,7 @@ private:
 
 
 static_assert(sizeof(Mat3) == sizeof(F32) * 3 * 3, "Incorrect size");
 static_assert(sizeof(Mat3) == sizeof(F32) * 3 * 3, "Incorrect size");
 
 
-} // end namespace
+} // end namespace anki
 
 
 #include "anki/math/Mat3.inl.h"
 #include "anki/math/Mat3.inl.h"
 
 

+ 4 - 7
include/anki/resource/Material.h

@@ -84,7 +84,7 @@ public:
 		static_cast<MaterialVariableTemplate<T>*>(this)->set(x, size);
 		static_cast<MaterialVariableTemplate<T>*>(this)->set(x, size);
 	}
 	}
 
 
-	virtual U32 getValuesCount() const = 0;
+	U32 getArraySize() const;
 
 
 	/// Given a key return the uniform. If the uniform is not present in the
 	/// Given a key return the uniform. If the uniform is not present in the
 	/// LOD pass key then returns nullptr
 	/// LOD pass key then returns nullptr
@@ -109,10 +109,7 @@ public:
 	}
 	}
 
 
 	/// If false then it should be buildin
 	/// If false then it should be buildin
-	Bool hasValue() const
-	{
-		return getValuesCount() > 0;
-	}
+	virtual Bool hasValues() const = 0;
 	/// @}
 	/// @}
 
 
 private:
 private:
@@ -163,9 +160,9 @@ public:
 		}
 		}
 	}
 	}
 
 
-	U32 getValuesCount() const
+	Bool hasValues() const
 	{
 	{
-		return data.size();
+		return data.size() > 0;
 	}
 	}
 	/// @}
 	/// @}
 
 

+ 27 - 20
include/anki/resource/ParticleEmitterResource.h

@@ -6,7 +6,8 @@
 
 
 namespace anki {
 namespace anki {
 
 
-/// XXX
+/// The particle emitter properties. Different class from
+/// ParticleEmitterResource so it can be inherited
 struct ParticleEmitterProperties
 struct ParticleEmitterProperties
 {
 {
 public:
 public:
@@ -15,38 +16,44 @@ public:
 protected:
 protected:
 	/// @name Particle specific properties
 	/// @name Particle specific properties
 	/// @{
 	/// @{
-	F32 particleLife = 0.0; ///< Required and > 0.0. In seconds
-	F32 particleLifeDeviation = 0.0;
+	struct
+	{
+		F32 life = 10.0; ///< Required and > 0.0. In seconds
+		F32 lifeDeviation = 0.0;
+
+		/// Not-required, any value, Default 0.0, If not set only the gravity
+		/// applies
+		Vec3 forceDirection = Vec3(0.0, 1.0, 0.0);
+		Vec3 forceDirectionDeviation = Vec3(0.0);
+		F32 forceMagnitude = 0.0; ///< Default 0.0
+		F32 forceMagnitudeDeviation = 0.0;
 
 
-	/// Not-required, any value, Default 0.0, If not set only the gravity
-	/// applies
-	Vec3 forceDirection = Vec3(0.0, 1.0, 0.0);
-	Vec3 forceDirectionDeviation = Vec3(0.0);
-	F32 forceMagnitude = 0; ///< Default 0.0
-	F32 forceMagnitudeDeviation = 0.0;
+		F32 mass = 1.0; ///< Required and > 0.0
+		F32 massDeviation = 0.0;
 
 
-	F32 particleMass = 1.0; ///< Required and > 0.0
-	F32 particleMassDeviation = 0.0;
+		/// Not-required, any value. If not set then it uses the world's default
+		Vec3 gravity = Vec3(0.0);
+		Vec3 gravityDeviation = Vec3(0.0);
 
 
-	/// Not-required, any value. If not set then it uses the world's default
-	Vec3 gravity = Vec3(0.0);
-	Vec3 gravityDeviation = Vec3(0.0);
+		Vec3 startingPos = Vec3(0.0); ///< If not set the default is zero
+		Vec3 startingPosDeviation = Vec3(0.0);
 
 
-	Vec3 startingPos = Vec3(0.0); ///< If not set the default is zero
-	Vec3 startingPosDeviation = Vec3(0.0);
+		F32 size = 1.0; ///< The size of the collision shape. Required and > 0.0
+		F32 sizeAnimation = 1.0;
 
 
-	F32 size = 0.0; ///< The size of the collision shape. Required and > 0.0
+		F32 alpha = 1.0;
+	} particle;
 	/// @}
 	/// @}
 
 
 	/// @name Emitter specific properties
 	/// @name Emitter specific properties
 	/// @{
 	/// @{
 
 
 	/// The size of the particles vector. Required
 	/// The size of the particles vector. Required
-	U32 maxNumOfParticles;
+	U32 maxNumOfParticles = 16;
 	/// How often the emitter emits new particles. In secs. Required
 	/// How often the emitter emits new particles. In secs. Required
-	F32 emissionPeriod;
+	F32 emissionPeriod = 1.0;
 	/// How many particles are emitted every emission. Required
 	/// How many particles are emitted every emission. Required
-	U32 particlesPerEmittion;
+	U32 particlesPerEmittion = 1;
 	/// @}
 	/// @}
 };
 };
 
 

+ 8 - 8
include/anki/scene/Movable.h

@@ -58,7 +58,7 @@ public:
 		lTrf.setRotation(x);
 		lTrf.setRotation(x);
 		movableMarkUpdated();
 		movableMarkUpdated();
 	}
 	}
-	void setLocalScale(float x)
+	void setLocalScale(F32 x)
 	{
 	{
 		lTrf.setScale(x);
 		lTrf.setScale(x);
 		movableMarkUpdated();
 		movableMarkUpdated();
@@ -82,40 +82,40 @@ public:
 
 
 	/// @name Mess with the local transform
 	/// @name Mess with the local transform
 	/// @{
 	/// @{
-	void rotateLocalX(float angDegrees)
+	void rotateLocalX(F32 angDegrees)
 	{
 	{
 		lTrf.getRotation().rotateXAxis(angDegrees);
 		lTrf.getRotation().rotateXAxis(angDegrees);
 		movableMarkUpdated();
 		movableMarkUpdated();
 	}
 	}
-	void rotateLocalY(float angDegrees)
+	void rotateLocalY(F32 angDegrees)
 	{
 	{
 		lTrf.getRotation().rotateYAxis(angDegrees);
 		lTrf.getRotation().rotateYAxis(angDegrees);
 		movableMarkUpdated();
 		movableMarkUpdated();
 	}
 	}
-	void rotateLocalZ(float angDegrees)
+	void rotateLocalZ(F32 angDegrees)
 	{
 	{
 		lTrf.getRotation().rotateZAxis(angDegrees);
 		lTrf.getRotation().rotateZAxis(angDegrees);
 		movableMarkUpdated();
 		movableMarkUpdated();
 	}
 	}
-	void moveLocalX(float distance)
+	void moveLocalX(F32 distance)
 	{
 	{
 		Vec3 x_axis = lTrf.getRotation().getColumn(0);
 		Vec3 x_axis = lTrf.getRotation().getColumn(0);
 		lTrf.getOrigin() += x_axis * distance;
 		lTrf.getOrigin() += x_axis * distance;
 		movableMarkUpdated();
 		movableMarkUpdated();
 	}
 	}
-	void moveLocalY(float distance)
+	void moveLocalY(F32 distance)
 	{
 	{
 		Vec3 y_axis = lTrf.getRotation().getColumn(1);
 		Vec3 y_axis = lTrf.getRotation().getColumn(1);
 		lTrf.getOrigin() += y_axis * distance;
 		lTrf.getOrigin() += y_axis * distance;
 		movableMarkUpdated();
 		movableMarkUpdated();
 	}
 	}
-	void moveLocalZ(float distance)
+	void moveLocalZ(F32 distance)
 	{
 	{
 		Vec3 z_axis = lTrf.getRotation().getColumn(2);
 		Vec3 z_axis = lTrf.getRotation().getColumn(2);
 		lTrf.getOrigin() += z_axis * distance;
 		lTrf.getOrigin() += z_axis * distance;
 		movableMarkUpdated();
 		movableMarkUpdated();
 	}
 	}
-	void scale(float s)
+	void scale(F32 s)
 	{
 	{
 		lTrf.getScale() += s;
 		lTrf.getScale() += s;
 		movableMarkUpdated();
 		movableMarkUpdated();

+ 17 - 1
include/anki/scene/ParticleEmitter.h

@@ -41,6 +41,19 @@ public:
 	}
 	}
 	/// @}
 	/// @}
 
 
+	F32 getTimeOfBirth() const
+	{
+		return timeOfBirth;
+	}
+	F32& getTimeOfBirth()
+	{
+		return timeOfBirth;
+	}
+	void setTimeOfBirth(F32 x)
+	{
+		timeOfBirth = x;
+	}
+
 	F32 getTimeOfDeath() const
 	F32 getTimeOfDeath() const
 	{
 	{
 		return timeOfDeath;
 		return timeOfDeath;
@@ -60,7 +73,8 @@ public:
 	}
 	}
 
 
 private:
 private:
-	F32 timeOfDeath; ///< Life of death. If < 0.0 then dead. In seconds
+	F32 timeOfBirth; ///< Keep the time of birth for nice effects
+	F32 timeOfDeath; ///< Time of death. If < 0.0 then dead. In seconds
 };
 };
 
 
 /// The particle emitter scene node. This scene node emitts
 /// The particle emitter scene node. This scene node emitts
@@ -147,6 +161,8 @@ private:
 
 
 	Vector<Transform> instancingTransformations;
 	Vector<Transform> instancingTransformations;
 
 
+	RenderableVariable* alphaRenderableVar = nullptr;
+
 	void init(const char* filename, Scene* scene);
 	void init(const char* filename, Scene* scene);
 
 
 	static F32 getRandom(F32 initial, F32 deviation);
 	static F32 getRandom(F32 initial, F32 deviation);

+ 100 - 13
include/anki/scene/Renderable.h

@@ -9,7 +9,6 @@
 namespace anki {
 namespace anki {
 
 
 class ModelPatchBase;
 class ModelPatchBase;
-class Transform;
 
 
 /// @addtogroup Scene
 /// @addtogroup Scene
 /// @{
 /// @{
@@ -21,15 +20,37 @@ enum BuildinMaterialVariableId
 	BMV_MODEL_VIEW_PROJECTION_MATRIX,
 	BMV_MODEL_VIEW_PROJECTION_MATRIX,
 	BMV_MODEL_VIEW_MATRIX,
 	BMV_MODEL_VIEW_MATRIX,
 	BMV_NORMAL_MATRIX,
 	BMV_NORMAL_MATRIX,
+	BMV_BILLBOARD_MVP_MATRIX,
 	BMV_BLURRING,
 	BMV_BLURRING,
 	BMV_COUNT
 	BMV_COUNT
 };
 };
 
 
+// Forward
+class RenderableVariable;
+
+template<typename T>
+class RenderableVariableTemplate;
+
+/// Renderable variable base. Its a visitable
+typedef VisitableCommonBase<
+	RenderableVariable,
+	RenderableVariableTemplate<F32>,
+	RenderableVariableTemplate<Vec2>,
+	RenderableVariableTemplate<Vec3>,
+	RenderableVariableTemplate<Vec4>,
+	RenderableVariableTemplate<Mat3>,
+	RenderableVariableTemplate<Mat4>,
+	RenderableVariableTemplate<TextureResourcePointer>>
+	RenderableVariableVisitable;
+
 /// A wrapper on top of MaterialVariable
 /// A wrapper on top of MaterialVariable
-class RenderableMaterialVariable
+class RenderableVariable: public RenderableVariableVisitable
 {
 {
 public:
 public:
-	RenderableMaterialVariable(const MaterialVariable* mvar_);
+	typedef RenderableVariableVisitable Base;
+
+	RenderableVariable(const MaterialVariable* mvar_);
+	virtual ~RenderableVariable();
 
 
 	/// @name Accessors
 	/// @name Accessors
 	/// @{
 	/// @{
@@ -38,28 +59,94 @@ public:
 		return buildinId;
 		return buildinId;
 	}
 	}
 
 
-	const MaterialVariable& getMaterialVariable() const
+	const std::string& getName() const
 	{
 	{
-		return *mvar;
+		return mvar->getName();
 	}
 	}
 
 
-	const std::string& getName() const
+	template<typename T>
+	const T* getValues() const
 	{
 	{
-		return mvar->getName();
+		ANKI_ASSERT(Base::getVariadicTypeId<RenderableVariableTemplate<T>>()
+			== Base::getVisitableTypeId());
+		return static_cast<const RenderableVariableTemplate<T>*>(this)->get();
+	}
+
+	/// This will trigger copy on write
+	template<typename T>
+	void setValues(const T* values, U32 size)
+	{
+		ANKI_ASSERT(Base::getVariadicTypeId<RenderableVariableTemplate<T>>()
+			== Base::getVisitableTypeId());
+		static_cast<RenderableVariableTemplate<T>*>(this)->set(
+			values, size);
+	}
+
+	U32 getArraySize() const
+	{
+		return mvar->getArraySize();
 	}
 	}
 	/// @}
 	/// @}
 
 
+	const ShaderProgramUniformVariable* tryFindShaderProgramUniformVariable(
+		const PassLevelKey key) const
+	{
+		return mvar->findShaderProgramUniformVariable(key);
+	}
+
+protected:
+	const MaterialVariable* mvar = nullptr;
+
 private:
 private:
 	BuildinMaterialVariableId buildinId;
 	BuildinMaterialVariableId buildinId;
-	const MaterialVariable* mvar = nullptr;
-	PropertyBase* prop = nullptr;
+};
+
+/// XXX
+template<typename T>
+class RenderableVariableTemplate: public RenderableVariable
+{
+public:
+	typedef T Type;
+
+	RenderableVariableTemplate(const MaterialVariable* mvar_)
+		: RenderableVariable(mvar_)
+	{
+		setupVisitable(this);
+	}
+
+	~RenderableVariableTemplate()
+	{
+		if(copy)
+		{
+			propperDelete(copy);
+		}
+	}
+
+	const T* get() const
+	{
+		ANKI_ASSERT((mvar->hasValues() || copy != nullptr)
+			&& "Variable does not have any values");
+		return (copy) ? copy : mvar->getValues<T>();
+	}
+
+	void set(const T* values, U32 size)
+	{
+		ANKI_ASSERT(size <= mvar->getArraySize());
+		if(copy == nullptr)
+		{
+			copy = new T[mvar->getArraySize()];
+		}
+		memcpy(copy, values, sizeof(T) * size);
+	}
+private:
+	T* copy = nullptr;
 };
 };
 
 
 /// Renderable interface. Implemented by renderable scene nodes
 /// Renderable interface. Implemented by renderable scene nodes
 class Renderable
 class Renderable
 {
 {
 public:
 public:
-	typedef PtrVector<RenderableMaterialVariable> RenderableMaterialVariables;
+	typedef PtrVector<RenderableVariable> RenderableVariables;
 
 
 	Renderable()
 	Renderable()
 	{}
 	{}
@@ -86,11 +173,11 @@ public:
 
 
 	/// @name Accessors
 	/// @name Accessors
 	/// @{
 	/// @{
-	RenderableMaterialVariables::iterator getVariablesBegin()
+	RenderableVariables::iterator getVariablesBegin()
 	{
 	{
 		return vars.begin();
 		return vars.begin();
 	}
 	}
-	RenderableMaterialVariables::iterator getVariablesEnd()
+	RenderableVariables::iterator getVariablesEnd()
 	{
 	{
 		return vars.end();
 		return vars.end();
 	}
 	}
@@ -111,7 +198,7 @@ protected:
 	void init(PropertyMap& pmap);
 	void init(PropertyMap& pmap);
 
 
 private:
 private:
-	RenderableMaterialVariables vars;
+	RenderableVariables vars;
 	Ubo ubo;
 	Ubo ubo;
 	Ubo instancingUbo;
 	Ubo instancingUbo;
 };
 };

+ 10 - 0
include/anki/util/Functions.h

@@ -59,6 +59,16 @@ inline void propperDelete(T*& x)
 	x = nullptr;
 	x = nullptr;
 }
 }
 
 
+/// Delete a pointer properly
+template<typename T>
+inline void propperDeleteArray(T*& x)
+{
+	typedef char TypeMustBeComplete[sizeof(T) ? 1 : -1];
+	(void) sizeof(TypeMustBeComplete);
+	delete[] x;
+	x = nullptr;
+}
+
 /// A simple template trick to remove the pointer from one type
 /// A simple template trick to remove the pointer from one type
 ///
 ///
 /// Example:
 /// Example:

+ 10 - 0
shaders/BsFragmentCommon.glsl

@@ -1,5 +1,7 @@
 #define vTexCoords_DEFINED
 #define vTexCoords_DEFINED
 in vec2 vTexCoords;
 in vec2 vTexCoords;
+#define vInstanceId_DEFINED
+in flat uint vInstanceId;
 
 
 #if defined(PASS_COLOR)
 #if defined(PASS_COLOR)
 layout(location = 0) out vec4 fColor;
 layout(location = 0) out vec4 fColor;
@@ -19,3 +21,11 @@ void writeFais(in vec4 color)
 	fColor = color;
 	fColor = color;
 }
 }
 #endif
 #endif
+
+#define particleAlpha_DEFINED
+void particleAlpha(in sampler2D tex, in float alpha)
+{
+	vec4 color = texture(tex, vTexCoords);
+	color.w *= alpha;
+	writeFais(color);
+}

+ 10 - 0
shaders/BsVertexCommon.glsl

@@ -4,6 +4,7 @@ layout(location = 3) in vec2 texCoords;
 /// @name Varyings
 /// @name Varyings
 /// @{
 /// @{
 out vec2 vTexCoords;
 out vec2 vTexCoords;
+out flat uint vInstanceId;
 /// @}
 /// @}
 
 
 #pragma anki include "shaders/MaterialCommonFunctions.glsl"
 #pragma anki include "shaders/MaterialCommonFunctions.glsl"
@@ -28,3 +29,12 @@ void setTexCoords(in vec2 x)
 {
 {
 	vTexCoords = x;
 	vTexCoords = x;
 }
 }
+
+//==============================================================================
+#define particle_DEFINED
+void particle(in mat4 mvp)
+{
+	vTexCoords = texCoords;
+	gl_Position = mvp * vec4(position, 1);
+	vInstanceId = gl_InstanceID;
+}

+ 11 - 2
shaders/MaterialCommonFunctions.glsl

@@ -1,13 +1,22 @@
 /// Generic add
 /// Generic add
 #define add_DEFINED
 #define add_DEFINED
-#define add(a, b) (a + b)
+#define add(a, b) ((a) + (b))
 
 
 /// Generic mul
 /// Generic mul
 #define mul_DEFINED
 #define mul_DEFINED
-#define mul(a, b) (a * b)
+#define mul(a, b) ((a) * (b))
+
+#define assign_DEFINED
+#define assign(a, b) ((a) = (b))
 
 
 #define vec4ToVec3_DEFINED
 #define vec4ToVec3_DEFINED
 #define vec4ToVec3(a) ((a).xyz)
 #define vec4ToVec3(a) ((a).xyz)
 
 
 #define vec3ToVec4_DEFINED
 #define vec3ToVec4_DEFINED
 #define vec3ToVec4(a, w) (vec4((a), (w)))
 #define vec3ToVec4(a, w) (vec4((a), (w)))
+
+#define getW_DEFINED
+#define getW(a) ((a).w)
+
+#define setW_DEFINED
+#define setW(a, b) ((a).w = (b))

+ 19 - 0
src/gl/GlState.cpp

@@ -135,6 +135,9 @@ void GlState::sync()
 	// blend funcs
 	// blend funcs
 	glGetIntegerv(GL_BLEND_SRC, (GLint*)&blendFuncs[0]);
 	glGetIntegerv(GL_BLEND_SRC, (GLint*)&blendFuncs[0]);
 	glGetIntegerv(GL_BLEND_DST, (GLint*)&blendFuncs[1]);
 	glGetIntegerv(GL_BLEND_DST, (GLint*)&blendFuncs[1]);
+
+	// depth mask
+	glGetIntegerv(GL_DEPTH_WRITEMASK, &depthMask);
 }
 }
 
 
 //==============================================================================
 //==============================================================================
@@ -220,4 +223,20 @@ void GlState::setBlendFunctions(const GLenum sFactor, const GLenum dFactor)
 	}
 	}
 }
 }
 
 
+//==============================================================================
+void GlState::setDepthMaskEnabled(const Bool enable)
+{
+#if ANKI_DEBUG
+	GLint real;
+	glGetIntegerv(GL_DEPTH_WRITEMASK, &real);
+	ANKI_ASSERT(real == depthMask);
+#endif
+
+	if(depthMask != enable)
+	{
+		glDepthMask(enable);
+		depthMask = enable;
+	}
+}
+
 } // end namespace anki
 } // end namespace anki

+ 7 - 7
src/gl/ShaderProgram.cpp

@@ -57,7 +57,7 @@ void ShaderProgramUniformVariable::set(const F32 x[], U32 size) const
 {
 {
 	doCommonSetCode();
 	doCommonSetCode();
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT);
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT);
-	ANKI_ASSERT(size <= getSize());
+	ANKI_ASSERT(size <= getSize() && size != 0);
 	
 	
 	glUniform1fv(getLocation(), size, x);
 	glUniform1fv(getLocation(), size, x);
 }
 }
@@ -67,7 +67,7 @@ void ShaderProgramUniformVariable::set(const Vec2 x[], U32 size) const
 {
 {
 	doCommonSetCode();
 	doCommonSetCode();
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_VEC2);
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_VEC2);
-	ANKI_ASSERT(size <= getSize());
+	ANKI_ASSERT(size <= getSize() && size != 0);
 
 
 	glUniform2fv(getLocation(), size, &(const_cast<Vec2&>(x[0]))[0]);
 	glUniform2fv(getLocation(), size, &(const_cast<Vec2&>(x[0]))[0]);
 }
 }
@@ -77,7 +77,7 @@ void ShaderProgramUniformVariable::set(const Vec3 x[], U32 size) const
 {
 {
 	doCommonSetCode();
 	doCommonSetCode();
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_VEC3);
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_VEC3);
-	ANKI_ASSERT(size <= getSize());
+	ANKI_ASSERT(size <= getSize() && size != 0);
 
 
 	glUniform3fv(getLocation(), size, &(const_cast<Vec3&>(x[0]))[0]);
 	glUniform3fv(getLocation(), size, &(const_cast<Vec3&>(x[0]))[0]);
 }
 }
@@ -87,7 +87,7 @@ void ShaderProgramUniformVariable::set(const Vec4 x[], U32 size) const
 {
 {
 	doCommonSetCode();
 	doCommonSetCode();
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_VEC4);
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_VEC4);
-	ANKI_ASSERT(size <= getSize());
+	ANKI_ASSERT(size <= getSize() && size != 0);
 	
 	
 	glUniform4fv(getLocation(), size, &(const_cast<Vec4&>(x[0]))[0]);
 	glUniform4fv(getLocation(), size, &(const_cast<Vec4&>(x[0]))[0]);
 }
 }
@@ -97,7 +97,7 @@ void ShaderProgramUniformVariable::set(const Mat3 x[], U32 size) const
 {
 {
 	doCommonSetCode();
 	doCommonSetCode();
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_MAT3);
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_MAT3);
-	ANKI_ASSERT(size <= getSize());
+	ANKI_ASSERT(size <= getSize() && size != 0);
 
 
 	glUniformMatrix3fv(getLocation(), size, true, &(x[0])[0]);
 	glUniformMatrix3fv(getLocation(), size, true, &(x[0])[0]);
 }
 }
@@ -107,7 +107,7 @@ void ShaderProgramUniformVariable::set(const Mat4 x[], U32 size) const
 {
 {
 	doCommonSetCode();
 	doCommonSetCode();
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_MAT4);
 	ANKI_ASSERT(getGlDataType() == GL_FLOAT_MAT4);
-	ANKI_ASSERT(size <= getSize());
+	ANKI_ASSERT(size <= getSize() && size != 0);
 
 
 	glUniformMatrix4fv(getLocation(), size, true, &(x[0])[0]);
 	glUniformMatrix4fv(getLocation(), size, true, &(x[0])[0]);
 }
 }
@@ -129,7 +129,7 @@ void ShaderProgramUniformVariable::set(const Texture* const texes[],
 	const U32 count) const
 	const U32 count) const
 {
 {
 	doCommonSetCode();
 	doCommonSetCode();
-	ANKI_ASSERT(count <= getSize());
+	ANKI_ASSERT(count <= getSize() && count != 0);
 	ANKI_ASSERT(count <= 128);
 	ANKI_ASSERT(count <= 128);
 	Array<GLint, 128> units;
 	Array<GLint, 128> units;
 
 

+ 5 - 0
src/renderer/Bs.cpp

@@ -18,6 +18,9 @@ void Bs::init(const RendererInitializer& /*initializer*/)
 //==============================================================================
 //==============================================================================
 void Bs::run()
 void Bs::run()
 {
 {
+	GlStateSingleton::get().enable(GL_DEPTH_TEST);
+	GlStateSingleton::get().setDepthMaskEnabled(false);
+
 	RenderableDrawer& drawer = r->getSceneDrawer();
 	RenderableDrawer& drawer = r->getSceneDrawer();
 	drawer.prepareDraw();
 	drawer.prepareDraw();
 	Scene& scene = r->getScene();
 	Scene& scene = r->getScene();
@@ -30,6 +33,8 @@ void Bs::run()
 		drawer.render(scene.getActiveCamera(), RenderableDrawer::RS_BLEND,
 		drawer.render(scene.getActiveCamera(), RenderableDrawer::RS_BLEND,
 			0, *((*it)->getRenderable()));
 			0, *((*it)->getRenderable()));
 	}
 	}
+
+	GlStateSingleton::get().setDepthMaskEnabled(true);
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 37 - 17
src/renderer/Drawer.cpp

@@ -12,14 +12,14 @@ namespace anki {
 
 
 //==============================================================================
 //==============================================================================
 /// Visitor that sets a uniform
 /// Visitor that sets a uniform
-struct SetupMaterialVariableVisitor
+struct SetupRenderableVariableVisitor
 {
 {
 	PassLevelKey key;
 	PassLevelKey key;
 	const Frustumable* fr = nullptr;
 	const Frustumable* fr = nullptr;
 	Renderer* r = nullptr;
 	Renderer* r = nullptr;
 	Renderable* renderable = nullptr;
 	Renderable* renderable = nullptr;
 	Array<U8, RenderableDrawer::UNIFORM_BLOCK_MAX_SIZE> clientBlock;
 	Array<U8, RenderableDrawer::UNIFORM_BLOCK_MAX_SIZE> clientBlock;
-	RenderableMaterialVariable* rvar = nullptr;
+	RenderableVariable* rvar = nullptr;
 
 
 	/// Set a uniform in a client block
 	/// Set a uniform in a client block
 	template<typename T>
 	template<typename T>
@@ -29,13 +29,11 @@ struct SetupMaterialVariableVisitor
 		ANKI_ASSERT(0);
 		ANKI_ASSERT(0);
 	}
 	}
 
 
-	template<typename MtlVariableTemplate>
-	void visit(MtlVariableTemplate& x)
+	template<typename TRenderableVariableTemplate>
+	void visit(TRenderableVariableTemplate& x)
 	{
 	{
-		const MaterialVariable& mvar = rvar->getMaterialVariable();
-
 		const ShaderProgramUniformVariable* uni =
 		const ShaderProgramUniformVariable* uni =
-			mvar.findShaderProgramUniformVariable(key);
+			x.tryFindShaderProgramUniformVariable(key);
 		if(!uni)
 		if(!uni)
 		{
 		{
 			return;
 			return;
@@ -53,13 +51,11 @@ struct SetupMaterialVariableVisitor
 
 
 		const U maxInstances = 32; // XXX Use a proper vector with allocator
 		const U maxInstances = 32; // XXX Use a proper vector with allocator
 
 
-		Array<Mat4, maxInstances> mv;
-
-		switch(rvar->getBuildinId())
+		switch(x.getBuildinId())
 		{
 		{
 		case BMV_NO_BUILDIN:
 		case BMV_NO_BUILDIN:
-			uniSet<typename MtlVariableTemplate::Type>(
-				*uni, x.get(), x.getValuesCount());
+			uniSet<typename TRenderableVariableTemplate::Type>(
+				*uni, x.get(), x.getArraySize());
 			break;
 			break;
 		case BMV_MODEL_VIEW_PROJECTION_MATRIX:
 		case BMV_MODEL_VIEW_PROJECTION_MATRIX:
 			{
 			{
@@ -74,6 +70,30 @@ struct SetupMaterialVariableVisitor
 				uniSet(*uni, &mvp[0], size);
 				uniSet(*uni, &mvp[0], size);
 			}
 			}
 			break;
 			break;
+		case BMV_BILLBOARD_MVP_MATRIX:
+			{
+				// Calc the billboard rotation matrix
+				Mat3 rot =
+					fr->getViewMatrix().getRotationPart().getTransposed();
+
+				/*Vec3 up(0.0, 1.0, 0.0);
+				Vec3 front = rot.getColumn(2);
+				Vec3 right = up.cross(front);
+				up = front.cross(right);
+				rot.setColumns(right, up, front);*/
+
+				Array<Mat4, maxInstances> bmvp;
+
+				for(U i = 0; i < size; i++)
+				{
+					Transform trf = trfs[i];
+					trf.setRotation(rot);
+					bmvp[i] = vp * Mat4(trf);
+				}
+
+				uniSet(*uni, &bmvp[0], size);
+			}
+			break;
 		case BMV_MODEL_VIEW_MATRIX:
 		case BMV_MODEL_VIEW_MATRIX:
 			{
 			{
 				ANKI_ASSERT(trfs != nullptr);
 				ANKI_ASSERT(trfs != nullptr);
@@ -117,7 +137,7 @@ struct SetupMaterialVariableVisitor
 /// all Property types like strings, we don't need strings in our case
 /// all Property types like strings, we don't need strings in our case
 #define TEMPLATE_SPECIALIZATION(type) \
 #define TEMPLATE_SPECIALIZATION(type) \
 	template<> \
 	template<> \
-	void SetupMaterialVariableVisitor::uniSet<type>( \
+	void SetupRenderableVariableVisitor::uniSet<type>( \
 		const ShaderProgramUniformVariable& uni, const type* values, \
 		const ShaderProgramUniformVariable& uni, const type* values, \
 		U32 size) \
 		U32 size) \
 	{ \
 	{ \
@@ -142,7 +162,7 @@ TEMPLATE_SPECIALIZATION(Mat4)
 
 
 // Texture specialization
 // Texture specialization
 template<>
 template<>
-void SetupMaterialVariableVisitor::uniSet<TextureResourcePointer>(
+void SetupRenderableVariableVisitor::uniSet<TextureResourcePointer>(
 	const ShaderProgramUniformVariable& uni, 
 	const ShaderProgramUniformVariable& uni, 
 	const TextureResourcePointer* values, U32 size)
 	const TextureResourcePointer* values, U32 size)
 {
 {
@@ -162,7 +182,7 @@ void RenderableDrawer::setupShaderProg(
 
 
 	sprog.bind();
 	sprog.bind();
 	
 	
-	SetupMaterialVariableVisitor vis;
+	SetupRenderableVariableVisitor vis;
 
 
 	vis.fr = &fr;
 	vis.fr = &fr;
 	vis.key = key;
 	vis.key = key;
@@ -173,9 +193,9 @@ void RenderableDrawer::setupShaderProg(
 	for(auto it = renderable.getVariablesBegin();
 	for(auto it = renderable.getVariablesBegin();
 		it != renderable.getVariablesEnd(); ++it)
 		it != renderable.getVariablesEnd(); ++it)
 	{
 	{
-		RenderableMaterialVariable* rvar = *it;
+		RenderableVariable* rvar = *it;
 		vis.rvar = rvar;
 		vis.rvar = rvar;
-		rvar->getMaterialVariable().acceptVisitor(vis);
+		rvar->acceptVisitor(vis);
 	}
 	}
 
 
 	// Write the block
 	// Write the block

+ 1 - 1
src/renderer/MainRenderer.cpp

@@ -81,7 +81,7 @@ void MainRenderer::initGl()
 	GlStateSingleton::get().disable(GL_BLEND);
 	GlStateSingleton::get().disable(GL_BLEND);
 	GlStateSingleton::get().disable(GL_STENCIL_TEST);
 	GlStateSingleton::get().disable(GL_STENCIL_TEST);
 	//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
 	//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-	glDepthMask(GL_TRUE);
+	GlStateSingleton::get().setDepthMaskEnabled(true);
 	glDepthFunc(GL_LESS);
 	glDepthFunc(GL_LESS);
 
 
 	try
 	try

+ 1 - 2
src/renderer/Ms.cpp

@@ -44,8 +44,7 @@ void Ms::init(const RendererInitializer& initializer)
 void Ms::run()
 void Ms::run()
 {
 {
 	fbo.bind();
 	fbo.bind();
-	r->clearAfterBindingFbo(
-		GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 
 
 	/*if(ez.getEnabled())
 	/*if(ez.getEnabled())
 	{
 	{

+ 6 - 0
src/resource/Material.cpp

@@ -145,6 +145,12 @@ void MaterialVariable::init(const char* shaderProgVarName,
 	}
 	}
 }
 }
 
 
+//==============================================================================
+U32 MaterialVariable::getArraySize() const
+{
+	return oneSProgVar->getSize();
+}
+
 //==============================================================================
 //==============================================================================
 // Material                                                                    =
 // Material                                                                    =
 //==============================================================================
 //==============================================================================

+ 32 - 22
src/resource/ParticleEmitterResource.cpp

@@ -37,42 +37,50 @@ ParticleEmitterResource::~ParticleEmitterResource()
 void ParticleEmitterResource::load(const char* /*filename*/)
 void ParticleEmitterResource::load(const char* /*filename*/)
 {
 {
 	// dummy load
 	// dummy load
-	particleLife = 7.0;
-	particleLifeDeviation = 2.0;
+	particle.life = 7.0;
+	particle.lifeDeviation = 2.0;
 
 
-	forceDirection = Vec3(0.0, 0.1, 0.0);
-	forceDirectionDeviation = Vec3(0.0, 0.0, 0.0);
-	forceMagnitude = 900.0;
-	forceMagnitudeDeviation = 100.0;
+	particle.size = 1.0;
+	particle.sizeAnimation = 2.0;
 
 
-	particleMass = 1.0;
-	particleMassDeviation = 0.0;
+	particle.forceDirection = Vec3(0.0, 1.0, 0.0);
+	particle.forceDirectionDeviation = Vec3(0.2);
+	particle.forceMagnitude = 100.0;
+	particle.forceMagnitudeDeviation = 0.0;
+
+	particle.mass = 1.0;
+	particle.massDeviation = 0.0;
+
+	particle.gravity = Vec3(0.0, 1.0, 0.0);
+	particle.gravityDeviation = Vec3(0.0, 0.0, 0.0);
+
+	particle.alpha = 0.25;
 
 
 	/*gravity = Vec3(0.0, 10.0, 0.0);
 	/*gravity = Vec3(0.0, 10.0, 0.0);
 	gravityDeviation = Vec3(0.0, 0.0, 0.0);*/
 	gravityDeviation = Vec3(0.0, 0.0, 0.0);*/
 
 
-	startingPos = Vec3(0.0, 1.0, 0.0);
-	startingPosDeviation = Vec3(0.0, 0.0, 0.0);
-	size = 0.5;
+	particle.startingPos = Vec3(0.0, -0.5, 0.0);
+	particle.startingPosDeviation = Vec3(0.3, 0.0, 0.3);
+
 	maxNumOfParticles = 16;
 	maxNumOfParticles = 16;
-	emissionPeriod = 1.0;
+	emissionPeriod = 0.5;
 	particlesPerEmittion = 1;
 	particlesPerEmittion = 1;
 	model.load("data/particles/fire/fire.mdl");
 	model.load("data/particles/fire/fire.mdl");
 
 
 	// sanity checks
 	// sanity checks
 	//
 	//
 
 
-	if(particleLife <= 0.0)
+	if(particle.life <= 0.0)
 	{
 	{
-		throw PE_EXCEPTION(errMsg + "particleLife");
+		throw PE_EXCEPTION(errMsg + "life");
 	}
 	}
 
 
-	if(particleLife - particleLifeDeviation <= 0.0)
+	if(particle.life - particle.lifeDeviation <= 0.0)
 	{
 	{
-		throw PE_EXCEPTION(errMsg + "particleLifeDeviation");
+		throw PE_EXCEPTION(errMsg + "lifeDeviation");
 	}
 	}
 
 
-	if(size <= 0.0)
+	if(particle.size <= 0.0)
 	{
 	{
 		throw PE_EXCEPTION(errMsg + "size");
 		throw PE_EXCEPTION(errMsg + "size");
 	}
 	}
@@ -95,12 +103,14 @@ void ParticleEmitterResource::load(const char* /*filename*/)
 	// Calc some stuff
 	// Calc some stuff
 	//
 	//
 
 
-	forceEnabled =
-		(!isZero(forceDirection.getLengthSquared())
-		|| !isZero(forceDirectionDeviation.getLengthSquared()))
-		&& (forceMagnitude != 0.0 || forceMagnitudeDeviation != 0.0);
+	forceEnabled = !isZero(particle.forceDirection.getLengthSquared());
+	forceEnabled = forceEnabled
+		|| !isZero(particle.forceDirectionDeviation.getLengthSquared());
+	forceEnabled = forceEnabled
+		&& (particle.forceMagnitude != 0.0
+		|| particle.forceMagnitudeDeviation != 0.0);
 
 
-	wordGravityEnabled = isZero(gravity.getLengthSquared());
+	wordGravityEnabled = isZero(particle.gravity.getLengthSquared());
 }
 }
 
 
 } // end namespace anki
 } // end namespace anki

+ 58 - 19
src/scene/ParticleEmitter.cpp

@@ -46,6 +46,16 @@ ParticleEmitter::ParticleEmitter(
 
 
 	instancesCount = particles.size();
 	instancesCount = particles.size();
 	Renderable::init(*this);
 	Renderable::init(*this);
+
+	// Find the "alpha" material variable
+	for(auto it = Renderable::getVariablesBegin();
+		it != Renderable::getVariablesEnd(); ++it)
+	{
+		if((*it)->getName() == "alpha")
+		{
+			alphaRenderableVar = *it;
+		}
+	}
 }
 }
 
 
 //==============================================================================
 //==============================================================================
@@ -108,7 +118,7 @@ void ParticleEmitter::init(const char* filename, Scene* scene)
 	me = other;
 	me = other;
 
 
 	// create the particles
 	// create the particles
-	collShape.reset(new btSphereShape(size));
+	collShape.reset(new btSphereShape(particle.size));
 
 
 	RigidBody::Initializer binit;
 	RigidBody::Initializer binit;
 	binit.shape = collShape.get();
 	binit.shape = collShape.get();
@@ -117,17 +127,17 @@ void ParticleEmitter::init(const char* filename, Scene* scene)
 
 
 	for(U i = 0; i < maxNumOfParticles; i++)
 	for(U i = 0; i < maxNumOfParticles; i++)
 	{
 	{
-		binit.mass = getRandom(particleMass, particleMassDeviation);
+		binit.mass = getRandom(particle.mass, particle.massDeviation);
 
 
-		Particle* particle = new Particle(
+		Particle* part = new Particle(
 			-1.0, 
 			-1.0, 
 			(getName() + std::to_string(i)).c_str(), scene,
 			(getName() + std::to_string(i)).c_str(), scene,
 			Movable::MF_NONE, nullptr,
 			Movable::MF_NONE, nullptr,
 			&scene->getPhysics(), binit);
 			&scene->getPhysics(), binit);
 
 
-		particles.push_back(particle);
+		particles.push_back(part);
 
 
-		particle->forceActivationState(DISABLE_SIMULATION);
+		part->forceActivationState(DISABLE_SIMULATION);
 	}
 	}
 
 
 	timeLeftForNextEmission = 0.0;
 	timeLeftForNextEmission = 0.0;
@@ -145,16 +155,22 @@ void ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime, I frame)
 	Vec3 aabbmin(std::numeric_limits<F32>::max());
 	Vec3 aabbmin(std::numeric_limits<F32>::max());
 	Vec3 aabbmax(std::numeric_limits<F32>::min());
 	Vec3 aabbmax(std::numeric_limits<F32>::min());
 	instancingTransformations.clear();
 	instancingTransformations.clear();
+	Vector<F32> alpha;
 	for(Particle* p : particles)
 	for(Particle* p : particles)
 	{
 	{
-		// if its already dead so dont deactivate it again
-		if(!p->isDead() && p->getTimeOfDeath() < crntTime)
+		if(p->isDead())
+		{
+			// if its already dead so dont deactivate it again
+			continue;
+		}
+
+		if(p->getTimeOfDeath() < crntTime)
 		{
 		{
 			p->setActivationState(DISABLE_SIMULATION);
 			p->setActivationState(DISABLE_SIMULATION);
 			p->setTimeOfDeath(-1.0);
 			p->setTimeOfDeath(-1.0);
 		}
 		}
 		else
 		else
-		{
+		{	
 			const Vec3& origin = p->Movable::getWorldTransform().getOrigin();
 			const Vec3& origin = p->Movable::getWorldTransform().getOrigin();
 
 
 			for(U i = 0; i < 3; i++)
 			for(U i = 0; i < 3; i++)
@@ -163,15 +179,27 @@ void ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime, I frame)
 				aabbmax[i] = std::max(aabbmax[i], origin[i]);
 				aabbmax[i] = std::max(aabbmax[i], origin[i]);
 			}
 			}
 
 
-			instancingTransformations.push_back(
-				p->Movable::getWorldTransform());
+			F32 lifePercent = (crntTime - p->getTimeOfBirth())
+				/ (p->getTimeOfDeath() - p->getTimeOfBirth());
+
+			Transform trf = p->Movable::getWorldTransform();
+			// XXX set a flag for scale
+			trf.setScale(particle.size + (lifePercent * particle.sizeAnimation));
+
+			instancingTransformations.push_back(trf);
+
+			// Set alpha
+			if(alphaRenderableVar)
+			{
+				alpha.push_back((1.0 - lifePercent) * particle.alpha);
+			}
 		}
 		}
 	}
 	}
 
 
 	instancesCount = instancingTransformations.size();
 	instancesCount = instancingTransformations.size();
 	if(instancesCount != 0)
 	if(instancesCount != 0)
 	{
 	{
-		aabb = Aabb(aabbmin - size, aabbmax + size);
+		aabb = Aabb(aabbmin - particle.size, aabbmax + particle.size);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -179,6 +207,15 @@ void ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime, I frame)
 	}
 	}
 	spatialMarkUpdated();
 	spatialMarkUpdated();
 
 
+	if(alphaRenderableVar)
+	{
+		alphaRenderableVar->setValues(&alpha[0], alpha.size());
+	}
+
+	//
+	// Emit new particles
+	//
+
 	// pre calculate
 	// pre calculate
 	Bool forceFlag = particleEmitterResource->hasForce();
 	Bool forceFlag = particleEmitterResource->hasForce();
 	Bool worldGravFlag = particleEmitterResource->usingWorldGravity();
 	Bool worldGravFlag = particleEmitterResource->usingWorldGravity();
@@ -199,10 +236,8 @@ void ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime, I frame)
 
 
 			// life
 			// life
 			p.setTimeOfDeath(
 			p.setTimeOfDeath(
-				getRandom(crntTime + particleLife, particleLifeDeviation));
-
-			//cout << "Time of death " << p.timeOfDeath << endl;
-			//cout << "Particle life " << p.timeOfDeath - crntTime << endl;
+				getRandom(crntTime + particle.life, particle.lifeDeviation));
+			p.setTimeOfBirth(crntTime);
 
 
 			// activate it (Bullet stuff)
 			// activate it (Bullet stuff)
 			body.forceActivationState(ACTIVE_TAG);
 			body.forceActivationState(ACTIVE_TAG);
@@ -217,7 +252,8 @@ void ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime, I frame)
 			if(forceFlag)
 			if(forceFlag)
 			{
 			{
 				Vec3 forceDir =
 				Vec3 forceDir =
-					getRandom(forceDirection, forceDirectionDeviation);
+					getRandom(particle.forceDirection,
+					particle.forceDirectionDeviation);
 				forceDir.normalize();
 				forceDir.normalize();
 
 
 				if(!identityRotation)
 				if(!identityRotation)
@@ -227,7 +263,8 @@ void ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime, I frame)
 				}
 				}
 
 
 				F32 forceMag =
 				F32 forceMag =
-					getRandom(forceMagnitude, forceMagnitudeDeviation);
+					getRandom(particle.forceMagnitude,
+					particle.forceMagnitudeDeviation);
 
 
 				body.applyCentralForce(toBt(forceDir * forceMag));
 				body.applyCentralForce(toBt(forceDir * forceMag));
 			}
 			}
@@ -235,11 +272,13 @@ void ParticleEmitter::frameUpdate(F32 prevUpdateTime, F32 crntTime, I frame)
 			// gravity
 			// gravity
 			if(!worldGravFlag)
 			if(!worldGravFlag)
 			{
 			{
-				body.setGravity(toBt(getRandom(gravity, gravityDeviation)));
+				body.setGravity(toBt(
+					getRandom(particle.gravity, particle.gravityDeviation)));
 			}
 			}
 
 
 			// Starting pos. In local space
 			// Starting pos. In local space
-			Vec3 pos = getRandom(startingPos, startingPosDeviation);
+			Vec3 pos =
+				getRandom(particle.startingPos, particle.startingPosDeviation);
 
 
 			if(identityRotation)
 			if(identityRotation)
 			{
 			{

+ 20 - 13
src/scene/Renderable.cpp

@@ -7,29 +7,30 @@
 namespace anki {
 namespace anki {
 
 
 //==============================================================================
 //==============================================================================
-// CreateNewPropertyVisitor                                                    =
+// CreateNewRenderableVariableVisitor                                                    =
 //==============================================================================
 //==============================================================================
 
 
-/// Create a new RenderableMaterialVariable given a MaterialVariable
-struct CreateNewPropertyVisitor
+/// Create a new RenderableVariable given a MaterialVariable
+struct CreateNewRenderableVariableVisitor
 {
 {
 	const MaterialVariable* mvar = nullptr;
 	const MaterialVariable* mvar = nullptr;
 	PropertyMap* pmap = nullptr;
 	PropertyMap* pmap = nullptr;
-	Renderable::RenderableMaterialVariables* vars = nullptr;
+	Renderable::RenderableVariables* vars = nullptr;
 
 
-	template<typename T>
-	void visit(const T&) const
+	template<typename TMaterialVariableTemplate>
+	void visit(const TMaterialVariableTemplate&) const
 	{
 	{
-		RenderableMaterialVariable* rvar =
-			new RenderableMaterialVariable(mvar);
+		typedef typename TMaterialVariableTemplate::Type Type;
+
+		RenderableVariableTemplate<Type>* rvar =
+			new RenderableVariableTemplate<Type>(mvar);
 
 
-		//pmap->addNewProperty(prop);
 		vars->push_back(rvar);
 		vars->push_back(rvar);
 	}
 	}
 };
 };
 
 
 //==============================================================================
 //==============================================================================
-// RenderableMaterialVariable                                                  =
+// RenderableVariable                                                          =
 //==============================================================================
 //==============================================================================
 
 
 //==============================================================================
 //==============================================================================
@@ -38,10 +39,11 @@ static Array<const char*, BMV_COUNT - 1> buildinNames = {{
 	"modelViewProjectionMat",
 	"modelViewProjectionMat",
 	"modelViewMat",
 	"modelViewMat",
 	"normalMat",
 	"normalMat",
+	"billboardMvpMatrix",
 	"blurring"}};
 	"blurring"}};
 
 
 //==============================================================================
 //==============================================================================
-RenderableMaterialVariable::RenderableMaterialVariable(
+RenderableVariable::RenderableVariable(
 	const MaterialVariable* mvar_)
 	const MaterialVariable* mvar_)
 	: mvar(mvar_)
 	: mvar(mvar_)
 {
 {
@@ -59,13 +61,17 @@ RenderableMaterialVariable::RenderableMaterialVariable(
 	}
 	}
 
 
 	// Sanity checks
 	// Sanity checks
-	if(!mvar->hasValue() && buildinId == BMV_NO_BUILDIN)
+	if(!mvar->hasValues() && buildinId == BMV_NO_BUILDIN)
 	{
 	{
 		ANKI_LOGW("Material variable no buildin and not initialized: "
 		ANKI_LOGW("Material variable no buildin and not initialized: "
 			<< name);
 			<< name);
 	}
 	}
 }
 }
 
 
+//==============================================================================
+RenderableVariable::~RenderableVariable()
+{}
+
 //==============================================================================
 //==============================================================================
 // Renderable                                                                  =
 // Renderable                                                                  =
 //==============================================================================
 //==============================================================================
@@ -79,7 +85,8 @@ void Renderable::init(PropertyMap& pmap)
 {
 {
 	const Material& mtl = getRenderableMaterial();
 	const Material& mtl = getRenderableMaterial();
 
 
-	CreateNewPropertyVisitor vis;
+	// Create the material variables using a visitor
+	CreateNewRenderableVariableVisitor vis;
 	vis.pmap = &pmap;
 	vis.pmap = &pmap;
 	vis.vars = &vars;
 	vis.vars = &vars;
 
 

+ 27 - 10
testapp/Main.cpp

@@ -205,13 +205,26 @@ void init()
 	spot->setShadowEnabled(true);
 	spot->setShadowEnabled(true);
 #endif
 #endif
 
 
-	/*PointLight* point = new PointLight("point0", &scene, Movable::MF_NONE,
-		nullptr);
-	point->setRadius(3.0);
-	point->setDiffuseColor(Vec4(1.0, 0.0, 0.0, 0.0));
-	point->setSpecularColor(Vec4(0.0, 0.0, 1.0, 0.0));
+	// Vase point lights
+	Array<Vec3, 4> vaseLightPos = {{Vec3(32.4, 9, -13.2), Vec3(32.4, 9, 10),
+		Vec3(-37.6001, 9, 10), Vec3(-37.6001, 9, -13.2)}};
+	for(U i = 0; i < vaseLightPos.getSize(); i++)
+	{
+		PointLight* point =
+			new PointLight(("vase_plight" + std::to_string(i)).c_str(),
+			&scene, Movable::MF_NONE, nullptr);
+		point->setRadius(3.5);
+		point->setLocalTranslation(vaseLightPos[i]);
+		point->setDiffuseColor(Vec4(3.0, 0.0, 0.0, 0.0));
+		point->setSpecularColor(Vec4(1.0, 1.0, 0.0, 0.0));
+
+		ParticleEmitter* pe = new ParticleEmitter("todo",
+			("pe" + std::to_string(i)).c_str(), &scene,
+			Movable::MF_NONE, nullptr);
+		pe->setLocalTranslation(vaseLightPos[i]);
+	}
 
 
-	PointLight* point1 = new PointLight("point1", &scene, Movable::MF_NONE,
+	/*PointLight* point1 = new PointLight("point1", &scene, Movable::MF_NONE,
 		nullptr);
 		nullptr);
 	point1->setRadius(3.0);
 	point1->setRadius(3.0);
 	point1->setDiffuseColor(Vec4(2.0, 2.0, 2.0, 0.0));
 	point1->setDiffuseColor(Vec4(2.0, 2.0, 2.0, 0.0));
@@ -248,9 +261,9 @@ void init()
 
 
 	initPhysics();
 	initPhysics();
 
 
-	ParticleEmitter* pe = new ParticleEmitter("todo", "pe", &scene,
+	/*ParticleEmitter* pe = new ParticleEmitter("todo", "pe", &scene,
 		Movable::MF_NONE, nullptr);
 		Movable::MF_NONE, nullptr);
-	(void)pe;
+	(void)pe;*/
 }
 }
 
 
 //==============================================================================
 //==============================================================================
@@ -312,8 +325,7 @@ void mainLoopExtra()
 	}
 	}
 	if(in.getKey(KC_6))
 	if(in.getKey(KC_6))
 	{
 	{
-		mover = SceneSingleton::get().findSceneNode("camera1").getMovable();
-		mover->setLocalTransform(cam->getLocalTransform());
+		mover = SceneSingleton::get().findSceneNode("vase_plight0").getMovable();
 	}
 	}
 
 
 	if(in.getKey(KC_L) == 1)
 	if(in.getKey(KC_L) == 1)
@@ -351,6 +363,11 @@ void mainLoopExtra()
 	{
 	{
 		mover->scale(-scale);
 		mover->scale(-scale);
 	}
 	}
+	if(in.getKey(KC_P))
+	{
+		ANKI_LOGI("pos: " << mover->getWorldTransform().getOrigin());
+	}
+
 
 
 	mover->rotateLocalY(-ang * in.getMousePosition().x() * mouseSensivity * 
 	mover->rotateLocalY(-ang * in.getMousePosition().x() * mouseSensivity * 
 		MainRendererSingleton::get().getAspectRatio());
 		MainRendererSingleton::get().getAspectRatio());