Browse Source

Clusterer

Panagiotis Christopoulos Charitos 10 years ago
parent
commit
da4b92cb7e

+ 3 - 0
include/anki/collision/Frustum.h

@@ -188,7 +188,10 @@ public:
 	/// Set all the parameters and recalculate the planes and shape
 	/// Set all the parameters and recalculate the planes and shape
 	void setAll(F32 fovX, F32 fovY, F32 near, F32 far)
 	void setAll(F32 fovX, F32 fovY, F32 near, F32 far)
 	{
 	{
+		ANKI_ASSERT(near > 0.0);
 		ANKI_ASSERT(far > near);
 		ANKI_ASSERT(far > near);
+		ANKI_ASSERT(fovX <= getPi<F32>());
+		ANKI_ASSERT(fovY <= getPi<F32>());
 		m_fovX = fovX;
 		m_fovX = fovX;
 		m_fovY = fovY,
 		m_fovY = fovY,
 		m_near = near;
 		m_near = near;

+ 9 - 9
include/anki/math/Vec3.h

@@ -40,28 +40,28 @@ public:
 
 
 	/// @name Constructors
 	/// @name Constructors
 	/// @{
 	/// @{
-	explicit TVec3()
-	:	Base()
+	TVec3()
+		: Base()
 	{}
 	{}
 
 
 	TVec3(const TVec3& b)
 	TVec3(const TVec3& b)
-	:	Base(b)
+		: Base(b)
 	{}
 	{}
 
 
-	explicit TVec3(const T x_, const T y_, const T z_)
-	:	Base(x_, y_, z_)
+	TVec3(const T x_, const T y_, const T z_)
+		: Base(x_, y_, z_)
 	{}
 	{}
 
 
 	explicit TVec3(const T f)
 	explicit TVec3(const T f)
-	:	Base(f)
+		: Base(f)
 	{}
 	{}
 
 
 	explicit TVec3(const T arr[])
 	explicit TVec3(const T arr[])
-	:	Base(arr)
+		: Base(arr)
 	{}
 	{}
 
 
-	explicit TVec3(const TVec2<T>& v, const T z_)
-	:	Base(v.x(), v.y(), z_)
+	TVec3(const TVec2<T>& v, const T z_)
+		: Base(v.x(), v.y(), z_)
 	{}
 	{}
 	/// @}
 	/// @}
 
 

+ 26 - 5
include/anki/scene/Clusterer.h

@@ -63,10 +63,11 @@ public:
 		m_clusters.destroy(m_alloc);
 		m_clusters.destroy(m_alloc);
 	}
 	}
 
 
-	void init(U tileCountX, U tileCountY)
+	void init(U clusterCountX, U clusterCountY, U clusterCountZ)
 	{
 	{
-		m_counts[0] = tileCountX;
-		m_counts[1] = tileCountY;
+		m_counts[0] = clusterCountX;
+		m_counts[1] = clusterCountY;
+		m_counts[2] = clusterCountZ;
 	}
 	}
 
 
 	void prepare(
 	void prepare(
@@ -75,8 +76,9 @@ public:
 	void initTempTestResults(const GenericMemoryPoolAllocator<U8>& alloc,
 	void initTempTestResults(const GenericMemoryPoolAllocator<U8>& alloc,
 		ClustererTestResult& rez) const;
 		ClustererTestResult& rez) const;
 
 
-	void bin(const CollisionShape& cs, const Aabb& aabb,
-		ClustererTestResult& rez) const;
+	/// Bin collision shape.
+	/// @param[in] cs The collision shape should be in view space.
+	void bin(const CollisionShape& cs, ClustererTestResult& rez) const;
 
 
 public:
 public:
 	class Cluster
 	class Cluster
@@ -100,6 +102,7 @@ public:
 	F32 m_fovX = 0.0;
 	F32 m_fovX = 0.0;
 
 
 	F32 m_calcNearOpt = 0.0;
 	F32 m_calcNearOpt = 0.0;
+	Mat4 m_projMat;
 
 
 	Cluster& cluster(U x, U y, U z)
 	Cluster& cluster(U x, U y, U z)
 	{
 	{
@@ -109,9 +112,27 @@ public:
 		return m_clusters[m_counts[0] * (z * m_counts[1] + y) + x];
 		return m_clusters[m_counts[0] * (z * m_counts[1] + y) + x];
 	}
 	}
 
 
+	const Cluster& cluster(U x, U y, U z) const
+	{
+		ANKI_ASSERT(x < m_counts[0]);
+		ANKI_ASSERT(y < m_counts[1]);
+		ANKI_ASSERT(z < m_counts[2]);
+		return m_clusters[m_counts[0] * (z * m_counts[1] + y) + x];
+	}
+
 	F32 calcNear(U k) const;
 	F32 calcNear(U k) const;
+	U calcK(F32 zVspace) const;
 
 
 	void initClusters();
 	void initClusters();
+
+	void binSphere(const Sphere& s, ClustererTestResult& rez) const;
+
+	void binGeneric(const CollisionShape& cs, ClustererTestResult& rez) const;
+
+	void findTilesFromAabb(const Aabb& box, U& tileBeginX, U& tileBeginY,
+		U& tileEndX, U& tileEndY) const;
+
+	void findSplitsFromAabb(const Aabb& box, U& zFrom, U& zTo) const;
 };
 };
 /// @}
 /// @}
 
 

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

