Browse Source

Camera path

Panagiotis Christopoulos Charitos 12 years ago
parent
commit
e75f1422c9

+ 42 - 9
include/anki/event/Event.h

@@ -3,6 +3,7 @@
 
 
 #include "anki/scene/Common.h"
 #include "anki/scene/Common.h"
 #include "anki/util/Bitset.h"
 #include "anki/util/Bitset.h"
+#include "anki/math/Math.h"
 
 
 namespace anki {
 namespace anki {
 
 
@@ -54,29 +55,61 @@ public:
 	/// @}
 	/// @}
 
 
 	/// This method should be implemented by the derived classes
 	/// This method should be implemented by the derived classes
-	/// @param[in] prevUpdateTime The time of the previous update (sec)
-	/// @param[in] crntTime The current time (sec)
+	/// @param prevUpdateTime The time of the previous update (sec)
+	/// @param crntTime The current time (sec)
 	virtual void update(F32 prevUpdateTime, F32 crntTime) = 0;
 	virtual void update(F32 prevUpdateTime, F32 crntTime) = 0;
 
 
+	/// This is called when the event is killed
+	/// @param prevUpdateTime The time of the previous update (sec)
+	/// @param crntTime The current time (sec)
+	/// @return Return false if you don't want to be killed
+	virtual Bool onKilled(F32 prevUpdateTime, F32 crntTime)
+	{
+		return true;
+	}
+
 protected:
 protected:
 	/// Linear interpolation between values
 	/// Linear interpolation between values
 	/// @param[in] from Starting value
 	/// @param[in] from Starting value
 	/// @param[in] to Ending value
 	/// @param[in] to Ending value
-	/// @param[in] delta The percentage from the from "from" value. Values
-	///                  from [0.0, 1.0]
+	/// @param[in] u The percentage from the from "from" value. Values
+	///              from [0.0, 1.0]
+	template<typename Type>
+	static Type interpolate(const Type& from, const Type& to, F32 u)
+	{
+		//ANKI_ASSERT(u >= 0 && u <= 1.0);
+		return from * (1.0 - u) + to * u;
+	}
+
+	template<typename Type>
+	static Type cosInterpolate(const Type& from, const Type& to, F32 u)
+	{
+		F32 u2 = (1.0 - cos(u * getPi<F32>())) / 2.0;
+		return from * (1.0 - u2) + to * u2;
+	}
+
 	template<typename Type>
 	template<typename Type>
-	static Type interpolate(const Type& from, const Type& to, F32 delta)
+	static Type cubicInterpolate(
+		const Type& a, const Type& b, const Type& c, 
+		const Type& d, F32 u)
 	{
 	{
-		ANKI_ASSERT(delta >= 0 && delta <= 1.0);
-		return from * (1.0 - delta) + to * delta;
+		F32 u2 = u * u;
+		Type a0 = d - c - a + b;
+		Type a1 = a - b - a0;
+		Type a2 = c - a;
+		Type a3 = b;
+
+		return(a0 * u * u2 + a1 * u2 + a2 * u + a3);
 	}
 	}
 
 
-	/// Return the delta between current time and when the event started
+	/// Return the u between current time and when the event started
 	/// @return A number [0.0, 1.0]
 	/// @return A number [0.0, 1.0]
 	F32 getDelta(F32 crntTime) const;
 	F32 getDelta(F32 crntTime) const;
 
 
 private:
 private:
-	F32 startTime; ///< The time the event will start. Eg 23:00
+	/// The time the event will start. Eg 23:00. If it's < 0 then start the 
+	/// event now
+	F32 startTime;
 	F32 duration; ///< The duration of the event
 	F32 duration; ///< The duration of the event
 	EventManager* manager = nullptr; ///< Keep it here to access allocators etc
 	EventManager* manager = nullptr; ///< Keep it here to access allocators etc
 };
 };

+ 7 - 0
include/anki/event/EventManager.h

