Forráskód Böngészése

Huge commit with various physics work

- Physics works with smart pointers now
- Adding a new resource
- Reworking the resource
- Adding two new nodes related to physics
Panagiotis Christopoulos Charitos 10 éve
szülő
commit
553570733a
39 módosított fájl, 733 hozzáadás és 274 törlés
  1. 2 0
      include/anki/Scene.h
  2. 19 0
      include/anki/physics/Common.h
  3. 12 0
      include/anki/physics/Forward.h
  4. 8 1
      include/anki/physics/PhysicsBody.h
  5. 6 0
      include/anki/physics/PhysicsCollisionShape.h
  6. 15 8
      include/anki/physics/PhysicsObject.h
  7. 5 0
      include/anki/physics/PhysicsPlayerController.h
  8. 34 43
      include/anki/physics/PhysicsWorld.h
  9. 50 0
      include/anki/resource/CollisionResource.h
  10. 1 0
      include/anki/resource/Common.h
  11. 13 0
      include/anki/resource/Forward.h
  12. 0 10
      include/anki/resource/Model.h
  13. 2 1
      include/anki/resource/ResourceManager.h
  14. 0 3
      include/anki/resource/ResourcePointer.h
  15. 5 5
      include/anki/scene/BodyComponent.h
  16. 38 0
      include/anki/scene/BodyNode.h
  17. 0 3
      include/anki/scene/ModelNode.h
  18. 3 2
      include/anki/scene/PlayerControllerComponent.h
  19. 2 4
      include/anki/scene/PlayerNode.h
  20. 40 0
      include/anki/scene/StaticCollisionNode.h
  21. 23 1
      include/anki/util/Ptr.h
  22. 3 7
      src/physics/Common.cpp
  23. 26 5
      src/physics/PhysicsBody.cpp
  24. 1 1
      src/physics/PhysicsCollisionShape.cpp
  25. 44 32
      src/physics/PhysicsWorld.cpp
  26. 8 7
      src/renderer/DebugDrawer.cpp
  27. 73 0
      src/resource/CollisionResource.cpp
  28. 0 56
      src/resource/Model.cpp
  29. 1 0
      src/resource/ResourceManager.cpp
  30. 14 0
      src/scene/BodyComponent.cpp
  31. 81 0
      src/scene/BodyNode.cpp
  32. 0 55
      src/scene/ModelNode.cpp
  33. 3 7
      src/scene/PlayerNode.cpp
  34. 36 0
      src/scene/StaticCollisionNode.cpp
  35. 123 0
      src/script/Scene.cpp
  36. 16 0
      src/script/Scene.xml
  37. 1 1
      testapp/Main.cpp
  38. 22 22
      tools/scene/Exporter.cpp
  39. 3 0
      tools/scene/Exporter.h

+ 2 - 0
include/anki/Scene.h

@@ -17,5 +17,7 @@
 #include "anki/scene/Light.h"
 #include "anki/scene/Path.h"
 #include "anki/scene/InstanceNode.h"
+#include "anki/scene/StaticCollisionNode.h"
+#include "anki/scene/BodyNode.h"
 
 #endif

+ 19 - 0
include/anki/physics/Common.h

@@ -8,18 +8,37 @@
 
 #include "anki/util/StdTypes.h"
 #include "anki/util/Enum.h"
+#include "anki/util/Ptr.h"
 #include "anki/Math.h"
 #include <Newton.h>
 
 namespace anki {
 
 // Forward
+class PhysicsObject;
 class PhysicsWorld;
 class PhysicsCollisionShape;
+class PhysicsBody;
+class PhysicsPlayerController;
 
 /// @addtogroup physics
 /// @{
 
+/// PhysicsPtr custom deleter.
+class PhysicsPtrDeleter
+{
+public:
+	void operator()(PhysicsObject* ptr);
+};
+
+/// Smart pointer for physics objects.
+template<typename T>
+using PhysicsPtr = IntrusivePtr<T, PhysicsPtrDeleter>;
+
+using PhysicsCollisionShapePtr = PhysicsPtr<PhysicsCollisionShape>;
+using PhysicsBodyPtr = PhysicsPtr<PhysicsBody>;
+using PhysicsPlayerControllerPtr = PhysicsPtr<PhysicsPlayerController>;
+
 /// Material types.
 enum class PhysicsMaterialBit: U16
 {

+ 12 - 0
include/anki/physics/Forward.h

@@ -0,0 +1,12 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_PHYSICS_FORWARD_H
+#define ANKI_PHYSICS_FORWARD_H
+
+#include "anki/physics/Common.h"
+
+#endif
+

+ 8 - 1
include/anki/physics/PhysicsBody.h

@@ -16,11 +16,12 @@ namespace anki {
 /// Initializer for PhysicsBody.
 struct PhysicsBodyInitializer
 {
-	const PhysicsCollisionShape* m_shape = nullptr;
+	PhysicsCollisionShapePtr m_shape;
 	F32 m_mass = 0.0;
 	Transform m_startTrf = Transform::getIdentity();
 	Bool m_kinematic = false;
 	Bool m_gravity = true;
+	Bool m_static = false;
 };
 
 /// Rigid body.
@@ -44,8 +45,14 @@ public:
 
 	void setTransform(const Transform& trf);
 
+	static Bool classof(const PhysicsObject& c)
+	{
+		return c.getType() == Type::BODY;
+	}
+
 private:
 	NewtonBody* m_body = nullptr;
+	void* m_sceneCollisionProxy = nullptr;
 	Transform m_trf = Transform::getIdentity();
 	Bool8 m_updated = true;
 

+ 6 - 0
include/anki/physics/PhysicsCollisionShape.h

@@ -38,6 +38,11 @@ public:
 
 	~PhysicsCollisionShape();
 
+	static Bool classof(const PhysicsObject& c)
+	{
+		return c.getType() == Type::COLLISION_SHAPE;
+	}
+
 	/// @privatesection
 	/// @{
 	NewtonCollision* _getNewtonShape() const
@@ -49,6 +54,7 @@ public:
 
 protected:
 	NewtonCollision* m_shape = nullptr;
+	void* m_sceneCollisionProxy = nullptr;
 	static I32 m_gid;
 };
 

+ 15 - 8
include/anki/physics/PhysicsObject.h

@@ -29,26 +29,33 @@ public:
 
 	PhysicsObject(Type type, PhysicsWorld* world)
 	:	m_world(world),
-		m_type(type)
+		m_type(type),
+		m_refcount(0)
 	{
 		ANKI_ASSERT(m_world);
 	}
 
 	virtual ~PhysicsObject()
-	{
-		ANKI_ASSERT(m_markedForDeletion == true);
-	}
+	{}
 
 	Type getType() const
 	{
 		return m_type;
 	}
 
-	void setMarkedForDeletion();
+	PhysicsWorld& getWorld()
+	{
+		return *m_world;
+	}
+
+	const PhysicsWorld& getWorld() const
+	{
+		return *m_world;
+	}
 
-	Bool getMarkedForDeletion() const
+	Atomic<I32>& getRefcount()
 	{
-		return m_markedForDeletion;
+		return m_refcount;
 	}
 
 protected:
@@ -56,7 +63,7 @@ protected:
 
 private:
 	Type m_type;
-	Bool8 m_markedForDeletion = false;
+	Atomic<I32> m_refcount;
 };
 /// @}
 

+ 5 - 0
include/anki/physics/PhysicsPlayerController.h

@@ -55,6 +55,11 @@ public:
 		return m_trf;
 	}
 