@@ -51,6 +51,7 @@ inline T max(T a, T b)
 template<typename T>
 template<typename T>
 inline T clamp(T v, T minv, T maxv)
 inline T clamp(T v, T minv, T maxv)
 {
 {
+	ANKI_ASSERT(minv < maxv);
 	return min<T>(max<T>(minv, v), maxv);
 	return min<T>(max<T>(minv, v), maxv);
 }
 }
 
 

+ 41 - 5
src/renderer/Dbg.cpp

@@ -159,12 +159,14 @@ Error Dbg::run(CommandBufferPtr& cmdb)
 
 
 		PerspectiveFrustum fr;
 		PerspectiveFrustum fr;
 		const F32 ang = 55.0;
 		const F32 ang = 55.0;
-		fr.setAll(toRad(ang) * m_r->getAspectRatio(), toRad(ang), 1.0, 250.0);
+		F32 far = 200.0;
+		fr.setAll(toRad(ang) * m_r->getAspectRatio(), toRad(ang), 1.0, far);
 		fr.resetTransform(Transform(origin, Mat3x4::getIdentity(), 1.0));
 		fr.resetTransform(Transform(origin, Mat3x4::getIdentity(), 1.0));
 
 
 		Clusterer c(getAllocator());
 		Clusterer c(getAllocator());
 
 
-		c.init(m_r->getWidth() / 64, m_r->getHeight() / 64);
+		c.init(m_r->getWidth() / 64, m_r->getHeight() / 64, 20);
+		//c.init(5, 3, 10);
 		c.prepare(fr, SArray<Vec2>());
 		c.prepare(fr, SArray<Vec2>());
 
 
 		CollisionDebugDrawer cd(m_drawer);
 		CollisionDebugDrawer cd(m_drawer);
@@ -191,11 +193,45 @@ Error Dbg::run(CommandBufferPtr& cmdb)
 			++k;
 			++k;
 		}
 		}
 
 
+		SceneNode& lnode = scene.findSceneNode("spot0");
+		SpatialComponent& sp = lnode.getComponent<SpatialComponent>();
+
+		m_drawer->setColor(Vec4(0.0, 0.0, 1.0, 1.0));
+		sp.getSpatialCollisionShape().accept(cd);
+
+		ClustererTestResult rez;
+		c.initTempTestResults(getAllocator(), rez);
+
+		c.bin(sp.getSpatialCollisionShape(), rez);
+
 		m_drawer->setColor(Vec4(1.0));
 		m_drawer->setColor(Vec4(1.0));