@@ -11,12 +11,15 @@ namespace anki {
 
 
 // Forward
 // Forward
 class SceneGraph;
 class SceneGraph;
+class Path;
+class SceneNode;
 
 
 class SceneAmbientColorEvent;
 class SceneAmbientColorEvent;
 class LightEvent;
 class LightEvent;
 class LightEventData;
 class LightEventData;
 class MovableEvent;
 class MovableEvent;
 class MovableEventData;
 class MovableEventData;
+class FollowPathEvent;
 
 
 /// This manager creates the events ands keeps tracks of them
 /// This manager creates the events ands keeps tracks of them
 class EventManager
 class EventManager
@@ -44,6 +47,10 @@ public:
 
 
 	std::shared_ptr<Event> newMovableEvent(
 	std::shared_ptr<Event> newMovableEvent(
 		F32 startTime, F32 duration, const MovableEventData& data);
 		F32 startTime, F32 duration, const MovableEventData& data);
+
+	std::shared_ptr<Event> newFollowPathEvent(
+		F32 startTime, F32 duration,
+		SceneNode* movableSceneNode, Path* path, F32 distPerTime);
 	/// @}
 	/// @}
 
 
 	/// Update
 	/// Update

+ 32 - 0
include/anki/event/FollowPathEvent.h

@@ -0,0 +1,32 @@
+#ifndef ANKI_EVENT_FOLLOW_PATH_EVENT_H
+#define ANKI_EVENT_FOLLOW_PATH_EVENT_H
+
+#include "anki/event/Event.h"
+#include "anki/scene/SceneNode.h"
+
+namespace anki {
+
+class FollowPathEvent: public Event
+{
+public:
+	/// @name Constructors/Destructor
+	/// @{
+
+	/// Constructor
+	FollowPathEvent(
+		F32 startTime, F32 duration, EventManager* manager, U8 flags, // Event
+		SceneNode* movableSceneNode, Path* path, F32 distPerTime);
+	/// @}
+
+	/// Implements Event::update
+	void update(F32 prevUpdateTime, F32 crntTime);
+
+private:
+	F32 distPerTime;
+	SceneNode* movableSceneNode;
+	Path* path;
+};
+
+} // end namespace anki
+
+#endif

+ 3 - 3
include/anki/math/Quat.inl.h

@@ -64,7 +64,7 @@ inline Quat::Quat(const Quat& b)
 	w() = b.w();
 	w() = b.w();
 }
 }
 
 
-// mat3
+// Mat3
 inline Quat::Quat(const Mat3& m3)
 inline Quat::Quat(const Mat3& m3)
 {
 {
 	F32 trace = m3(0, 0) + m3(1, 1) + m3(2, 2) + 1.0;
 	F32 trace = m3(0, 0) + m3(1, 1) + m3(2, 2) + 1.0;
@@ -391,8 +391,8 @@ inline const Quat& Quat::getIdentity()
 // print
 // print
 inline std::ostream& operator<<(std::ostream& s, const Quat& q)
 inline std::ostream& operator<<(std::ostream& s, const Quat& q)
 {
 {
-	s << q.w() << ' ' << q.x() << ' ' << q.y() << ' ' << q.z();
+	s << q.x() << ' ' << q.y() << ' ' << q.z() << ' ' << q.w();
 	return s;
 	return s;
 }
 }
 
 
-} // end namespace
+} // end namespace anki

+ 7 - 5
include/anki/renderer/DebugDrawer.h

@@ -163,12 +163,12 @@ public:
 		: dbg(d)
 		: dbg(d)
 	{}
 	{}
 
 
-	virtual ~SceneDebugDrawer()
+	~SceneDebugDrawer()
 	{}
 	{}
 
 
 	void draw(SceneNode& node);
 	void draw(SceneNode& node);
 
 
-	virtual void draw(const Octree& octree) const;
+	void draw(const Octree& octree) const;
 
 
 	void draw(const Sector& sector);
 	void draw(const Sector& sector);
 
 
@@ -180,12 +180,14 @@ public:
 private:
 private:
 	DebugDrawer* dbg;
 	DebugDrawer* dbg;
 
 
-	virtual void draw(Frustumable& fr) const;
+	void draw(Frustumable& fr) const;
 
 
-	virtual void draw(Spatial& sp) const;
+	void draw(Spatial& sp) const;
 
 
-	virtual void draw(const OctreeNode& octnode,
+	void draw(const OctreeNode& octnode,
 		U32 depth, const Octree& octree) const;
 		U32 depth, const Octree& octree) const;
+
+	void drawPath(const Path& path) const;
 };
 };
 
 
 } // end namespace anki
 } // end namespace anki

+ 71 - 0
include/anki/scene/Path.h