+	static Bool classof(const PhysicsObject& c)
+	{
+		return c.getType() == Type::PLAYER_CONTROLLER;
+	}
+
 	/// @privatesection
 	/// @{
 

+ 34 - 43
include/anki/physics/PhysicsWorld.h

@@ -7,10 +7,8 @@
 #define ANKI_PHYSICS_PHYSICS_WORLD_H
 
 #include "anki/physics/Common.h"
-#include "anki/physics/PhysicsCollisionShape.h"
-#include "anki/physics/PhysicsBody.h"
-#include "anki/physics/PhysicsPlayerController.h"
 #include "anki/util/List.h"
+#include "anki/util/Rtti.h"
 
 namespace anki {
 
@@ -28,23 +26,7 @@ public:
 		AllocAlignedCallback allocCb, void* allocCbData);
 
 	template<typename T, typename... TArgs>
-	T* newCollisionShape(TArgs&&... args)
-	{
-		return newObjectInternal<T>(m_collisions, std::forward<TArgs>(args)...);
-	}
-
-	template<typename T, typename... TArgs>
-	T* newBody(TArgs&&... args)
-	{
-		return newObjectInternal<T>(m_bodies, std::forward<TArgs>(args)...);
-	}
-
-	PhysicsPlayerController* newPlayerController(
-		const PhysicsPlayerController::Initializer& init)
-	{
-		return newObjectInternal<PhysicsPlayerController>(
-			m_playerControllers, init);
-	}
+	PhysicsPtr<T> newInstance(TArgs&&... args);
 
 	/// Start asynchronous update.
 	Error updateAsync(F32 dt);
@@ -60,7 +42,10 @@ public:
 		return m_world;
 	}
 
-	void _increaseObjectsMarkedForDeletion(PhysicsObject::Type type);
+	NewtonCollision* getNewtonScene() const
+	{
+		return m_scene;
+	}
 
 	const Vec4& getGravity() const
 	{
@@ -71,25 +56,30 @@ public:
 	{
 		return m_dt;
 	}
+
+	void deleteObjectDeferred(PhysicsObject* obj)
+	{
+		LockGuard<Mutex> lock(m_mtx);
+		m_forDeletion.pushBack(m_alloc, obj);
+	}
 	/// @}
 
 private:
-	ChainAllocator<U8> m_alloc;
-	List<PhysicsCollisionShape*> m_collisions;
-	List<PhysicsBody*> m_bodies;
-	List<PhysicsPlayerController*> m_playerControllers;
-	Array<Atomic<U32>, static_cast<U>(PhysicsObject::Type::COUNT)> 
-		m_forDeletionCount = {{{0}, {0}, {0}}};
+	HeapAllocator<U8> m_alloc;
 	mutable NewtonWorld* m_world = nullptr;
+	NewtonCollision* m_scene = nullptr;
 	Vec4 m_gravity = Vec4(0.0, -9.8, 0.0, 0.0);
 	F32 m_dt = 0.0;
 
-	template<typename T, typename TContainer, typename... TArgs>
-	T* newObjectInternal(TContainer& cont, TArgs&&... args);
+	List<PhysicsPlayerController*> m_playerControllers;
 
-	template<typename T>
-	void cleanupMarkedForDeletion(
-		List<T*>& container, Atomic<U32>& count);
+	Mutex m_mtx;
+	List<PhysicsObject*> m_forDeletion;
+
+	template<typename T, typename... TArgs>
+	PhysicsPtr<T> newObjectInternal(TArgs&&... args);
+
+	void cleanupMarkedForDeletion();
 
 	/// Custom update
 	static void postUpdateCallback(
@@ -105,35 +95,36 @@ private:
 		const NewtonWorld* const world, 
 		void* const listenerUserData)
 	{}
+
+	void registerObject(PhysicsObject* ptr);
 };
 
 //==============================================================================
-template<typename T, typename TContainer, typename... TArgs>
-inline T* PhysicsWorld::newObjectInternal(TContainer& cont, TArgs&&... args)
+template<typename T, typename... TArgs>
+inline PhysicsPtr<T> PhysicsWorld::newInstance(TArgs&&... args)
 {
 	Error err = ErrorCode::NONE;
-	ChainAllocator<T> al = m_alloc;
-
-	T* ptr = al.template newInstance<T>(this);
+	PhysicsPtr<T> out;
+		
+	T* ptr = m_alloc.template newInstance<T>(this);
 	err = ptr->create(std::forward<TArgs>(args)...);
 	
 	if(!err)
 	{
-		cont.pushBack(m_alloc, ptr);
+		registerObject(ptr);
+		out.reset(ptr);
 	}
-
-	if(err)
+	else
 	{
 		ANKI_LOGE("Failed to create physics object");
 
 		if(ptr)
 		{
-			al.deleteInstance(ptr);
-			ptr = nullptr;
+			m_alloc.deleteInstance(ptr);
 		}
 	}
 
-	return ptr;
+	return out;
 }
 /// @}
 

+ 50 - 0
include/anki/resource/CollisionResource.h

@@ -0,0 +1,50 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_RESOURCE_COLLISION_RESOURCE_H
+#define ANKI_RESOURCE_COLLISION_RESOURCE_H
+
+#include "anki/resource/ResourceObject.h"
+#include "anki/resource/ResourcePointer.h"
+#include "anki/physics/PhysicsCollisionShape.h"
+
+namespace anki {
+
+/// @addtogroup resource
+/// @{
+
+/// Load a collision shape.
+///
+/// XML file format:
+/// <collisionShape>
+/// 	<type>sphere | box | mesh</type>
+/// 	<value>radius | extend | path/to/mesh</value>
+/// </collisionShape>
+class CollisionResource: public ResourceObject
+{
+public:
+	CollisionResource(ResourceManager* manager)
+	:	ResourceObject(manager)
+	{}
+
+	~CollisionResource()
+	{}
+
+	ANKI_USE_RESULT Error load(const CString& filename);
+
+	PhysicsCollisionShapePtr getShape() const
+	{
+		return m_physicsShape;
+	}
+
+private:
+	PhysicsCollisionShapePtr m_physicsShape;
+};
+/// @}
+
+} // end namespace anki
+
+#endif
+

+ 1 - 0
include/anki/resource/Common.h

@@ -37,6 +37,7 @@ ANKI_FORWARD(ParticleEmitterResource, ParticleEmitterResourcePointer)
 ANKI_FORWARD(Model, ModelResourcePointer)
 ANKI_FORWARD(Script, ScriptResourcePointer)
 ANKI_FORWARD(DummyRsrc, DummyResourcePointer)