-		for(U i = 0; i < c.m_clusters.getSize(); ++i)
+		for(U z = 0; z < c.m_counts[2]; ++z)
 		{
 		{
-			Aabb box(c.m_clusters[i].m_min.xyz0(), c.m_clusters[i].m_max.xyz0());
-			box.accept(cd);
+			for(U y = 0; y < c.m_counts[1]; ++y)
+			{
+				for(U x = 0; x < c.m_counts[0]; ++x)
+				{
+					auto& cluster = c.cluster(x, y, z);
+					Aabb box(cluster.m_min.xyz0(), cluster.m_max.xyz0());
+					m_drawer->setColor(Vec4(1.0));
+
+					Bool found = false;
+					auto it = rez.getClustersBegin();
+					auto end = rez.getClustersEnd();
+					for(; it != end; ++it)
+					{
+						if((*it)[0] == x && (*it)[1] == y && (*it)[2] == z)
+						{
+							m_drawer->setColor(Vec4(1.0, 0.0, 0.0, 1.0));
+							found = true;
+							break;
+						}
+					}
+
+					if(found)
+					box.accept(cd);
+				}
+			}
 		}
 		}
 	}
 	}
 #endif
 #endif

+ 197 - 24
src/scene/Clusterer.cpp

@@ -5,6 +5,7 @@
 
 
 #include "anki/scene/Clusterer.h"
 #include "anki/scene/Clusterer.h"
 #include "anki/scene/FrustumComponent.h"
 #include "anki/scene/FrustumComponent.h"