@@ -0,0 +1,71 @@
+#ifndef ANKI_SCENE_PATH_H
+#define ANKI_SCENE_PATH_H
+
+#include "anki/scene/Common.h"
+#include "anki/scene/SceneNode.h"
+#include "anki/scene/Movable.h"
+
+namespace anki {
+
+/// XXX
+class PathPoint
+{
+	friend class Path;
+
+public:
+	/// @name Accessors
+	/// @{
+	const Vec3& getPosition() const
+	{
+		return pos;
+	}
+
+	const Quat& getRotation() const
+	{
+		return rot;
+	}
+
+	F32 getDistance() const
+	{
+		return dist;
+	}
+
+	F32 getDistanceFromFirst() const
+	{
+		return distStart;
+	}
+	/// @}
+
+private:
+	Vec3 pos;
+	Quat rot;
+	F32 distStart; ///< Distance from the first PathPoint of the path
+	F32 dist; ///< Distance from the previous PathPoint in the path list
+};
+
+/// XXX
+class Path: public SceneNode, public Movable
+{
+public:
+	Path(const char* filename,
+		const char* name, SceneGraph* scene, // SceneNode
+		U32 movableFlags, Movable* movParent); // Movable
+
+	const SceneVector<PathPoint>& getPoints() const
+	{
+		return points;
+	}
+
+	F32 getDistance() const
+	{
+		return distance;
+	}
+
+private:
+	SceneVector<PathPoint> points;
+	F32 distance;
+};
+
+} // end namespace anki
+
+#endif

+ 1 - 0
include/anki/scene/Scene.h

@@ -8,5 +8,6 @@
 #include "anki/scene/ParticleEmitter.h"
 #include "anki/scene/ParticleEmitter.h"
 #include "anki/scene/Camera.h"
 #include "anki/scene/Camera.h"
 #include "anki/scene/Light.h"
 #include "anki/scene/Light.h"
+#include "anki/scene/Path.h"
 
 
 #endif
 #endif

+ 11 - 1
include/anki/scene/SceneNode.h

@@ -17,6 +17,7 @@ class Frustumable;
 class Spatial;
 class Spatial;
 class Light;
 class Light;
 class RigidBody;
 class RigidBody;
+class Path;
 
 
 /// @addtogroup Scene
 /// @addtogroup Scene
 /// @{
 /// @{
@@ -102,6 +103,15 @@ public:
 	{
 	{
 		return sceneNodeProtected.rigidBody;
 		return sceneNodeProtected.rigidBody;
 	}
 	}
+
+	Path* getPath()
+	{
+		return sceneNodeProtected.path;
+	}
+	const Path* getPath() const
+	{
+		return sceneNodeProtected.path;
+	}
 	/// @}
 	/// @}
 
 
 	/// This is called by the scene every frame after logic and before
 	/// This is called by the scene every frame after logic and before
@@ -128,10 +138,10 @@ protected:
 		Spatial* spatial = nullptr;
 		Spatial* spatial = nullptr;
 		Light* light = nullptr;
 		Light* light = nullptr;
 		RigidBody* rigidBody = nullptr;
 		RigidBody* rigidBody = nullptr;
+		Path* path = nullptr;
 	} sceneNodeProtected;
 	} sceneNodeProtected;
 
 
 private:
 private:
-	SceneGraph* scene = nullptr; ///< Keep it here for unregistering
 	SceneString name; ///< A unique name
 	SceneString name; ///< A unique name
 };
 };
 /// @}
 /// @}

+ 20 - 1
src/event/EventManager.cpp

@@ -4,6 +4,7 @@
 #include "anki/event/SceneAmbientColorEvent.h"
 #include "anki/event/SceneAmbientColorEvent.h"
 #include "anki/event/LightEvent.h"
 #include "anki/event/LightEvent.h"
 #include "anki/event/MovableEvent.h"
 #include "anki/event/MovableEvent.h"
