Browse Source

GJK & some windows bits

Panagiotis Christopoulos Charitos 11 years ago
parent
commit
eebc3bac05

+ 36 - 0
include/anki/collision/ConvexShape.h

@@ -0,0 +1,36 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_COLLISION_CONVEX_SHAPE_H
+#define ANKI_COLLISION_CONVEX_SHAPE_H
+
+#include "anki/collision/CollisionShape.h"
+
+namespace anki {
+
+/// @addtogroup collision
+/// @{
+
+/// Abstact class for convex collision shapes
+class ConvexShape: public CollisionShape
+{
+public:
+	/// @name Constructors & destructor
+	/// @{
+	ConvexShape(Type cid)
+		: CollisionShape(cid)
+	{}
+	/// @}
+
+	/// Get a support vector for the GJK algorithm
+	virtual Vec4 computeSupport(const Vec4& dir) const = 0;
+};
+
+/// @}
+
+} // end namespace anki
+
+#endif
+

+ 51 - 0
include/anki/collision/GjkEpa.h

@@ -0,0 +1,51 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_COLLISION_GJK_EPA_H
+#define ANKI_COLLISION_GJK_EPA_H
+
+#include "anki/Math.h"
+
+namespace anki {
+
+// Forward
+class ConvexShape;
+
+/// @addtogroup collision
+/// @{
+
+/// The implementation of the GJK algorithm. This algorithm is being used for
+/// checking the intersection between convex shapes.
+class Gjk
+{
+public:
+	/// Return true if the two convex shapes intersect
+	Bool intersect(const ConvexShape& shape0, const ConvexShape& shape1);
+
+private:
+	U32 m_count; ///< Simplex count
+	Vec4 m_b, m_c, m_d; ///< Simplex
+	Vec4 m_dir;
+
+	/// Compute the support
+	static Vec4 support(const ConvexShape& shape0, const ConvexShape& shape1,
+		const Vec4& dir);
+
+	/// Update simplex
+	Bool update(const Vec4& a);
+
+	/// Helper of axbxa
+	static Vec4 crossAba(const Vec4& a, const Vec4& b)
+	{
+		return a.cross(b).cross(a);
+	}
+};
+
+/// @}
+
+} // end namespace anki
+
+#endif
+

+ 24 - 21
include/anki/collision/Sphere.h

@@ -6,7 +6,7 @@
 #ifndef ANKI_COLLISION_SPHERE_H
 #define ANKI_COLLISION_SPHERE_H
 