+#include "anki/util/Rtti.h"
 
 
 namespace anki {
 namespace anki {
 
 
@@ -20,10 +21,22 @@ void Clusterer::initTempTestResults(const GenericMemoryPoolAllocator<U8>& alloc,
 //==============================================================================
 //==============================================================================
 F32 Clusterer::calcNear(U k) const
 F32 Clusterer::calcNear(U k) const
 {
 {
-	F32 neark = m_near * pow(m_calcNearOpt, F32(k));
+	F32 neark = m_calcNearOpt * pow(k, 2.0) + m_near;
 	return neark;
 	return neark;
 }
 }
 
 
+//==============================================================================
+U Clusterer::calcK(F32 zVspace) const
+{
+	zVspace = clamp(zVspace, -m_far, -m_near);
+	zVspace = -zVspace;
+	F32 fk = sqrt((zVspace - m_near) / m_calcNearOpt);
+	ANKI_ASSERT(fk >= 0.0);
+	U k = min<U>(static_cast<U>(fk), m_counts[2]);
+
+	return k;
+}
+
 //==============================================================================
 //==============================================================================
 void Clusterer::prepare(const PerspectiveFrustum& fr,
 void Clusterer::prepare(const PerspectiveFrustum& fr,
 	const SArray<Vec2>& minMaxTileDepth)
 	const SArray<Vec2>& minMaxTileDepth)
@@ -45,23 +58,7 @@ void Clusterer::prepare(const PerspectiveFrustum& fr,
 	m_near = near;
 	m_near = near;
 	m_far = far;
 	m_far = far;
 
 
-	{
-		F32 Sy = m_counts[1];
-		F32 theta = m_fovY / 2.0;
-		m_calcNearOpt = 1.0 + (2.0 * tan(theta)) / Sy;
-	}
-
-	// Calc depth
-	U k = 1;
-	while(1)
-	{
-		F32 neark = calcNear(k++);
-		if(neark >= far)
-		{
-			break;
-		}
-	}
-	m_counts[2] = k - 1;
+	m_calcNearOpt = (m_far - m_near) / pow(m_counts[2], 2.0);
 
 
 	// Alloc and init clusters
 	// Alloc and init clusters
 	U clusterCount = m_counts[0] * m_counts[1] * m_counts[2];
 	U clusterCount = m_counts[0] * m_counts[1] * m_counts[2];
@@ -76,10 +73,9 @@ void Clusterer::prepare(const PerspectiveFrustum& fr,
 //==============================================================================
 //==============================================================================
 void Clusterer::initClusters()
 void Clusterer::initClusters()
 {
 {
-	Mat4 proj;
 	PerspectiveFrustum::calculateProjectionMatrix(m_fovX, m_fovY, m_near, m_far,
 	PerspectiveFrustum::calculateProjectionMatrix(m_fovX, m_fovY, m_near, m_far,
-		proj);
-	Mat4 invProj = proj.getInverse();
+		m_projMat);
+	Mat4 invProj = m_projMat.getInverse();
 
 
 	// For every claster
 	// For every claster
 	for(U z = 0; z < m_counts[2]; ++z)
 	for(U z = 0; z < m_counts[2]; ++z)
@@ -108,15 +104,15 @@ void Clusterer::initClusters()
 
 
 				Vec4 min = invProj * Vec4(tileMin, 0.0, 1.0);
 				Vec4 min = invProj * Vec4(tileMin, 0.0, 1.0);
 				min.w() = 0.0;
 				min.w() = 0.0;
-				Plane p(Vec4(0.0, 0.0, -1.0, 0.0), near);
+				Plane p(Vec4(0.0, 0.0, -1.0, 0.0), far);
 				p.intersectVector(min, min);
 				p.intersectVector(min, min);
-				min.z() = -far;
 
 
 				Vec4 max = invProj * Vec4(tileMax, 0.0, 1.0);
 				Vec4 max = invProj * Vec4(tileMax, 0.0, 1.0);
 				max.w() = 0.0;
 				max.w() = 0.0;
-				p = Plane(Vec4(0.0, 0.0, -1.0, 0.0), near);
 				p.intersectVector(max, max);
 				p.intersectVector(max, max);
+				max.z() = -near;
 
 
+				ANKI_ASSERT(min.xyz() < max.xyz());
 				cluster(x, y, z).m_min = min.xyz();
 				cluster(x, y, z).m_min = min.xyz();
 				cluster(x, y, z).m_max = max.xyz();
 				cluster(x, y, z).m_max = max.xyz();
 			}
 			}
@@ -124,5 +120,182 @@ void Clusterer::initClusters()
 	}
 	}
 }
 }
 
 
+//==============================================================================
+void Clusterer::bin(const CollisionShape& cs, ClustererTestResult& rez) const
+{
+	rez.m_count = 0;
+
+	if(isa<Sphere>(cs))
+	{
+		// Specialized
+		binSphere(dcast<const Sphere&>(cs), rez);
+	}
+	else
+	{
+		binGeneric(cs, rez);
+	}
+}
+
+//==============================================================================
+void Clusterer::findTilesFromAabb(const Aabb& box, U& tileBeginX, U& tileBeginY,
+	U& tileEndX, U& tileEndY) const
+{
+	// Project front face of the AABB
+	const Vec4& minv = box.getMin();
+	const Vec4& maxv = box.getMax();
+
+	Array<Vec4, 8> projPoints;
+	projPoints[0] = Vec4(minv.x(), minv.y(), maxv.z(), 1.0);
+	projPoints[1] = Vec4(maxv.x(), minv.y(), maxv.z(), 1.0);
+	projPoints[2] = Vec4(maxv.x(), maxv.y(), maxv.z(), 1.0);
+	projPoints[3] = Vec4(minv.x(), maxv.y(), maxv.z(), 1.0);
+	projPoints[4] = Vec4(minv.x(), minv.y(), minv.z(), 1.0);
+	projPoints[5] = Vec4(maxv.x(), minv.y(), minv.z(), 1.0);
+	projPoints[6] = Vec4(maxv.x(), maxv.y(), minv.z(), 1.0);
+	projPoints[7] = Vec4(minv.x(), maxv.y(), minv.z(), 1.0);
+
+	Vec2 min2(MAX_F32), max2(MIN_F32);
+	for(Vec4& p : projPoints)
+	{
+		p = m_projMat * p;
+		p /= abs(p.w());
+
+		for(U i = 0; i < 2; ++i)
+		{
+			min2[i] = min(min2[i], p[i]);
+			max2[i] = max(max2[i], p[i]);
+		}
+	}
+
+	min2 = min2 * 0.5 + 0.5;
+	max2 = max2 * 0.5 + 0.5;
+
+	// Find tiles affected
+	F32 tcountX = m_counts[0];
+	F32 tcountY = m_counts[1];
+
+	I xFrom = floor(tcountX * min2.x());
+	xFrom = clamp<I>(xFrom, 0, tcountX);
+
+	I xTo = ceil(tcountX * max2.x());
+	xTo = min<U>(xTo, tcountX);
+
+	I yFrom = floor(tcountY * min2.y());
+	yFrom = clamp<I>(yFrom, 0, tcountY);
+
+	I yTo = ceil(tcountY * max2.y());
+	yTo = min<I>(yTo, tcountY);
+
+	ANKI_ASSERT(xFrom >= 0 && xFrom <= tcountX
+		&& xTo >= 0 && xTo <= tcountX);
+	ANKI_ASSERT(yFrom >= 0 && yFrom <= tcountX
+		&& yTo >= 0 && yFrom <= tcountY);
+
+	tileBeginX = xFrom;
+	tileBeginY = yFrom;
+	tileEndX = xTo;
+	tileEndY = yTo;
+}
+
+//==============================================================================
+void Clusterer::findSplitsFromAabb(const Aabb& box, U& zFrom, U& zTo) const
+{
+	zFrom = calcK(box.getMax().z());
+	zTo = calcK(box.getMin().z()) + 1;
+	ANKI_ASSERT(zFrom <= zTo);
+}
+
+//==============================================================================
+void Clusterer::binSphere(const Sphere& s, ClustererTestResult& rez) const
+{
+	Aabb aabb;
+	s.computeAabb(aabb);
+
+	const Vec4& scent = s.getCenter();
+	const F32 srad = s.getRadius();
+
+	// Find the tiles that are covered
+	//
+
+	U tileBeginX, tileBeginY, tileEndX, tileEndY;
+
+	if(ANKI_UNLIKELY(scent.getLengthSquared() <= srad * srad))
+	{
+		// The eye is inside the sphere. All tiles are covered
+		tileBeginX = tileBeginY = 0;
+		tileEndX = m_counts[0];
+		tileEndY = m_counts[1];
+	}
+	else
+	{
+		findTilesFromAabb(aabb, tileBeginX, tileBeginY, tileEndX, tileEndY);
+	}
+
+	U zFrom, zTo;
+	findSplitsFromAabb(aabb, zFrom, zTo);
+
+	// Detailed tests
+	for(U z = zFrom; z < zTo; ++z)
+	{
+		for(U y = tileBeginY; y < tileEndY; ++y)
+		{
+			for(U x = tileBeginX; x < tileEndX; ++x)
+			{
+				const Cluster& cl = cluster(x, y, z);
+				Aabb clusterAabb(cl.m_min.xyz0(), cl.m_max.xyz0());
+				if(testCollisionShapes(s, clusterAabb))
+				{
+					Array<U8, 3> ids = {static_cast<U8>(x),
+						static_cast<U8>(y), static_cast<U8>(z)};
+					rez.m_clusterIds[rez.m_count++] = ids;
+				}
+			}
+		}
+	}
+}
+
+//==============================================================================
+void Clusterer::binGeneric(const CollisionShape& cs,
+	ClustererTestResult& rez) const
+{
+	Aabb aabb;
+	cs.computeAabb(aabb);
+
+	U tileBeginX, tileBeginY, tileEndX, tileEndY;
+	if(ANKI_UNLIKELY(aabb.getMax().z() >= -m_near))
+	{
+		// The eye is inside the sphere. All tiles are covered
+		tileBeginX = tileBeginY = 0;
+		tileEndX = m_counts[0];
+		tileEndY = m_counts[1];
+	}
+	else
+	{
+		findTilesFromAabb(aabb, tileBeginX, tileBeginY, tileEndX, tileEndY);
+	}
+
+	U zFrom, zTo;
+	findSplitsFromAabb(aabb, zFrom, zTo);
+
+	// Detailed tests
+	for(U z = zFrom; z < zTo; ++z)
+	{
+		for(U y = tileBeginY; y < tileEndY; ++y)
+		{
+			for(U x = tileBeginX; x < tileEndX; ++x)
+			{
+				const Cluster& cl = cluster(x, y, z);
+				Aabb clusterAabb(cl.m_min.xyz0(), cl.m_max.xyz0());
+				if(testCollisionShapes(cs, clusterAabb))
+				{
+					Array<U8, 3> ids = {static_cast<U8>(x),
+						static_cast<U8>(y), static_cast<U8>(z)};
+					rez.m_clusterIds[rez.m_count++] = ids;
+				}
+			}
+		}
+	}
+}
+
 } // end namespace anki
 } // end namespace anki
 
 