+#include "anki/event/FollowPathEvent.h"
 
 
 namespace anki {
 namespace anki {
 
 
@@ -72,6 +73,12 @@ void EventManager::updateAllEvents(F32 prevUpdateTime_, F32 crntTime_)
 	{
 	{
 		std::shared_ptr<Event>& pevent = *it;
 		std::shared_ptr<Event>& pevent = *it;
 
 
+		// Audjust starting time
+		if(pevent->startTime < 0.0)
+		{
+			pevent->startTime = crntTime_;
+		}
+
 		// If not dead update it
 		// If not dead update it
 		if(!pevent->isDead(crntTime))
 		if(!pevent->isDead(crntTime))
 		{
 		{
@@ -89,7 +96,10 @@ void EventManager::updateAllEvents(F32 prevUpdateTime_, F32 crntTime_)
 			}
 			}
 			else
 			else
 			{
 			{
-				forDeletion.push_back(it);
+				if((*it)->onKilled(prevUpdateTime, crntTime))
+				{
+					forDeletion.push_back(it);
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -125,4 +135,13 @@ std::shared_ptr<Event> EventManager::newMovableEvent(
 		Event::EF_NONE, data));
 		Event::EF_NONE, data));
 }
 }
 
 
+//==============================================================================
+std::shared_ptr<Event> EventManager::newFollowPathEvent(
+	F32 startTime, F32 duration,
+	SceneNode* movableSceneNode, Path* path, F32 distPerTime)
+{
+	return registerEvent(new FollowPathEvent(startTime, duration, this,
+		Event::EF_NONE, movableSceneNode, path, distPerTime));
+}
+
 } // end namespace anki
 } // end namespace anki

+ 115 - 0
src/event/FollowPathEvent.cpp

@@ -0,0 +1,115 @@
+#include "anki/event/FollowPathEvent.h"
+#include "anki/scene/Path.h"
+
+namespace anki {
+
+//==============================================================================
+FollowPathEvent::FollowPathEvent(
+	F32 startTime, F32 duration, EventManager* manager, U8 flags,
+	SceneNode* movableSceneNode_, Path* path_, F32 distPerTime_)
+	:	Event(startTime, duration, manager, flags),
+		distPerTime(distPerTime_),
+		movableSceneNode(movableSceneNode_),
+		path(path_)
+{
+	ANKI_ASSERT(path && movableSceneNode);
+}
+
+//==============================================================================
+void FollowPathEvent::update(F32 prevUpdateTime, F32 crntTime)
+{
+	Movable* mov = movableSceneNode->getMovable();
+	ANKI_ASSERT(mov);
+
+	I pointA = 0;
+	I pointB = 0;
+
+	// Calculate the current distance. Clamp it to max distance
+	F32 crntDistance = distPerTime * (crntTime - getStartTime());
+	ANKI_ASSERT(crntDistance >= 0.0);
+	crntDistance = std::min(crntDistance, path->getDistance());
+
+	const I pointsCount = path->getPoints().size();
+	ANKI_ASSERT(pointsCount > 1);
+
+	// Find the points that we lie between
+	for(I i = 1; i < pointsCount; i++)
+	{
+		const PathPoint& ppa = path->getPoints()[i - 1];
+		const PathPoint& ppb = path->getPoints()[i];
+
+		if(crntDistance > ppa.getDistanceFromFirst() 
+			&& crntDistance <= ppb.getDistanceFromFirst())
+		{
+			pointA = i - 1;
+			pointB = i;
+			break;
+		}
+	}
+
+	ANKI_ASSERT(pointA < pointsCount && pointB < pointsCount);
+
+	I preA = std::max((I)0, pointA - 1);
+	I postB = std::min(pointsCount - 1, pointB + 1);
+	/*I pminus2 = std::max((I)0, pointA - 2);
+	I pminus1 = std::max((I)0, pointA - 1);*/
+
+	// Calculate the u [0.0, 1.0]
+	F32 u = path->getPoints()[pointB].getDistance() + getEpsilon<F32>();
+	ANKI_ASSERT(u != 0.0);
+
+	const F32 c = crntDistance 
+		- path->getPoints()[pointA].getDistanceFromFirst();
+
+	u = c / u;
+
+	// Calculate and set new position and rotation for the movable
+	/*Vec3 newPos = cubicInterpolate(
+		path->getPoints()[preA].getPosition(),
+		path->getPoints()[pointA].getPosition(),
+		path->getPoints()[pointB].getPosition(),
+		path->getPoints()[postB].getPosition(),
+		u);*/
+	Vec3 newPos = interpolate(
+		path->getPoints()[pointA].getPosition(),
+		path->getPoints()[pointB].getPosition(),
+		u);
+
+	{
+		F32 u2 = u * u;
+		Vec4 us(1, u, u2, u2 * u);
+		F32 t = 0.7;
+		
+		Mat4 tentionMat(
+			0.0, 1.0, 0.0, 0.0,
+			-t, 0.0, t, 0.0,
+			2.0 * t, t - 3.0, 3.0 - 2.0 * t, -t,
+			-t, 2.0 - t, t - 2.0, t);
+
+		Vec4 tmp = us * tentionMat;
+
+		Mat4 posMat;
+		posMat.setRows(
+			Vec4(path->getPoints()[preA].getPosition(), 1.0),
+			Vec4(path->getPoints()[pointA].getPosition(), 1.0),
+			Vec4(path->getPoints()[pointB].getPosition(), 1.0),
+			Vec4(path->getPoints()[postB].getPosition(), 1.0));
+
+		Vec4 finalPos = tmp * posMat;
+
+		newPos = finalPos.xyz();
+	}
+
+	Quat newRot = path->getPoints()[pointA].getRotation().slerp(
+			path->getPoints()[pointB].getRotation(),
+			u);
+
+	F32 scale = mov->getLocalTransform().getScale();
+	Transform trf;
+	trf.setOrigin(newPos);
+	trf.setRotation(Mat3(newRot));
+	trf.setScale(scale);
+	mov->setLocalTransform(trf);
+}
+
+} // end namespace anki

