Browse Source

Tessellation optimizations. Scene/resources refactoring

Panagiotis Christopoulos Charitos 12 years ago
parent
commit
98d646d691

+ 1 - 0
include/anki/core/Counters.h

@@ -13,6 +13,7 @@ enum Counter
 	C_RENDERER_MS_TIME,
 	C_RENDERER_IS_TIME,
 	C_RENDERER_PPS_TIME,
+	C_RENDERER_SHADOW_PASSES,
 	C_RENDERER_DRAWCALLS_COUNT,
 	C_RENDERER_VERTICES_COUNT,
 	C_RENDERER_LIGHTS_COUNT,

+ 1 - 1
include/anki/gl/Drawcall.h

@@ -17,7 +17,7 @@ struct Drawcall
 	U32 instancesCount = 1;
 	GLsizei* indicesCountArray = nullptr;
 	const GLvoid** offsetsArray = nullptr;
-	U32 primCount = 1;
+	U32 drawcallCount = 1;
 
 	/// Execute the dracall
 	void enque();

+ 2 - 1
include/anki/renderer/Drawer.h

@@ -48,7 +48,8 @@ private:
 		const ShaderProgram& prog,
 		RenderComponent& renderable,
 		U32* subSpatialIndices,
-		U subSpatialIndicesCount);
+		U subSpatialIndicesCount,
+		F32 flod);
 };
 
 } // end namespace anki

+ 1 - 1
include/anki/renderer/Renderer.h

@@ -230,7 +230,7 @@ public:
 		Vec2& limitsOfNearPlane);
 
 	/// Get the LOD given the distance of an object from the camera
-	U calculateLod(F32 distance) const
+	F32 calculateLod(F32 distance) const
 	{
 		return distance / lodDistance;
 	}

+ 33 - 62
include/anki/resource/Mesh.h

@@ -10,9 +10,8 @@ namespace anki {
 
 class MeshLoader;
 
-/// This is the interface class for meshes. Its interface because the skin
-/// nodes override it
-class MeshBase
+/// Mesh Resource. It contains the geometry packed in VBOs
+class Mesh
 {
 public:
 	enum VertexAttribute
@@ -29,62 +28,71 @@ public:
 		VA_COUNT
 	};
 
-	virtual ~MeshBase()
+	/// Default constructor. Do nothing
+	Mesh()
 	{}
 
-	/// Get info on how to attach a VBO to a VAO
-	virtual void getVboInfo(
-		const VertexAttribute attrib, const Vbo*& vbo,
-		U32& size, GLenum& type, U32& stride, U32& offset) const = 0;
+	/// Does nothing
+	~Mesh()
+	{}
 
 	U32 getTextureChannelsCount() const
 	{
-		return meshProtected.texChannelsCount;
+		return texChannelsCount;
 	}
 
 	Bool hasWeights() const
 	{
-		return meshProtected.weights;
+		return weights;
 	}
 
 	/// Used only to clone the VBO
 	U32 getVerticesCount() const
 	{
-		return meshProtected.vertsCount;
+		return vertsCount;
 	}
 
 	U32 getIndicesCount() const
 	{
-		return meshProtected.indicesCount;
+		return indicesCount;
 	}
 
 	const Obb& getBoundingShape() const
 	{
-		return meshProtected.obb;
+		return obb;
 	}
 
 	/// Get indices count and offset of submesh
 	U32 getIndicesCountSub(U subMeshId, U32& offset) const
 	{
-		ANKI_ASSERT(subMeshId < meshProtected.subMeshes.size());
-		const SubMesh& sm = meshProtected.subMeshes[subMeshId];
+		ANKI_ASSERT(subMeshId < subMeshes.size());
+		const SubMesh& sm = subMeshes[subMeshId];
 		offset = sm.indicesOffset;
 		return sm.indicesCount;
 	}
 
 	const Obb& getBoundingShapeSub(U subMeshId) const
 	{
-		ANKI_ASSERT(subMeshId < meshProtected.subMeshes.size());
-		return meshProtected.subMeshes[subMeshId].obb;
+		ANKI_ASSERT(subMeshId < subMeshes.size());
+		return subMeshes[subMeshId].obb;
 	}
 
+	/// If returns zero then the mesh is a single uniform mesh
 	U32 getSubMeshesCount() const
 	{
-		return meshProtected.subMeshes.size();
+		return subMeshes.size();
 	}
 
+	/// Get info on how to attach a VBO to a VAO
+	void getVboInfo(
+		const VertexAttribute attrib, const Vbo*& vbo,
+		U32& size, GLenum& type, U32& stride, U32& offset) const;
+
 	/// Helper function for correct loading
-	Bool isCompatible(const MeshBase& other) const;
+	Bool isCompatible(const Mesh& other) const;
+
+	/// Load from a .mesh file
+	void load(const char* filename);
 
 protected:
 	/// Per sub mesh data
@@ -95,50 +103,13 @@ protected:
 		Obb obb;
 	};
 
-	struct
-	{
-		Vector<SubMesh> subMeshes;
-		U32 indicesCount;
-		U32 vertsCount;
-		Obb obb;
-		U8 texChannelsCount;
-		Bool8 weights;
-	} meshProtected;
-};
-
-/// Mesh Resource. It contains the geometry packed in VBOs
-class Mesh: public MeshBase
-{
-public:
-	/// @name Constructors
-	/// @{
+	Vector<SubMesh> subMeshes;
+	U32 indicesCount;
+	U32 vertsCount;
+	Obb obb;
+	U8 texChannelsCount;
+	Bool8 weights;
 
-	/// Default constructor. Do nothing
-	Mesh()
-	{}
-
-	/// Load file
-	Mesh(const char* filename)
-	{
-		load(filename);
-	}
-	/// @}
-
-	/// Does nothing
-	~Mesh()
-	{}
-
-	/// @name MeshBase implementers
-	/// @{
-	void getVboInfo(
-		const VertexAttribute attrib, const Vbo*& vbo,
-		U32& size, GLenum& type, U32& stride, U32& offset) const;
-	/// @}
-
-	/// Load from a .mesh file
-	void load(const char* filename);
-
-protected:
 	Vbo vbo;
 	Vbo indicesVbo;
 

+ 10 - 11
include/anki/resource/Model.h

@@ -30,7 +30,7 @@ public:
 		return *modelPatchProtected.mtl;
 	}
 
-	const MeshBase& getMeshBase(const PassLevelKey& key) const
+	const Mesh& getMesh(const PassLevelKey& key) const
 	{
 		ANKI_ASSERT(key.level < modelPatchProtected.meshes.size());
 		return *modelPatchProtected.meshes[key.level];
@@ -44,19 +44,19 @@ public:
 	const Obb& getBoundingShape() const
 	{
 		PassLevelKey key(COLOR_PASS, 0);
-		return getMeshBase(key).getBoundingShape();
+		return getMesh(key).getBoundingShape();
 	}
 
 	const Obb& getBoundingShapeSub(U32 subMeshId) const
 	{
 		PassLevelKey key(COLOR_PASS, 0);
-		return getMeshBase(key).getBoundingShapeSub(subMeshId);
+		return getMesh(key).getBoundingShapeSub(subMeshId);
 	}
 
 	U32 getSubMeshesCount() const
 	{
 		PassLevelKey key(COLOR_PASS, 0);
-		return getMeshBase(key).getSubMeshesCount();
+		return getMesh(key).getSubMeshesCount();
 	}
 
 	/// Given a pass lod key retrieve variables useful for rendering
@@ -70,11 +70,10 @@ public:
 		const PassLevelKey& key, 
 		const Vao*& vao, 
 		const ShaderProgram*& prog,
-		const U32* subMeshIndicesArray,
-		U subMeshIndicesCount,
-		U32* indicesCountArray, 
-		const void** indicesOffsetArray, 
-		U32& primcount) const;
+		const U32* subMeshIndicesArray, U subMeshIndicesCount,
+		Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCountArray,
+		Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesOffsetArray, 
+		U32& drawcallCount) const;
 
 protected:
 	struct