+ 17 - 31
testapp/Main.cpp

@@ -44,7 +44,7 @@ ModelNode* horse;
 PerspectiveCamera* cam;
 PerspectiveCamera* cam;
 
 
 #define PLAYER 0
 #define PLAYER 0
-#define MOUSE 1
+#define MOUSE 0
 
 
 Bool profile = false;
 Bool profile = false;
 
 
@@ -94,34 +94,7 @@ Error init()
 		1.0));*/
 		1.0));*/
 #endif
 #endif
 
 
-	// lights
-#if 0
-	Vec3 lpos(-24.0, 0.1, -10.0);
-	for(int i = 0; i < 50; i++)
-	{
-		for(int j = 0; j < 10; j++)
-		{
-			std::string name = "plight" + std::to_string(i) + std::to_string(j);
-
-			PointLight* point;
-			err = scene.newSceneNode<PointLight>(name.c_str(), point);
-			if(err) return err;
-			point->setRadius(0.5);
-			point->setDiffuseColor(Vec4(randFloat(6.0) - 2.0,
-				randFloat(6.0) - 2.0, randFloat(6.0) - 2.0, 0.0));
-			point->setSpecularColor(Vec4(randFloat(6.0) - 3.0,
-				randFloat(6.0) - 3.0, randFloat(6.0) - 3.0, 0.0));
-			point->setLocalOrigin(lpos.xyz0());
-
-			lpos.z() += 2.0;
-		}
-
-		lpos.x() += 0.93;
-		lpos.z() = -10;
-	}
-#endif
-
-#if 0
+#if 1
 	PointLight* plight;
 	PointLight* plight;
 	scene.newSceneNode<PointLight>("spot0", plight);
 	scene.newSceneNode<PointLight>("spot0", plight);
 
 