+ 2 - 2
src/renderer/Dbg.cpp

@@ -58,8 +58,8 @@ void Dbg::run()
 		it != scene.getSceneNodesEnd(); it++)
 		it != scene.getSceneNodesEnd(); it++)
 	{
 	{
 		SceneNode* node = *it;
 		SceneNode* node = *it;
-		Spatial* sp = node->getSpatial();
-		if(bitsEnabled(DF_SPATIAL) && sp)
+		/*Spatial* sp = node->getSpatial();
+		if(bitsEnabled(DF_SPATIAL) && sp)*/
 		{
 		{
 			sceneDrawer->draw(*node);
 			sceneDrawer->draw(*node);
 		}
 		}

+ 26 - 8
src/renderer/DebugDrawer.cpp

@@ -2,13 +2,7 @@
 #include "anki/resource/ShaderProgramResource.h"
 #include "anki/resource/ShaderProgramResource.h"
 #include "anki/physics/Converters.h"
 #include "anki/physics/Converters.h"
 #include "anki/collision/Collision.h"
 #include "anki/collision/Collision.h"
-#include "anki/scene/Frustumable.h"
-#include "anki/scene/Octree.h"
-#include "anki/scene/Sector.h"
-#include "anki/resource/Material.h"
-#include "anki/scene/Renderable.h"
-#include "anki/scene/Camera.h"
-#include "anki/scene/ModelNode.h"
+#include "anki/scene/Scene.h"
 #include "anki/resource/TextureResource.h"
 #include "anki/resource/TextureResource.h"
 #include "anki/renderer/Renderer.h"
 #include "anki/renderer/Renderer.h"
 
 
@@ -483,7 +477,7 @@ void SceneDebugDrawer::draw(SceneNode& node)
 		dbg->setModelMatrix(Mat4::getIdentity());
 		dbg->setModelMatrix(Mat4::getIdentity());
 	}
 	}
 
 
-	Frustumable* fr;
+	/*Frustumable* fr;
 	if((fr = node.getFrustumable()))
 	if((fr = node.getFrustumable()))
 	{
 	{
 		draw(*fr);
 		draw(*fr);
@@ -494,6 +488,12 @@ void SceneDebugDrawer::draw(SceneNode& node)
 		&& sp->bitsEnabled(Spatial::SF_VISIBLE_CAMERA))
 		&& sp->bitsEnabled(Spatial::SF_VISIBLE_CAMERA))
 	{
 	{
 		draw(*sp);
 		draw(*sp);
+	}*/
+
+	Path* path = node.getPath();
+	if(path)
+	{
+		drawPath(*path);
 	}
 	}
 }
 }
 
 
@@ -592,4 +592,22 @@ void SceneDebugDrawer::draw(const Sector& sector)
 	}
 	}
 }
 }
 
 