-#include "anki/collision/CollisionShape.h"
+#include "anki/collision/ConvexShape.h"
 #include "anki/Math.h"
 
 namespace anki {
@@ -15,7 +15,7 @@ namespace anki {
 /// @{
 
 /// Sphere collision shape
-class Sphere: public CollisionShape
+class Sphere: public ConvexShape
 {
 public:
 	/// @name Constructors
@@ -23,21 +23,21 @@ public:
 
 	/// Default constructor
 	Sphere()
-		: CollisionShape(Type::SPHERE)
+		: ConvexShape(Type::SPHERE)
 	{}
 
 	/// Copy constructor
 	Sphere(const Sphere& b)
-		:	CollisionShape(Type::SPHERE), 
-			center(b.center), 
-			radius(b.radius)
-	{}
+		:	ConvexShape(Type::SPHERE)
+	{
+		operator=(b);
+	}
 
 	/// Constructor
-	Sphere(const Vec3& center_, F32 radius_)
-		:	CollisionShape(Type::SPHERE), 
-			center(center_), 
-			radius(radius_)
+	Sphere(const Vec3& center, F32 radius)
+		:	ConvexShape(Type::SPHERE), 
+			m_center(center), 
+			m_radius(radius)
 	{}
 	/// @}
 
@@ -45,28 +45,28 @@ public:
 	/// @{
 	const Vec3& getCenter() const
 	{
-		return center;
+		return m_center;
 	}
 	Vec3& getCenter()
 	{
-		return center;
+		return m_center;
 	}
 	void setCenter(const Vec3& x)
 	{
-		center = x;
+		m_center = x;
 	}
 
 	F32 getRadius() const
 	{
-		return radius;
+		return m_radius;
 	}
 	F32& getRadius()
 	{
-		return radius;
+		return m_radius;
 	}
 	void setRadius(const F32 x)
 	{
-		radius = x;
+		m_radius = x;
 	}
 	/// @}
 
@@ -74,8 +74,8 @@ public:
 	/// @{
 	Sphere& operator=(const Sphere& b)
 	{
-		center = b.center;
-		radius = b.radius;
+		m_center = b.m_center;
+		m_radius = b.m_radius;
 		return *this;
 	}
 	/// @}
@@ -120,9 +120,12 @@ public:
 	void setFromPointCloud(
 		const void* buff, U count, PtrSize stride, PtrSize buffSize);
 
+	/// Implements CompoundShape::computeSupport
+	Vec4 computeSupport(const Vec4& dir) const;
+
 private:
-	Vec3 center;
-	F32 radius;
+	Vec3 m_center;
+	F32 m_radius;
 };
 /// @}
 

+ 15 - 0
include/anki/math/Vec4.h

@@ -97,6 +97,18 @@ public:
 	{}
 	/// @}
 
+	/// @name Operators with same
+	/// @{
+
+	/// It's like calculating the cross of a TVec3
+	TVec4 cross(const TVec4& b) const
+	{
+		ANKI_ASSERT(isZero<T>(Base::w()));
+		ANKI_ASSERT(isZero<T>(b.w()));
+		return TVec4(Base::xyz().template cross(b.xyz(), static_cast<T>(0)));
+	}
+	/// @{
+
 	/// @name Operators with other
 	/// @{
 
@@ -164,6 +176,9 @@ TVec4<F32> TVec4<F32>::Base::getNormalized() const;
 template<>
 void TVec4<F32>::Base::normalize();
 
+template<>
+TVec4<F32> TVec4<F32>::cross(const TVec4<F32>& b) const;
+
 #elif ANKI_SIMD == ANKI_SIMD_NEON
 
 #	error "TODO"

+ 19 - 0
include/anki/math/Vec4.inl.h

@@ -155,6 +155,25 @@ inline TVec4<F32>& TVec4<F32>::Base::operator/=(const TVec4<F32>& b)
 	return static_cast<TVec4<F32>&>(*this);
 }
 
+//==============================================================================
+template<>
+inline TVec4<F32> TVec4<F32>::cross(const TVec4<F32>& b) const
+{
+	ANKI_ASSERT(isZero<F32>(Base::w()));
+	ANKI_ASSERT(isZero<F32>(b.w()));
+
+	const auto& a = *this;
+	const auto mask0 = _MM_SHUFFLE(3, 0, 2, 1);
+	const auto mask1 = _MM_SHUFFLE(3, 1, 0, 2);
+
+	__m128 tmp0 = _mm_mul_ps(_mm_shuffle_ps(a.m_simd, a.m_simd, mask0), 
+		_mm_shuffle_ps(b.m_simd, b.m_simd, mask1));
+	__m128 tmp1 = _mm_mul_ps(_mm_shuffle_ps(a.m_simd, a.m_simd, mask0), 
+		_mm_shuffle_ps(b.m_simd, b.m_simd, mask1));
+
+	return TVec4<F32>(_mm_sub_ps(tmp0, tmp1));	
+}
+
 //==============================================================================
 // Other                                                                       =
 //==============================================================================

+ 222 - 0
src/collision/GjkEpa.cpp

@@ -0,0 +1,222 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+// Inspired by http://vec3.ca/gjk/implementation/
+
+#include "anki/collision/GjkEpa.h"
+#include "anki/collision/ConvexShape.h"
+
+namespace anki {
+
+//==============================================================================
+Vec4 Gjk::support(const ConvexShape& shape0, const ConvexShape& shape1,
+	const Vec4& dir)
+{
+	Vec4 s0 = shape0.computeSupport(dir);
+	Vec4 s1 = shape1.computeSupport(-dir);
+	return s0 - s1;
+}
+
+//==============================================================================
+Bool Gjk::update(const Vec4& a)
+{
+	if(m_count == 0)
+	{
+		m_b = a;
+		m_dir = -a;
+		++m_count;
+
+		return false;
+	}
+	else if(m_count == 1)
+	{
+		m_dir = crossAba(m_b - a, -a);
+ 
+		m_c = m_b;
+		m_b = a;
+		++m_count;
+
+		return false;
+	}
+	else if(m_count == 2)
+	{
+		Vec4 ao = -a;
+		 
+		// Compute the vectors parallel to the edges we'll test
+		Vec4 ab = m_b - a;
+		Vec4 ac = m_c - a;
+		 
+		// Compute the triangle's normal
+		Vec4 abc = ab.cross(ac);
+		 
+		// Compute a vector within the plane of the triangle,
+		// Pointing away from the edge ab
+		Vec4 abp = ab.cross(abc);
+		 
+		if(abp.dot(ao) > 0.0)
+		{
+			// The origin lies outside the triangle, near the edge ab
+			m_c = m_b;
+			m_b = a;
+		 
+			m_dir = crossAba(ab,ao);
+		 
+			return false;
+		}
+
+		// Perform a similar test for the edge ac
+		Vec4 acp = abc.cross(ac);
+		 
+		if(acp.dot(ao) > 0.0)
+		{
+			m_b = a;
+			m_dir = crossAba(ac, ao);
+
+			return false;
+		}
+		 
+		// If we get here, then the origin must be within the triangle, 
+		// but we care whether it is above or below it, so test
+		if(abc.dot(ao) > 0.0)
+		{
+			m_d = m_c;
+			m_c = m_b;
+			m_b = a;
+		 
+			m_dir = abc;
+		}
+		else
+		{
+			m_d = m_b;
+			m_b = a;
+		 
+			m_dir = -abc;
+		}
+		 
+		++m_count;
+		 
+		// Again, need a tetrahedron to enclose the origin
+		return false;
+	}
+	else if(m_count == 3)
+	{
+		Vec4 ao = -a;
+		 
+		Vec4 ab = m_b - a;
+		Vec4 ac = m_c - a;
+		 
+		Vec4 abc = ab.cross(ac);
+		 
+		Vec4 ad, acd, adb;
+		 
+		if(abc.dot(ao) > 0.0)
+		{
+			// In front of triangle ABC
+			goto check_face;
+		}
+		 
+		ad = m_d - a;
+		acd = ac.cross(ad);
+		 
+		if(acd.dot(ao) > 0.0)
+		{
+			// In front of triangle ACD
+			m_b = m_c;
+			m_c = m_d;
+		 
+			ab = ac;
+			ac = ad;
+		 
+			abc = acd;
+			goto check_face;
+		}
+		 
+		adb = ad.cross(ab);
+		 
+		if(adb.dot(ao) > 0.0)
+		{
+			// In front of triangle ADB
+		 
+			m_c = m_b;
+			m_b = m_d;
+		 
+			ac = ab;
+			ab = ad;
+		 
+			abc = adb;
+			goto check_face;
+		}
+		 
+		// Behind all three faces, the origin is in the tetrahedron, we're done
+		return true;
+		 
+check_face:
+		 
+		// We have a CCW wound triangle ABC
+		// the point is in front of this triangle
+		// it is NOT "below" edge BC
+		// it is NOT "above" the plane through A that's parallel to BC
+		Vec4 abp = ab.cross(abc);
+		 
+		if(abp.dot(ao) > 0.0)
+		{
+			m_c = m_b;
+			m_b = a;
+		 
+			m_dir = crossAba(ab, ao);
+		 
+			m_count = 2;
+			return false;
+		}
+		 
+		Vec4 acp = abc.cross(ac);	
+		 
+		if(acp.dot(ao) > 0.0)
+		{
+			m_b = a;
+		 
+			m_dir = crossAba(ac,ao);
+		 
+			m_count = 2;
+			return false;
+		}
+		 
+		m_d = m_c;
+		m_c = m_b;
+		m_b = a;
+		 
+		m_dir = abc;
+		m_count = 3;
+		 
+		return false;		
+	}
+
+	ANKI_ASSERT(0);
+	return true;
+}
+
+//==============================================================================
+Bool Gjk::intersect(const ConvexShape& shape0, const ConvexShape& shape1)
+{
+	m_dir = Vec4(1.0, 0.0, 0.0, 0.0);
+	m_count = 0;
+
+	while(1)
+	{
+		Vec4 a = support(shape0, shape1, m_dir);
+
+		if(a.dot(m_dir) < 0.0)
+		{
+			return false;
+		}
+
+		if(update(a))
+		{
+			return true;
+		}
+	}
+}
+
+} // end namespace anki
+

+ 17 - 11
src/collision/Sphere.cpp

@@ -12,16 +12,16 @@ namespace anki {
 //==============================================================================
 F32 Sphere::testPlane(const Plane& p) const
 {
-	F32 dist = p.test(center);
+	F32 dist = p.test(m_center);
 
-	F32 out = dist - radius;
+	F32 out = dist - m_radius;
 	if(out > 0)
 	{
 		// Do nothing
 	}
 	else
 	{
-		out = dist + radius;
+		out = dist + m_radius;
 		if(out < 0)
 		{
 			// Do nothing
@@ -40,12 +40,12 @@ Sphere Sphere::getTransformed(const Transform& transform) const
 {
 	Sphere newSphere;
 
-	newSphere.center = center.getTransformed(
+	newSphere.m_center = m_center.getTransformed(
 		transform.getOrigin(),
 		transform.getRotation(),
 		transform.getScale());
 
-	newSphere.radius = radius * transform.getScale();
+	newSphere.m_radius = m_radius * transform.getScale();
 	return newSphere;
 }
 
@@ -54,7 +54,7 @@ Sphere Sphere::getCompoundShape(const Sphere& b) const
 {
 	const Sphere& a = *this;
 
-	/// @todo test this
+	/// TODO test this
 	/*
 	Vec3 centerDiff = b.center - a.center;
 	F32 radiusDiff = b.radius - a.radius;
@@ -105,8 +105,8 @@ Sphere Sphere::getCompoundShape(const Sphere& b) const
 //==============================================================================
 void Sphere::computeAabb(Aabb& aabb) const
 {
-	aabb.setMin(center - radius);
-	aabb.setMax(center + radius);
+	aabb.setMin((m_center - m_radius).xyz());
+	aabb.setMax((m_center + m_radius).xyz());
 }
 
 //==============================================================================
@@ -132,21 +132,27 @@ void Sphere::setFromPointCloud(
 		}
 	});
 
-	center = (min + max) * 0.5; // average
+	m_center = (min + max) * 0.5; // average
 
 	// max distance between center and the vec3 arr
 	F32 maxDist = MIN_F32;
 
 	iteratePointCloud(buff, count, stride, buffSize, [&](const Vec3& pos)
 	{
-		F32 dist = (pos - center).getLengthSquared();
+		F32 dist = (pos - m_center).getLengthSquared();
 		if(dist > maxDist)
 		{
 			maxDist = dist;
 		}
 	});
 
-	radius = sqrt(maxDist);
+	m_radius = sqrt(maxDist);
+}
+
+//==============================================================================
+Vec4 Sphere::computeSupport(const Vec4& dir) const
+{
+	//return s.center + v.getNormalized() * s.radius;
 }
 
 } // end namespace anki

+ 1 - 1
src/util/CMakeLists.txt

@@ -3,7 +3,7 @@ set(ANKI_UTIL_SOURCES Assert.cpp Exception.cpp Functions.cpp File.cpp Memory.cpp
 if(LINUX OR ANDROID OR MACOS)
 	set(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerPosix.cpp FilePosix.cpp)
 else()
-	message(FATAL "See file")
+	set(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerWindows.cpp FileWindows.cpp)
 endif()
 
 add_library(ankiutil ${ANKI_UTIL_SOURCES})

+ 46 - 0
src/util/FileWindows.cpp

@@ -0,0 +1,46 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/util/File.h"
+#include "anki/util/Exception.h"
+#include "anki/util/Assert.h"
+
+namespace anki {
+
+//==============================================================================
+// File                                                                        =
+//==============================================================================
+
+//==============================================================================
+Bool File::fileExists(const char* filename)
+{
+	ANKI_ASSERT(filename);
+	// TODO
+}
+
+//==============================================================================
+// Functions                                                                   =
+//==============================================================================
+
+//==============================================================================
+Bool directoryExists(const char* filename)
+{
+	ANKI_ASSERT(filename);
+	// TODO
+}
+
+//==============================================================================
+void removeDirectory(const char* dirname)
+{
+	// TODO
+}
+
+//==============================================================================
+void createDirectory(const char* dir)
+{
+	// TODO
+}
+
+} // end namespace anki

+ 52 - 0
src/util/HighRezTimerWindows.cpp

@@ -0,0 +1,52 @@
+// Copyright (C) 2014, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/util/HighRezTimer.h"
+#include "anki/util/Assert.h"
+#include <Windows.h>
+
+namespace anki {
+
+//==============================================================================
+namespace {
+
+/// A dummy struct that inits the timer
+class DummyInitTimer
+{
+public:
+	DWORD m_start;
+
+	DummyInitTimer()
+	{
+		m_start = GetTickCount();
+	}
+};
+
+DummyInitTimer init;
+
+} // end namespace anonymous 
+
+//==============================================================================
+static U32 getMs()
+{
+	DWORD now = GetTickCount();
+	return now - init.m_start;
+}
+
+//==============================================================================
+void HighRezTimer::sleep(Scalar sec)
+{
+	U32 ms = static_cast<U32>(sec * 1000.0);
+	Sleep(ms);
+}
+
+//==============================================================================
+HighRezTimer::Scalar HighRezTimer::getCurrentTime()
+{
+	// Scalar(ticks) / 1000.0
+	return static_cast<Scalar>(getMs()) * 0.001;
+}
+
+} // end namespace anki