+ANKI_FORWARD(CollisionResource, CollisionResourcePtr)
 
 #undef ANKI_FORWARD
 

+ 13 - 0
include/anki/resource/Forward.h

@@ -0,0 +1,13 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_RESOURCE_FORWARD_H
+#define ANKI_RESOURCE_FORWARD_H
+
+#include "anki/resource/Common.h"
+#include "anki/resource/ResourcePointer.h"
+
+#endif
+

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

@@ -154,10 +154,6 @@ private:
 /// 		<animation>path/to/animation.anim</animation>
 /// 		...
 /// 	</skeletonAnimations>]
-/// 	[<collisionShape>
-/// 		<type>sphere | box | mesh</type>
-/// 		<value>...</value>
-/// 	</collisionShape>]
 /// </model>
 /// @endcode
 ///
@@ -184,11 +180,6 @@ public:
 		return m_visibilityShape;
 	}
 
-	const PhysicsCollisionShape* getPhysicsCollisionShape() const
-	{
-		return m_physicsShape;
-	}
-
 	ANKI_USE_RESULT Error load(const CString& filename);
 
 private:
@@ -196,7 +187,6 @@ private:
 	Obb m_visibilityShape;
 	SkeletonResourcePointer m_skeleton;
 	DArray<AnimationResourcePointer> m_animations;
-	PhysicsCollisionShape* m_physicsShape = nullptr;
 };
 
 } // end namespace anki

+ 2 - 1
include/anki/resource/ResourceManager.h

@@ -103,7 +103,8 @@ class ResourceManager:
 	ANKI_RESOURCE(ParticleEmitterResource),
 	ANKI_RESOURCE(Model),
 	ANKI_RESOURCE(Script),
-	ANKI_RESOURCE(DummyRsrc)
+	ANKI_RESOURCE(DummyRsrc),
+	ANKI_RESOURCE(CollisionResource)
 {
 public:
 	class Initializer

+ 0 - 3
include/anki/resource/ResourcePointer.h

@@ -12,9 +12,6 @@
 
 namespace anki {
 
-// Forward
-class ResourceManager;
-
 /// @addtogroup resource
 /// @{
 

+ 5 - 5
include/anki/scene/BodyComponent.h

@@ -24,12 +24,12 @@ public:
 		return c.getType() == Type::BODY;
 	}
 
-	BodyComponent(SceneNode* node, PhysicsBody* body)
+	BodyComponent(SceneNode* node, PhysicsBodyPtr body)
 	:	SceneComponent(Type::BODY, node), 
 		m_body(body)
-	{
-		ANKI_ASSERT(m_body);
-	}
+	{}
+
+	~BodyComponent();
 
 	const Transform& getTransform() const
 	{
@@ -51,7 +51,7 @@ public:
 	/// @}
 
 private:
-	PhysicsBody* m_body;
+	PhysicsBodyPtr m_body;
 	Transform m_trf;
 };
 /// @}

+ 38 - 0
include/anki/scene/BodyNode.h

@@ -0,0 +1,38 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_SCENE_BODY_NODE_H
+#define ANKI_SCENE_BODY_NODE_H
+
+#include "anki/scene/SceneNode.h"
+#include "anki/resource/CollisionResource.h"
+
+namespace anki {
+
+/// @addtogroup scene
+/// @{
+
+/// Node that gets affected by physics.
+class BodyNode: public SceneNode
+{
+public:
+	BodyNode(SceneGraph* scene)
+	:	SceneNode(scene)
+	{}
+
+	~BodyNode();
+
+	ANKI_USE_RESULT Error create(
+		const CString& name, const CString& resourceFname);
+
+private:
+	CollisionResourcePtr m_rsrc;
+	PhysicsBodyPtr m_body;
+};
+/// @}
+
+} // end namespace anki
+
+#endif

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

@@ -18,7 +18,6 @@ namespace anki {
 // Forward
 class ObbSpatialComponent;
 class BodyComponent;
-class PhysicsBody;
 
 /// @addtogroup scene
 /// @{
@@ -78,8 +77,6 @@ private:
 	DArray<ModelPatchNode*> m_modelPatches;
 	DArray<Transform> m_transforms; ///< Cache the transforms of instances
 	Timestamp m_transformsTimestamp;
-	PhysicsBody* m_body = nullptr;
-	BodyComponent* m_bodyComp = nullptr;
 
 	void onMoveComponentUpdate(MoveComponent& move);
 };

+ 3 - 2
include/anki/scene/PlayerControllerComponent.h

@@ -23,7 +23,8 @@ public:
 		return c.getType() == Type::PLAYER_CONTROLLER;
 	}
 
-	PlayerControllerComponent(SceneNode* node, PhysicsPlayerController* player)
+	PlayerControllerComponent(SceneNode* node, 
+		PhysicsPlayerControllerPtr player)
 	:	SceneComponent(Type::PLAYER_CONTROLLER, node),
 		m_player(player)
 	{}
@@ -54,7 +55,7 @@ public:
 	/// @}
 
 private:
-	PhysicsPlayerController* m_player;
+	PhysicsPlayerControllerPtr m_player;
 	Transform m_trf;
 };
 /// @}

+ 2 - 4
include/anki/scene/PlayerNode.h

@@ -8,12 +8,10 @@
 
 #include "anki/scene/SceneNode.h"
 #include "anki/Math.h"
+#include "anki/physics/PhysicsPlayerController.h"
 
 namespace anki {
 
-// Forward
-class PhysicsPlayerController;
-
 /// @addtogroup scene
 /// @{
 
@@ -30,7 +28,7 @@ public:
 	ANKI_USE_RESULT Error create(const CString& name, const Vec4& position);
 
 private:
-	PhysicsPlayerController* m_player = nullptr;
+	PhysicsPlayerControllerPtr m_player;
 };
 /// @}
 

+ 40 - 0
include/anki/scene/StaticCollisionNode.h

@@ -0,0 +1,40 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#ifndef ANKI_SCENE_STATIC_COLLISION_H
+#define ANKI_SCENE_STATIC_COLLISION_H
+
+#include "anki/scene/SceneNode.h"
+#include "anki/resource/Forward.h"
+#include "anki/physics/Forward.h"
+
+namespace anki {
+
+/// @addtogroup scene
+/// @{
+
+/// Node that interacts with physics.
+class StaticCollisionNode: public SceneNode
+{
+public:
+	StaticCollisionNode(SceneGraph* scene)
+	:	SceneNode(scene)
+	{}
+
+	~StaticCollisionNode();
+
+	ANKI_USE_RESULT Error create(
+		const CString& name, const CString& resourceFname);
+
+private:
+	CollisionResourcePtr m_rsrc;
+	PhysicsBodyPtr m_body;
+};
+/// @}
+
+} // end namespace anki
+
+#endif
+

+ 23 - 1
include/anki/util/Ptr.h