+//==============================================================================
+void SceneDebugDrawer::drawPath(const Path& path) const
+{
+	const U count = path.getPoints().size();
+
+	dbg->setColor(Vec3(1.0, 1.0, 0.0));
+
+	dbg->begin();
+	
+	for(U i = 0; i < count - 1; i++)
+	{
+		dbg->pushBackVertex(path.getPoints()[i].getPosition());
+		dbg->pushBackVertex(path.getPoints()[i + 1].getPosition());
+	}
+
+	dbg->end();
+}
+
 }  // end namespace anki
 }  // end namespace anki

+ 1 - 1
src/renderer/Is.cpp

@@ -616,7 +616,7 @@ void Is::run()
 	Vec3 groundLightDir;
 	Vec3 groundLightDir;
 	if(groundLightEnabled)
 	if(groundLightEnabled)
 	{
 	{
-		groundLightDir = cam->getViewMatrix().getColumn(1).xyz();
+		groundLightDir = -cam->getViewMatrix().getColumn(1).xyz();
 	}
 	}
 
 
 	// Write common block
 	// Write common block

+ 21 - 11
src/resource/MeshLoader.cpp

@@ -53,7 +53,7 @@ void MeshLoader::load(const char* filename)
 
 
 		// Magic word
 		// Magic word
 		char magic[8];
 		char magic[8];
-		bs.read(magic, 8);
+		bs.read(magic, sizeof(magic));
 		if(bs.fail() || memcmp(magic, "ANKIMESH", 8))
 		if(bs.fail() || memcmp(magic, "ANKIMESH", 8))
 		{
 		{
 			throw ANKI_EXCEPTION("Incorrect magic word");
 			throw ANKI_EXCEPTION("Incorrect magic word");
@@ -69,20 +69,20 @@ void MeshLoader::load(const char* filename)
 		// Vert coords
 		// Vert coords
 		for(Vec3& vertCoord : vertCoords)
 		for(Vec3& vertCoord : vertCoords)
 		{
 		{
-			for(uint j = 0; j < 3; j++)
+			for(U j = 0; j < 3; j++)
 			{
 			{
 				vertCoord[j] = bs.readFloat();
 				vertCoord[j] = bs.readFloat();
 			}
 			}
 		}
 		}
 
 
 		// Faces num
 		// Faces num
-		uint facesNum = bs.readUint();
+		U facesNum = bs.readUint();
 		tris.resize(facesNum);
 		tris.resize(facesNum);
 
 
 		// Faces IDs
 		// Faces IDs
 		for(Triangle& tri : tris)
 		for(Triangle& tri : tris)
 		{
 		{
-			for(uint j = 0; j < 3; j++)
+			for(U j = 0; j < 3; j++)
 			{
 			{
 				tri.vertIds[j] = bs.readUint();
 				tri.vertIds[j] = bs.readUint();
 
 
@@ -95,7 +95,7 @@ void MeshLoader::load(const char* filename)
 		}
 		}
 
 
 		// Tex coords num
 		// Tex coords num
-		uint texCoordsNum = bs.readUint();
+		U texCoordsNum = bs.readUint();
 		texCoords.resize(texCoordsNum);
 		texCoords.resize(texCoordsNum);
 
 
 		// Tex coords
 		// Tex coords
@@ -108,7 +108,7 @@ void MeshLoader::load(const char* filename)
 		}
 		}
 
 
 		// Vert weights num
 		// Vert weights num
-		uint vertWeightsNum = bs.readUint();
+		U vertWeightsNum = bs.readUint();
 		vertWeights.resize(vertWeightsNum);
 		vertWeights.resize(vertWeightsNum);
 
 
 		// Vert weights
 		// Vert weights
@@ -187,11 +187,14 @@ void MeshLoader::doPostLoad()
 void MeshLoader::createVertIndeces()
 void MeshLoader::createVertIndeces()
 {
 {
 	vertIndices.resize(tris.size() * 3);
 	vertIndices.resize(tris.size() * 3);
-	for(uint i = 0; i < tris.size(); i++)
+	U j = 0;
+	for(U i = 0; i < tris.size(); ++i)
 	{
 	{
-		vertIndices[i * 3 + 0] = tris[i].vertIds[0];
-		vertIndices[i * 3 + 1] = tris[i].vertIds[1];
-		vertIndices[i * 3 + 2] = tris[i].vertIds[2];
+		vertIndices[j + 0] = tris[i].vertIds[0];
+		vertIndices[j + 1] = tris[i].vertIds[1];
+		vertIndices[j + 2] = tris[i].vertIds[2];
+
+		j += 3;
 	}
 	}
 }
 }
 
 
@@ -206,7 +209,14 @@ void MeshLoader::createFaceNormals()
 
 
 		tri.normal = (v1 - v0).cross(v2 - v0);
 		tri.normal = (v1 - v0).cross(v2 - v0);
 
 
-		tri.normal.normalize();
+		if(tri.normal != Vec3(0.0))
+		{
+			tri.normal.normalize();
+		}
+		else
+		{
+			tri.normal = Vec3(1.0, 0.0, 0.0);
+		}
 	}
 	}
 }
 }
 
 