@@ -129,12 +102,25 @@ Error init()
 	lightc->setDiffuseColor(Vec4(0.0, 30.0, 0.0, 1.0));
 	lightc->setDiffuseColor(Vec4(0.0, 30.0, 0.0, 1.0));
 	lightc->setSpecularColor(Vec4(1.2));
 	lightc->setSpecularColor(Vec4(1.2));
 	lightc->setDistance(5.0);
 	lightc->setDistance(5.0);
-	lightc->setShadowEnabled(true);
 
 
 	move = plight->tryGetComponent<MoveComponent>();
 	move = plight->tryGetComponent<MoveComponent>();
-	move->setLocalTransform(Transform(Vec4(-11.0, 2.5, 12.0, 0.0),
+	move->setLocalTransform(Transform(Vec4(0.0, 0.5, 0.0, 0.0),
 		Mat3x4::getIdentity(), 1.0));
 		Mat3x4::getIdentity(), 1.0));
+#endif
+#if 0
+	SpotLight* light;
+	scene.newSceneNode<SpotLight>("spot0", light);
 
 
+	lightc = light->tryGetComponent<LightComponent>();
+	lightc->setOuterAngle(toRad(45.0));
+	lightc->setInnerAngle(toRad(15.0));
+	lightc->setDiffuseColor(Vec4(0.0, 30.0, 0.0, 1.0));
+	lightc->setSpecularColor(Vec4(1.2));
+	lightc->setDistance(5.0);
+
+	move = light->tryGetComponent<MoveComponent>();
+	move->setLocalTransform(Transform(Vec4(0.0, 0.5, 0.0, 0.0),
+		Mat3x4::getIdentity(), 1.0));
 #endif
 #endif
 
 
 #if 0
 #if 0