@@ -18,6 +18,9 @@ namespace anki {
 template<typename T>
 class PtrBase
 {
+	template<typename Y>
+	friend class PtrBase;
+
 public:
 	/// Dereference
 	T& operator*() const
@@ -274,6 +277,9 @@ private:
 template<typename T, typename TDeleter = DefaultPtrDeleter<T>>
 class IntrusivePtr: public PtrBase<T>
 {
+	template<typename Y, typename TD>
+	friend class IntrusivePtr;
+
 public:
 	using Base = PtrBase<T>;
 
@@ -281,7 +287,7 @@ public:
 	:	Base()
 	{}
 
-	explicit IntrusivePtr(T* ptr)
+	IntrusivePtr(T* ptr)
 	:	Base()
 	{
 		reset(ptr);
@@ -294,6 +300,14 @@ public:
 		reset(other.m_ptr);
 	}
 
+	/// Copy, compatible pointer.
+	template<typename Y>
+	IntrusivePtr(const IntrusivePtr<Y, TDeleter>& other)
+	:	Base()
+	{
+		reset(other.m_ptr);
+	}
+
 	/// Decrease refcount and delete the pointer if refcount is zero.
 	~IntrusivePtr()
 	{
@@ -307,6 +321,14 @@ public:
 		return *this;
 	}
 
+	/// Copy, compatible.
+	template<typename Y>
+	IntrusivePtr& operator=(const IntrusivePtr<Y, TDeleter>& other)
+	{
+		reset(other.m_ptr);
+		return *this;
+	}
+
 	/// Set a new pointer. Will destroy the previous.
 	void reset(T* ptr)
 	{

+ 3 - 7
src/physics/PhysicsObject.cpp → src/physics/Common.cpp

@@ -3,20 +3,16 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
+#include "anki/physics/Common.h"
 #include "anki/physics/PhysicsObject.h"
 #include "anki/physics/PhysicsWorld.h"
 
 namespace anki {
 
 //==============================================================================
-void PhysicsObject::setMarkedForDeletion()
+void PhysicsPtrDeleter::operator()(PhysicsObject* ptr)
 {
-	if(!m_markedForDeletion)
-	{
-		m_markedForDeletion = true;
-		m_world->_increaseObjectsMarkedForDeletion(m_type);
-	}
+	ptr->getWorld().deleteObjectDeferred(ptr);
 }
 
 } // end namespace anki
-

+ 26 - 5
src/physics/PhysicsBody.cpp

@@ -17,6 +17,14 @@ PhysicsBody::PhysicsBody(PhysicsWorld* world)
 //==============================================================================
 PhysicsBody::~PhysicsBody()
 {
+	if(m_sceneCollisionProxy)
+	{
+		NewtonCollision* scene = m_world->getNewtonScene();
+		NewtonSceneCollisionBeginAddRemove(scene);
+		NewtonSceneCollisionRemoveSubCollision(scene, m_sceneCollisionProxy);
+		NewtonSceneCollisionEndAddRemove(scene);
+	}
+
 	if(m_body)
 	{
 		NewtonDestroyBody(m_body);
@@ -26,14 +34,27 @@ PhysicsBody::~PhysicsBody()
 //==============================================================================
 Error PhysicsBody::create(const Initializer& init)
 {
-	ANKI_ASSERT(init.m_shape);
-
 	//I collisionType = NewtonCollisionGetType(init.m_shape->_getNewtonShape());
 
 	// Create
-	Mat4 trf = Mat4(init.m_startTrf);
-	trf.transpose();
-	if(init.m_kinematic)
+	Mat4 trf = toNewton(Mat4(init.m_startTrf));
+	
+	if(init.m_static)
+	{
+		// Create static collision
+		NewtonCollision* scene = m_world->getNewtonScene();
+
+		NewtonSceneCollisionBeginAddRemove(scene);
+		m_sceneCollisionProxy = NewtonSceneCollisionAddSubCollision(
+			scene, init.m_shape->_getNewtonShape());
+		NewtonSceneCollisionEndAddRemove(scene);
+
+		NewtonSceneCollisionSetSubCollisionMatrix(
+			scene, m_sceneCollisionProxy, &trf[0]);
+
+		return ErrorCode::NONE;
+	}
+	else if(init.m_kinematic)
 	{
 		// TODO
 	}

+ 1 - 1
src/physics/PhysicsCollisionShape.cpp

@@ -13,7 +13,7 @@ namespace anki {
 //==============================================================================
 
 //==============================================================================
-I32 PhysicsCollisionShape::m_gid = 0;
+I32 PhysicsCollisionShape::m_gid = 1;
 
 //==============================================================================
 PhysicsCollisionShape::~PhysicsCollisionShape()

+ 44 - 32
src/physics/PhysicsWorld.cpp

@@ -4,12 +4,14 @@
 // http://www.anki3d.org/LICENSE
 
 #include "anki/physics/PhysicsWorld.h"
+#include "anki/physics/PhysicsPlayerController.h"
+#include "anki/physics/PhysicsCollisionShape.h"
 
 namespace anki {
 
 //==============================================================================
 // Ugly but there is no other way
-static ChainAllocator<U8>* gAlloc = nullptr;
+static HeapAllocator<U8>* gAlloc = nullptr;
 
 static void* newtonAlloc(int size)
 {
@@ -41,11 +43,7 @@ Error PhysicsWorld::create(AllocAlignedCallback allocCb, void* allocCbData)
 {
 	Error err = ErrorCode::NONE;
 
-	m_alloc = ChainAllocator<U8>(
-		allocCb, allocCbData, 
-		1024 * 10,
-		2.0,
-		0);
+	m_alloc = HeapAllocator<U8>(allocCb, allocCbData);
 	
 	// Set allocators
 	gAlloc = &m_alloc;
@@ -62,6 +60,9 @@ Error PhysicsWorld::create(AllocAlignedCallback allocCb, void* allocCbData)
 	// Set the simplified solver mode (faster but less accurate)
 	NewtonSetSolverModel(m_world, 1);
 
+	// Create scene collision
+	m_scene = NewtonCreateSceneCollision(m_world, 0);
+
 	// Set the post update listener
 	NewtonWorldAddPostListener(m_world, "world", this, postUpdateCallback, 
 		destroyCallback);
@@ -69,22 +70,13 @@ Error PhysicsWorld::create(AllocAlignedCallback allocCb, void* allocCbData)
 	return err;
 }
 
-//==============================================================================
-void PhysicsWorld::_increaseObjectsMarkedForDeletion(PhysicsObject::Type type)
-{
-	m_forDeletionCount[static_cast<U>(type)].fetchAdd(1);
-}
-
 //==============================================================================
 Error PhysicsWorld::updateAsync(F32 dt)
 {
 	m_dt = dt;
 
 	// Do cleanup of marked for deletion
-	cleanupMarkedForDeletion(m_bodies, 
-		m_forDeletionCount[static_cast<U>(PhysicsObject::Type::BODY)]);
-	cleanupMarkedForDeletion(m_collisions, m_forDeletionCount[
-		static_cast<U>(PhysicsObject::Type::COLLISION_SHAPE)]);
+	cleanupMarkedForDeletion();
 
 	// Update
 	NewtonUpdateAsync(m_world, dt);
@@ -99,29 +91,38 @@ void PhysicsWorld::waitUpdate()
 }
 
 //==============================================================================
-template<typename T>
-void PhysicsWorld::cleanupMarkedForDeletion(
-	List<T*>& container, Atomic<U32>& count)
+void PhysicsWorld::cleanupMarkedForDeletion()
 {
-	while(count.load() > 0)
+	LockGuard<Mutex> lock(m_mtx);
+
+	while(!m_forDeletion.isEmpty())
 	{
-		Bool found = false;
-		auto it = container.begin();
-		auto end = container.end();
-		for(; it != end; it++)
+		auto it = m_forDeletion.getBegin();
+		PhysicsObject* obj = *it;
+
+		// Remove from objects marked for deletion
+		m_forDeletion.erase(m_alloc, it);
+
+		// Remove from player controllers
+		if(obj->getType() == PhysicsObject::Type::PLAYER_CONTROLLER)
 		{
-			if((*it)->getMarkedForDeletion())
+		
+			auto it2 = m_playerControllers.getBegin();
+			for(; it2 != m_playerControllers.getEnd(); ++it2)
 			{
-				// Delete node
-				container.erase(m_alloc, it);
-				m_alloc.deleteInstance(*it);
-				found = true;
-				break;
+				PhysicsObject* obj2 = *it2;
+				if(obj2 == obj)
+				{
+					break;
+				}
 			}
+
+			ANKI_ASSERT(it2 != m_playerControllers.getEnd());
+			m_playerControllers.erase(m_alloc, it2);
 		}
 
-		(void)found;
-		ANKI_ASSERT(found && "Something is wrong with marked for deletion");
+		// Finaly, delete it
+		m_alloc.deleteInstance(obj);
 	}
 }
 
@@ -135,4 +136,15 @@ void PhysicsWorld::postUpdate(F32 dt)
 	}
 }
 
+//==============================================================================
+void PhysicsWorld::registerObject(PhysicsObject* ptr)
+{
+	if(isa<PhysicsPlayerController>(ptr))
+	{
+		LockGuard<Mutex> lock(m_mtx);
+		m_playerControllers.pushBack(
+			m_alloc, dcast<PhysicsPlayerController*>(ptr));
+	}
+}
+
 } // end namespace anki

+ 8 - 7
src/renderer/DebugDrawer.cpp

@@ -11,6 +11,7 @@
 #include "anki/resource/TextureResource.h"
 #include "anki/renderer/Renderer.h"
 #include "anki/util/Logger.h"
+#include "anki/physics/PhysicsWorld.h"
 
 namespace anki {
 
@@ -142,10 +143,10 @@ Error DebugDrawer::flushInternal(GLenum primitive)
 
 	m_ppline.bind(m_jobs);
 
-	m_vertBuff.bindVertexBuffer(m_jobs, 
+	m_vertBuff.bindVertexBuffer(m_jobs,
 		4, GL_FLOAT, false, sizeof(Vertex), 0, 0); // Pos
 
-	m_vertBuff.bindVertexBuffer(m_jobs, 
+	m_vertBuff.bindVertexBuffer(m_jobs,
 		4, GL_FLOAT, true, sizeof(Vertex), sizeof(Vec4), 1); // Color
 
 	m_jobs.drawArrays(primitive, clientVerts);
@@ -244,7 +245,7 @@ void DebugDrawer::drawSphere(F32 radius, I complexity)
 	Mat4 oldMMat = m_mMat;
 	Mat4 oldVpMat = m_vpMat;
 
-	setModelMatrix(m_mMat * Mat4(Vec4(0.0, 0.0, 0.0, 1.0), 
+	setModelMatrix(m_mMat * Mat4(Vec4(0.0, 0.0, 0.0, 1.0),
 		Mat3::getIdentity(), radius));
 
 	begin(GL_LINES);
@@ -301,7 +302,7 @@ void DebugDrawer::drawCube(F32 size)
 	}};
 
 	static const Array<U32, 24> indeces = {{
-		0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 
+		0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6,
 		6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7}};
 
 	begin(GL_LINES);
@@ -397,7 +398,7 @@ void CollisionDebugDrawer::visit(const Frustum& f)
 				static_cast<const PerspectiveFrustum&>(f);
 
 			F32 camLen = pf.getFar();
-			F32 tmp0 = camLen / tan((getPi<F32>() - pf.getFovX()) * 0.5) 
+			F32 tmp0 = camLen / tan((getPi<F32>() - pf.getFovX()) * 0.5)
 				+ 0.001;
 			F32 tmp1 = camLen * tan(pf.getFovY() * 0.5) + 0.001;
 
@@ -427,7 +428,7 @@ void CollisionDebugDrawer::visit(const Frustum& f)
 void CollisionDebugDrawer::visit(const CompoundShape& cs)
 {
 	CollisionDebugDrawer* self = this;
-	Error err = cs.iterateShapes([&](const CollisionShape& a) -> Error 
+	Error err = cs.iterateShapes([&](const CollisionShape& a) -> Error
 	{
 		a.accept(*self);
 		return ErrorCode::NONE;
@@ -566,7 +567,7 @@ void SceneDebugDrawer::drawPath(const Path& path) const
 	m_dbg->setColor(Vec3(1.0, 1.0, 0.0));
 
 	m_dbg->begin();
-	
+
 	for(U i = 0; i < count - 1; i++)
 	{
 		m_dbg->pushBackVertex(path.getPoints()[i].getPosition());

+ 73 - 0
src/resource/CollisionResource.cpp

@@ -0,0 +1,73 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/resource/CollisionResource.h"
+#include "anki/resource/ResourceManager.h"
+#include "anki/resource/MeshLoader.h"
+#include "anki/physics/PhysicsWorld.h"
+#include "anki/misc/Xml.h"
+
+namespace anki {
+
+//==============================================================================
+Error CollisionResource::load(const CString& filename)
+{
+	XmlElement el;
+	XmlDocument doc;
+	ANKI_CHECK(doc.loadFile(filename, getTempAllocator()));
+
+	XmlElement collEl;
+	ANKI_CHECK(doc.getChildElement("collisionShape", collEl));
+
+	ANKI_CHECK(collEl.getChildElement("type", el));
+	CString type;
+	ANKI_CHECK(el.getText(type));
+
+	XmlElement valEl;
+	ANKI_CHECK(collEl.getChildElement("value", valEl));
+
+	PhysicsWorld& physics = getManager()._getPhysicsWorld();
+	PhysicsCollisionShape::Initializer csInit;
+
+	if(type == "sphere")
+	{
+		F64 tmp;
+		ANKI_CHECK(valEl.getF64(tmp));
+		m_physicsShape = physics.newInstance<PhysicsSphere>(csInit, tmp);
+	}
+	else if(type == "box")
+	{
+		Vec3 extend;
+		ANKI_CHECK(valEl.getVec3(extend));
+		m_physicsShape = physics.newInstance<PhysicsBox>(csInit, extend);
+	}
+	else if(type == "staticMesh")
+	{
+		CString filename;
+		ANKI_CHECK(valEl.getText(filename));
+
+		StringAuto fixedFilename(getTempAllocator());
+		getManager().fixResourceFilename(filename, fixedFilename);
+
+		MeshLoader loader;
+		ANKI_CHECK(loader.load(getTempAllocator(), fixedFilename.toCString()));
+
+		m_physicsShape = physics.newInstance<PhysicsTriangleSoup>(
+			csInit,
+			reinterpret_cast<const Vec3*>(loader.getVertexData()),
+			loader.getVertexSize(),
+			reinterpret_cast<const U16*>(loader.getIndexData()),
+			loader.getHeader().m_totalIndicesCount);
+	}
+	else
+	{
+		ANKI_LOGE("Incorrect collision type");
+		return ErrorCode::USER_DATA;
+	}
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki

+ 0 - 56
src/resource/Model.cpp

@@ -11,7 +11,6 @@
 #include "anki/resource/ShaderResource.h"
 #include "anki/misc/Xml.h"
 #include "anki/util/Logger.h"
-#include "anki/physics/PhysicsWorld.h"
 
 namespace anki {
 
@@ -273,61 +272,6 @@ Error Model::load(const CString& filename)
 	XmlElement rootEl;
 	ANKI_CHECK(doc.getChildElement("model", rootEl));
 
-	// <collisionShape>
-	XmlElement collEl;
-	ANKI_CHECK(rootEl.getChildElementOptional("collisionShape", collEl));
-	if(collEl)
-	{
-		ANKI_CHECK(collEl.getChildElement("type", el));
-		CString type;
-		ANKI_CHECK(el.getText(type));
-
-		XmlElement valEl;
-		ANKI_CHECK(collEl.getChildElement("value", valEl));
-
-		PhysicsWorld& physics = getManager()._getPhysicsWorld();
-		PhysicsCollisionShape::Initializer csInit;
-
-		if(type == "sphere")
-		{
-			F64 tmp;
-			ANKI_CHECK(valEl.getF64(tmp));
-			m_physicsShape = physics.newCollisionShape<PhysicsSphere>(
-				csInit, tmp);
-		}
-		else if(type == "box")
-		{
-			Vec3 extend;
-			ANKI_CHECK(valEl.getVec3(extend));
-			m_physicsShape = physics.newCollisionShape<PhysicsBox>(
-				csInit, extend);
-		}
-		else if(type == "staticMesh")
-		{
-			CString filename;
-			ANKI_CHECK(valEl.getText(filename));
-
-			StringAuto fixedFilename(getTempAllocator());
-			getManager().fixResourceFilename(filename, fixedFilename);
-
-			MeshLoader loader;
-			ANKI_CHECK(
-				loader.load(getTempAllocator(), fixedFilename.toCString()));
-
-			m_physicsShape = physics.newCollisionShape<PhysicsTriangleSoup>(
-				csInit,
-				reinterpret_cast<const Vec3*>(loader.getVertexData()),
-				loader.getVertexSize(),
-				reinterpret_cast<const U16*>(loader.getIndexData()),
-				loader.getHeader().m_totalIndicesCount);
-		}
-		else
-		{
-			ANKI_LOGE("Incorrect collision type");
-			return ErrorCode::USER_DATA;
-		}
-	}
-
 	// <modelPatches>
 	XmlElement modelPatchesEl;
 	ANKI_CHECK(rootEl.getChildElement("modelPatches", modelPatchesEl));

+ 1 - 0
src/resource/ResourceManager.cpp

@@ -85,6 +85,7 @@ Error ResourceManager::create(Initializer& init)
 	ANKI_RESOURCE(Model)
 	ANKI_RESOURCE(Script)
 	ANKI_RESOURCE(DummyRsrc)
+	ANKI_RESOURCE(CollisionResource)
 
 #undef ANKI_RESOURCE
 

+ 14 - 0
src/scene/BodyComponent.cpp

@@ -0,0 +1,14 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/scene/BodyComponent.h"
+
+namespace anki {
+
+//==============================================================================
+BodyComponent::~BodyComponent()
+{}
+
+} // end namespace anki

+ 81 - 0
src/scene/BodyNode.cpp

@@ -0,0 +1,81 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/scene/BodyNode.h"
+#include "anki/scene/BodyComponent.h"
+#include "anki/scene/MoveComponent.h"
+#include "anki/scene/SceneGraph.h"
+#include "anki/physics/PhysicsWorld.h"
+
+namespace anki {
+
+//==============================================================================
+// BodyFeedbackComponent                                                       =
+//==============================================================================
+
+/// Body feedback component.
+class BodyFeedbackComponent: public SceneComponent
+{
+public:
+	BodyFeedbackComponent(SceneNode* node)
+	:	SceneComponent(SceneComponent::Type::NONE, node)
+	{}
+
+	ANKI_USE_RESULT Error update(
+		SceneNode& node, F32, F32, Bool& updated)
+	{
+		updated = false;
+
+		BodyComponent& bodyc = node.getComponent<BodyComponent>();
+
+		if(bodyc.getTimestamp() == node.getGlobalTimestamp())
+		{
+			MoveComponent& move = node.getComponent<MoveComponent>();
+			move.setLocalTransform(bodyc.getTransform());
+		}
+
+		return ErrorCode::NONE;
+	}
+};
+
+//==============================================================================
+// BodyNode                                                                    =
+//==============================================================================
+
+//==============================================================================
+BodyNode::~BodyNode()
+{}
+
+//==============================================================================
+Error BodyNode::create(const CString& name, const CString& resourceFname)
+{
+	SceneComponent* comp;
+
+	// Load resource
+	ANKI_CHECK(m_rsrc.load(resourceFname, &getResourceManager()));
+
+	// Create body
+	PhysicsBody::Initializer init;
+	init.m_mass = 1.0;
+	init.m_shape = m_rsrc->getShape();
+	m_body = getSceneGraph()._getPhysicsWorld().newInstance<PhysicsBody>(init);
+
+	// Body component
+	comp = getSceneAllocator().newInstance<BodyComponent>(this, m_body);
+	addComponent(comp, true);
+
+	// Feedback component
+	comp = getSceneAllocator().newInstance<BodyFeedbackComponent>(this);
+	addComponent(comp, true);
+
+	// Move component
+	comp = getSceneAllocator().newInstance<MoveComponent>(this);
+	addComponent(comp, true);
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace anki
+

+ 0 - 55
src/scene/ModelNode.cpp

@@ -244,35 +244,6 @@ public:
 	}
 };
 
-//==============================================================================
-// ModelBodyFeedbackComponent                                                  =
-//==============================================================================
-
-/// Body feedback component.
-class ModelBodyFeedbackComponent: public SceneComponent
-{
-public:
-	ModelBodyFeedbackComponent(SceneNode* node)
-	:	SceneComponent(SceneComponent::Type::NONE, node)
-	{}
-
-	ANKI_USE_RESULT Error update(
-		SceneNode& node, F32, F32, Bool& updated)
-	{
-		updated = false;
-
-		BodyComponent& bodyc = node.getComponent<BodyComponent>();
-
-		if(bodyc.getTimestamp() == node.getGlobalTimestamp())
-		{
-			MoveComponent& move = node.getComponent<MoveComponent>();
-			move.setLocalTransform(bodyc.getTransform());
-		}
-
-		return ErrorCode::NONE;
-	}
-};
-
 //==============================================================================
 // ModelNode                                                                   =
 //==============================================================================
@@ -288,11 +259,6 @@ ModelNode::~ModelNode()
 {
 	m_modelPatches.destroy(getSceneAllocator());
 	m_transforms.destroy(getSceneAllocator());
-
-	if(m_body)
-	{
-		getSceneAllocator().deleteInstance(m_body);
-	}
 }
 
 //==============================================================================
@@ -319,27 +285,6 @@ Error ModelNode::create(const CString& name, const CString& modelFname)
 		addChild(mpn);
 	}
 
-	// Load rigid body
-	const PhysicsCollisionShape* shape = m_model->getPhysicsCollisionShape();
-	if(shape != nullptr)
-	{
-		PhysicsBody::Initializer init;
-		init.m_mass = 1.0;
-		init.m_shape = shape;
-
-		m_body = 
-			getSceneGraph()._getPhysicsWorld().newBody<PhysicsBody>(init);
-
-		// Body component
-		comp = getSceneAllocator().newInstance<BodyComponent>(this, m_body);
-		addComponent(comp, true);
-
-		// Feedback component
-		comp = 
-			getSceneAllocator().newInstance<ModelBodyFeedbackComponent>(this);
-		addComponent(comp, true);
-	}
-
 	// Move component
 	comp = getSceneAllocator().newInstance<MoveComponent>(this);
 	addComponent(comp, true);

+ 3 - 7
src/scene/PlayerNode.cpp

@@ -134,12 +134,7 @@ PlayerNode::PlayerNode(SceneGraph* scene)
 
 //==============================================================================
 PlayerNode::~PlayerNode()
-{
-	if(m_player)
-	{
-		m_player->setMarkedForDeletion();	
-	}
-}
+{}
 
 //==============================================================================
 Error PlayerNode::create(const CString& name, const Vec4& position)
@@ -149,7 +144,8 @@ Error PlayerNode::create(const CString& name, const Vec4& position)
 	// Create physics object
 	PhysicsPlayerController::Initializer init;
 	init.m_position = position;
-	m_player = getSceneGraph()._getPhysicsWorld().newPlayerController(init);
+	m_player = getSceneGraph()._getPhysicsWorld().
+		newInstance<PhysicsPlayerController>(init);
 
 	SceneComponent* comp;
 

+ 36 - 0
src/scene/StaticCollisionNode.cpp

@@ -0,0 +1,36 @@
+// Copyright (C) 2009-2015, Panagiotis Christopoulos Charitos.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#include "anki/scene/StaticCollisionNode.h"
+#include "anki/scene/SceneGraph.h"
+#include "anki/resource/CollisionResource.h"
+#include "anki/physics/PhysicsBody.h"
+#include "anki/physics/PhysicsWorld.h"
+
+namespace anki {
+
+//==============================================================================
+StaticCollisionNode::~StaticCollisionNode()
+{}
+
+//==============================================================================
+Error StaticCollisionNode::create(
+	const CString& name, const CString& resourceFname)
+{
+	// Load resource
+	ANKI_CHECK(m_rsrc.load(resourceFname, &getResourceManager()));
+
+	// Create body
+	PhysicsBody::Initializer init;
+	init.m_shape = m_rsrc->getShape();
+	init.m_static = true;
+	// TODO: set trf
+
+	m_body = getSceneGraph()._getPhysicsWorld().newInstance<PhysicsBody>(init);
+
+	return ErrorCode::NONE;
+}
+
+} // end namespace

+ 123 - 0
src/script/Scene.cpp

@@ -1511,6 +1511,74 @@ static inline void wrapSpotLight(lua_State* l)
 	lua_settop(l, 0);
 }
 
+//==============================================================================
+// StaticCollisionNode                                                         =
+//==============================================================================
+
+//==============================================================================
+static const char* classnameStaticCollisionNode = "StaticCollisionNode";
+
+template<>
+I64 LuaBinder::getWrappedTypeSignature<StaticCollisionNode>()
+{
+	return -4376619865753613291;
+}
+
+template<>
+const char* LuaBinder::getWrappedTypeName<StaticCollisionNode>()
+{
+	return classnameStaticCollisionNode;
+}
+
+//==============================================================================
+/// Pre-wrap method StaticCollisionNode::getSceneNodeBase.
+static inline int pwrapStaticCollisionNodegetSceneNodeBase(lua_State* l)
+{
+	UserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	
+	LuaBinder::checkArgsCount(l, 1);
+	
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameStaticCollisionNode, -4376619865753613291, ud)) return -1;
+	StaticCollisionNode* self = static_cast<StaticCollisionNode*>(ud->m_data);
+	ANKI_ASSERT(self != nullptr);
+	
+	// Call the method
+	SceneNode& ret = *self;
+	
+	// Push return value
+	voidp = lua_newuserdata(l, sizeof(UserData));
+	ud = static_cast<UserData*>(voidp);
+	luaL_setmetatable(l, "SceneNode");
+	ud->m_data = static_cast<void*>(&ret);
+	ud->m_gc = false;
+	ud->m_sig = -2220074417980276571;
+	
+	return 1;
+}
+
+//==============================================================================
+/// Wrap method StaticCollisionNode::getSceneNodeBase.
+static int wrapStaticCollisionNodegetSceneNodeBase(lua_State* l)
+{
+	int res = pwrapStaticCollisionNodegetSceneNodeBase(l);
+	if(res >= 0) return res;
+	lua_error(l);
+	return 0;
+}
+
+//==============================================================================
+/// Wrap class StaticCollisionNode.
+static inline void wrapStaticCollisionNode(lua_State* l)
+{
+	LuaBinder::createClass(l, classnameStaticCollisionNode);
+	LuaBinder::pushLuaCFuncMethod(l, "getSceneNodeBase", wrapStaticCollisionNodegetSceneNodeBase);
+	lua_settop(l, 0);
+}
+
 //==============================================================================
 // SceneGraph                                                                  =
 //==============================================================================
@@ -1733,6 +1801,59 @@ static int wrapSceneGraphnewSpotLight(lua_State* l)
 	return 0;
 }
 
+//==============================================================================
+/// Pre-wrap method SceneGraph::newStaticCollisionNode.
+static inline int pwrapSceneGraphnewStaticCollisionNode(lua_State* l)
+{
+	UserData* ud;
+	(void)ud;
+	void* voidp;
+	(void)voidp;
+	
+	LuaBinder::checkArgsCount(l, 3);
+	
+	// Get "this" as "self"
+	if(LuaBinder::checkUserData(l, 1, classnameSceneGraph, -7754439619132389154, ud)) return -1;
+	SceneGraph* self = static_cast<SceneGraph*>(ud->m_data);
+	ANKI_ASSERT(self != nullptr);
+	
+	// Pop arguments
+	const char* arg0;
+	if(LuaBinder::checkString(l, 2, arg0)) return -1;
+	
+	const char* arg1;
+	if(LuaBinder::checkString(l, 3, arg1)) return -1;
+	
+	// Call the method
+	StaticCollisionNode* ret = newSceneNode<StaticCollisionNode>(self, arg0, arg1);
+	
+	// Push return value
+	if(ANKI_UNLIKELY(ret == nullptr))
+	{
+		lua_pushstring(l, "Glue code returned nullptr");
+		return -1;
+	}
+	
+	voidp = lua_newuserdata(l, sizeof(UserData));
+	ud = static_cast<UserData*>(voidp);
+	luaL_setmetatable(l, "StaticCollisionNode");
+	ud->m_data = static_cast<void*>(ret);
+	ud->m_gc = false;
+	ud->m_sig = -4376619865753613291;
+	
+	return 1;
+}
+
+//==============================================================================
+/// Wrap method SceneGraph::newStaticCollisionNode.
+static int wrapSceneGraphnewStaticCollisionNode(lua_State* l)
+{
+	int res = pwrapSceneGraphnewStaticCollisionNode(l);
+	if(res >= 0) return res;
+	lua_error(l);
+	return 0;
+}
+
 //==============================================================================
 /// Wrap class SceneGraph.
 static inline void wrapSceneGraph(lua_State* l)
@@ -1742,6 +1863,7 @@ static inline void wrapSceneGraph(lua_State* l)
 	LuaBinder::pushLuaCFuncMethod(l, "newInstanceNode", wrapSceneGraphnewInstanceNode);
 	LuaBinder::pushLuaCFuncMethod(l, "newPointLight", wrapSceneGraphnewPointLight);
 	LuaBinder::pushLuaCFuncMethod(l, "newSpotLight", wrapSceneGraphnewSpotLight);
+	LuaBinder::pushLuaCFuncMethod(l, "newStaticCollisionNode", wrapSceneGraphnewStaticCollisionNode);
 	lua_settop(l, 0);
 }
 
@@ -1798,6 +1920,7 @@ void wrapModuleScene(lua_State* l)
 	wrapInstanceNode(l);
 	wrapPointLight(l);
 	wrapSpotLight(l);
+	wrapStaticCollisionNode(l);
 	wrapSceneGraph(l);
 	LuaBinder::pushLuaCFunc(l, "getSceneGraph", wrapgetSceneGraph);
 }

+ 16 - 0
src/script/Scene.xml

@@ -203,6 +203,14 @@ static SceneGraph* getSceneGraph(lua_State* l)
 				</method>
 			</methods>
 		</class>
+		<class name="StaticCollisionNode">
+			<methods>
+				<method name="getSceneNodeBase">
+					<overrideCall>SceneNode&amp; ret = *self;</overrideCall>
+					<return>SceneNode&amp;</return>
+				</method>
+			</methods>
+		</class>
 		<class name="SceneGraph">
 			<methods>
 				<method name="newModelNode">
@@ -234,6 +242,14 @@ static SceneGraph* getSceneGraph(lua_State* l)
 					</args>
 					<return>SpotLight*</return>
 				</method>
+				<method name="newStaticCollisionNode">
+					<overrideCall><![CDATA[StaticCollisionNode* ret = newSceneNode<StaticCollisionNode>(self, arg0, arg1);]]></overrideCall>
+					<args>
+						<arg>const CString&amp;</arg>
+						<arg>const CString&amp;</arg>
+					</args>
+					<return>StaticCollisionNode*</return>
+				</method>
 			</methods>
 		</class>
 	</classes>

+ 1 - 1
testapp/Main.cpp

@@ -260,7 +260,7 @@ Error init()
 	{
 		ScriptResourcePointer script;
 
-		err = script.load("maps/techdemo/scene.lua", &resources);
+		err = script.load("maps/adis/scene.lua", &resources);
 		if(err) return err;
 
 		err = app->getScriptManager().evalString(script->getSource());

+ 22 - 22
tools/scene/Exporter.cpp

@@ -659,15 +659,6 @@ void Exporter::exportModel(const Model& model) const
 	file << "\t\t</modelPatch>\n";
 	file << "\t</modelPatches>\n";
 
-	// Write collision mesh
-	if(model.m_collisionMeshIndex != INVALID_INDEX)
-	{
-		file << "\t<collisionShape><type>staticMesh</type><value>"
-			<< m_rpath
-			<< getMeshName(getMeshAt(model.m_collisionMeshIndex))
-			<< ".ankimesh</value></collisionShape>\n";
-	}
-
 	file << "</model>\n";
 }
 
@@ -1016,6 +1007,22 @@ void Exporter::visitNode(const aiNode* ainode)
 	}
 }
 
+//==============================================================================
+void Exporter::exportCollisionMesh(uint32_t meshIdx)
+{
+	std::string name = getMeshName(getMeshAt(meshIdx));
+
+	std::fstream file;
+	file.open(m_outputDirectory + name + ".ankicl", std::ios::out);
+
+	file << XML_HEADER << '\n';
+
+	// Write collision mesh
+	file << "<collisionShape>\n\t<type>staticMesh</type>\n\t<value>"
+		<< m_rpath << name
+		<< ".ankimesh</value>\n</collisionShape>\n";
+}
+
 //==============================================================================
 void Exporter::exportAll()
 {
@@ -1044,6 +1051,12 @@ void Exporter::exportAll()
 	for(auto idx : m_collisionMeshIds)
 	{
 		exportMesh(*m_scene->mMeshes[idx], nullptr);
+		exportCollisionMesh(idx);
+
+		std::string name = getMeshName(getMeshAt(idx));
+		std::string fname = m_rpath + name + ".ankicl";
+		file << "\nnode = scene:newStaticCollisionNode(\""
+			<< name << "\", \"" << fname << "\")\n";
 	}
 
 	//
@@ -1054,19 +1067,6 @@ void Exporter::exportAll()
 		Node& node = m_nodes[i];
 		Model& model = m_models[node.m_modelIndex];
 
-		// Check if it has a collision mesh
-		std::string collisionMeshName = std::string("ak_collision_")
-			+ m_scene->mMeshes[model.m_meshIndex]->mName.C_Str();
-		for(unsigned i = 0; i < m_collisionMeshIds.size(); ++i)
-		{
-			if(m_scene->mMeshes[m_collisionMeshIds[i]]->mName.C_Str()
-				== collisionMeshName)
-			{
-				model.m_collisionMeshIndex = m_collisionMeshIds[i];
-				break;
-			}
-		}
-
 		// TODO If not instanced bake transform
 		exportMesh(*m_scene->mMeshes[model.m_meshIndex], nullptr);
 

+ 3 - 0
tools/scene/Exporter.h

@@ -123,6 +123,9 @@ private:
 	void exportAnimation(
 		const aiAnimation& anim,
 		unsigned index);
+
+	/// Export a static collision mesh.
+	void exportCollisionMesh(uint32_t meshIdx);
 };
 
 #endif