+ 73 - 0
src/scene/Path.cpp

@@ -0,0 +1,73 @@
+#include "anki/scene/Path.h"
+
+namespace anki {
+
+//==============================================================================
+Path::Path(const char* filename,
+	const char* name, SceneGraph* scene,
+	U32 movableFlags, Movable* movParent)
+	:	SceneNode(name, scene), 
+		Movable(movableFlags, movParent, *this, getSceneAllocator()),
+		points(getSceneAllocator())
+{
+	// Set scene node related flags
+	sceneNodeProtected.movable = this;
+	sceneNodeProtected.path = this;
+
+	// XXX Fix the dummy load 
+	Vector<std::pair<Vec3, Quat>> tmp = {
+		{Vec3(-12.4964, 10.3002, -8.83644),  Quat(0.0174752, -0.969103, -0.00854539, 0.245888)},
+{Vec3(16.1371, 11.5968, -8.3768),  Quat(-0.0310868, 0.940534, 0.023991, 0.337438)},
+{Vec3(19.3678, 11.8323, -7.89213),  Quat(-0.0268366, 0.91397, 0.0201037, 0.40441)},
+{Vec3(20.7731, 12.5386, -4.00229), Quat(-0.0971082, 0.782116, 0.0873222, 0.6093)},
+{Vec3(20.2446, 11.8249, 1.2901),  Quat(-0.00907603, 0.719142, 0.0141181, 0.694673)},
+{Vec3(12.5349, 11.1931, 0.648789),  Quat(-0.127753, 0.711443, 0.129103, 0.678884)},
+{Vec3(9.90018, 2.31293, 1.5228),  Quat(0.0185091, 0.774396, -0.00581863, 0.632404)},
+{Vec3(11.3538, 1.32283, 0.125991),  Quat(0.00583224, 0.771427, 0.0107741, 0.636202)},
+{Vec3(19.0313, 2.58059, -3.90416),  Quat(-0.00359187, 0.797582, 0.0205641, 0.602854)},
+{Vec3(20.7252, 2.72255, -7.11897),  Quat(-0.00762224, 0.845843, 0.0233639, 0.532872)},
+{Vec3(16.2244, 2.63427, -9.57957),  Quat(0.00111139, 0.923906, -0.00257634, 0.382623)},
+{Vec3(-1.72731, 2.57222, -10.4981),  Quat(-0.00251057, 0.999248, 0.00509784, 0.0385309)},
+{Vec3(-16.3919, 3.66866, -9.5139),  Quat(0.00272353, -0.97944, -0.0238562, 0.200345)},
+{Vec3(-21.3014, 3.56377, -6.92104),  Quat(0.00880986, -0.89649, -0.0243779, 0.442329)},
+{Vec3(-22.5468, 4.19827, -0.0445367),  Quat(0.0118534, -0.639768, -0.0276997, 0.767993)},
+{Vec3(-20.5534, 4.01987, 6.74214),  Quat(0.00108179, -0.474133, -0.0374377, 0.879656)},
+{Vec3(-14.6694, 3.63242, 4.82707),  Quat(-0.0702802, -0.423791, -0.06851, 0.900429)},
+{Vec3(-13.8528, 4.5495, -1.21759),  Quat(-0.0974997, -0.731442, -0.0571003, 0.672482)},
+{Vec3(-6.98517, 3.04676, -1.17589),  Quat(-0.0974314, -0.785521, -0.0440555, 0.609533)},
+{Vec3(-5.46171, 2.75274, -0.785615),  Quat(-0.0947099, -0.78128, -0.0416765, 0.61555)},
+{Vec3(-2.24792, 2.63747, -3.55529),  Quat(-0.0249922, -0.998763, -0.0296618, 0.031339)},
+{Vec3(1.26739, 2.34591, 1.15404),  Quat(0.0520454, 0.723209, -0.0448162, 0.687212)},
+{Vec3(-0.376718, 2.57919, 3.98087),  Quat(0.0168362, 0.303837, -0.0616157, 0.950586)},
+{Vec3(-1.35176, 2.79261, 2.48651),  Quat(0.099356, 0.247582, -0.0892974, 0.95962)}
+	};
+
+	distance = 0.0;
+
+	for(U i = 0; i < tmp.size(); i++)
+	{
+		PathPoint pp;
+
+		pp.pos = tmp[i].first;
+		pp.rot = tmp[i].second;
+
+		// Update the distances
+		if(i != 0)
+		{
+			pp.dist = (pp.pos - points[i - 1].pos).getLength();
+			pp.distStart = distance + pp.dist;
+
+			distance = pp.distStart;
+		}
+		else
+		{
+			pp.dist = 0.0;
+			pp.distStart = 0.0;
+		}
+
+		// Push back point
+		points.push_back(pp);
+	}
+}
+
+} // end namespace anki