@@ -82,7 +81,7 @@ protected:
 		/// Array [lod][pass]
 		VaosContainer vaos;
 		Material* mtl = nullptr;
-		Vector<MeshBase*> meshes;
+		Vector<Mesh*> meshes;
 	} modelPatchProtected;
 
 	/// Create VAOs using a material and a mesh. It writes a VaosContainer and
@@ -94,7 +93,7 @@ private:
 	/// VAO
 	static void createVao(
 		const ShaderProgram &prog,
-		const MeshBase& mesh,
+		const Mesh& mesh,
 		Vao& vao);
 };
 

+ 11 - 3
include/anki/scene/ModelNode.h

@@ -71,10 +71,18 @@ public:
 	/// @name RenderComponent virtuals
 	/// @{
 
-	/// Implements RenderComponent::getModelPatchBase
-	const ModelPatchBase& getModelPatchBase()
+	/// Implements RenderComponent::getRenderingData
+	void getRenderingData(
+		const PassLevelKey& key, 
+		const Vao*& vao, const ShaderProgram*& prog,
+		const U32* subMeshIndicesArray, U subMeshIndicesCount,
+		Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCountArray,
+		Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesOffsetArray, 
+		U32& drawcallCount) const
 	{
-		return *modelPatch;
+		modelPatch->getRenderingDataSub(key, vao, prog, 
+			subMeshIndicesArray, subMeshIndicesCount, 
+			indicesCountArray, indicesOffsetArray, drawcallCount);
 	}
 
 	/// Implements  RenderComponent::getMaterial

+ 8 - 2
include/anki/scene/ParticleEmitter.h

@@ -160,8 +160,14 @@ public:
 	/// @name RenderComponent virtuals
 	/// @{
 
-	/// Implements RenderComponent::getModelPatchBase
-	const ModelPatchBase& getModelPatchBase();
+	/// Implements RenderComponent::getRenderingData
+	void getRenderingData(
+		const PassLevelKey& key, 
+		const Vao*& vao, const ShaderProgram*& prog,
+		const U32* subMeshIndicesArray, U subMeshIndicesCount,
+		Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCountArray,
+		Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesOffsetArray, 
+		U32& drawcallCount) const;
 
 	/// Implements  RenderComponent::getMaterial
 	const Material& getMaterial();

+ 12 - 8
include/anki/scene/RenderComponent.h

@@ -22,6 +22,7 @@ enum BuildinMaterialVariableId
 	BMV_VP_MATRIX,
 	BMV_NORMAL_MATRIX,
 	BMV_BILLBOARD_MVP_MATRIX,
+	BMV_MAX_TESS_LEVEL,
 	BMV_BLURRING,
 	BMV_MS_DEPTH_MAP,
 	BMV_COUNT
@@ -163,13 +164,21 @@ class RenderComponent
 public:
 	typedef SceneVector<RenderComponentVariable*> Variables;
 
-	/// @param node Pass note to steal it's allocator
+	/// @param node Pass node to steal it's allocator
 	RenderComponent(SceneNode* node);
 
 	virtual ~RenderComponent();
 
-	/// Access to VAOs
-	virtual const ModelPatchBase& getModelPatchBase() = 0;
+	/// Get information for rendering.
+	/// Given an array of submeshes that are visible return the correct indices
+	/// offsets and counts
+	virtual void getRenderingData(
+		const PassLevelKey& key, 
+		const Vao*& vao, const ShaderProgram*& prog,
+		const U32* subMeshIndicesArray, U subMeshIndicesCount,
+		Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCountArray,
+		Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesOffsetArray, 
+		U32& drawcallCount) const = 0;
 
 	/// Access the material
 	virtual const Material& getMaterial() = 0;
@@ -208,11 +217,6 @@ public:
 		}
 	}
 
-	U32 getSubMeshesCount()
-	{
-		return getModelPatchBase().getSubMeshesCount();
-	}
-
 	/// Reset on frame start
 	void resetFrame()
 	{}

+ 4 - 0
include/anki/scene/SkinNode.h

@@ -12,6 +12,8 @@ namespace anki {
 
 class Skin;
 
+#if 0
+
 /// Skin specific mesh. It contains a number of VBOs for transform feedback
 class SkinMesh: public MeshBase
 {
@@ -315,6 +317,8 @@ private:
 		SceneVector<Vec3>& heads, SceneVector<Vec3>& tails);
 };
 
+#endif
+
 } // end namespace
 
 #endif

+ 11 - 3
include/anki/scene/StaticGeometryNode.h

@@ -40,10 +40,18 @@ public:
 	/// @name RenderComponent virtuals
 	/// @{
 
-	/// Implements RenderComponent::getModelPatchBase
-	const ModelPatchBase& getModelPatchBase()
+	/// Implements RenderComponent::getRenderingData
+	void getRenderingData(
+		const PassLevelKey& key, 
+		const Vao*& vao, const ShaderProgram*& prog,
+		const U32* subMeshIndicesArray, U subMeshIndicesCount,
+		Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCountArray,
+		Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesOffsetArray, 
+		U32& drawcallCount) const
 	{
-		return *modelPatch;
+		modelPatch->getRenderingDataSub(key, vao, prog, 
+			subMeshIndicesArray, subMeshIndicesCount, 
+			indicesCountArray, indicesOffsetArray, drawcallCount);
 	}
 
 	/// Implements  RenderComponent::getMaterial

+ 134 - 100
shaders/MsCommonTessc.glsl

@@ -1,10 +1,12 @@
 #pragma anki include "shaders/MsBsCommon.glsl"
 
-layout(vertices = 1) out;
+layout(vertices = 3) out;
 
-// Varyings in
-in highp vec3 vPosition[];
-in highp vec2 vTexCoords[];
+#define IID gl_InvocationID
+
+// In
+in vec3 vPosition[];
+in vec2 vTexCoords[];
 in mediump vec3 vNormal[];
 #if PASS_COLOR
 in mediump vec4 vTangent[];
@@ -14,18 +16,19 @@ flat in uint vInstanceId[];
 #endif
 
 // Out