+ 14 - 11
testapp/Main.cpp

@@ -279,6 +279,15 @@ void init()
 
 
 	sgroup.createNewPortal(sectorA, sectorC, Obb(Vec3(-1.1, 2.0, -11.0),
 	sgroup.createNewPortal(sectorA, sectorC, Obb(Vec3(-1.1, 2.0, -11.0),
 		Mat3::getIdentity(), Vec3(1.3, 1.8, 0.5)));
 		Mat3::getIdentity(), Vec3(1.3, 1.8, 0.5)));
+
+	// Path
+	Path* path = new Path("todo", "path", &scene, Movable::MF_NONE, nullptr);
+	(void)path;
+
+	const F32 distPerSec = 2.0;
+	scene.getEventManager().newFollowPathEvent(-1.0, 
+		path->getDistance() / distPerSec, 
+		cam, path, distPerSec);
 }
 }
 
 
 //==============================================================================
 //==============================================================================
@@ -354,14 +363,6 @@ void mainLoopExtra()
 		static_cast<PointLight*>(l)->setRadius(10.0);
 		static_cast<PointLight*>(l)->setRadius(10.0);
 	}
 	}
 
 
-	if(in.getKey(KC_P) == 1)
-	{
-		//MainRendererSingleton::get().getPps().getHdr().setExposure(20);
-		//in.hideCursor(true);
-		MainRendererSingleton::get().getDbg().setDepthTestEnabled(
-			!MainRendererSingleton::get().getDbg().getDepthTestEnabled());
-	}
-
 	if(in.getKey(KC_F1) == 1)
 	if(in.getKey(KC_F1) == 1)
 	{
 	{
 		MainRendererSingleton::get().getDbg().setEnabled(
 		MainRendererSingleton::get().getDbg().setEnabled(
@@ -413,9 +414,11 @@ void mainLoopExtra()
 	{
 	{
 		mover->scale(-scale);
 		mover->scale(-scale);
 	}
 	}
-	if(in.getKey(KC_P))
+	if(in.getKey(KC_P) == 1)
 	{
 	{
-		ANKI_LOGI("pos: " << mover->getWorldTransform().getOrigin());
+		std::cout << "{Vec3(" << mover->getWorldTransform().getOrigin()
+			<< "), Quat(" << Quat(mover->getWorldTransform().getRotation())
+			<< ")}," << std::endl;
 	}
 	}
 
 
 
 
@@ -531,7 +534,7 @@ void initSubsystems(int argc, char* argv[])
 	initializer.ms.ez.enabled = true;
 	initializer.ms.ez.enabled = true;
 	initializer.dbg.enabled = false;
 	initializer.dbg.enabled = false;
 	initializer.is.sm.bilinearEnabled = true;
 	initializer.is.sm.bilinearEnabled = true;
-	initializer.is.groundLightEnabled = false;
+	initializer.is.groundLightEnabled = true;
 	initializer.is.sm.enabled = true;
 	initializer.is.sm.enabled = true;
 	initializer.is.sm.pcfEnabled = false;
 	initializer.is.sm.pcfEnabled = false;
 	initializer.is.sm.resolution = 512;
 	initializer.is.sm.resolution = 512;