-struct CommonPatch
-{
-	vec3 positions[3];
-	vec2 texCoord[3];
-	vec3 normal[3];
+out vec3 tcPosition[];
+out vec2 tcTexCoord[];
+out vec3 tcNormal[];
 #if PASS_COLOR
-	vec4 tangent[3];
+out vec4 tcTangent[];
 #endif
+
 #if INSTANCE_ID_FRAGMENT_SHADER
+struct CommonPatch
+{
 	uint instanceId;
-#endif
 };
+#endif
 
 struct PNPatch
 {
@@ -38,9 +41,9 @@ struct PNPatch
 	vec3 pos111;
 };
 
-#define pos030 positions[0]
-#define pos003 positions[1]
-#define pos300 positions[2]
+#define pos030 tcPosition[0]
+#define pos003 tcPosition[1]
+#define pos300 tcPosition[2]
 
 struct PhongPatch
 {
@@ -49,7 +52,9 @@ struct PhongPatch
 
 out patch PNPatch pnPatch;
 out patch PhongPatch phongPatch;
+#if INSTANCE_ID_FRAGMENT_SHADER
 out patch CommonPatch commonPatch;
+#endif
 
 // Project point to plane
 vec3 projectToPlane(vec3 point, vec3 planePoint, vec3 planeNormal)
@@ -64,39 +69,38 @@ vec3 projectToPlane(vec3 point, vec3 planePoint, vec3 planeNormal)
 void calcPositions()
 {
 	// The original vertices stay the same
-	commonPatch.pos030 = vPosition[0];
-	commonPatch.pos003 = vPosition[1];
-	commonPatch.pos300 = vPosition[2];
+	pos030 = vPosition[0];
+	pos003 = vPosition[1];
+	pos300 = vPosition[2];
 
 	// edges are names according to the opposing vertex
-	vec3 edgeB300 = commonPatch.pos003 - commonPatch.pos030;
-	vec3 edgeB030 = commonPatch.pos300 - commonPatch.pos003;
-	vec3 edgeB003 = commonPatch.pos030 - commonPatch.pos300;
+	vec3 edgeB300 = pos003 - pos030;
+	vec3 edgeB030 = pos300 - pos003;
+	vec3 edgeB003 = pos030 - pos300;
 
 	// Generate two midpoints on each edge
-	pnPatch.pos021 = commonPatch.pos030 + edgeB300 / 3.0;
-	pnPatch.pos012 = commonPatch.pos030 + edgeB300 * 2.0 / 3.0;
-	pnPatch.pos102 = commonPatch.pos003 + edgeB030 / 3.0;
-	pnPatch.pos201 = commonPatch.pos003 + edgeB030 * 2.0 / 3.0;
-	pnPatch.pos210 = commonPatch.pos300 + edgeB003 / 3.0;
-	pnPatch.pos120 = commonPatch.pos300 + edgeB003 * 2.0 / 3.0;
+	pnPatch.pos021 = pos030 + edgeB300 / 3.0;
+	pnPatch.pos012 = pos030 + edgeB300 * 2.0 / 3.0;
+	pnPatch.pos102 = pos003 + edgeB030 / 3.0;
+	pnPatch.pos201 = pos003 + edgeB030 * 2.0 / 3.0;
+	pnPatch.pos210 = pos300 + edgeB003 / 3.0;
+	pnPatch.pos120 = pos300 + edgeB003 * 2.0 / 3.0;
 
 	pnPatch.pos021 = projectToPlane(
-		pnPatch.pos021, commonPatch.pos030, commonPatch.normal[0]);
+		pnPatch.pos021, pos030, tcNormal[0]);
 	pnPatch.pos012 = projectToPlane(
-		pnPatch.pos012, commonPatch.pos003, commonPatch.normal[1]);
+		pnPatch.pos012, pos003, tcNormal[1]);
 	pnPatch.pos102 = projectToPlane(
-		pnPatch.pos102, commonPatch.pos003, commonPatch.normal[1]);
+		pnPatch.pos102, pos003, tcNormal[1]);
 	pnPatch.pos201 = projectToPlane(
-		pnPatch.pos201, commonPatch.pos300, commonPatch.normal[2]);
+		pnPatch.pos201, pos300, tcNormal[2]);
 	pnPatch.pos210 = projectToPlane(
-		pnPatch.pos210, commonPatch.pos300, commonPatch.normal[2]);
+		pnPatch.pos210, pos300, tcNormal[2]);
 	pnPatch.pos120 = projectToPlane(
-		pnPatch.pos120, commonPatch.pos030, commonPatch.normal[0]);
+		pnPatch.pos120, pos030, tcNormal[0]);
 
 	// Handle the center
-	vec3 center = (commonPatch.pos003 + commonPatch.pos030 
-		+ commonPatch.pos300) / 3.0;
+	vec3 center = (pos003 + pos030 + pos300) / 3.0;
 	pnPatch.pos111 = (pnPatch.pos021 + pnPatch.pos012 + pnPatch.pos102 +
 		pnPatch.pos201 + pnPatch.pos210 + pnPatch.pos120) / 6.0;
 	pnPatch.pos111 += (pnPatch.pos111 - center) / 2.0;
@@ -146,6 +150,59 @@ bool isFaceOutsideClipSpace(in vec2 posNdc[3])
 		posOutsideClipSpace(posNdc[2])));
 }
 
+void setSilhouetteTessLevels(in mat3 normalMat, in float maxTessLevel)
+{
+	// Calculate the normals in view space
+	vec3 nv[3];
+	for(int i = 0; i < 3; i++)
+	{
+		nv[i] = normalMat * vNormal[i];
+	}
+
+	gl_TessLevelOuter[0] = calcEdgeTessLevel(nv[1], nv[2], maxTessLevel);
+	gl_TessLevelOuter[1] = calcEdgeTessLevel(nv[2], nv[0], maxTessLevel);
+	gl_TessLevelOuter[2] = calcEdgeTessLevel(nv[0], nv[1], maxTessLevel);
+	gl_TessLevelInner[0] = (gl_TessLevelOuter[0] + gl_TessLevelOuter[1]
+		+ gl_TessLevelOuter[2]) / 3.0;
+}
+
+void setConstantTessLevels(in float maxTessLevel)
+{
+	gl_TessLevelOuter[0] = maxTessLevel;
+	gl_TessLevelOuter[1] = maxTessLevel;
+	gl_TessLevelOuter[2] = maxTessLevel;
+	gl_TessLevelInner[0] = maxTessLevel;
+}
+
+void discardPatch()
+{
+	gl_TessLevelOuter[0] = 0.0;
+	gl_TessLevelOuter[1] = 0.0;
+	gl_TessLevelOuter[2] = 0.0;
+	gl_TessLevelInner[0] = 0.0;
+}
+
+// Check if a face is visible
+bool isFaceVisible(in mat4 mvp)
+{
+	// Calculate clip positions
+	vec2 clip[3];
+	for(int i = 0 ; i < 3 ; i++) 
+	{
+		vec4 v = mvp * vec4(vPosition[i], 1.0);
+		clip[i] = v.xy / v.w;
+	}
+
+	// Check the face orientation and clipping
+	return isFaceFrontFacing(clip) && !isFaceOutsideClipSpace(clip);
+}
+
+float calcPhongTerm(int ivId, int i, vec3 q)
+{
+	vec3 qMinusP = q - vPosition[i];
+	return q[ivId] - dot(qMinusP, vNormal[i]) * vNormal[i][ivId];
+}
+
 // This function is part of the point-normal tessellation method
 #define tessellatePNPositionNormalTangentTexCoord_DEFINED
 void tessellatePNPositionNormalTangentTexCoord(
@@ -165,10 +222,10 @@ void tessellatePNPositionNormalTangentTexCoord(
 
 		for(int i = 0 ; i < 3 ; i++) 
 		{		
-			commonPatch.texCoord[i] = vTexCoords[i];
-			commonPatch.normal[i] = vNormal[i];
+			tcTexCoord[i] = vTexCoords[i];
+			tcNormal[i] = vNormal[i];
 #if PASS_COLOR
-			commonPatch.tangent[i] = vTangent[i];
+			tcTangent[i] = vTangent[i];
 #endif
 		}
 
@@ -186,94 +243,71 @@ void tessellatePNPositionNormalTangentTexCoord(
 	gl_TessLevelInner[0] = tessLevel;
 }
 
-float calcTerm(int ivId, int i, vec3 q)
-{
-	vec3 qMinusP = q - vPosition[i];
-	return q[ivId] - dot(qMinusP, vNormal[i]) * vNormal[i][ivId];
-}
-
 #define tessellatePhongPositionNormalTangentTexCoord_DEFINED
 void tessellatePhongPositionNormalTangentTexCoord(
 	in float maxTessLevel,
 	in mat4 mvp,
 	in mat3 normalMat)
 {
-	// Calculate clip positions
-	vec2 clip[3];
-	for(int i = 0 ; i < 3 ; i++) 
-	{
-		vec4 v = mvp * vec4(vPosition[i], 1.0);
-		clip[i] = v.xy / v.w;
-	}
-
-	// Check the face orientation and clipping
-	if(isFaceFrontFacing(clip) && !isFaceOutsideClipSpace(clip))
+	if(IID == 0)
 	{
-		// The face is front facing and inside the clip space
-
-		for(int i = 0 ; i < 3 ; i++) 
+		if(isFaceVisible(mvp))
 		{
-			commonPatch.positions[i] = vPosition[i];
-			commonPatch.texCoord[i] = vTexCoords[i];
-			commonPatch.normal[i] = vNormal[i];
-#if PASS_COLOR
-			commonPatch.tangent[i] = vTangent[i];
-#endif
-
-			phongPatch.terms[i][0] = 
-				calcTerm(i, 0, vPosition[1]) + calcTerm(i, 1, vPosition[0]);
-			phongPatch.terms[i][1] = 
-				calcTerm(i, 1, vPosition[2]) + calcTerm(i, 2, vPosition[1]);
-			phongPatch.terms[i][2] = 
-				calcTerm(i, 2, vPosition[0]) + calcTerm(i, 0, vPosition[2]);
+			setSilhouetteTessLevels(normalMat, maxTessLevel);
 		}
-
-		// Calculate the normals in view space
-		vec3 nv[3];
-		for(int i = 0; i < 3; i++)
+		else
 		{
-			nv[i] = normalMat * vNormal[i];
+			discardPatch();
 		}
-
-		gl_TessLevelOuter[0] = calcEdgeTessLevel(nv[1], nv[2], maxTessLevel);
-		gl_TessLevelOuter[1] = calcEdgeTessLevel(nv[2], nv[0], maxTessLevel);
-		gl_TessLevelOuter[2] = calcEdgeTessLevel(nv[0], nv[1], maxTessLevel);
-		gl_TessLevelInner[0] = 1.0;
-		gl_TessLevelInner[0] = (gl_TessLevelOuter[0] + gl_TessLevelOuter[1]
-			+ gl_TessLevelOuter[2]) / 3.0;
 	}
-	else
+
+	tcPosition[IID] = vPosition[IID]; // Do that here to trick the barrier
+
+	barrier();
+
+	if(gl_TessLevelOuter[0] > 0.0)
 	{
-		gl_TessLevelOuter[0] = 0.0;
-		gl_TessLevelOuter[1] = 0.0;
-		gl_TessLevelOuter[2] = 0.0;
-		gl_TessLevelInner[0] = 0.0;
+		tcTexCoord[IID] = vTexCoords[IID];
+		tcNormal[IID] = vNormal[IID];
+#if PASS_COLOR
+		tcTangent[IID] = vTangent[IID];
+#endif
+
+		phongPatch.terms[IID][0] = calcPhongTerm(IID, 0, vPosition[1]) 
+			+ calcPhongTerm(IID, 1, vPosition[0]);
+		phongPatch.terms[IID][1] = calcPhongTerm(IID, 1, vPosition[2]) 
+			+ calcPhongTerm(IID, 2, vPosition[1]);
+		phongPatch.terms[IID][2] = calcPhongTerm(IID, 2, vPosition[0]) 
+			+ calcPhongTerm(IID, 0, vPosition[2]);
 	}
 }
 
-
 #define tessellateDispMapPositionNormalTangentTexCoord_DEFINED
 void tessellateDispMapPositionNormalTangentTexCoord(
 	in float maxTessLevel,
 	in mat4 mvp,
 	in mat3 normalMat)
 {
-	for(int i = 0 ; i < 3 ; i++) 
+	if(IID == 0)
 	{
-		commonPatch.positions[i] = vPosition[i];
-		commonPatch.texCoord[i] = vTexCoords[i];
-		commonPatch.normal[i] = vNormal[i];
-#if PASS_COLOR
-		commonPatch.tangent[i] = vTangent[i];
-#endif
-	}
+		if(isFaceVisible(mvp))
+		{
+			setSilhouetteTessLevels(normalMat, maxTessLevel);
 
 #if INSTANCE_ID_FRAGMENT_SHADER
-	commonPatch.instanceId = vInstanceId[0];
+			commonPatch.instanceId = vInstanceId[0];
 #endif
+		}
+		else
+		{
+			discardPatch();
+		}
+	}
 
-	gl_TessLevelOuter[0] = maxTessLevel;
-	gl_TessLevelOuter[1] = maxTessLevel;
-	gl_TessLevelOuter[2] = maxTessLevel;
-	gl_TessLevelInner[0] = maxTessLevel;
+	tcPosition[IID] = vPosition[IID];
+	tcTexCoord[IID] = vTexCoords[IID];
+	tcNormal[IID] = vNormal[IID];
+#if PASS_COLOR
+	tcTangent[IID] = vTangent[IID];
+#endif
 }

+ 32 - 29
shaders/MsCommonTesse.glsl

@@ -11,31 +11,34 @@ struct PNPatch
 	vec3 pos111;
 };
 
-#define pos030 positions[0]
-#define pos003 positions[1]
-#define pos300 positions[2]
+#define pos030 tcPosition[0]
+#define pos003 tcPosition[1]
+#define pos300 tcPosition[2]
 
 struct PhongPatch
 {
 	vec3 terms[3];
 };
 
+#if INSTANCE_ID_FRAGMENT_SHADER
 struct CommonPatch
 {
-	vec3 positions[3];
-	vec2 texCoord[3];
-	vec3 normal[3];
-#if PASS_COLOR
-	vec4 tangent[3];
-#endif
-#if INSTANCE_ID_FRAGMENT_SHADER
 	uint instanceId;
-#endif
 };
+#endif
 
 in patch PNPatch pnPatch;
 in patch PhongPatch phongPatch;
+#if INSTANCE_ID_FRAGMENT_SHADER
 in patch CommonPatch commonPatch;
+#endif
+
+in vec3 tcPosition[];
+in vec2 tcTexCoord[];
+in vec3 tcNormal[];
+#if PASS_COLOR
+in vec4 tcTangent[];
+#endif
 
 // Varyings out
 out highp vec2 teTexCoords;
@@ -51,12 +54,12 @@ out mediump vec4 teTangent;
 void tessellatePNPositionNormalTangentTexCoord(in mat4 mvp, in mat3 normalMat)
 {
 #if PASS_COLOR
-	teNormal = normalize(normalMat * INTERPOLATE(commonPatch.normal));
-	teTangent = INTERPOLATE(commonPatch.tangent);
+	teNormal = normalize(normalMat * INTERPOLATE(tcNormal));
+	teTangent = INTERPOLATE(tcTangent);
 	teTangent.xyz = normalize(normalMat * teTangent.xyz);
 #endif
 
-	teTexCoords = INTERPOLATE(commonPatch.texCoord);
+	teTexCoords = INTERPOLATE(tcTexCoord);
 
 	float u = gl_TessCoord.x;
 	float v = gl_TessCoord.y;
@@ -70,9 +73,9 @@ void tessellatePNPositionNormalTangentTexCoord(in mat4 mvp, in mat3 normalMat)
 	float wPow2 = pow(w, 2);
 
 	vec3 pos = 
-		commonPatch.pos300 * wPow3
-		+ commonPatch.pos030 * uPow3
-		+ commonPatch.pos003 * vPow3
+		pos300 * wPow3
+		+ pos030 * uPow3
+		+ pos003 * vPow3
 		+ pnPatch.pos210 * 3.0 * wPow2 * u 
 		+ pnPatch.pos120 * 3.0 * w * uPow2 
 		+ pnPatch.pos201 * 3.0 * wPow2 * v 
@@ -89,15 +92,15 @@ void tessellatePhongPositionNormalTangentTexCoord(
 	in mat4 mvp, in mat3 normalMat)
 {
 #if PASS_COLOR
-	teNormal = normalize(normalMat * INTERPOLATE(commonPatch.normal));
-	teTangent = INTERPOLATE(commonPatch.tangent);
+	teNormal = normalize(normalMat * INTERPOLATE(tcNormal));
+	teTangent = INTERPOLATE(tcTangent);
 	teTangent.xyz = normalize(normalMat * teTangent.xyz);
 #endif
 
-	teTexCoords = INTERPOLATE(commonPatch.texCoord);
+	teTexCoords = INTERPOLATE(tcTexCoord);
 
 	// interpolated position
-	vec3 barPos = INTERPOLATE(commonPatch.positions);
+	vec3 barPos = INTERPOLATE(tcPosition);
 
 	// build terms
 	vec3 termIJ = vec3(
@@ -117,9 +120,9 @@ void tessellatePhongPositionNormalTangentTexCoord(
 
 	// phong tesselated pos
 	vec3 phongPos = 
-		tc2[0] * commonPatch.positions[0]
-		 + tc2[1] * commonPatch.positions[1]
-		 + tc2[2] * commonPatch.positions[2]
+		tc2[0] * tcPosition[0]
+		 + tc2[1] * tcPosition[1]
+		 + tc2[2] * tcPosition[2]
 		 + gl_TessCoord[0] * gl_TessCoord[1] * termIJ
 		 + gl_TessCoord[1] * gl_TessCoord[2] * termJK
 		 + gl_TessCoord[2] * gl_TessCoord[0] * termIK;
@@ -133,19 +136,19 @@ void tessellatePhongPositionNormalTangentTexCoord(
 void tessellateDispMapPositionNormalTangentTexCoord(
 	in mat4 mvp, in mat3 normalMat, in sampler2D dispMap)
 {
-	vec3 norm = INTERPOLATE(commonPatch.normal);
+	vec3 norm = INTERPOLATE(tcNormal);
 #if PASS_COLOR
 	teNormal = normalize(normalMat * norm);
-	teTangent = INTERPOLATE(commonPatch.tangent);
+	teTangent = INTERPOLATE(tcTangent);
 	teTangent.xyz = normalize(normalMat * teTangent.xyz);
 #endif
 
-	teTexCoords = INTERPOLATE(commonPatch.texCoord);
+	teTexCoords = INTERPOLATE(tcTexCoord);
 
 	float height = texture(dispMap, teTexCoords).r;
-	height *= 1.0;
+	height = height * 0.7 - 0.35;
 
-	vec3 pos = INTERPOLATE(commonPatch.positions) + norm * height;
+	vec3 pos = INTERPOLATE(tcPosition) + norm * height;
 	gl_Position = mvp * vec4(pos, 1.0);
 }
 

+ 1 - 0
src/core/Counters.cpp

@@ -31,6 +31,7 @@ static const Array<CounterInfo, C_COUNT> cinfo = {{
 	{"RENDERER_MS_TIME", CF_PER_FRAME | CF_PER_RUN | CF_F64},
 	{"RENDERER_IS_TIME", CF_PER_FRAME | CF_PER_RUN | CF_F64},
 	{"RENDERER_PPS_TIME", CF_PER_FRAME | CF_PER_RUN | CF_F64},
+	{"RENDERER_SHADOW_PASSES", CF_PER_FRAME | CF_PER_RUN | CF_U64},
 	{"RENDERER_DRAWCALLS_COUNT", CF_PER_RUN | CF_U64},
 	{"RENDERER_VERTICES_COUNT", CF_PER_FRAME | CF_PER_RUN | CF_U64},
 	{"RENDERER_LIGHTS_COUNT", CF_PER_RUN | CF_U64},

+ 4 - 4
src/gl/Drawcall.cpp

@@ -6,7 +6,7 @@ namespace anki {
 //==============================================================================
 void Drawcall::enque()
 {
-	ANKI_ASSERT(primitiveType && instancesCount > 0 && primCount > 0
+	ANKI_ASSERT(primitiveType && instancesCount > 0 && drawcallCount > 0
 		&& offsetsArray);
 
 	if(indicesCountArray != nullptr)
@@ -19,7 +19,7 @@ void Drawcall::enque()
 		{
 			// No  instancing
 
-			if(primCount == 1)
+			if(drawcallCount == 1)
 			{
 				// No multidraw
 
@@ -39,9 +39,9 @@ void Drawcall::enque()
 					indicesCountArray, 
 					indicesType, 
 					offsetsArray, 
-					primCount);
+					drawcallCount);
 #else
-				for(U i = 0; i < primCount; i++)
+				for(U i = 0; i < drawcallCount; i++)
 				{
 					glDrawElements(
 						primitiveType, 

+ 44 - 34
src/renderer/Drawer.cpp

@@ -16,12 +16,14 @@ static const U UNIFORM_BLOCK_MAX_SIZE = 1024 * 12;
 
 //==============================================================================
 #if ANKI_ENABLE_COUNTERS
-static U64 countVerts(U32* indicesCount, I primCount)
+static U64 countVerts(
+	const Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCount, 
+	I drawcallCount)
 {
 	U64 sum = 0;
-	while(--primCount >= 0)
+	while(--drawcallCount >= 0)
 	{
-		sum += indicesCount[primCount];
+		sum += indicesCount[drawcallCount];
 	}
 	return sum;
 }
@@ -38,6 +40,7 @@ ANKI_ATTRIBUTE_ALIGNED(struct, 16) SetupRenderableVariableVisitor
 	RenderComponent* renderable = nullptr;
 	RenderComponentVariable* rvar = nullptr;
 	const ShaderProgramUniformVariable* uni;
+	F32 flod;
 
 	// Used for 
 	const U32* subSpatialIndices = nullptr;
@@ -183,6 +186,25 @@ ANKI_ATTRIBUTE_ALIGNED(struct, 16) SetupRenderableVariableVisitor
 				uniSet(*uni, &bmvp[0], size);
 			}
 			break;
+		case BMV_MAX_TESS_LEVEL:
+			{
+				RenderComponentVariable& tmp = x;
+				F32 maxtess = tmp.getValues<F32>()[0];
+				F32 tess;
+				
+				if(flod >= 1.0)
+				{
+					tess = 1.0;
+				}
+				else
+				{
+					tess = maxtess - flod * maxtess;
+					tess = std::max(tess, 1.0f);
+				}
+				
+				uniSet(*uni, &tess, 1);
+			}
+			break;
 		case BMV_BLURRING:
 			{
 				F32 blurring = 0.0;
@@ -241,7 +263,8 @@ void SetupRenderableVariableVisitor::uniSet<TextureResourcePointer>(
 void RenderableDrawer::setupShaderProg(const PassLevelKey& key_,
 	const FrustumComponent& fr, const ShaderProgram &prog,
 	RenderComponent& renderable, 
-	U32* subSpatialIndices, U subSpatialIndicesCount)
+	U32* subSpatialIndices, U subSpatialIndicesCount,
+	F32 flod)
 {
 	prog.bind();
 	
@@ -252,6 +275,7 @@ void RenderableDrawer::setupShaderProg(const PassLevelKey& key_,
 	vis.r = r;
 	vis.subSpatialIndices = subSpatialIndices;
 	vis.subSpatialIndicesCount = subSpatialIndicesCount;
+	vis.flod = flod;
 
 	PassLevelKey key(key_.pass,
 		std::min(key_.level,
@@ -340,41 +364,26 @@ void RenderableDrawer::render(SceneNode& frsn, RenderingStage stage,
 
 	F32 dist = 
 		(rsn.getSpatialComponent()->getSpatialOrigin() - camPos).getLength();
-	U8 lod = r->calculateLod(dist);
+	F32 lod = r->calculateLod(dist);
 
 	PassLevelKey key(pass, lod);
 
 	// Get rendering useful stuff
 	const ShaderProgram* prog;
 	const Vao* vao;
-	U32 indicesCountArray[ANKI_MAX_MULTIDRAW_PRIMITIVES];
-	const void* indicesOffsetArray[ANKI_MAX_MULTIDRAW_PRIMITIVES];
+	Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES> indicesCountArray;
+	Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES> indicesOffsetArray;
 #if ANKI_DEBUG
-	memset(indicesCountArray, 0, sizeof(indicesCountArray));
-	memset(indicesOffsetArray, 0, sizeof(indicesOffsetArray));
+	memset(&indicesCountArray[0], 0, sizeof(indicesCountArray));
+	memset(&indicesOffsetArray[0], 0, sizeof(indicesOffsetArray));
 #endif
 
-	U32 primCount = 1;
+	U32 drawcallCount = 1;
 
-	const ModelPatchBase& resource = renderable->getModelPatchBase();
-	if(subSpatialIndicesCount == 0 || resource.getSubMeshesCount() == 0)
-	{
-		// No multimesh
-
-		resource.getRenderingData(
-			key, vao, prog, indicesCountArray[0]);
-
-		indicesOffsetArray[0] = nullptr;
-	}
-	else
-	{
-		// It's a multimesh
-
-		resource.getRenderingDataSub(
-			key, vao, prog, 
-			subSpatialIndices, subSpatialIndicesCount,
-			indicesCountArray, indicesOffsetArray, primCount);
-	}
+	renderable->getRenderingData(
+		key, vao, prog, 
+		subSpatialIndices, subSpatialIndicesCount,
+		indicesCountArray, indicesOffsetArray, drawcallCount);
 
 	// Hack the instances count
 	if(subSpatialIndicesCount > 0 && instancesCount > 1)
@@ -384,7 +393,8 @@ void RenderableDrawer::render(SceneNode& frsn, RenderingStage stage,
 
 	// Setup shader
 	setupShaderProg(
-		key, fr, *prog, *renderable, subSpatialIndices, subSpatialIndicesCount);
+		key, fr, *prog, *renderable, subSpatialIndices, subSpatialIndicesCount,
+		lod);
 
 	// Render
 	ANKI_ASSERT(vao->getAttachmentsCount() > 1);
@@ -407,15 +417,15 @@ void RenderableDrawer::render(SceneNode& frsn, RenderingStage stage,
 
 	dc.indicesType = GL_UNSIGNED_SHORT;
 	dc.instancesCount = instancesCount;
-	dc.indicesCountArray = (GLsizei*)indicesCountArray;
-	dc.offsetsArray = indicesOffsetArray;
-	dc.primCount = primCount;
+	dc.indicesCountArray = (GLsizei*)&indicesCountArray[0];
+	dc.offsetsArray = &indicesOffsetArray[0];
+	dc.drawcallCount = drawcallCount;
 
 	dc.enque();
 
 	ANKI_COUNTER_INC(C_RENDERER_DRAWCALLS_COUNT, (U64)1);
 	ANKI_COUNTER_INC(C_RENDERER_VERTICES_COUNT, 
-		countVerts(indicesCountArray, (I)primCount));
+		countVerts(indicesCountArray, (I)drawcallCount));
 }
 
 }  // end namespace anki

+ 3 - 0
src/renderer/Sm.cpp

@@ -1,6 +1,7 @@
 #include "anki/renderer/Sm.h"
 #include "anki/renderer/Renderer.h"
 #include "anki/core/App.h"
+#include "anki/core/Counters.h"
 #include "anki/scene/SceneGraph.h"
 #include "anki/scene/Camera.h"
 #include "anki/scene/Light.h"
@@ -212,6 +213,8 @@ Sm::Shadowmap* Sm::doLight(Light& light)
 			(*it).subSpatialIndicesCount);
 	}
 
+	ANKI_COUNTER_INC(C_RENDERER_SHADOW_PASSES, (U64)1);
+
 	return &sm;
 }
 

+ 5 - 1
src/resource/Image.cpp

@@ -447,7 +447,11 @@ void Image::load(const char* filename, U32 maxTextureSize)
 {
 	// get the extension
 	const char* ext = File::getFileExtension(filename);
-	ANKI_ASSERT(ext);
+	
+	if(ext == nullptr)
+	{
+		throw ANKI_EXCEPTION("Wrong filename: " + filename);
+	}
 
 	// load from this extension
 	try

+ 39 - 43
src/resource/Mesh.cpp

@@ -7,20 +7,16 @@
 namespace anki {
 
 //==============================================================================
-// MeshBase                                                                    =
+// Mesh                                                                        =
 //==============================================================================
 
 //==============================================================================
-Bool MeshBase::isCompatible(const MeshBase& other) const
+Bool Mesh::isCompatible(const Mesh& other) const
 {
 	return hasWeights() == other.hasWeights() 
 		&& getSubMeshesCount() == other.getSubMeshesCount(); 
 }
 
-//==============================================================================
-// Mesh                                                                        =
-//==============================================================================
-
 //==============================================================================
 void Mesh::load(const char* filename)
 {
@@ -28,18 +24,18 @@ void Mesh::load(const char* filename)
 	{
 		MeshLoader loader(filename);
 
-		meshProtected.indicesCount = loader.getIndices().size();
-		meshProtected.obb.set(loader.getPositions());
-		ANKI_ASSERT(meshProtected.indicesCount > 0);
-		ANKI_ASSERT(meshProtected.indicesCount % 3 == 0 
+		indicesCount = loader.getIndices().size();
+		obb.set(loader.getPositions());
+		ANKI_ASSERT(indicesCount > 0);
+		ANKI_ASSERT(indicesCount % 3 == 0 
 			&& "Expecting triangles");
 
 		// Set the non-VBO members
-		meshProtected.vertsCount = loader.getPositions().size();
-		ANKI_ASSERT(meshProtected.vertsCount > 0);
+		vertsCount = loader.getPositions().size();
+		ANKI_ASSERT(vertsCount > 0);
 
-		meshProtected.texChannelsCount = loader.getTextureChannelsCount();
-		meshProtected.weights = loader.getWeights().size() > 1;
+		texChannelsCount = loader.getTextureChannelsCount();
+		weights = loader.getWeights().size() > 1;
 
 		createVbos(loader);
 	}
@@ -53,8 +49,8 @@ void Mesh::load(const char* filename)
 U32 Mesh::calcVertexSize() const
 {
 	U32 a = sizeof(Vec3) + sizeof(HVec3) + sizeof(HVec4) 
-		+ meshProtected.texChannelsCount * sizeof(HVec2);
-	if(meshProtected.weights)
+		+ texChannelsCount * sizeof(HVec2);
+	if(weights)
 	{
 		a += sizeof(MeshLoader::VertexWeight);
 	}
@@ -66,19 +62,19 @@ U32 Mesh::calcVertexSize() const
 //==============================================================================
 void Mesh::createVbos(const MeshLoader& loader)
 {
-	ANKI_ASSERT(meshProtected.vertsCount == loader.getPositions().size()
-		&& meshProtected.vertsCount == loader.getNormals().size()
-		&& meshProtected.vertsCount == loader.getTangents().size());
+	ANKI_ASSERT(vertsCount == loader.getPositions().size()
+		&& vertsCount == loader.getNormals().size()
+		&& vertsCount == loader.getTangents().size());
 
 	// Calculate VBO size
 	U32 vertexsize = calcVertexSize();
-	U32 vbosize = vertexsize * meshProtected.vertsCount;
+	U32 vbosize = vertexsize * vertsCount;
 
 	// Create a temp buffer and populate it
 	Vector<U8> buff(vbosize, 0);
 
 	U8* ptra = &buff[0];
-	for(U i = 0; i < meshProtected.vertsCount; i++)
+	for(U i = 0; i < vertsCount; i++)
 	{
 		U8* ptr = ptra;
 		ANKI_ASSERT(ptr + vertexsize <= &buff[0] + vbosize);
@@ -92,13 +88,13 @@ void Mesh::createVbos(const MeshLoader& loader)
 		memcpy(ptr, &loader.getTangents()[i], sizeof(HVec4));
 		ptr += sizeof(HVec4);
 
-		for(U j = 0; j < meshProtected.texChannelsCount; j++)
+		for(U j = 0; j < texChannelsCount; j++)
 		{
 			memcpy(ptr, &loader.getTextureCoordinates(j)[i], sizeof(HVec2));
 			ptr += sizeof(HVec2);
 		}
 
-		if(meshProtected.weights)
+		if(weights)
 		{
 			memcpy(ptr, &loader.getWeights()[i], 
 				sizeof(MeshLoader::VertexWeight));
@@ -156,7 +152,7 @@ void Mesh::getVboInfo(const VertexAttribute attrib, const Vbo*& v, U32& size,
 		offset = sizeof(Vec3) + sizeof(HVec3);
 		break;
 	case VA_TEXTURE_COORD:
-		if(meshProtected.texChannelsCount > 0)
+		if(texChannelsCount > 0)
 		{
 			v = &vbo;
 			size = 2;
@@ -165,7 +161,7 @@ void Mesh::getVboInfo(const VertexAttribute attrib, const Vbo*& v, U32& size,
 		}
 		break;
 	case VA_TEXTURE_COORD_1:
-		if(meshProtected.texChannelsCount > 1)
+		if(texChannelsCount > 1)
 		{
 			v = &vbo;
 			size = 2;
@@ -175,33 +171,33 @@ void Mesh::getVboInfo(const VertexAttribute attrib, const Vbo*& v, U32& size,
 		}
 		break;
 	case VA_BONE_COUNT:
-		if(meshProtected.weights)
+		if(weights)
 		{
 			v = &vbo;
 			size = 1;
 			type = GL_UNSIGNED_SHORT;
 			offset = sizeof(Vec3) + sizeof(HVec3) + sizeof(HVec4) 
-				+ meshProtected.texChannelsCount * sizeof(HVec2);
+				+ texChannelsCount * sizeof(HVec2);
 		}
 		break;
 	case VA_BONE_IDS:
-		if(meshProtected.weights)
+		if(weights)
 		{
 			v = &vbo;
 			size = 4;
 			type = GL_UNSIGNED_SHORT;
 			offset = sizeof(Vec3) + sizeof(HVec3) + sizeof(HVec4) 
-				+ meshProtected.texChannelsCount * sizeof(HVec2) + sizeof(U16);
+				+ texChannelsCount * sizeof(HVec2) + sizeof(U16);
 		}
 		break;
 	case VA_BONE_WEIGHTS:
-		if(meshProtected.weights)
+		if(weights)
 		{
 			v = &vbo;
 			size = 4;
 			type = GL_HALF_FLOAT;
 			offset = sizeof(Vec3) + sizeof(HVec3) + sizeof(HVec4) 
-				+ meshProtected.texChannelsCount * sizeof(HVec2) + sizeof(U16) 
+				+ texChannelsCount * sizeof(HVec2) + sizeof(U16) 
 				+ sizeof(U16) * 4;
 		}
 	case VA_INDICES:
@@ -229,9 +225,9 @@ void BucketMesh::load(const char* filename)
 		XmlElement meshesEl = rootEl.getChildElement("meshes");
 		XmlElement meshEl = meshesEl.getChildElement("mesh");
 
-		meshProtected.vertsCount = 0;
-		meshProtected.subMeshes.reserve(4); // XXX
-		meshProtected.indicesCount = 0;
+		vertsCount = 0;
+		subMeshes.reserve(4);
+		indicesCount = 0;
 
 		MeshLoader fullLoader;
 		U i = 0;
@@ -256,13 +252,13 @@ void BucketMesh::load(const char* filename)
 				loader = &subLoader;
 
 				// Sanity checks
-				if(meshProtected.weights != (loader->getWeights().size() > 1))
+				if(weights != (loader->getWeights().size() > 1))
 				{
 					throw ANKI_EXCEPTION("All sub meshes should have or not "
 						"have vertex weights");
 				}
 
-				if(meshProtected.texChannelsCount 
+				if(texChannelsCount 
 					!= loader->getTextureChannelsCount())
 				{
 					throw ANKI_EXCEPTION("All sub meshes should have the "
@@ -279,8 +275,8 @@ void BucketMesh::load(const char* filename)
 				loader = &fullLoader;
 
 				// Set properties
-				meshProtected.weights = loader->getWeights().size() > 1;
-				meshProtected.texChannelsCount = 
+				weights = loader->getWeights().size() > 1;
+				texChannelsCount = 
 					loader->getTextureChannelsCount();
 			}
 
@@ -288,14 +284,14 @@ void BucketMesh::load(const char* filename)
 			SubMesh submesh;
 
 			submesh.indicesCount = loader->getIndices().size();
-			submesh.indicesOffset = meshProtected.indicesCount * sizeof(U16);
+			submesh.indicesOffset = indicesCount * sizeof(U16);
 			submesh.obb.set(loader->getPositions());
 
-			meshProtected.subMeshes.push_back(submesh);
+			subMeshes.push_back(submesh);
 
 			// Set the global numbers
-			meshProtected.vertsCount += loader->getPositions().size();
-			meshProtected.indicesCount += loader->getIndices().size();
+			vertsCount += loader->getPositions().size();
+			indicesCount += loader->getIndices().size();
 
 			// Move to next
 			meshEl = meshEl.getNextSiblingElement("mesh");
@@ -304,7 +300,7 @@ void BucketMesh::load(const char* filename)
 
 		// Create the bucket mesh
 		createVbos(fullLoader);
-		meshProtected.obb.set(fullLoader.getPositions());
+		obb.set(fullLoader.getPositions());
 	}
 	catch(std::exception& e)
 	{

+ 59 - 46
src/resource/Model.cpp

@@ -14,23 +14,23 @@ namespace anki {
 struct Attrib
 {
 	const char* name;
-	MeshBase::VertexAttribute id;
+	Mesh::VertexAttribute id;
 };
 
-static const Array<Attrib, MeshBase::VA_COUNT - 1> attribs = {{
-	{"position", MeshBase::VA_POSITION},
-	{"normal", MeshBase::VA_NORMAL},
-	{"tangent", MeshBase::VA_TANGENT},
-	{"texCoord", MeshBase::VA_TEXTURE_COORD},
-	{"texCoord1", MeshBase::VA_TEXTURE_COORD_1},
-	{"bonesCount", MeshBase::VA_BONE_COUNT},
-	{"boneIds", MeshBase::VA_BONE_IDS},
-	{"boneWeights", MeshBase::VA_BONE_WEIGHTS}
+static const Array<Attrib, Mesh::VA_COUNT - 1> attribs = {{
+	{"position", Mesh::VA_POSITION},
+	{"normal", Mesh::VA_NORMAL},
+	{"tangent", Mesh::VA_TANGENT},
+	{"texCoord", Mesh::VA_TEXTURE_COORD},
+	{"texCoord1", Mesh::VA_TEXTURE_COORD_1},
+	{"bonesCount", Mesh::VA_BONE_COUNT},
+	{"boneIds", Mesh::VA_BONE_IDS},
+	{"boneWeights", Mesh::VA_BONE_WEIGHTS}
 }};
 
 //==============================================================================
 void ModelPatchBase::createVao(const ShaderProgram& prog,
-	const MeshBase& meshb, Vao& vao)
+	const Mesh& meshb, Vao& vao)
 {
 	vao.create();
 
@@ -64,7 +64,7 @@ void ModelPatchBase::createVao(const ShaderProgram& prog,
 	}
 
 	// The indices VBO
-	meshb.getVboInfo(MeshBase::VA_INDICES, vbo, size, type,
+	meshb.getVboInfo(Mesh::VA_INDICES, vbo, size, type,
 			stride, offset);
 
 	ANKI_ASSERT(vbo != nullptr);
@@ -93,8 +93,8 @@ void ModelPatchBase::getRenderingData(const PassLevelKey& key, const Vao*& vao,
 	meshKey.pass = key.pass;
 	meshKey.level = std::min(key.level, (U8)(meshLods - 1));
 
-	const MeshBase& meshBase = getMeshBase(meshKey);
-	indicesCount = meshBase.getIndicesCount();
+	const Mesh& mesh = getMesh(meshKey);
+	indicesCount = mesh.getIndicesCount();
 
 	// Prog
 	PassLevelKey mtlKey;
@@ -108,8 +108,9 @@ void ModelPatchBase::getRenderingData(const PassLevelKey& key, const Vao*& vao,
 void ModelPatchBase::getRenderingDataSub(const PassLevelKey& key,
 	const Vao*& vao, const ShaderProgram*& prog, 
 	const U32* subMeshIndexArray, U subMeshIndexCount,
-	U32* indicesCountArray, const void** indicesOffsetArray, 
-	U32& primcount) const
+	Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCountArray,
+	Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesOffsetArray, 
+	U32& drawcallCount) const
 {
 	const U meshLods = getMeshesCount();
 	ANKI_ASSERT(meshLods > 0);
@@ -136,39 +137,51 @@ void ModelPatchBase::getRenderingDataSub(const PassLevelKey& key,
 	meshKey.pass = key.pass;
 	meshKey.level = std::min(key.level, (U8)(meshLods - 1));
 
-	const MeshBase& meshBase = getMeshBase(meshKey);
+	const Mesh& mesh = getMesh(meshKey);
 
-	ANKI_ASSERT(subMeshIndexCount <= meshBase.getSubMeshesCount());
-
-	primcount = 0;
-	I prevIndex = -1;
-	for(U i = 0; i < subMeshIndexCount; i++)
+	if(subMeshIndexCount == 0 || subMeshIndexArray == nullptr
+		|| mesh.getSubMeshesCount() == 0)
 	{
-		I index = (I)subMeshIndexArray[i];
-	
-		// Check if we can merge with the previous submesh
-		if(index > 0 && (index - 1) == prevIndex)
-		{
-			ANKI_ASSERT(primcount > 0);
+		drawcallCount = 1;
+		indicesOffsetArray[0] = nullptr;
+		indicesCountArray[0] = mesh.getIndicesCount();
+	}
+	else
+	{
+		ANKI_ASSERT(subMeshIndexCount <= mesh.getSubMeshesCount());
 
-			// increase the indices count, leave offset alone
-			U32 offset;
-			indicesCountArray[primcount - 1] +=
-				meshBase.getIndicesCountSub((U)index, offset);
-		}
-		else
+		drawcallCount = 0;
+		I prevIndex = -1;
+		for(U i = 0; i < subMeshIndexCount; i++)
 		{
-			U32 offset;
-			indicesCountArray[primcount] =
-				meshBase.getIndicesCountSub((U)index, offset);
+			I index = (subMeshIndexArray == nullptr) 
+				? (I)i
+				: (I)subMeshIndexArray[i];
+		
+			// Check if we can merge with the previous submesh
+			if(index > 0 && (index - 1) == prevIndex)
+			{
+				ANKI_ASSERT(drawcallCount > 0);
 
-			indicesOffsetArray[primcount] = 
-				reinterpret_cast<const void*>((PtrSize)offset);
+				// increase the indices count, leave offset alone
+				U32 offset;
+				indicesCountArray[drawcallCount - 1] +=
+					mesh.getIndicesCountSub((U)index, offset);
+			}
+			else
+			{
+				U32 offset;
+				indicesCountArray[drawcallCount] =
+					mesh.getIndicesCountSub((U)index, offset);
 
-			++primcount;
-		}
+				indicesOffsetArray[drawcallCount] = 
+					reinterpret_cast<const void*>((PtrSize)offset);
 
-		prevIndex = index;
+				++drawcallCount;
+			}
+
+			prevIndex = index;
+		}
 	}
 }
 
@@ -187,13 +200,13 @@ void ModelPatchBase::create()
 		{
 			PassLevelKey key(pass, lod);
 			const ShaderProgram* prog;
-			const MeshBase* mesh;
+			const Mesh* mesh;
 
 			// Get mesh
 			ANKI_ASSERT(getMeshesCount() > 0);
 			PassLevelKey meshKey = key;
 			meshKey.level = std::min(key.level, (U8)(getMeshesCount() - 1));
-			mesh = &getMeshBase(meshKey);
+			mesh = &getMesh(meshKey);
 
 			// Get shader prog
 			ANKI_ASSERT(getMaterial().getLevelsOfDetail() > 0);
@@ -318,14 +331,14 @@ void Model::load(const char* filename)
 		// Calculate compound bounding volume
 		PassLevelKey key;
 		key.level = 0;
-		visibilityShape = modelPatches[0]->getMeshBase(key).getBoundingShape();
+		visibilityShape = modelPatches[0]->getMesh(key).getBoundingShape();
 
 		for(ModelPatchesContainer::const_iterator it = modelPatches.begin() + 1;
 			it != modelPatches.end();
 			++it)
 		{
 			visibilityShape = visibilityShape.getCompoundShape(
-				(*it)->getMeshBase(key).getBoundingShape());
+				(*it)->getMesh(key).getBoundingShape());
 		}
 	}
 	catch(std::exception& e)

+ 1 - 1
src/resource/Skin.cpp

@@ -75,7 +75,7 @@ void Skin::load(const char* filename)
 			{
 				PassLevelKey key;
 				key.level = i;
-				const MeshBase& meshBase = patch->getMeshBase(key);
+				const Mesh& meshBase = patch->getMesh(key);
 				if(!meshBase.hasWeights())
 				{
 					throw ANKI_EXCEPTION("Mesh does not support HW skinning");

+ 1 - 1
src/scene/ModelNode.cpp

@@ -33,7 +33,7 @@ void ModelPatchNodeInstance::moveUpdate()
 	ANKI_ASSERT(modelPatchNode);
 
 	// Update the obb of self
-	obb = modelPatchNode->getModelPatchBase().getBoundingShape().getTransformed(
+	obb = modelPatchNode->modelPatch->getBoundingShape().getTransformed(
 		getWorldTransform());
 	SpatialComponent::markForUpdate();
 

+ 11 - 2
src/scene/ParticleEmitter.cpp

@@ -270,9 +270,18 @@ ParticleEmitter::~ParticleEmitter()
 }
 
 //==============================================================================
-const ModelPatchBase& ParticleEmitter::getModelPatchBase()
+void ParticleEmitter::getRenderingData(
+	const PassLevelKey& key, 
+	const Vao*& vao, const ShaderProgram*& prog,
+	const U32* subMeshIndicesArray, U subMeshIndicesCount,
+	Array<U32, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesCountArray,
+	Array<const void*, ANKI_MAX_MULTIDRAW_PRIMITIVES>& indicesOffsetArray, 
+	U32& drawcallCount) const
 {
-	return *particleEmitterResource->getModel().getModelPatches()[0];
+	particleEmitterResource->getModel().getModelPatches()[0]->
+		getRenderingDataSub(key, vao, prog, 
+		subMeshIndicesArray, subMeshIndicesCount, 
+		indicesCountArray, indicesOffsetArray, drawcallCount);
 }
 
 //==============================================================================

+ 1 - 0
src/scene/RenderComponent.cpp

@@ -37,6 +37,7 @@ static Array<const char*, BMV_COUNT - 1> buildinNames = {{
 	"viewProjectionMat",
 	"normalMat",
 	"billboardMvpMatrix",
+	"maxTessLevel",
 	"blurring",
 	"msDepthMap"}};
 

+ 4 - 0
tools/scene/Main.cpp

@@ -87,6 +87,10 @@ static std::string getFilename(const std::string& path)
 	{
 		out.insert(out.end(), path.begin() + last + 1, path.end());
 	}
+	else
+	{
+		out = path;
+	}
 
 	return out;
 }