Jelajahi Sumber

Fixed an issue where child SO transforms would be incorrectly restored after deserialization
Limited box/sphere/capsule collider extents to a valid range
Check if child collider is initialized when adding it to rigidbody
Fix for a crash due to mutex not unlocking in Physics
Project library new stores non-subresource path for the primary resource
Modified how geometry is applied to colliders, so that geometry type change is supported
Modified PhysX error reporting to be more relaxed with errors
Fixed box/sphere gizmo offsets

BearishSun 9 tahun lalu
induk
melakukan
e9c446939c

+ 1 - 1
README.md

@@ -21,6 +21,6 @@ Aside from being a fully featured game engine and toolkit, Banshee also aims to
 [Editor video overview] (https://youtu.be/WJsYOyCXGEU)
 [Editor video overview] (https://youtu.be/WJsYOyCXGEU)
 
 
 # About
 # About
-Banshee was created and is developed by Marko Pintera. It started as a hobby project where I wanted to create a high quality codebase I can use for my own game projects, as well as learn something new along the way. Eventually it evolved into something bigger and I decided to share it with the world. It's still a work in progress but it's slowly closing in towards the first stable release.
+Banshee was created and is developed by Marko Pintera. It started as a hobby project but is now on track to becoming a quality professional solution. It's still a work in progress but new features are added steadily, and it's slowly closing in towards the first stable release.
 
 
 I'm happy to connect with other developers, so feel free to contact me at [e-mail] (http://scr.im/39d1) or add me on [LinkedIn] (https://goo.gl/t6pPPs). 
 I'm happy to connect with other developers, so feel free to contact me at [e-mail] (http://scr.im/39d1) or add me on [LinkedIn] (https://goo.gl/t6pPPs). 

+ 181 - 181
Source/BansheeCore/Include/BsSceneObjectRTTI.h

@@ -1,182 +1,182 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsCorePrerequisites.h"
-#include "BsRTTIType.h"
-#include "BsSceneObject.h"
-#include "BsGameObjectHandle.h"
-#include "BsGameObjectManager.h"
-#include "BsComponent.h"
-#include "BsGameObjectRTTI.h"
-#include "BsPrefabDiff.h"
-
-namespace BansheeEngine
-{
-	/** @cond RTTI */
-	/** @addtogroup RTTI-Impl-Core
-	 *  @{
-	 */
-
-	/** Provides temporary storage for data used during SceneObject deserialization. */
-	struct SODeserializationData
-	{
-		Vector<SPtr<SceneObject>> children;
-		Vector<SPtr<Component>> components;
-	};
-
-	class BS_CORE_EXPORT SceneObjectRTTI : public RTTIType<SceneObject, GameObject, SceneObjectRTTI>
-	{
-	private:
-		Vector3& getPosition(SceneObject* obj) { return obj->mPosition; }
-		void setPosition(SceneObject* obj, Vector3& value) { obj->mPosition = value; }
-
-		Quaternion& getRotation(SceneObject* obj) { return obj->mRotation; }
-		void setRotation(SceneObject* obj, Quaternion& value) { obj->mRotation = value; }
-
-		Vector3& getScale(SceneObject* obj) { return obj->mScale; }
-		void setScale(SceneObject* obj, Vector3& value) { obj->mScale = value; }
-
-		bool& getActive(SceneObject* obj) { return obj->mActiveSelf; }
-		void setActive(SceneObject* obj, bool& value) { obj->mActiveSelf = value; }
-
-		// NOTE - These can only be set sequentially, specific array index is ignored
-		std::shared_ptr<SceneObject> getChild(SceneObject* obj, UINT32 idx) { return obj->mChildren[idx].getInternalPtr(); }
-		void setChild(SceneObject* obj, UINT32 idx, SPtr<SceneObject> param)
-		{
-			SceneObject* so = static_cast<SceneObject*>(obj);
-			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
-			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
-
-			soDeserializationData.children.push_back(param);
-		} 
-
-		UINT32 getNumChildren(SceneObject* obj) { return (UINT32)obj->mChildren.size(); }
-		void setNumChildren(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
-
-		// NOTE - These can only be set sequentially, specific array index is ignored
-		std::shared_ptr<Component> getComponent(SceneObject* obj, UINT32 idx) { return obj->mComponents[idx].getInternalPtr(); }
-		void setComponent(SceneObject* obj, UINT32 idx, SPtr<Component> param)
-		{
-			SceneObject* so = static_cast<SceneObject*>(obj);
-			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
-			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
-
-			soDeserializationData.components.push_back(param);
-		}
-		UINT32 getNumComponents(SceneObject* obj) { return (UINT32)obj->mComponents.size(); }
-		void setNumComponents(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
-
-		String& getPrefabLink(SceneObject* obj) { return obj->mPrefabLinkUUID; }
-		void setPrefabLink(SceneObject* obj, String& value) { obj->mPrefabLinkUUID = value; }
-
-		PrefabDiffPtr getPrefabDiff(SceneObject* obj) { return obj->mPrefabDiff; }
-		void setPrefabDiff(SceneObject* obj, PrefabDiffPtr value) { obj->mPrefabDiff = value; }
-
-		UINT32& getFlags(SceneObject* obj) { return obj->mFlags; }
-		void setFlags(SceneObject* obj, UINT32& value) { obj->mFlags = value; }
-
-		UINT32& getPrefabHash(SceneObject* obj) { return obj->mPrefabHash; }
-		void setPrefabHash(SceneObject* obj, UINT32& value) { obj->mPrefabHash = value; }
-	public:
-		SceneObjectRTTI()
-		{
-			addReflectablePtrArrayField("mChildren", 0, &SceneObjectRTTI::getChild, 
-				&SceneObjectRTTI::getNumChildren, &SceneObjectRTTI::setChild, &SceneObjectRTTI::setNumChildren);
-			addReflectablePtrArrayField("mComponents", 1, &SceneObjectRTTI::getComponent, 
-				&SceneObjectRTTI::getNumComponents, &SceneObjectRTTI::setComponent, &SceneObjectRTTI::setNumComponents);
-			addPlainField("mPrefabLink", 2, &SceneObjectRTTI::getPrefabLink, &SceneObjectRTTI::setPrefabLink);
-			addPlainField("mFlags", 3, &SceneObjectRTTI::getFlags, &SceneObjectRTTI::setFlags);
-			addReflectablePtrField("mPrefabDiff", 4, &SceneObjectRTTI::getPrefabDiff, &SceneObjectRTTI::setPrefabDiff);
-			addPlainField("mPrefabHash", 5, &SceneObjectRTTI::getPrefabHash, &SceneObjectRTTI::setPrefabHash);
-			addPlainField("mPosition", 6, &SceneObjectRTTI::getPosition, &SceneObjectRTTI::setPosition);
-			addPlainField("mRotation", 7, &SceneObjectRTTI::getRotation, &SceneObjectRTTI::setRotation);
-			addPlainField("mScale", 8, &SceneObjectRTTI::getScale, &SceneObjectRTTI::setScale);
-			addPlainField("mActiveSelf", 9, &SceneObjectRTTI::getActive, &SceneObjectRTTI::setActive);
-		}
-
-		void onDeserializationStarted(IReflectable* obj) override
-		{
-			// If this is the root scene object we're deserializing, activate game object deserialization so the system
-			// can resolve deserialized handles to the newly created objects
-			SceneObject* so = static_cast<SceneObject*>(obj);
-			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
-
-			if (!GameObjectManager::instance().isGameObjectDeserializationActive())
-			{
-				GameObjectManager::instance().startDeserialization();
-				
-				// Mark it as the object that started the GO deserialization so it knows to end it
-				deserializationData.isDeserializationParent = true;
-			}
-			else
-				deserializationData.isDeserializationParent = false;
-		}
-
-		void onDeserializationEnded(IReflectable* obj) override
-		{
-			SceneObject* so = static_cast<SceneObject*>(obj);
-			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
-
-			// Register the newly created SO with the GameObjectManager and provide it with the original ID so that
-			// deserialized handles pointing to this object can be resolved.
-			SceneObjectPtr soPtr = std::static_pointer_cast<SceneObject>(goDeserializationData.ptr);
-			SceneObject::createInternal(soPtr, goDeserializationData.originalId);
-
-			// We stored all components and children in a temporary structure because they rely on the SceneObject being
-			// initialized with the GameObjectManager. Now that it is, we add them.
-			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
-
-			for (auto& component : soDeserializationData.components)
-				so->addComponentInternal(component);
-
-			for (auto& child : soDeserializationData.children)
-				child->_setParent(so->mThisHandle);
-
-			// If this is the deserialization parent, end deserialization (which resolves all game object handles, if we 
-			// provided valid IDs), and instantiate (i.e. activate) the deserialized hierarchy.
-			if (goDeserializationData.isDeserializationParent)
-			{
-				GameObjectManager::instance().endDeserialization();
-
-				if ((so->mFlags & SOF_DontInstantiate) == 0)
-					so->_instantiate();
-			}
-
-			so->mRTTIData = nullptr;
-		}
-
-		const String& getRTTIName() override
-		{
-			static String name = "SceneObject";
-			return name;
-		}
-
-		UINT32 getRTTIId() override
-		{
-			return TID_SceneObject;
-		}
-
-		std::shared_ptr<IReflectable> newRTTIObject() override
-		{
-			SPtr<SceneObject> sceneObjectPtr = 
-				SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject("", SOF_DontInstantiate),
-				&bs_delete<SceneObject>, StdAlloc<SceneObject>());
-
-			// Every GameObject must store GODeserializationData in its RTTI data field during deserialization
-			sceneObjectPtr->mRTTIData = GODeserializationData();
-			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(sceneObjectPtr->mRTTIData);
-
-			// Store shared pointer since the system only provides us with raw ones
-			deserializationData.ptr = sceneObjectPtr;
-
-			// We delay adding children/components and instead store them here
-			deserializationData.moreData = SODeserializationData();
-
-			return sceneObjectPtr;
-		}
-	};
-
-	/** @} */
-	/** @endcond */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsCorePrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsSceneObject.h"
+#include "BsGameObjectHandle.h"
+#include "BsGameObjectManager.h"
+#include "BsComponent.h"
+#include "BsGameObjectRTTI.h"
+#include "BsPrefabDiff.h"
+
+namespace BansheeEngine
+{
+	/** @cond RTTI */
+	/** @addtogroup RTTI-Impl-Core
+	 *  @{
+	 */
+
+	/** Provides temporary storage for data used during SceneObject deserialization. */
+	struct SODeserializationData
+	{
+		Vector<SPtr<SceneObject>> children;
+		Vector<SPtr<Component>> components;
+	};
+
+	class BS_CORE_EXPORT SceneObjectRTTI : public RTTIType<SceneObject, GameObject, SceneObjectRTTI>
+	{
+	private:
+		Vector3& getPosition(SceneObject* obj) { return obj->mPosition; }
+		void setPosition(SceneObject* obj, Vector3& value) { obj->mPosition = value; }
+
+		Quaternion& getRotation(SceneObject* obj) { return obj->mRotation; }
+		void setRotation(SceneObject* obj, Quaternion& value) { obj->mRotation = value; }
+
+		Vector3& getScale(SceneObject* obj) { return obj->mScale; }
+		void setScale(SceneObject* obj, Vector3& value) { obj->mScale = value; }
+
+		bool& getActive(SceneObject* obj) { return obj->mActiveSelf; }
+		void setActive(SceneObject* obj, bool& value) { obj->mActiveSelf = value; }
+
+		// NOTE - These can only be set sequentially, specific array index is ignored
+		std::shared_ptr<SceneObject> getChild(SceneObject* obj, UINT32 idx) { return obj->mChildren[idx].getInternalPtr(); }
+		void setChild(SceneObject* obj, UINT32 idx, SPtr<SceneObject> param)
+		{
+			SceneObject* so = static_cast<SceneObject*>(obj);
+			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
+			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
+
+			soDeserializationData.children.push_back(param);
+		} 
+
+		UINT32 getNumChildren(SceneObject* obj) { return (UINT32)obj->mChildren.size(); }
+		void setNumChildren(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
+
+		// NOTE - These can only be set sequentially, specific array index is ignored
+		std::shared_ptr<Component> getComponent(SceneObject* obj, UINT32 idx) { return obj->mComponents[idx].getInternalPtr(); }
+		void setComponent(SceneObject* obj, UINT32 idx, SPtr<Component> param)
+		{
+			SceneObject* so = static_cast<SceneObject*>(obj);
+			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
+			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
+
+			soDeserializationData.components.push_back(param);
+		}
+		UINT32 getNumComponents(SceneObject* obj) { return (UINT32)obj->mComponents.size(); }
+		void setNumComponents(SceneObject* obj, UINT32 size) { /* DO NOTHING */ }
+
+		String& getPrefabLink(SceneObject* obj) { return obj->mPrefabLinkUUID; }
+		void setPrefabLink(SceneObject* obj, String& value) { obj->mPrefabLinkUUID = value; }
+
+		PrefabDiffPtr getPrefabDiff(SceneObject* obj) { return obj->mPrefabDiff; }
+		void setPrefabDiff(SceneObject* obj, PrefabDiffPtr value) { obj->mPrefabDiff = value; }
+
+		UINT32& getFlags(SceneObject* obj) { return obj->mFlags; }
+		void setFlags(SceneObject* obj, UINT32& value) { obj->mFlags = value; }
+
+		UINT32& getPrefabHash(SceneObject* obj) { return obj->mPrefabHash; }
+		void setPrefabHash(SceneObject* obj, UINT32& value) { obj->mPrefabHash = value; }
+	public:
+		SceneObjectRTTI()
+		{
+			addReflectablePtrArrayField("mChildren", 0, &SceneObjectRTTI::getChild, 
+				&SceneObjectRTTI::getNumChildren, &SceneObjectRTTI::setChild, &SceneObjectRTTI::setNumChildren);
+			addReflectablePtrArrayField("mComponents", 1, &SceneObjectRTTI::getComponent, 
+				&SceneObjectRTTI::getNumComponents, &SceneObjectRTTI::setComponent, &SceneObjectRTTI::setNumComponents);
+			addPlainField("mPrefabLink", 2, &SceneObjectRTTI::getPrefabLink, &SceneObjectRTTI::setPrefabLink);
+			addPlainField("mFlags", 3, &SceneObjectRTTI::getFlags, &SceneObjectRTTI::setFlags);
+			addReflectablePtrField("mPrefabDiff", 4, &SceneObjectRTTI::getPrefabDiff, &SceneObjectRTTI::setPrefabDiff);
+			addPlainField("mPrefabHash", 5, &SceneObjectRTTI::getPrefabHash, &SceneObjectRTTI::setPrefabHash);
+			addPlainField("mPosition", 6, &SceneObjectRTTI::getPosition, &SceneObjectRTTI::setPosition);
+			addPlainField("mRotation", 7, &SceneObjectRTTI::getRotation, &SceneObjectRTTI::setRotation);
+			addPlainField("mScale", 8, &SceneObjectRTTI::getScale, &SceneObjectRTTI::setScale);
+			addPlainField("mActiveSelf", 9, &SceneObjectRTTI::getActive, &SceneObjectRTTI::setActive);
+		}
+
+		void onDeserializationStarted(IReflectable* obj) override
+		{
+			// If this is the root scene object we're deserializing, activate game object deserialization so the system
+			// can resolve deserialized handles to the newly created objects
+			SceneObject* so = static_cast<SceneObject*>(obj);
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
+
+			if (!GameObjectManager::instance().isGameObjectDeserializationActive())
+			{
+				GameObjectManager::instance().startDeserialization();
+				
+				// Mark it as the object that started the GO deserialization so it knows to end it
+				deserializationData.isDeserializationParent = true;
+			}
+			else
+				deserializationData.isDeserializationParent = false;
+		}
+
+		void onDeserializationEnded(IReflectable* obj) override
+		{
+			SceneObject* so = static_cast<SceneObject*>(obj);
+			GODeserializationData& goDeserializationData = any_cast_ref<GODeserializationData>(so->mRTTIData);
+
+			// Register the newly created SO with the GameObjectManager and provide it with the original ID so that
+			// deserialized handles pointing to this object can be resolved.
+			SceneObjectPtr soPtr = std::static_pointer_cast<SceneObject>(goDeserializationData.ptr);
+			SceneObject::createInternal(soPtr, goDeserializationData.originalId);
+
+			// We stored all components and children in a temporary structure because they rely on the SceneObject being
+			// initialized with the GameObjectManager. Now that it is, we add them.
+			SODeserializationData& soDeserializationData = any_cast_ref<SODeserializationData>(goDeserializationData.moreData);
+
+			for (auto& component : soDeserializationData.components)
+				so->addComponentInternal(component);
+
+			for (auto& child : soDeserializationData.children)
+				child->_setParent(so->mThisHandle, false);
+
+			// If this is the deserialization parent, end deserialization (which resolves all game object handles, if we 
+			// provided valid IDs), and instantiate (i.e. activate) the deserialized hierarchy.
+			if (goDeserializationData.isDeserializationParent)
+			{
+				GameObjectManager::instance().endDeserialization();
+
+				if ((so->mFlags & SOF_DontInstantiate) == 0)
+					so->_instantiate();
+			}
+
+			so->mRTTIData = nullptr;
+		}
+
+		const String& getRTTIName() override
+		{
+			static String name = "SceneObject";
+			return name;
+		}
+
+		UINT32 getRTTIId() override
+		{
+			return TID_SceneObject;
+		}
+
+		std::shared_ptr<IReflectable> newRTTIObject() override
+		{
+			SPtr<SceneObject> sceneObjectPtr = 
+				SPtr<SceneObject>(new (bs_alloc<SceneObject>()) SceneObject("", SOF_DontInstantiate),
+				&bs_delete<SceneObject>, StdAlloc<SceneObject>());
+
+			// Every GameObject must store GODeserializationData in its RTTI data field during deserialization
+			sceneObjectPtr->mRTTIData = GODeserializationData();
+			GODeserializationData& deserializationData = any_cast_ref<GODeserializationData>(sceneObjectPtr->mRTTIData);
+
+			// Store shared pointer since the system only provides us with raw ones
+			deserializationData.ptr = sceneObjectPtr;
+
+			// We delay adding children/components and instead store them here
+			deserializationData.moreData = SODeserializationData();
+
+			return sceneObjectPtr;
+		}
+	};
+
+	/** @} */
+	/** @endcond */
 }
 }

+ 61 - 59
Source/BansheeCore/Source/BsCBoxCollider.cpp

@@ -1,60 +1,62 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsCBoxCollider.h"
-#include "BsSceneObject.h"
-#include "BsCRigidbody.h"
-#include "BsCBoxColliderRTTI.h"
-
-namespace BansheeEngine
-{
-	CBoxCollider::CBoxCollider(const HSceneObject& parent, const Vector3& extents)
-		: CCollider(parent), mExtents(extents)
-	{
-		setName("BoxCollider");
-	}
-
-	void CBoxCollider::setExtents(const Vector3& extents)
-	{
-		if (mExtents == extents)
-			return;
-
-		mExtents = extents; 
-
-		if (mInternal != nullptr)
-		{
-			_getInternal()->setExtents(extents);
-
-			if (mParent != nullptr)
-				mParent->_updateMassDistribution();
-		}
-	}
-
-	void CBoxCollider::setCenter(const Vector3& center)
-	{
-		if (mLocalPosition == center)
-			return;
-
-		mLocalPosition = center;
-
-		if (mInternal != nullptr)
-			updateTransform();
-	}
-
-	SPtr<Collider> CBoxCollider::createInternal()
-	{
-		SPtr<Collider> collider = BoxCollider::create(mExtents, SO()->getWorldPosition(), SO()->getWorldRotation());
-		collider->_setOwner(PhysicsOwnerType::Component, this);
-
-		return collider;
-	}
-
-	RTTITypeBase* CBoxCollider::getRTTIStatic()
-	{
-		return CBoxColliderRTTI::instance();
-	}
-
-	RTTITypeBase* CBoxCollider::getRTTI() const
-	{
-		return CBoxCollider::getRTTIStatic();
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsCBoxCollider.h"
+#include "BsSceneObject.h"
+#include "BsCRigidbody.h"
+#include "BsCBoxColliderRTTI.h"
+
+namespace BansheeEngine
+{
+	CBoxCollider::CBoxCollider(const HSceneObject& parent, const Vector3& extents)
+		: CCollider(parent), mExtents(extents)
+	{
+		setName("BoxCollider");
+	}
+
+	void CBoxCollider::setExtents(const Vector3& extents)
+	{
+		Vector3 clampedExtents = Vector3::max(extents, Vector3(0.01f, 0.01f, 0.01f));
+
+		if (mExtents == clampedExtents)
+			return;
+
+		mExtents = clampedExtents;
+
+		if (mInternal != nullptr)
+		{
+			_getInternal()->setExtents(clampedExtents);
+
+			if (mParent != nullptr)
+				mParent->_updateMassDistribution();
+		}
+	}
+
+	void CBoxCollider::setCenter(const Vector3& center)
+	{
+		if (mLocalPosition == center)
+			return;
+
+		mLocalPosition = center;
+
+		if (mInternal != nullptr)
+			updateTransform();
+	}
+
+	SPtr<Collider> CBoxCollider::createInternal()
+	{
+		SPtr<Collider> collider = BoxCollider::create(mExtents, SO()->getWorldPosition(), SO()->getWorldRotation());
+		collider->_setOwner(PhysicsOwnerType::Component, this);
+
+		return collider;
+	}
+
+	RTTITypeBase* CBoxCollider::getRTTIStatic()
+	{
+		return CBoxColliderRTTI::instance();
+	}
+
+	RTTITypeBase* CBoxCollider::getRTTI() const
+	{
+		return CBoxCollider::getRTTIStatic();
+	}
 }
 }

+ 92 - 90
Source/BansheeCore/Source/BsCCapsuleCollider.cpp

@@ -1,91 +1,93 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsCCapsuleCollider.h"
-#include "BsSceneObject.h"
-#include "BsCRigidbody.h"
-#include "BsCCapsuleColliderRTTI.h"
-
-namespace BansheeEngine
-{
-	CCapsuleCollider::CCapsuleCollider(const HSceneObject& parent, float radius, float halfHeight)
-		: CCollider(parent), mRadius(radius), mHalfHeight(halfHeight)
-	{
-		setName("CapsuleCollider");
-	}
-
-	void CCapsuleCollider::setNormal(const Vector3& normal)
-	{
-		if (mNormal == normal)
-			return;
-
-		mNormal = normal;
-		mNormal.normalize();
-
-		mLocalRotation = Quaternion::getRotationFromTo(Vector3::UNIT_X, normal);
-
-		if (mInternal != nullptr)
-			updateTransform();
-	}
-
-	void CCapsuleCollider::setCenter(const Vector3& center)
-	{
-		if (mLocalPosition == center)
-			return;
-
-		mLocalPosition = center;
-
-		if (mInternal != nullptr)
-			updateTransform();
-	}
-
-	void CCapsuleCollider::setHalfHeight(float halfHeight)
-	{
-		if (mHalfHeight == halfHeight)
-			return;
-
-		mHalfHeight = halfHeight;
-
-		if (mInternal != nullptr)
-		{
-			_getInternal()->setHalfHeight(halfHeight);
-
-			if (mParent != nullptr)
-				mParent->_updateMassDistribution();
-		}
-	}
-
-	void CCapsuleCollider::setRadius(float radius)
-	{
-		if (mRadius == radius)
-			return;
-
-		mRadius = radius;
-
-		if (mInternal != nullptr)
-		{
-			_getInternal()->setRadius(radius);
-
-			if (mParent != nullptr)
-				mParent->_updateMassDistribution();
-		}
-	}
-
-	SPtr<Collider> CCapsuleCollider::createInternal()
-	{
-		SPtr<Collider> collider = CapsuleCollider::create(mRadius, mHalfHeight, SO()->getWorldPosition(), 
-			SO()->getWorldRotation());
-
-		collider->_setOwner(PhysicsOwnerType::Component, this);
-		return collider;
-	}
-
-	RTTITypeBase* CCapsuleCollider::getRTTIStatic()
-	{
-		return CCapsuleColliderRTTI::instance();
-	}
-
-	RTTITypeBase* CCapsuleCollider::getRTTI() const
-	{
-		return CCapsuleCollider::getRTTIStatic();
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsCCapsuleCollider.h"
+#include "BsSceneObject.h"
+#include "BsCRigidbody.h"
+#include "BsCCapsuleColliderRTTI.h"
+
+namespace BansheeEngine
+{
+	CCapsuleCollider::CCapsuleCollider(const HSceneObject& parent, float radius, float halfHeight)
+		: CCollider(parent), mRadius(radius), mHalfHeight(halfHeight)
+	{
+		setName("CapsuleCollider");
+	}
+
+	void CCapsuleCollider::setNormal(const Vector3& normal)
+	{
+		if (mNormal == normal)
+			return;
+
+		mNormal = normal;
+		mNormal.normalize();
+
+		mLocalRotation = Quaternion::getRotationFromTo(Vector3::UNIT_X, normal);
+
+		if (mInternal != nullptr)
+			updateTransform();
+	}
+
+	void CCapsuleCollider::setCenter(const Vector3& center)
+	{
+		if (mLocalPosition == center)
+			return;
+
+		mLocalPosition = center;
+
+		if (mInternal != nullptr)
+			updateTransform();
+	}
+
+	void CCapsuleCollider::setHalfHeight(float halfHeight)
+	{
+		float clampedHalfHeight = std::max(halfHeight, 0.01f);
+		if (mHalfHeight == clampedHalfHeight)
+			return;
+
+		mHalfHeight = clampedHalfHeight;
+
+		if (mInternal != nullptr)
+		{
+			_getInternal()->setHalfHeight(clampedHalfHeight);
+
+			if (mParent != nullptr)
+				mParent->_updateMassDistribution();
+		}
+	}
+
+	void CCapsuleCollider::setRadius(float radius)
+	{
+		float clampedRadius = std::max(radius, 0.01f);
+		if (mRadius == clampedRadius)
+			return;
+
+		mRadius = clampedRadius;
+
+		if (mInternal != nullptr)
+		{
+			_getInternal()->setRadius(clampedRadius);
+
+			if (mParent != nullptr)
+				mParent->_updateMassDistribution();
+		}
+	}
+
+	SPtr<Collider> CCapsuleCollider::createInternal()
+	{
+		SPtr<Collider> collider = CapsuleCollider::create(mRadius, mHalfHeight, SO()->getWorldPosition(), 
+			SO()->getWorldRotation());
+
+		collider->_setOwner(PhysicsOwnerType::Component, this);
+		return collider;
+	}
+
+	RTTITypeBase* CCapsuleCollider::getRTTIStatic()
+	{
+		return CCapsuleColliderRTTI::instance();
+	}
+
+	RTTITypeBase* CCapsuleCollider::getRTTI() const
+	{
+		return CCapsuleCollider::getRTTIStatic();
+	}
 }
 }

+ 7 - 3
Source/BansheeCore/Source/BsCRigidbody.cpp

@@ -281,10 +281,14 @@ namespace BansheeEngine
 						continue;
 						continue;
 
 
 					entry->setRigidbody(mThisHandle, true);
 					entry->setRigidbody(mThisHandle, true);
-					entry->_getInternal()->setRigidbody(mInternal.get());
-
 					mChildren.push_back(entry);
 					mChildren.push_back(entry);
-					mInternal->addCollider(entry->_getInternal()->_getInternal());
+
+					Collider* collider = entry->_getInternal();
+					if (collider != nullptr)
+					{
+						collider->setRigidbody(mInternal.get());
+						mInternal->addCollider(collider->_getInternal());
+					}
 				}
 				}
 			}
 			}
 
 

+ 60 - 59
Source/BansheeCore/Source/BsCSphereCollider.cpp

@@ -1,60 +1,61 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsCSphereCollider.h"
-#include "BsSceneObject.h"
-#include "BsCRigidbody.h"
-#include "BsCSphereColliderRTTI.h"
-
-namespace BansheeEngine
-{
-	CSphereCollider::CSphereCollider(const HSceneObject& parent, float radius)
-		: CCollider(parent), mRadius(radius)
-	{
-		setName("SphereCollider");
-	}
-
-	void CSphereCollider::setRadius(float radius)
-	{
-		if (mRadius == radius)
-			return;
-
-		mRadius = radius; 
-
-		if (mInternal != nullptr)
-		{
-			_getInternal()->setRadius(radius);
-
-			if (mParent != nullptr)
-				mParent->_updateMassDistribution();
-		}
-	}
-
-	void CSphereCollider::setCenter(const Vector3& center)
-	{
-		if (mLocalPosition == center)
-			return;
-
-		mLocalPosition = center; 
-		
-		if (mInternal != nullptr)
-			updateTransform();
-	}
-
-	SPtr<Collider> CSphereCollider::createInternal()
-	{
-		SPtr<Collider> collider = SphereCollider::create(mRadius, SO()->getWorldPosition(), SO()->getWorldRotation());
-
-		collider->_setOwner(PhysicsOwnerType::Component, this);
-		return collider;
-	}
-
-	RTTITypeBase* CSphereCollider::getRTTIStatic()
-	{
-		return CSphereColliderRTTI::instance();
-	}
-
-	RTTITypeBase* CSphereCollider::getRTTI() const
-	{
-		return CSphereCollider::getRTTIStatic();
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsCSphereCollider.h"
+#include "BsSceneObject.h"
+#include "BsCRigidbody.h"
+#include "BsCSphereColliderRTTI.h"
+
+namespace BansheeEngine
+{
+	CSphereCollider::CSphereCollider(const HSceneObject& parent, float radius)
+		: CCollider(parent), mRadius(radius)
+	{
+		setName("SphereCollider");
+	}
+
+	void CSphereCollider::setRadius(float radius)
+	{
+		float clampedRadius = std::max(radius, 0.01f);
+		if (mRadius == clampedRadius)
+			return;
+
+		mRadius = clampedRadius;
+
+		if (mInternal != nullptr)
+		{
+			_getInternal()->setRadius(clampedRadius);
+
+			if (mParent != nullptr)
+				mParent->_updateMassDistribution();
+		}
+	}
+
+	void CSphereCollider::setCenter(const Vector3& center)
+	{
+		if (mLocalPosition == center)
+			return;
+
+		mLocalPosition = center; 
+		
+		if (mInternal != nullptr)
+			updateTransform();
+	}
+
+	SPtr<Collider> CSphereCollider::createInternal()
+	{
+		SPtr<Collider> collider = SphereCollider::create(mRadius, SO()->getWorldPosition(), SO()->getWorldRotation());
+
+		collider->_setOwner(PhysicsOwnerType::Component, this);
+		return collider;
+	}
+
+	RTTITypeBase* CSphereCollider::getRTTIStatic()
+	{
+		return CSphereColliderRTTI::instance();
+	}
+
+	RTTITypeBase* CSphereCollider::getRTTI() const
+	{
+		return CSphereCollider::getRTTIStatic();
+	}
 }
 }

+ 2 - 2
Source/BansheeCore/Source/BsPhysics.cpp

@@ -16,7 +16,7 @@ namespace BansheeEngine
 	{
 	{
 		assert(groupA < CollisionMapSize && groupB < CollisionMapSize);
 		assert(groupA < CollisionMapSize && groupB < CollisionMapSize);
 
 
-		mMutex.lock();
+		BS_LOCK_MUTEX(mMutex);
 		mCollisionMap[groupA][groupB] = enabled;
 		mCollisionMap[groupA][groupB] = enabled;
 	}
 	}
 
 
@@ -24,7 +24,7 @@ namespace BansheeEngine
 	{
 	{
 		assert(groupA < CollisionMapSize && groupB < CollisionMapSize);
 		assert(groupA < CollisionMapSize && groupB < CollisionMapSize);
 
 
-		mMutex.lock();
+		BS_LOCK_MUTEX(mMutex);
 		return mCollisionMap[groupA][groupB];
 		return mCollisionMap[groupA][groupB];
 	}
 	}
 
 

+ 650 - 656
Source/BansheeEditor/Source/BsGUISceneTreeView.cpp

@@ -1,657 +1,651 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsGUISceneTreeView.h"
-#include "BsSceneObject.h"
-#include "BsSceneManager.h"
-#include "BsGUISkin.h"
-#include "BsCmdRecordSO.h"
-#include "BsCmdReparentSO.h"
-#include "BsCmdDeleteSO.h"
-#include "BsCmdCloneSO.h"
-#include "BsCmdCreateSO.h"
-#include "BsCmdInstantiateSO.h"
-#include "BsDragAndDropManager.h"
-#include "BsSelection.h"
-#include "BsGUIResourceTreeView.h"
-#include "BsProjectLibrary.h"
-#include "BsProjectResourceMeta.h"
-#include "BsPrefab.h"
-#include "BsResources.h"
-#include "BsGUIContextMenu.h"
-
-namespace BansheeEngine
-{
-	const MessageId GUISceneTreeView::SELECTION_CHANGED_MSG = MessageId("SceneTreeView_SelectionChanged");
-	const Color GUISceneTreeView::PREFAB_TINT = Color(1.0f, (168.0f / 255.0f), 0.0f, 1.0f);
-
-	DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
-		:numObjects(numObjects)
-	{
-		objects = bs_newN<HSceneObject>(numObjects);
-	}
-
-	DraggedSceneObjects::~DraggedSceneObjects()
-	{
-		bs_deleteN(objects, numObjects);
-		objects = nullptr;
-	}
-
-	GUISceneTreeView::GUISceneTreeView(const String& backgroundStyle, const String& elementBtnStyle, 
-		const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, 
-		const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions)
-		:GUITreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle, highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle,
-		dragSepHighlightStyle, dimensions), mCutFlag(false)
-	{
-		SceneTreeViewLocator::_provide(this);
-
-		GUIContextMenuPtr contextMenu = bs_shared_ptr_new<GUIContextMenu>();
-
-		contextMenu->addMenuItem(L"New", std::bind(&GUISceneTreeView::createNewSO, this), 50);
-		contextMenu->addMenuItem(L"Rename", std::bind(&GUISceneTreeView::renameSelected, this), 49, ShortcutKey(ButtonModifier::None, BC_F2));
-		contextMenu->addMenuItem(L"Delete", std::bind(&GUISceneTreeView::deleteSelection, this), 48, ShortcutKey(ButtonModifier::None, BC_DELETE));
-		contextMenu->addSeparator(L"", 40);
-		contextMenu->addMenuItem(L"Duplicate", std::bind(&GUISceneTreeView::duplicateSelection, this), 39, ShortcutKey(ButtonModifier::Ctrl, BC_D));
-		contextMenu->addMenuItem(L"Copy", std::bind(&GUISceneTreeView::copySelection, this), 38, ShortcutKey(ButtonModifier::Ctrl, BC_C));
-		contextMenu->addMenuItem(L"Cut", std::bind(&GUISceneTreeView::cutSelection, this), 37, ShortcutKey(ButtonModifier::Ctrl, BC_X));
-		contextMenu->addMenuItem(L"Paste", std::bind(&GUISceneTreeView::paste, this), 36, ShortcutKey(ButtonModifier::Ctrl, BC_V));
-
-		setContextMenu(contextMenu);
-	}
-
-	GUISceneTreeView::~GUISceneTreeView()
-	{
-		SceneTreeViewLocator::_remove(this);
-	}
-
-	GUISceneTreeView* GUISceneTreeView::create(const String& backgroundStyle, const String& elementBtnStyle, const String& foldoutBtnStyle, 
-		const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, const String& editBoxStyle, const String& dragHighlightStyle,
-		const String& dragSepHighlightStyle)
-	{
-		return new (bs_alloc<GUISceneTreeView>()) GUISceneTreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle, 
-			highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create());
-	}
-
-	GUISceneTreeView* GUISceneTreeView::create(const GUIOptions& options, const String& backgroundStyle, const String& elementBtnStyle, 
-		const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle,
-		const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle)
-	{
-		return new (bs_alloc<GUISceneTreeView>()) GUISceneTreeView(backgroundStyle, elementBtnStyle, 
-			foldoutBtnStyle, highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, 
-			dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create(options));
-	}
-
-	void GUISceneTreeView::updateTreeElement(SceneTreeElement* element)
-	{
-		HSceneObject currentSO = element->mSceneObject;
-
-		// Check if SceneObject has changed in any way and update the tree element
-
-		// Early exit case - Most commonly there will be no changes between active and cached data so 
-		// we first do a quick check in order to avoid expensive comparison later
-		bool completeMatch = true;
-		UINT32 visibleChildCount = 0;
-		for (UINT32 i = 0; i < currentSO->getNumChildren(); i++)
-		{
-			if (i >= element->mChildren.size())
-			{
-				completeMatch = false;
-				break;
-			}
-
-			HSceneObject currentSOChild = currentSO->getChild(i);
-
-#if BS_DEBUG_MODE == 0
-			if (currentSOChild->hasFlag(SOF_Internal))
-				continue;
-#endif
-
-			SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[visibleChildCount]);
-			visibleChildCount++;
-
-			UINT64 curId = currentSOChild->getInstanceId();
-			if (curId != currentChild->mId)
-			{
-				completeMatch = false;
-				break;
-			}
-		}
-
-		completeMatch &= visibleChildCount == element->mChildren.size();
-
-		// Not a complete match, compare everything and insert/delete elements as needed
-		bool needsUpdate = false;
-		if(!completeMatch)
-		{
-			Vector<TreeElement*> newChildren;
-
-			bool* tempToDelete = (bool*)bs_stack_alloc(sizeof(bool) * (UINT32)element->mChildren.size());
-			for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
-				tempToDelete[i] = true;
-
-			for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
-			{
-				HSceneObject currentSOChild = currentSO->getChild(i);
-				bool isInternal = currentSOChild->hasFlag(SOF_Internal);
-
-				HSceneObject prefabParent = currentSOChild->getPrefabParent();
-
-				// Only count it as a prefab instance if its not scene root (otherwise every object would be colored as a prefab)
-				bool isPrefabInstance = prefabParent != nullptr && prefabParent->getParent() != nullptr;
-
-#if BS_DEBUG_MODE == 0
-				if (isInternal)
-					continue;
-#endif
-
-				UINT64 curId = currentSOChild->getInstanceId();
-				bool found = false;
-
-				for(UINT32 j = 0; j < element->mChildren.size(); j++)
-				{
-					SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[j]);
-
-					if(curId == currentChild->mId)
-					{
-						tempToDelete[j] = false;
-						currentChild->mSortedIdx = (UINT32)newChildren.size();
-						newChildren.push_back(currentChild);
-
-						found = true;
-						break;
-					}
-				}
-
-				if(!found)
-				{
-					SceneTreeElement* newChild = bs_new<SceneTreeElement>();
-					newChild->mParent = element;
-					newChild->mSceneObject = currentSOChild;
-					newChild->mId = currentSOChild->getInstanceId();
-					newChild->mName = currentSOChild->getName();
-					newChild->mSortedIdx = (UINT32)newChildren.size();
-					newChild->mIsVisible = element->mIsVisible && element->mIsExpanded;
-					newChild->mTint = isInternal ? Color::Red : (isPrefabInstance ? PREFAB_TINT : Color::White);
-					newChild->mIsPrefabInstance = isPrefabInstance;
-
-					newChildren.push_back(newChild);
-
-					updateElementGUI(newChild);
-				}
-			}
-
-			for(UINT32 i = 0; i < element->mChildren.size(); i++)
-			{
-				if(!tempToDelete[i])
-					continue;
-
-				deleteTreeElementInternal(element->mChildren[i]);
-			}
-
-			bs_stack_free(tempToDelete);
-
-			element->mChildren = newChildren;
-			needsUpdate = true;
-		}
-
-		// Check if name needs updating
-		const String& name = element->mSceneObject->getName();
-		if(element->mName != name)
-		{
-			element->mName = name;
-			needsUpdate = true;	
-		}
-
-		// Check if active state needs updating
-		bool isDisabled = !element->mSceneObject->getActive();
-		if(element->mIsDisabled != isDisabled)
-		{
-			element->mIsDisabled = isDisabled;
-			needsUpdate = true;
-		}
-
-		// Check if prefab instance state needs updating
-		HSceneObject prefabParent = element->mSceneObject->getPrefabParent();
-
-		// Only count it as a prefab instance if its not scene root (otherwise every object would be colored as a prefab)
-		bool isPrefabInstance = prefabParent != nullptr && prefabParent->getParent() != nullptr;
-		if (element->mIsPrefabInstance != isPrefabInstance)
-		{
-			element->mIsPrefabInstance = isPrefabInstance;
-
-			bool isInternal = element->mSceneObject->hasFlag(SOF_Internal);
-			element->mTint = isInternal ? Color::Red : (isPrefabInstance ? PREFAB_TINT : Color::White);
-
-			needsUpdate = true;
-		}
-
-		if(needsUpdate)
-			updateElementGUI(element);
-
-		for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
-		{
-			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(element->mChildren[i]);
-			updateTreeElement(sceneElement);
-		}
-
-		// Calculate the sorted index of the elements based on their name
-		bs_frame_mark();
-		FrameVector<SceneTreeElement*> sortVector;
-		for (auto& child : element->mChildren)
-			sortVector.push_back(static_cast<SceneTreeElement*>(child));
-
-		std::sort(sortVector.begin(), sortVector.end(),
-			[&](const SceneTreeElement* lhs, const SceneTreeElement* rhs)
-		{
-			return StringUtil::compare(lhs->mName, rhs->mName, false) < 0;
-		});
-
-		UINT32 idx = 0;
-		for (auto& child : sortVector)
-		{
-			child->mSortedIdx = idx;
-			idx++;
-		}
-
-		bs_frame_clear();
-	}
-
-	void GUISceneTreeView::updateTreeElementHierarchy()
-	{
-		HSceneObject root = gCoreSceneManager().getRootNode();
-		mRootElement.mSceneObject = root;
-		mRootElement.mId = root->getInstanceId();
-		mRootElement.mSortedIdx = 0;
-		mRootElement.mIsExpanded = true;
-
-		updateTreeElement(&mRootElement);
-	}
-
-	void GUISceneTreeView::renameTreeElement(GUITreeView::TreeElement* element, const WString& name)
-	{
-		SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
-
-		HSceneObject so = sceneTreeElement->mSceneObject;
-		CmdRecordSO::execute(so, false, L"Renamed \"" + toWString(so->getName()) + L"\"");
-		so->setName(toString(name));
-
-		onModified();
-	}
-
-	void GUISceneTreeView::deleteTreeElement(TreeElement* element)
-	{
-		SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
-
-		HSceneObject so = sceneTreeElement->mSceneObject;
-		CmdDeleteSO::execute(so, L"Deleted \"" + toWString(so->getName()) + L"\"");
-
-		onModified();
-	}
-
-	void GUISceneTreeView::deleteTreeElementInternal(GUITreeView::TreeElement* element)
-	{
-		closeTemporarilyExpandedElements(); // In case this element is one of them
-
-		if (element->mIsHighlighted)
-			clearPing();
-
-		if(element->mIsSelected)
-			unselectElement(element);
-
-		bs_delete(element);
-	}
-
-	bool GUISceneTreeView::acceptDragAndDrop() const
-	{
-		return DragAndDropManager::instance().isDragInProgress() && 
-			(DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject ||
-			DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::Resources);
-	}
-
-	void GUISceneTreeView::dragAndDropStart()
-	{
-		DraggedSceneObjects* draggedSceneObjects = bs_new<DraggedSceneObjects>((UINT32)mSelectedElements.size());
-
-		UINT32 cnt = 0;
-		for(auto& selectedElement : mSelectedElements)
-		{
-			SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(selectedElement.element);
-			draggedSceneObjects->objects[cnt] = sceneTreeElement->mSceneObject;
-			cnt++;
-		}
-
-		DragAndDropManager::instance().startDrag((UINT32)DragAndDropType::SceneObject, (void*)draggedSceneObjects, 
-			std::bind(&GUISceneTreeView::dragAndDropFinalize, this), false);
-	}
-
-	void GUISceneTreeView::dragAndDropEnded(TreeElement* overTreeElement)
-	{
-		UINT32 dragTypeId = DragAndDropManager::instance().getDragTypeId();
-
-		if (dragTypeId == (UINT32)DragAndDropType::SceneObject)
-		{
-			if (overTreeElement != nullptr)
-			{
-				DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
-
-				Vector<HSceneObject> sceneObjects;
-				SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(overTreeElement);
-				HSceneObject newParent = sceneTreeElement->mSceneObject;
-
-				for (UINT32 i = 0; i < draggedSceneObjects->numObjects; i++)
-				{
-					if (draggedSceneObjects->objects[i] != newParent)
-						sceneObjects.push_back(draggedSceneObjects->objects[i]);
-				}
-
-				CmdReparentSO::execute(sceneObjects, newParent);
-				onModified();
-			}
-		}
-		else if (dragTypeId == (UINT32)DragAndDropType::Resources)
-		{
-			DraggedResources* draggedResources = reinterpret_cast<DraggedResources*>(DragAndDropManager::instance().getDragData());
-
-			HSceneObject newParent;
-			if (overTreeElement != nullptr)
-			{
-				SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(overTreeElement);
-				newParent = sceneTreeElement->mSceneObject;
-			}
-
-			onResourceDropped(newParent, draggedResources->resourcePaths);
-		}
-	}
-
-	void GUISceneTreeView::dragAndDropFinalize()
-	{
-		mDragInProgress = false;
-		_markLayoutAsDirty();
-
-		if (DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
-		{
-			DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
-			bs_delete(draggedSceneObjects);
-		}
-	}
-
-	bool GUISceneTreeView::_acceptDragAndDrop(const Vector2I position, UINT32 typeId) const
-	{
-		return (typeId == (UINT32)DragAndDropType::SceneObject || typeId == (UINT32)DragAndDropType::Resources) && !_isDisabled();
-	}
-
-	void GUISceneTreeView::selectionChanged()
-	{
-		onSelectionChanged();
-		sendMessage(SELECTION_CHANGED_MSG);
-	}
-
-	Vector<HSceneObject> GUISceneTreeView::getSelection() const
-	{
-		Vector<HSceneObject> selectedSOs;
-		for (auto& selectedElem : mSelectedElements)
-		{
-			SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(selectedElem.element);
-
-			selectedSOs.push_back(sceneTreeElement->mSceneObject);
-		}
-
-		return selectedSOs;
-	}
-
-	void GUISceneTreeView::setSelection(const Vector<HSceneObject>& objects)
-	{
-		unselectAll(false);
-
-		// Note: I could queue the selection update until after the next frame in order to avoid the hierarchy update here
-		// for better performance.
-		updateTreeElementHierarchy();
-
-		SceneTreeElement& root = mRootElement;
-
-		Stack<SceneTreeElement*> todo;
-		todo.push(&mRootElement);
-
-		while (!todo.empty())
-		{
-			SceneTreeElement* currentElem = todo.top();
-			todo.pop();
-
-			auto iterFind = std::find(objects.begin(), objects.end(), currentElem->mSceneObject);
-			if (iterFind != objects.end())
-			{
-				expandToElement(currentElem);
-				selectElement(currentElem);
-			}
-
-			for (auto& child : currentElem->mChildren)
-			{
-				SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
-				todo.push(sceneChild);
-			}
-		}
-	}
-
-	void GUISceneTreeView::ping(const HSceneObject& object)
-	{
-		SceneTreeElement& root = mRootElement;
-
-		Stack<SceneTreeElement*> todo;
-		todo.push(&mRootElement);
-
-		while (!todo.empty())
-		{
-			SceneTreeElement* currentElem = todo.top();
-			todo.pop();
-
-			if (currentElem->mSceneObject == object)
-			{
-				GUITreeView::ping(currentElem);
-				break;
-			}
-
-			for (auto& child : currentElem->mChildren)
-			{
-				SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
-				todo.push(sceneChild);
-			}
-		}
-	}
-
-	GUISceneTreeView::SceneTreeElement* GUISceneTreeView::findTreeElement(const HSceneObject& so)
-	{
-		SceneTreeElement& root = mRootElement;
-
-		Stack<SceneTreeElement*> todo;
-		todo.push(&mRootElement);
-
-		while (!todo.empty())
-		{
-			SceneTreeElement* currentElem = todo.top();
-			todo.pop();
-
-			if (so == currentElem->mSceneObject)
-				return currentElem;
-
-			for (auto& child : currentElem->mChildren)
-			{
-				SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
-				todo.push(sceneChild);
-			}
-		}
-
-		return nullptr;
-	}
-
-	void GUISceneTreeView::duplicateSelection()
-	{
-		Vector<HSceneObject> duplicateList;
-		for (auto& selectedElem : mSelectedElements)
-		{
-			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
-			duplicateList.push_back(sceneElement->mSceneObject);
-		}
-
-		cleanDuplicates(duplicateList);
-
-		if (duplicateList.size() == 0)
-			return;
-
-		WString message;
-		if (duplicateList.size() == 1)
-			message = L"Duplicated " + toWString(duplicateList[0]->getName());
-		else
-			message = L"Duplicated " + toWString(duplicateList.size()) + L" elements";
-
-		CmdCloneSO::execute(duplicateList, message);
-		onModified();
-	}
-
-	void GUISceneTreeView::copySelection()
-	{
-		clearCopyList();
-
-		for (auto& selectedElem : mSelectedElements)
-		{
-			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
-			mCopyList.push_back(sceneElement->mSceneObject);
-		}
-
-		mCutFlag = false;
-	}
-
-	void GUISceneTreeView::cutSelection()
-	{
-		clearCopyList();
-
-		for (auto& selectedElem : mSelectedElements)
-		{
-			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
-			mCopyList.push_back(sceneElement->mSceneObject);
-
-			sceneElement->mIsCut = true;
-			updateElementGUI(sceneElement);
-		}
-
-		mCutFlag = true;
-		_markLayoutAsDirty();
-	}
-
-	void GUISceneTreeView::paste()
-	{
-		cleanDuplicates(mCopyList);
-
-		if (mCopyList.size() == 0)
-			return;
-
-		HSceneObject parent = mRootElement.mSceneObject;
-		if (mSelectedElements.size() > 0)
-		{
-			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(mSelectedElements[0].element);
-			parent = sceneElement->mSceneObject;
-		}
-
-		if (mCutFlag)
-		{
-			WString message;
-			if (mCopyList.size() == 1)
-				message = L"Moved " + toWString(mCopyList[0]->getName());
-			else
-				message = L"Moved " + toWString(mCopyList.size()) + L" elements";
-
-			CmdReparentSO::execute(mCopyList, parent, message);
-			clearCopyList();
-		}
-		else
-		{
-			WString message;
-			if (mCopyList.size() == 1)
-				message = L"Copied " + toWString(mCopyList[0]->getName());
-			else
-				message = L"Copied " + toWString(mCopyList.size()) + L" elements";
-
-			Vector<HSceneObject> clones = CmdCloneSO::execute(mCopyList, message);
-			for (auto& clone : clones)
-				clone->setParent(parent);
-		}
-
-		onModified();
-	}
-
-	void GUISceneTreeView::clearCopyList()
-	{
-		for (auto& so : mCopyList)
-		{
-			if (so.isDestroyed())
-				continue;
-
-			TreeElement* treeElem = findTreeElement(so);
-
-			if (treeElem != nullptr)
-			{
-				treeElem->mIsCut = false;
-				updateElementGUI(treeElem);
-			}
-		}
-
-		mCopyList.clear();
-		_markLayoutAsDirty();
-	}
-
-	void GUISceneTreeView::createNewSO()
-	{
-		HSceneObject newSO = CmdCreateSO::execute("New", 0, L"Created a new SceneObject");
-
-		if (mSelectedElements.size() > 0)
-		{
-			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(mSelectedElements[0].element);
-			newSO->setParent(sceneElement->mSceneObject);
-		}
-
-		updateTreeElementHierarchy();
-
-		TreeElement* newTreeElement = findTreeElement(newSO);
-		expandToElement(newTreeElement);
-		setSelection({ newSO });
-		renameSelected();
-
-		onModified();
-	}
-
-	void GUISceneTreeView::cleanDuplicates(Vector<HSceneObject>& objects)
-	{
-		auto isChildOf = [&](const HSceneObject& parent, const HSceneObject& child)
-		{
-			HSceneObject elem = child;
-
-			while (elem != nullptr && elem != parent)
-				elem = elem->getParent();
-
-			return elem == parent;
-		};
-
-		Vector<HSceneObject> cleanList;
-		for (UINT32 i = 0; i < (UINT32)objects.size(); i++)
-		{
-			bool foundParent = false;
-			for (UINT32 j = 0; j < (UINT32)objects.size(); j++)
-			{
-				if (i != j && isChildOf(objects[j], objects[i]))
-				{
-					foundParent = true;
-					break;
-				}
-			}
-
-			if (!foundParent)
-				cleanList.push_back(objects[i]);
-		}
-
-		objects = cleanList;
-	}
-
-	const String& GUISceneTreeView::getGUITypeName()
-	{
-		static String typeName = "SceneTreeView";
-		return typeName;
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsGUISceneTreeView.h"
+#include "BsSceneObject.h"
+#include "BsSceneManager.h"
+#include "BsGUISkin.h"
+#include "BsCmdRecordSO.h"
+#include "BsCmdReparentSO.h"
+#include "BsCmdDeleteSO.h"
+#include "BsCmdCloneSO.h"
+#include "BsCmdCreateSO.h"
+#include "BsDragAndDropManager.h"
+#include "BsGUIResourceTreeView.h"
+#include "BsGUIContextMenu.h"
+
+namespace BansheeEngine
+{
+	const MessageId GUISceneTreeView::SELECTION_CHANGED_MSG = MessageId("SceneTreeView_SelectionChanged");
+	const Color GUISceneTreeView::PREFAB_TINT = Color(1.0f, (168.0f / 255.0f), 0.0f, 1.0f);
+
+	DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
+		:numObjects(numObjects)
+	{
+		objects = bs_newN<HSceneObject>(numObjects);
+	}
+
+	DraggedSceneObjects::~DraggedSceneObjects()
+	{
+		bs_deleteN(objects, numObjects);
+		objects = nullptr;
+	}
+
+	GUISceneTreeView::GUISceneTreeView(const String& backgroundStyle, const String& elementBtnStyle, 
+		const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, 
+		const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions)
+		:GUITreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle, highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle,
+		dragSepHighlightStyle, dimensions), mCutFlag(false)
+	{
+		SceneTreeViewLocator::_provide(this);
+
+		GUIContextMenuPtr contextMenu = bs_shared_ptr_new<GUIContextMenu>();
+
+		contextMenu->addMenuItem(L"New scene object", std::bind(&GUISceneTreeView::createNewSO, this), 50);
+		contextMenu->addMenuItem(L"Rename", std::bind(&GUISceneTreeView::renameSelected, this), 49, ShortcutKey(ButtonModifier::None, BC_F2));
+		contextMenu->addMenuItem(L"Delete", std::bind(&GUISceneTreeView::deleteSelection, this), 48, ShortcutKey(ButtonModifier::None, BC_DELETE));
+		contextMenu->addSeparator(L"", 40);
+		contextMenu->addMenuItem(L"Duplicate", std::bind(&GUISceneTreeView::duplicateSelection, this), 39, ShortcutKey(ButtonModifier::Ctrl, BC_D));
+		contextMenu->addMenuItem(L"Copy", std::bind(&GUISceneTreeView::copySelection, this), 38, ShortcutKey(ButtonModifier::Ctrl, BC_C));
+		contextMenu->addMenuItem(L"Cut", std::bind(&GUISceneTreeView::cutSelection, this), 37, ShortcutKey(ButtonModifier::Ctrl, BC_X));
+		contextMenu->addMenuItem(L"Paste", std::bind(&GUISceneTreeView::paste, this), 36, ShortcutKey(ButtonModifier::Ctrl, BC_V));
+
+		setContextMenu(contextMenu);
+	}
+
+	GUISceneTreeView::~GUISceneTreeView()
+	{
+		SceneTreeViewLocator::_remove(this);
+	}
+
+	GUISceneTreeView* GUISceneTreeView::create(const String& backgroundStyle, const String& elementBtnStyle, const String& foldoutBtnStyle, 
+		const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, const String& editBoxStyle, const String& dragHighlightStyle,
+		const String& dragSepHighlightStyle)
+	{
+		return new (bs_alloc<GUISceneTreeView>()) GUISceneTreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle, 
+			highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create());
+	}
+
+	GUISceneTreeView* GUISceneTreeView::create(const GUIOptions& options, const String& backgroundStyle, const String& elementBtnStyle, 
+		const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle,
+		const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle)
+	{
+		return new (bs_alloc<GUISceneTreeView>()) GUISceneTreeView(backgroundStyle, elementBtnStyle, 
+			foldoutBtnStyle, highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, 
+			dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create(options));
+	}
+
+	void GUISceneTreeView::updateTreeElement(SceneTreeElement* element)
+	{
+		HSceneObject currentSO = element->mSceneObject;
+
+		// Check if SceneObject has changed in any way and update the tree element
+
+		// Early exit case - Most commonly there will be no changes between active and cached data so 
+		// we first do a quick check in order to avoid expensive comparison later
+		bool completeMatch = true;
+		UINT32 visibleChildCount = 0;
+		for (UINT32 i = 0; i < currentSO->getNumChildren(); i++)
+		{
+			if (i >= element->mChildren.size())
+			{
+				completeMatch = false;
+				break;
+			}
+
+			HSceneObject currentSOChild = currentSO->getChild(i);
+
+#if BS_DEBUG_MODE == 0
+			if (currentSOChild->hasFlag(SOF_Internal))
+				continue;
+#endif
+
+			SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[visibleChildCount]);
+			visibleChildCount++;
+
+			UINT64 curId = currentSOChild->getInstanceId();
+			if (curId != currentChild->mId)
+			{
+				completeMatch = false;
+				break;
+			}
+		}
+
+		completeMatch &= visibleChildCount == element->mChildren.size();
+
+		// Not a complete match, compare everything and insert/delete elements as needed
+		bool needsUpdate = false;
+		if(!completeMatch)
+		{
+			Vector<TreeElement*> newChildren;
+
+			bool* tempToDelete = (bool*)bs_stack_alloc(sizeof(bool) * (UINT32)element->mChildren.size());
+			for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
+				tempToDelete[i] = true;
+
+			for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
+			{
+				HSceneObject currentSOChild = currentSO->getChild(i);
+				bool isInternal = currentSOChild->hasFlag(SOF_Internal);
+
+				HSceneObject prefabParent = currentSOChild->getPrefabParent();
+
+				// Only count it as a prefab instance if its not scene root (otherwise every object would be colored as a prefab)
+				bool isPrefabInstance = prefabParent != nullptr && prefabParent->getParent() != nullptr;
+
+#if BS_DEBUG_MODE == 0
+				if (isInternal)
+					continue;
+#endif
+
+				UINT64 curId = currentSOChild->getInstanceId();
+				bool found = false;
+
+				for(UINT32 j = 0; j < element->mChildren.size(); j++)
+				{
+					SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[j]);
+
+					if(curId == currentChild->mId)
+					{
+						tempToDelete[j] = false;
+						currentChild->mSortedIdx = (UINT32)newChildren.size();
+						newChildren.push_back(currentChild);
+
+						found = true;
+						break;
+					}
+				}
+
+				if(!found)
+				{
+					SceneTreeElement* newChild = bs_new<SceneTreeElement>();
+					newChild->mParent = element;
+					newChild->mSceneObject = currentSOChild;
+					newChild->mId = currentSOChild->getInstanceId();
+					newChild->mName = currentSOChild->getName();
+					newChild->mSortedIdx = (UINT32)newChildren.size();
+					newChild->mIsVisible = element->mIsVisible && element->mIsExpanded;
+					newChild->mTint = isInternal ? Color::Red : (isPrefabInstance ? PREFAB_TINT : Color::White);
+					newChild->mIsPrefabInstance = isPrefabInstance;
+
+					newChildren.push_back(newChild);
+
+					updateElementGUI(newChild);
+				}
+			}
+
+			for(UINT32 i = 0; i < element->mChildren.size(); i++)
+			{
+				if(!tempToDelete[i])
+					continue;
+
+				deleteTreeElementInternal(element->mChildren[i]);
+			}
+
+			bs_stack_free(tempToDelete);
+
+			element->mChildren = newChildren;
+			needsUpdate = true;
+		}
+
+		// Check if name needs updating
+		const String& name = element->mSceneObject->getName();
+		if(element->mName != name)
+		{
+			element->mName = name;
+			needsUpdate = true;	
+		}
+
+		// Check if active state needs updating
+		bool isDisabled = !element->mSceneObject->getActive();
+		if(element->mIsDisabled != isDisabled)
+		{
+			element->mIsDisabled = isDisabled;
+			needsUpdate = true;
+		}
+
+		// Check if prefab instance state needs updating
+		HSceneObject prefabParent = element->mSceneObject->getPrefabParent();
+
+		// Only count it as a prefab instance if its not scene root (otherwise every object would be colored as a prefab)
+		bool isPrefabInstance = prefabParent != nullptr && prefabParent->getParent() != nullptr;
+		if (element->mIsPrefabInstance != isPrefabInstance)
+		{
+			element->mIsPrefabInstance = isPrefabInstance;
+
+			bool isInternal = element->mSceneObject->hasFlag(SOF_Internal);
+			element->mTint = isInternal ? Color::Red : (isPrefabInstance ? PREFAB_TINT : Color::White);
+
+			needsUpdate = true;
+		}
+
+		if(needsUpdate)
+			updateElementGUI(element);
+
+		for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
+		{
+			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(element->mChildren[i]);
+			updateTreeElement(sceneElement);
+		}
+
+		// Calculate the sorted index of the elements based on their name
+		bs_frame_mark();
+		FrameVector<SceneTreeElement*> sortVector;
+		for (auto& child : element->mChildren)
+			sortVector.push_back(static_cast<SceneTreeElement*>(child));
+
+		std::sort(sortVector.begin(), sortVector.end(),
+			[&](const SceneTreeElement* lhs, const SceneTreeElement* rhs)
+		{
+			return StringUtil::compare(lhs->mName, rhs->mName, false) < 0;
+		});
+
+		UINT32 idx = 0;
+		for (auto& child : sortVector)
+		{
+			child->mSortedIdx = idx;
+			idx++;
+		}
+
+		bs_frame_clear();
+	}
+
+	void GUISceneTreeView::updateTreeElementHierarchy()
+	{
+		HSceneObject root = gCoreSceneManager().getRootNode();
+		mRootElement.mSceneObject = root;
+		mRootElement.mId = root->getInstanceId();
+		mRootElement.mSortedIdx = 0;
+		mRootElement.mIsExpanded = true;
+
+		updateTreeElement(&mRootElement);
+	}
+
+	void GUISceneTreeView::renameTreeElement(GUITreeView::TreeElement* element, const WString& name)
+	{
+		SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
+
+		HSceneObject so = sceneTreeElement->mSceneObject;
+		CmdRecordSO::execute(so, false, L"Renamed \"" + toWString(so->getName()) + L"\"");
+		so->setName(toString(name));
+
+		onModified();
+	}
+
+	void GUISceneTreeView::deleteTreeElement(TreeElement* element)
+	{
+		SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
+
+		HSceneObject so = sceneTreeElement->mSceneObject;
+		CmdDeleteSO::execute(so, L"Deleted \"" + toWString(so->getName()) + L"\"");
+
+		onModified();
+	}
+
+	void GUISceneTreeView::deleteTreeElementInternal(GUITreeView::TreeElement* element)
+	{
+		closeTemporarilyExpandedElements(); // In case this element is one of them
+
+		if (element->mIsHighlighted)
+			clearPing();
+
+		if(element->mIsSelected)
+			unselectElement(element);
+
+		bs_delete(element);
+	}
+
+	bool GUISceneTreeView::acceptDragAndDrop() const
+	{
+		return DragAndDropManager::instance().isDragInProgress() && 
+			(DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject ||
+			DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::Resources);
+	}
+
+	void GUISceneTreeView::dragAndDropStart()
+	{
+		DraggedSceneObjects* draggedSceneObjects = bs_new<DraggedSceneObjects>((UINT32)mSelectedElements.size());
+
+		UINT32 cnt = 0;
+		for(auto& selectedElement : mSelectedElements)
+		{
+			SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(selectedElement.element);
+			draggedSceneObjects->objects[cnt] = sceneTreeElement->mSceneObject;
+			cnt++;
+		}
+
+		DragAndDropManager::instance().startDrag((UINT32)DragAndDropType::SceneObject, (void*)draggedSceneObjects, 
+			std::bind(&GUISceneTreeView::dragAndDropFinalize, this), false);
+	}
+
+	void GUISceneTreeView::dragAndDropEnded(TreeElement* overTreeElement)
+	{
+		UINT32 dragTypeId = DragAndDropManager::instance().getDragTypeId();
+
+		if (dragTypeId == (UINT32)DragAndDropType::SceneObject)
+		{
+			if (overTreeElement != nullptr)
+			{
+				DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
+
+				Vector<HSceneObject> sceneObjects;
+				SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(overTreeElement);
+				HSceneObject newParent = sceneTreeElement->mSceneObject;
+
+				for (UINT32 i = 0; i < draggedSceneObjects->numObjects; i++)
+				{
+					if (draggedSceneObjects->objects[i] != newParent)
+						sceneObjects.push_back(draggedSceneObjects->objects[i]);
+				}
+
+				CmdReparentSO::execute(sceneObjects, newParent);
+				onModified();
+			}
+		}
+		else if (dragTypeId == (UINT32)DragAndDropType::Resources)
+		{
+			DraggedResources* draggedResources = reinterpret_cast<DraggedResources*>(DragAndDropManager::instance().getDragData());
+
+			HSceneObject newParent;
+			if (overTreeElement != nullptr)
+			{
+				SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(overTreeElement);
+				newParent = sceneTreeElement->mSceneObject;
+			}
+
+			onResourceDropped(newParent, draggedResources->resourcePaths);
+		}
+	}
+
+	void GUISceneTreeView::dragAndDropFinalize()
+	{
+		mDragInProgress = false;
+		_markLayoutAsDirty();
+
+		if (DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
+		{
+			DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
+			bs_delete(draggedSceneObjects);
+		}
+	}
+
+	bool GUISceneTreeView::_acceptDragAndDrop(const Vector2I position, UINT32 typeId) const
+	{
+		return (typeId == (UINT32)DragAndDropType::SceneObject || typeId == (UINT32)DragAndDropType::Resources) && !_isDisabled();
+	}
+
+	void GUISceneTreeView::selectionChanged()
+	{
+		onSelectionChanged();
+		sendMessage(SELECTION_CHANGED_MSG);
+	}
+
+	Vector<HSceneObject> GUISceneTreeView::getSelection() const
+	{
+		Vector<HSceneObject> selectedSOs;
+		for (auto& selectedElem : mSelectedElements)
+		{
+			SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(selectedElem.element);
+
+			selectedSOs.push_back(sceneTreeElement->mSceneObject);
+		}
+
+		return selectedSOs;
+	}
+
+	void GUISceneTreeView::setSelection(const Vector<HSceneObject>& objects)
+	{
+		unselectAll(false);
+
+		// Note: I could queue the selection update until after the next frame in order to avoid the hierarchy update here
+		// for better performance.
+		updateTreeElementHierarchy();
+
+		SceneTreeElement& root = mRootElement;
+
+		Stack<SceneTreeElement*> todo;
+		todo.push(&mRootElement);
+
+		while (!todo.empty())
+		{
+			SceneTreeElement* currentElem = todo.top();
+			todo.pop();
+
+			auto iterFind = std::find(objects.begin(), objects.end(), currentElem->mSceneObject);
+			if (iterFind != objects.end())
+			{
+				expandToElement(currentElem);
+				selectElement(currentElem);
+			}
+
+			for (auto& child : currentElem->mChildren)
+			{
+				SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
+				todo.push(sceneChild);
+			}
+		}
+	}
+
+	void GUISceneTreeView::ping(const HSceneObject& object)
+	{
+		SceneTreeElement& root = mRootElement;
+
+		Stack<SceneTreeElement*> todo;
+		todo.push(&mRootElement);
+
+		while (!todo.empty())
+		{
+			SceneTreeElement* currentElem = todo.top();
+			todo.pop();
+
+			if (currentElem->mSceneObject == object)
+			{
+				GUITreeView::ping(currentElem);
+				break;
+			}
+
+			for (auto& child : currentElem->mChildren)
+			{
+				SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
+				todo.push(sceneChild);
+			}
+		}
+	}
+
+	GUISceneTreeView::SceneTreeElement* GUISceneTreeView::findTreeElement(const HSceneObject& so)
+	{
+		SceneTreeElement& root = mRootElement;
+
+		Stack<SceneTreeElement*> todo;
+		todo.push(&mRootElement);
+
+		while (!todo.empty())
+		{
+			SceneTreeElement* currentElem = todo.top();
+			todo.pop();
+
+			if (so == currentElem->mSceneObject)
+				return currentElem;
+
+			for (auto& child : currentElem->mChildren)
+			{
+				SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
+				todo.push(sceneChild);
+			}
+		}
+
+		return nullptr;
+	}
+
+	void GUISceneTreeView::duplicateSelection()
+	{
+		Vector<HSceneObject> duplicateList;
+		for (auto& selectedElem : mSelectedElements)
+		{
+			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
+			duplicateList.push_back(sceneElement->mSceneObject);
+		}
+
+		cleanDuplicates(duplicateList);
+
+		if (duplicateList.size() == 0)
+			return;
+
+		WString message;
+		if (duplicateList.size() == 1)
+			message = L"Duplicated " + toWString(duplicateList[0]->getName());
+		else
+			message = L"Duplicated " + toWString(duplicateList.size()) + L" elements";
+
+		CmdCloneSO::execute(duplicateList, message);
+		onModified();
+	}
+
+	void GUISceneTreeView::copySelection()
+	{
+		clearCopyList();
+
+		for (auto& selectedElem : mSelectedElements)
+		{
+			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
+			mCopyList.push_back(sceneElement->mSceneObject);
+		}
+
+		mCutFlag = false;
+	}
+
+	void GUISceneTreeView::cutSelection()
+	{
+		clearCopyList();
+
+		for (auto& selectedElem : mSelectedElements)
+		{
+			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
+			mCopyList.push_back(sceneElement->mSceneObject);
+
+			sceneElement->mIsCut = true;
+			updateElementGUI(sceneElement);
+		}
+
+		mCutFlag = true;
+		_markLayoutAsDirty();
+	}
+
+	void GUISceneTreeView::paste()
+	{
+		cleanDuplicates(mCopyList);
+
+		if (mCopyList.size() == 0)
+			return;
+
+		HSceneObject parent = mRootElement.mSceneObject;
+		if (mSelectedElements.size() > 0)
+		{
+			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(mSelectedElements[0].element);
+			parent = sceneElement->mSceneObject;
+		}
+
+		if (mCutFlag)
+		{
+			WString message;
+			if (mCopyList.size() == 1)
+				message = L"Moved " + toWString(mCopyList[0]->getName());
+			else
+				message = L"Moved " + toWString(mCopyList.size()) + L" elements";
+
+			CmdReparentSO::execute(mCopyList, parent, message);
+			clearCopyList();
+		}
+		else
+		{
+			WString message;
+			if (mCopyList.size() == 1)
+				message = L"Copied " + toWString(mCopyList[0]->getName());
+			else
+				message = L"Copied " + toWString(mCopyList.size()) + L" elements";
+
+			Vector<HSceneObject> clones = CmdCloneSO::execute(mCopyList, message);
+			for (auto& clone : clones)
+				clone->setParent(parent);
+		}
+
+		onModified();
+	}
+
+	void GUISceneTreeView::clearCopyList()
+	{
+		for (auto& so : mCopyList)
+		{
+			if (so.isDestroyed())
+				continue;
+
+			TreeElement* treeElem = findTreeElement(so);
+
+			if (treeElem != nullptr)
+			{
+				treeElem->mIsCut = false;
+				updateElementGUI(treeElem);
+			}
+		}
+
+		mCopyList.clear();
+		_markLayoutAsDirty();
+	}
+
+	void GUISceneTreeView::createNewSO()
+	{
+		HSceneObject newSO = CmdCreateSO::execute("New", 0, L"Created a new SceneObject");
+
+		if (mSelectedElements.size() > 0)
+		{
+			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(mSelectedElements[0].element);
+			newSO->setParent(sceneElement->mSceneObject);
+		}
+
+		updateTreeElementHierarchy();
+
+		TreeElement* newTreeElement = findTreeElement(newSO);
+		expandToElement(newTreeElement);
+		setSelection({ newSO });
+		renameSelected();
+
+		onModified();
+	}
+
+	void GUISceneTreeView::cleanDuplicates(Vector<HSceneObject>& objects)
+	{
+		auto isChildOf = [&](const HSceneObject& parent, const HSceneObject& child)
+		{
+			HSceneObject elem = child;
+
+			while (elem != nullptr && elem != parent)
+				elem = elem->getParent();
+
+			return elem == parent;
+		};
+
+		Vector<HSceneObject> cleanList;
+		for (UINT32 i = 0; i < (UINT32)objects.size(); i++)
+		{
+			bool foundParent = false;
+			for (UINT32 j = 0; j < (UINT32)objects.size(); j++)
+			{
+				if (i != j && isChildOf(objects[j], objects[i]))
+				{
+					foundParent = true;
+					break;
+				}
+			}
+
+			if (!foundParent)
+				cleanList.push_back(objects[i]);
+		}
+
+		objects = cleanList;
+	}
+
+	const String& GUISceneTreeView::getGUITypeName()
+	{
+		static String typeName = "SceneTreeView";
+		return typeName;
+	}
 }
 }

+ 13 - 7
Source/BansheeEditor/Source/BsProjectLibrary.cpp

@@ -433,20 +433,26 @@ namespace BansheeEngine
 
 
 				fileEntry->meta = ProjectFileMeta::create(curImportOptions);
 				fileEntry->meta = ProjectFileMeta::create(curImportOptions);
 
 
+				for(auto& entry : importedResources)
+				{
+					ResourceMetaDataPtr subMeta = entry.value->getMetaData();
+					UINT32 typeId = entry.value->getTypeId();
+					const String& UUID = entry.value.getUUID();
+
+					ProjectResourceMetaPtr resMeta = ProjectResourceMeta::create(entry.name, UUID, typeId, subMeta);
+					fileEntry->meta->add(resMeta);
+				}
+
 				if(importedResources.size() > 0)
 				if(importedResources.size() > 0)
 				{
 				{
 					HResource primary = importedResources[0].value;
 					HResource primary = importedResources[0].value;
 
 
 					mUUIDToPath[primary.getUUID()] = fileEntry->path;
 					mUUIDToPath[primary.getUUID()] = fileEntry->path;
-					for(auto& entry : importedResources)
+					for (UINT32 i = 1; i < (UINT32)importedResources.size(); i++)
 					{
 					{
-						ResourceMetaDataPtr subMeta = entry.value->getMetaData();
-						UINT32 typeId = entry.value->getTypeId();
-						const String& UUID = entry.value.getUUID();
-
-						ProjectResourceMetaPtr resMeta = ProjectResourceMeta::create(entry.name, UUID, typeId, subMeta);
-						fileEntry->meta->add(resMeta);
+						SubResource& entry = importedResources[i];
 
 
+						const String& UUID = entry.value.getUUID();
 						mUUIDToPath[UUID] = fileEntry->path + entry.name;
 						mUUIDToPath[UUID] = fileEntry->path + entry.name;
 					}
 					}
 				}
 				}

+ 95 - 89
Source/BansheePhysX/Include/BsFPhysXCollider.h

@@ -1,90 +1,96 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPhysXPrerequisites.h"
-#include "BsPhysicsCommon.h"
-#include "BsFCollider.h"
-#include "PxRigidStatic.h"
-
-namespace BansheeEngine
-{
-	/** @addtogroup PhysX
-	 *  @{
-	 */
-
-	/** PhysX implementation of FCollider. */
-	class FPhysXCollider : public FCollider
-	{
-	public:
-		explicit FPhysXCollider(physx::PxShape* shape);
-		~FPhysXCollider();
-
-		/** @copydoc FCollider::getPosition */
-		Vector3 getPosition() const override;
-
-		/** @copydoc FCollider::getRotation */
-		Quaternion getRotation() const override;
-
-		/** @copydoc FCollider::setTransform */
-		void setTransform(const Vector3& pos, const Quaternion& rotation) override;
-
-		/** @copydoc FCollider::setIsTrigger */
-		void setIsTrigger(bool value) override;
-
-		/** @copydoc FCollider::getIsTrigger */
-		bool getIsTrigger() const override;
-
-		/** @copydoc FCollider::setIsStatic */
-		void setIsStatic(bool value) override;
-
-		/** @copydoc FCollider::getIsStatic */
-		bool getIsStatic() const override;
-
-		/** @copydoc FCollider::setContactOffset */
-		void setContactOffset(float value) override;
-
-		/** @copydoc FCollider::getContactOffset */
-		float getContactOffset() const override;
-
-		/** @copydoc FCollider::setRestOffset */
-		void setRestOffset(float value) override;
-
-		/** @copydoc FCollider::getRestOffset */
-		float getRestOffset() const override;
-
-		/** @copydoc FCollider::setMaterial */
-		void setMaterial(const HPhysicsMaterial& material) override;
-
-		/** @copydoc FCollider::getLayer */
-		UINT64 getLayer() const override;
-
-		/** @copydoc FCollider::setLayer */
-		void setLayer(UINT64 layer) override;
-
-		/** @copydoc FCollider::getCollisionReportMode */
-		CollisionReportMode getCollisionReportMode() const override;
-
-		/** @copydoc FCollider::setCollisionReportMode */
-		void setCollisionReportMode(CollisionReportMode mode) override;
-
-		/** @copydoc FCollider::_setCCD */
-		void _setCCD(bool enabled) override;
-
-		/** Gets the internal PhysX shape that represents the collider. */
-		physx::PxShape* _getShape() const { return mShape; }
-	protected:
-		/** Updates shape filter data from stored values. */
-		void updateFilter();
-
-		physx::PxShape* mShape = nullptr;
-		physx::PxRigidStatic* mStaticBody = nullptr;
-		bool mIsTrigger = false;
-		bool mIsStatic = true;
-		UINT64 mLayer = 1;
-		bool mCCD = false;
-		CollisionReportMode mCollisionReportMode = CollisionReportMode::None;
-	};
-
-	/** @} */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPhysXPrerequisites.h"
+#include "BsPhysicsCommon.h"
+#include "BsFCollider.h"
+#include "PxRigidStatic.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup PhysX
+	 *  @{
+	 */
+
+	/** PhysX implementation of FCollider. */
+	class FPhysXCollider : public FCollider
+	{
+	public:
+		explicit FPhysXCollider(physx::PxShape* shape);
+		~FPhysXCollider();
+
+		/** @copydoc FCollider::getPosition */
+		Vector3 getPosition() const override;
+
+		/** @copydoc FCollider::getRotation */
+		Quaternion getRotation() const override;
+
+		/** @copydoc FCollider::setTransform */
+		void setTransform(const Vector3& pos, const Quaternion& rotation) override;
+
+		/** @copydoc FCollider::setIsTrigger */
+		void setIsTrigger(bool value) override;
+
+		/** @copydoc FCollider::getIsTrigger */
+		bool getIsTrigger() const override;
+
+		/** @copydoc FCollider::setIsStatic */
+		void setIsStatic(bool value) override;
+
+		/** @copydoc FCollider::getIsStatic */
+		bool getIsStatic() const override;
+
+		/** @copydoc FCollider::setContactOffset */
+		void setContactOffset(float value) override;
+
+		/** @copydoc FCollider::getContactOffset */
+		float getContactOffset() const override;
+
+		/** @copydoc FCollider::setRestOffset */
+		void setRestOffset(float value) override;
+
+		/** @copydoc FCollider::getRestOffset */
+		float getRestOffset() const override;
+
+		/** @copydoc FCollider::setMaterial */
+		void setMaterial(const HPhysicsMaterial& material) override;
+
+		/** @copydoc FCollider::getLayer */
+		UINT64 getLayer() const override;
+
+		/** @copydoc FCollider::setLayer */
+		void setLayer(UINT64 layer) override;
+
+		/** @copydoc FCollider::getCollisionReportMode */
+		CollisionReportMode getCollisionReportMode() const override;
+
+		/** @copydoc FCollider::setCollisionReportMode */
+		void setCollisionReportMode(CollisionReportMode mode) override;
+
+		/** @copydoc FCollider::_setCCD */
+		void _setCCD(bool enabled) override;
+
+		/** Gets the internal PhysX shape that represents the collider. */
+		physx::PxShape* _getShape() const { return mShape; }
+
+		/** 
+		 * Assigns a new shape the the collider. Old shape is released, and the new shape inherits any properties from the
+		 * old shape, including parent, transform, flags and other.
+		 */
+		void _setShape(physx::PxShape* shape);
+	protected:
+		/** Updates shape filter data from stored values. */
+		void updateFilter();
+
+		physx::PxShape* mShape = nullptr;
+		physx::PxRigidStatic* mStaticBody = nullptr;
+		bool mIsTrigger = false;
+		bool mIsStatic = true;
+		UINT64 mLayer = 1;
+		bool mCCD = false;
+		CollisionReportMode mCollisionReportMode = CollisionReportMode::None;
+	};
+
+	/** @} */
 }
 }

+ 39 - 36
Source/BansheePhysX/Include/BsPhysXMeshCollider.h

@@ -1,37 +1,40 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include "BsPhysXPrerequisites.h"
-#include "BsMeshCollider.h"
-#include "PxPhysics.h"
-
-namespace BansheeEngine
-{
-	/** @addtogroup PhysX
-	 *  @{
-	 */
-
-	/** PhysX implementation of a MeshCollider. */
-	class PhysXMeshCollider : public MeshCollider
-	{
-	public:
-		PhysXMeshCollider(physx::PxPhysics* physx, const Vector3& position, const Quaternion& rotation);
-		~PhysXMeshCollider();
-
-		/** @copydoc MeshCollider::setScale */
-		void setScale(const Vector3& scale) override;
-
-	private:
-		/** Returns the PhysX collider implementation common to all colliders. */
-		FPhysXCollider* getInternal() const;
-
-		/** @copydoc MeshCollider::onMeshChanged */
-		void onMeshChanged() override;
-
-		/** Applies mesh geometry using the set mesh and scale. */
-		void applyGeometry();
-	};
-
-	/** @} */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPhysXPrerequisites.h"
+#include "BsMeshCollider.h"
+#include "PxPhysics.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup PhysX
+	 *  @{
+	 */
+
+	/** PhysX implementation of a MeshCollider. */
+	class PhysXMeshCollider : public MeshCollider
+	{
+	public:
+		PhysXMeshCollider(physx::PxPhysics* physx, const Vector3& position, const Quaternion& rotation);
+		~PhysXMeshCollider();
+
+		/** @copydoc MeshCollider::setScale */
+		void setScale(const Vector3& scale) override;
+
+	private:
+		/** Returns the PhysX collider implementation common to all colliders. */
+		FPhysXCollider* getInternal() const;
+
+		/** @copydoc MeshCollider::onMeshChanged */
+		void onMeshChanged() override;
+
+		/** Applies mesh geometry using the set mesh and scale. */
+		void applyGeometry();
+
+		/** Sets new geometry to the underlying shape. Rebuilds the shape if necessary. */
+		void setGeometry(const physx::PxGeometry& geometry);
+	};
+
+	/** @} */
 }
 }

+ 36 - 0
Source/BansheePhysX/Source/BsFPhysXCollider.cpp

@@ -29,6 +29,42 @@ namespace BansheeEngine
 		mShape->release();
 		mShape->release();
 	}
 	}
 
 
+	void FPhysXCollider::_setShape(PxShape* shape)
+	{
+		if (mShape != nullptr)
+		{
+			shape->setLocalPose(mShape->getLocalPose());
+			shape->setFlags(mShape->getFlags());
+			shape->setContactOffset(mShape->getContactOffset());
+			shape->setRestOffset(mShape->getRestOffset());
+
+			UINT32 numMaterials = mShape->getNbMaterials();
+			UINT32 bufferSize = sizeof(PxMaterial*) * numMaterials;
+			PxMaterial** materials = (PxMaterial**)bs_stack_alloc(bufferSize);
+
+			mShape->getMaterials(materials, bufferSize);
+			shape->setMaterials(materials, numMaterials);
+			shape->userData = mShape->userData;
+
+			bs_stack_free(materials);
+
+			PxActor* actor = mShape->getActor();
+			if (actor != nullptr)
+			{
+				PxRigidActor* rigidActor = mShape->is<PxRigidActor>();
+				if (rigidActor != nullptr)
+				{
+					rigidActor->detachShape(*mShape);
+					rigidActor->attachShape(*shape);
+				}
+			}
+		}
+
+		mShape = shape;
+
+		updateFilter();
+	}
+
 	Vector3 FPhysXCollider::getPosition() const
 	Vector3 FPhysXCollider::getPosition() const
 	{
 	{
 		return fromPxVector(mShape->getLocalPose().p);
 		return fromPxVector(mShape->getLocalPose().p);

+ 66 - 62
Source/BansheePhysX/Source/BsPhysX.cpp

@@ -48,72 +48,76 @@ namespace BansheeEngine
 	{
 	{
 	public:
 	public:
 		void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) override
 		void reportError(PxErrorCode::Enum code, const char* message, const char* file, int line) override
-		{
+	{
+			const char* errorCode = nullptr;
+
+			UINT32 severity = 0;
+
+			if ((code & PxErrorCode::eDEBUG_INFO) != 0)
 			{
 			{
-				const char* errorCode = nullptr;
+				errorCode = "Info";
+				severity = 0;
+			}
 
 
-				UINT32 severity = 0;
-				switch (code)
-				{
-				case PxErrorCode::eNO_ERROR:
-					errorCode = "No error";
-					break;
-				case PxErrorCode::eINVALID_PARAMETER:
-					errorCode = "Invalid parameter";
-					severity = 2;
-					break;
-				case PxErrorCode::eINVALID_OPERATION:
-					errorCode = "Invalid operation";
-					severity = 2;
-					break;
-				case PxErrorCode::eOUT_OF_MEMORY:
-					errorCode = "Out of memory";
-					severity = 2;
-					break;
-				case PxErrorCode::eDEBUG_INFO:
-					errorCode = "Info";
-					break;
-				case PxErrorCode::eDEBUG_WARNING:
-					errorCode = "Warning";
-					severity = 1;
-					break;
-				case PxErrorCode::ePERF_WARNING:
-					errorCode = "Performance warning";
-					severity = 1;
-					break;
-				case PxErrorCode::eABORT:
-					errorCode = "Abort";
-					severity = 2;
-					break;
-				case PxErrorCode::eINTERNAL_ERROR:
-					errorCode = "Internal error";
-					severity = 2;
-					break;
-				case PxErrorCode::eMASK_ALL:
-				default:
-					errorCode = "Unknown error";
-					severity = 2;
-					break;
-				}
+			if((code & PxErrorCode::eINVALID_PARAMETER) != 0)
+			{
+				errorCode = "Invalid parameter";
+				severity = 1;
+			}
 
 
-				StringStream ss;
+			if ((code & PxErrorCode::eINVALID_OPERATION) != 0)
+			{
+				errorCode = "Invalid operation";
+				severity = 1;
+			}
 
 
-				switch(severity)
-				{
-				case 0:
-					ss << "PhysX info (" << errorCode << "): " << message << " at " << file << ":" << line;
-					LOGDBG(ss.str());
-					break;
-				case 1:
-					ss << "PhysX warning (" << errorCode << "): " << message << " at " << file << ":" << line;
-					LOGWRN(ss.str());
-					break;
-				case 2:
-					ss << "PhysX error (" << errorCode << "): " << message << " at " << file << ":" << line;
-					LOGERR(ss.str());
-					BS_ASSERT(false); // Halt execution on debug builds when error occurs
-					break;
-				}
+			if ((code & PxErrorCode::eDEBUG_WARNING) != 0)
+			{
+				errorCode = "Generic";
+				severity = 1;
+			}
+
+			if ((code & PxErrorCode::ePERF_WARNING) != 0)
+			{
+				errorCode = "Performance";
+				severity = 1;
+			}
+
+			if ((code & PxErrorCode::eOUT_OF_MEMORY) != 0)
+			{
+				errorCode = "Out of memory";
+				severity = 2;
+			}
+
+			if ((code & PxErrorCode::eABORT) != 0)
+			{
+				errorCode = "Abort";
+				severity = 2;
+			}
+
+			if ((code & PxErrorCode::eINTERNAL_ERROR) != 0)
+			{
+				errorCode = "Internal";
+				severity = 2;
+			}
+
+			StringStream ss;
+
+			switch(severity)
+			{
+			case 0:
+				ss << "PhysX info (" << errorCode << "): " << message << " at " << file << ":" << line;
+				LOGDBG(ss.str());
+				break;
+			case 1:
+				ss << "PhysX warning (" << errorCode << "): " << message << " at " << file << ":" << line;
+				LOGWRN(ss.str());
+				break;
+			case 2:
+				ss << "PhysX error (" << errorCode << "): " << message << " at " << file << ":" << line;
+				LOGERR(ss.str());
+				BS_ASSERT(false); // Halt execution on debug builds when error occurs
+				break;
 			}
 			}
 		}
 		}
 	};
 	};

+ 58 - 57
Source/BansheePhysX/Source/BsPhysXBoxCollider.cpp

@@ -1,58 +1,59 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#include "BsPhysXBoxCollider.h"
-#include "BsPhysX.h"
-#include "PxPhysics.h"
-#include "BsFPhysXCollider.h"
-
-using namespace physx;
-
-namespace BansheeEngine
-{
-	PhysXBoxCollider::PhysXBoxCollider(PxPhysics* physx, const Vector3& position, const Quaternion& rotation, 
-		const Vector3& extents)
-		:mExtents(extents)
-	{
-		PxBoxGeometry geometry(extents.x, extents.y, extents.z);
-
-		PxShape* shape = physx->createShape(geometry, *gPhysX().getDefaultMaterial(), true);
-		shape->setLocalPose(toPxTransform(position, rotation));
-		shape->userData = this;
-
-		mInternal = bs_new<FPhysXCollider>(shape);
-	}
-
-	PhysXBoxCollider::~PhysXBoxCollider()
-	{
-		bs_delete(mInternal);
-	}
-
-	void PhysXBoxCollider::setScale(const Vector3& scale)
-	{
-		BoxCollider::setScale(scale);
-		applyGeometry();
-	}
-
-	void PhysXBoxCollider::setExtents(const Vector3& extents)
-	{
-		mExtents = extents;
-		applyGeometry();
-	}
-
-	Vector3 PhysXBoxCollider::getExtents() const
-	{
-		return mExtents;
-	}
-
-	void PhysXBoxCollider::applyGeometry()
-	{
-		PxBoxGeometry geometry(mExtents.x * mScale.x, mExtents.y * mScale.y, mExtents.z * mScale.z);
-
-		getInternal()->_getShape()->setGeometry(geometry);
-	}
-
-	FPhysXCollider* PhysXBoxCollider::getInternal() const
-	{
-		return static_cast<FPhysXCollider*>(mInternal);
-	}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsPhysXBoxCollider.h"
+#include "BsPhysX.h"
+#include "PxPhysics.h"
+#include "BsFPhysXCollider.h"
+
+using namespace physx;
+
+namespace BansheeEngine
+{
+	PhysXBoxCollider::PhysXBoxCollider(PxPhysics* physx, const Vector3& position, const Quaternion& rotation, 
+		const Vector3& extents)
+		:mExtents(extents)
+	{
+		PxBoxGeometry geometry(extents.x, extents.y, extents.z);
+
+		PxShape* shape = physx->createShape(geometry, *gPhysX().getDefaultMaterial(), true);
+		shape->setLocalPose(toPxTransform(position, rotation));
+		shape->userData = this;
+
+		mInternal = bs_new<FPhysXCollider>(shape);
+	}
+
+	PhysXBoxCollider::~PhysXBoxCollider()
+	{
+		bs_delete(mInternal);
+	}
+
+	void PhysXBoxCollider::setScale(const Vector3& scale)
+	{
+		BoxCollider::setScale(scale);
+		applyGeometry();
+	}
+
+	void PhysXBoxCollider::setExtents(const Vector3& extents)
+	{
+		mExtents = extents;
+		applyGeometry();
+	}
+
+	Vector3 PhysXBoxCollider::getExtents() const
+	{
+		return mExtents;
+	}
+
+	void PhysXBoxCollider::applyGeometry()
+	{
+		PxBoxGeometry geometry(std::max(0.01f, mExtents.x * mScale.x), 
+			std::max(0.01f, mExtents.y * mScale.y), std::max(0.01f, mExtents.z * mScale.z));
+
+		getInternal()->_getShape()->setGeometry(geometry);
+	}
+
+	FPhysXCollider* PhysXBoxCollider::getInternal() const
+	{
+		return static_cast<FPhysXCollider*>(mInternal);
+	}
 }
 }

+ 67 - 66
Source/BansheePhysX/Source/BsPhysXCapsuleCollider.cpp

@@ -1,67 +1,68 @@
-#include "BsPhysXCapsuleCollider.h"
-#include "BsPhysX.h"
-#include "PxPhysics.h"
-#include "BsFPhysXCollider.h"
-
-using namespace physx;
-
-namespace BansheeEngine
-{
-	PhysXCapsuleCollider::PhysXCapsuleCollider(PxPhysics* physx, const Vector3& position, const Quaternion& rotation,
-		float radius, float halfHeight)
-		:mRadius(radius), mHalfHeight(halfHeight)
-	{
-		PxCapsuleGeometry geometry(radius, halfHeight);
-
-		PxShape* shape = physx->createShape(geometry, *gPhysX().getDefaultMaterial(), true);
-		shape->setLocalPose(toPxTransform(position, rotation));
-		shape->userData = this;
-
-		mInternal = bs_new<FPhysXCollider>(shape);
-	}
-
-	PhysXCapsuleCollider::~PhysXCapsuleCollider()
-	{
-		bs_delete(mInternal);
-	}
-
-	void PhysXCapsuleCollider::setScale(const Vector3& scale)
-	{
-		CapsuleCollider::setScale(scale);
-		applyGeometry();
-	}
-
-	void PhysXCapsuleCollider::setHalfHeight(float halfHeight)
-	{
-		mHalfHeight = halfHeight;
-		applyGeometry();
-	}
-
-	float PhysXCapsuleCollider::getHalfHeight() const
-	{
-		return mHalfHeight;
-	}
-
-	void PhysXCapsuleCollider::setRadius(float radius)
-	{
-		mRadius = radius;
-		applyGeometry();
-	}
-
-	float PhysXCapsuleCollider::getRadius() const
-	{
-		return mRadius;
-	}
-
-	void PhysXCapsuleCollider::applyGeometry()
-	{
-		PxCapsuleGeometry geometry(mRadius * std::max(mScale.x, mScale.z), mHalfHeight * mScale.y);
-
-		getInternal()->_getShape()->setGeometry(geometry);
-	}
-
-	FPhysXCollider* PhysXCapsuleCollider::getInternal() const
-	{
-		return static_cast<FPhysXCollider*>(mInternal);
-	}
+#include "BsPhysXCapsuleCollider.h"
+#include "BsPhysX.h"
+#include "PxPhysics.h"
+#include "BsFPhysXCollider.h"
+
+using namespace physx;
+
+namespace BansheeEngine
+{
+	PhysXCapsuleCollider::PhysXCapsuleCollider(PxPhysics* physx, const Vector3& position, const Quaternion& rotation,
+		float radius, float halfHeight)
+		:mRadius(radius), mHalfHeight(halfHeight)
+	{
+		PxCapsuleGeometry geometry(radius, halfHeight);
+
+		PxShape* shape = physx->createShape(geometry, *gPhysX().getDefaultMaterial(), true);
+		shape->setLocalPose(toPxTransform(position, rotation));
+		shape->userData = this;
+
+		mInternal = bs_new<FPhysXCollider>(shape);
+	}
+
+	PhysXCapsuleCollider::~PhysXCapsuleCollider()
+	{
+		bs_delete(mInternal);
+	}
+
+	void PhysXCapsuleCollider::setScale(const Vector3& scale)
+	{
+		CapsuleCollider::setScale(scale);
+		applyGeometry();
+	}
+
+	void PhysXCapsuleCollider::setHalfHeight(float halfHeight)
+	{
+		mHalfHeight = halfHeight;
+		applyGeometry();
+	}
+
+	float PhysXCapsuleCollider::getHalfHeight() const
+	{
+		return mHalfHeight;
+	}
+
+	void PhysXCapsuleCollider::setRadius(float radius)
+	{
+		mRadius = radius;
+		applyGeometry();
+	}
+
+	float PhysXCapsuleCollider::getRadius() const
+	{
+		return mRadius;
+	}
+
+	void PhysXCapsuleCollider::applyGeometry()
+	{
+		PxCapsuleGeometry geometry(std::max(0.01f, mRadius * std::max(mScale.x, mScale.z)), 
+			std::max(0.01f, mHalfHeight * mScale.y));
+
+		getInternal()->_getShape()->setGeometry(geometry);
+	}
+
+	FPhysXCollider* PhysXCapsuleCollider::getInternal() const
+	{
+		return static_cast<FPhysXCollider*>(mInternal);
+	}
 }
 }

+ 15 - 4
Source/BansheePhysX/Source/BsPhysXMeshCollider.cpp

@@ -39,8 +39,7 @@ namespace BansheeEngine
 	{
 	{
 		if (!mMesh.isLoaded())
 		if (!mMesh.isLoaded())
 		{
 		{
-			PxSphereGeometry geometry(0.01f); // Dummy
-			getInternal()->_getShape()->setGeometry(geometry);
+			setGeometry(PxSphereGeometry(0.01f)); // Dummy
 			return;
 			return;
 		}
 		}
 
 
@@ -52,7 +51,7 @@ namespace BansheeEngine
 			geometry.scale = PxMeshScale(toPxVector(getScale()), PxIdentity);
 			geometry.scale = PxMeshScale(toPxVector(getScale()), PxIdentity);
 			geometry.convexMesh = physxMesh->_getConvex();
 			geometry.convexMesh = physxMesh->_getConvex();
 
 
-			getInternal()->_getShape()->setGeometry(geometry);
+			setGeometry(geometry);
 		}
 		}
 		else // Triangle
 		else // Triangle
 		{
 		{
@@ -60,10 +59,22 @@ namespace BansheeEngine
 			geometry.scale = PxMeshScale(toPxVector(getScale()), PxIdentity);
 			geometry.scale = PxMeshScale(toPxVector(getScale()), PxIdentity);
 			geometry.triangleMesh = physxMesh->_getTriangle();
 			geometry.triangleMesh = physxMesh->_getTriangle();
 
 
-			getInternal()->_getShape()->setGeometry(geometry);
+			setGeometry(geometry);
 		}
 		}
 	}
 	}
 
 
+	void PhysXMeshCollider::setGeometry(const PxGeometry& geometry)
+	{
+		PxShape* shape = getInternal()->_getShape();
+		if (shape->getGeometryType() != geometry.getType())
+		{
+			PxShape* newShape = gPhysX().getPhysX()->createShape(geometry, *gPhysX().getDefaultMaterial(), true);
+			getInternal()->_setShape(newShape);
+		}
+		else
+			getInternal()->_getShape()->setGeometry(geometry);
+	}
+
 	FPhysXCollider* PhysXMeshCollider::getInternal() const
 	FPhysXCollider* PhysXMeshCollider::getInternal() const
 	{
 	{
 		return static_cast<FPhysXCollider*>(mInternal);
 		return static_cast<FPhysXCollider*>(mInternal);

+ 56 - 55
Source/BansheePhysX/Source/BsPhysXSphereCollider.cpp

@@ -1,56 +1,57 @@
-#include "BsPhysXSphereCollider.h"
-#include "BsPhysX.h"
-#include "PxPhysics.h"
-#include "BsFPhysXCollider.h"
-
-using namespace physx;
-
-namespace BansheeEngine
-{
-	PhysXSphereCollider::PhysXSphereCollider(PxPhysics* physx, const Vector3& position, const Quaternion& rotation,
-		float radius)
-		:mRadius(radius)
-	{
-		PxSphereGeometry geometry(radius);
-
-		PxShape* shape = physx->createShape(geometry, *gPhysX().getDefaultMaterial(), true);
-		shape->setLocalPose(toPxTransform(position, rotation));
-		shape->userData = this;
-
-		mInternal = bs_new<FPhysXCollider>(shape);
-	}
-
-	PhysXSphereCollider::~PhysXSphereCollider()
-	{
-		bs_delete(mInternal);
-	}
-
-	void PhysXSphereCollider::setScale(const Vector3& scale)
-	{
-		SphereCollider::setScale(scale);
-		applyGeometry();
-	}
-
-	void PhysXSphereCollider::setRadius(float radius)
-	{
-		mRadius = radius;
-		applyGeometry();
-	}
-
-	float PhysXSphereCollider::getRadius() const
-	{
-		return mRadius;
-	}
-
-	void PhysXSphereCollider::applyGeometry()
-	{
-		PxSphereGeometry geometry(mRadius * std::max(std::max(mScale.x, mScale.y), mScale.z));
-
-		getInternal()->_getShape()->setGeometry(geometry);
-	}
-
-	FPhysXCollider* PhysXSphereCollider::getInternal() const
-	{
-		return static_cast<FPhysXCollider*>(mInternal);
-	}
+#include "BsPhysXSphereCollider.h"
+#include "BsPhysX.h"
+#include "PxPhysics.h"
+#include "BsFPhysXCollider.h"
+
+using namespace physx;
+
+namespace BansheeEngine
+{
+	PhysXSphereCollider::PhysXSphereCollider(PxPhysics* physx, const Vector3& position, const Quaternion& rotation,
+		float radius)
+		:mRadius(radius)
+	{
+		PxSphereGeometry geometry(radius);
+
+		PxShape* shape = physx->createShape(geometry, *gPhysX().getDefaultMaterial(), true);
+		shape->setLocalPose(toPxTransform(position, rotation));
+		shape->userData = this;
+
+		mInternal = bs_new<FPhysXCollider>(shape);
+	}
+
+	PhysXSphereCollider::~PhysXSphereCollider()
+	{
+		bs_delete(mInternal);
+	}
+
+	void PhysXSphereCollider::setScale(const Vector3& scale)
+	{
+		SphereCollider::setScale(scale);
+		applyGeometry();
+	}
+
+	void PhysXSphereCollider::setRadius(float radius)
+	{
+		mRadius = radius;
+		applyGeometry();
+	}
+
+	float PhysXSphereCollider::getRadius() const
+	{
+		return mRadius;
+	}
+
+	void PhysXSphereCollider::applyGeometry()
+	{
+		float radius = std::max(0.01f, mRadius * std::max(std::max(mScale.x, mScale.y), mScale.z));
+		PxSphereGeometry geometry(radius);
+
+		getInternal()->_getShape()->setGeometry(geometry);
+	}
+
+	FPhysXCollider* PhysXSphereCollider::getInternal() const
+	{
+		return static_cast<FPhysXCollider*>(mInternal);
+	}
 }
 }

+ 375 - 375
Source/BansheeUtility/Include/BsMemStack.h

@@ -1,376 +1,376 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-#pragma once
-
-#include <stack>
-#include <assert.h>
-#include "BsStdHeaders.h"
-#include "BsThreadDefines.h"
-
-namespace BansheeEngine
-{
-	/** @addtogroup Memory
-	 *  @{
-	 */
-
-	 /** @cond INTERNAL */
-
-	/**
-	 * Describes a memory stack of a certain block capacity. See ::MemStack for more information.
-	 *
-	 * @tparam	BlockCapacity Minimum size of a block. Larger blocks mean less memory allocations, but also potentially
-	 *						  more wasted memory. If an allocation requests more bytes than BlockCapacity, first largest 
-	 *						  multiple is used instead.
-	 */
-	template <int BlockCapacity = 1024 * 1024>
-	class MemStackInternal
-	{
-	private:
-		/** A single block of memory of BlockCapacity size. A pointer to the first free address is stored, and a remaining 
-		 *  size. 
-		 */
-		class MemBlock
-		{
-		public:
-			MemBlock(UINT32 size)
-				:mData(nullptr), mFreePtr(0), mSize(size), 
-				mNextBlock(nullptr), mPrevBlock(nullptr)
-			{ }
-
-			~MemBlock()
-			{ }
-
-			/**
-			 * Returns the first free address and increments the free pointer. Caller needs to ensure the remaining block 
-			 * size is adequate before calling.
-			 */
-			UINT8* alloc(UINT32 amount)
-			{
-				UINT8* freePtr = &mData[mFreePtr];
-				mFreePtr += amount;
-
-				return freePtr;
-			}
-
-			/**
-			 * Deallocates the provided pointer. Deallocation must happen in opposite order from allocation otherwise 
-			 * corruption will occur.
-			 * 			
-			 * @note	Pointer to @p data isn't actually needed, but is provided for debug purposes in order to more
-			 * 			easily track out-of-order deallocations.
-			 */
-			void dealloc(UINT8* data, UINT32 amount)
-			{
-				mFreePtr -= amount;
-				assert((&mData[mFreePtr]) == data && "Out of order stack deallocation detected. Deallocations need to happen in order opposite of allocations.");
-			}
-
-			UINT8* mData;
-			UINT32 mFreePtr;
-			UINT32 mSize;
-			MemBlock* mNextBlock;
-			MemBlock* mPrevBlock;
-		};
-
-	public:
-		MemStackInternal()
-			:mFreeBlock(nullptr)
-		{
-			mFreeBlock = allocBlock(BlockCapacity);
-		}
-
-		~MemStackInternal()
-		{
-			assert(mFreeBlock->mFreePtr == 0 && "Not all blocks were released before shutting down the stack allocator.");
-
-			MemBlock* curBlock = mFreeBlock;
-			while (curBlock != nullptr)
-			{
-				MemBlock* nextBlock = curBlock->mNextBlock;
-				deallocBlock(curBlock);
-
-				curBlock = nextBlock;
-			}
-		}
-
-		/**
-		 * Allocates the given amount of memory on the stack.
-		 *
-		 * @param[in]	amount	The amount to allocate in bytes.
-		 *
-		 * @note	
-		 * Allocates the memory in the currently active block if it is large enough, otherwise a new block is allocated. 
-		 * If the allocation is larger than default block size a separate block will be allocated only for that allocation,
-		 * making it essentially a slower heap allocator.
-		 * @note			
-		 * Each allocation comes with a 4 byte overhead.
-		 */
-		UINT8* alloc(UINT32 amount)
-		{
-			amount += sizeof(UINT32);
-
-			UINT32 freeMem = mFreeBlock->mSize - mFreeBlock->mFreePtr;
-			if(amount > freeMem)
-				allocBlock(amount);
-
-			UINT8* data = mFreeBlock->alloc(amount);
-
-			UINT32* storedSize = reinterpret_cast<UINT32*>(data);
-			*storedSize = amount;
-
-			return data + sizeof(UINT32);
-		}
-
-		/** Deallocates the given memory. Data must be deallocated in opposite order then when it was allocated. */
-		void dealloc(UINT8* data)
-		{
-			data -= sizeof(UINT32);
-
-			UINT32* storedSize = reinterpret_cast<UINT32*>(data);
-			mFreeBlock->dealloc(data, *storedSize);
-
-			if (mFreeBlock->mFreePtr == 0)
-			{
-				MemBlock* emptyBlock = mFreeBlock;
-
-				if (emptyBlock->mPrevBlock != nullptr)
-					mFreeBlock = emptyBlock->mPrevBlock;
-
-				// Merge with next block
-				if (emptyBlock->mNextBlock != nullptr)
-				{
-					UINT32 totalSize = emptyBlock->mSize + emptyBlock->mNextBlock->mSize;
-
-					if (emptyBlock->mPrevBlock != nullptr)
-						emptyBlock->mPrevBlock->mNextBlock = nullptr;
-					else
-						mFreeBlock = nullptr;
-
-					deallocBlock(emptyBlock->mNextBlock);
-					deallocBlock(emptyBlock);
-
-					allocBlock(totalSize);
-				}
-			}
-		}
-
-	private:
-		MemBlock* mFreeBlock;
-
-		/** 
-		 * Allocates a new block of memory using a heap allocator. Block will never be smaller than BlockCapacity no matter 
-		 * the @p wantedSize.
-		 */
-		MemBlock* allocBlock(UINT32 wantedSize)
-		{
-			UINT32 blockSize = BlockCapacity;
-			if(wantedSize > blockSize)
-				blockSize = wantedSize;
-
-			MemBlock* newBlock = nullptr;
-			MemBlock* curBlock = mFreeBlock;
-
-			while (curBlock != nullptr)
-			{
-				MemBlock* nextBlock = curBlock->mNextBlock;
-				if (nextBlock != nullptr && nextBlock->mSize >= blockSize)
-				{
-					newBlock = nextBlock;
-					break;
-				}
-
-				curBlock = nextBlock;
-			}
-
-			if (newBlock == nullptr)
-			{
-				UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc(blockSize + sizeof(MemBlock)));
-				newBlock = new (data)MemBlock(blockSize);
-				data += sizeof(MemBlock);
-
-				newBlock->mData = data;
-				newBlock->mPrevBlock = mFreeBlock;
-
-				if (mFreeBlock != nullptr)
-				{
-					if(mFreeBlock->mNextBlock != nullptr)
-						mFreeBlock->mNextBlock->mPrevBlock = newBlock;
-
-					newBlock->mNextBlock = mFreeBlock->mNextBlock;
-					mFreeBlock->mNextBlock = newBlock;
-				}
-			}
-
-			mFreeBlock = newBlock;
-			return newBlock;
-		}
-
-		/** Deallocates a block of memory. */
-		void deallocBlock(MemBlock* block)
-		{
-			block->~MemBlock();
-			bs_free(block);
-		}
-	};
-
-	/**
-	 * One of the fastest, but also very limiting type of allocator. All deallocations must happen in opposite order from 
-	 * allocations. 
-	 * 			
-	 * @note	
-	 * It's mostly useful when you need to allocate something temporarily on the heap, usually something that gets 
-	 * allocated and freed within the same method.
-	 * @note
-	 * Each allocation comes with a pretty hefty 4 byte memory overhead, so don't use it for small allocations. 
-	 * @note
-	 * Thread safe. But you cannot allocate on one thread and deallocate on another. Threads will keep
-	 * separate stacks internally. Make sure to call beginThread()/endThread() for any thread this stack is used on.
-	 */
-	class MemStack
-	{
-	public:
-		/**
-		 * Sets up the stack with the currently active thread. You need to call this on any thread before doing any 
-		 * allocations or deallocations.
-		 */
-		static BS_UTILITY_EXPORT void beginThread();
-
-		/**
-		 * Cleans up the stack for the current thread. You may not perform any allocations or deallocations after this is 
-		 * called, unless you call beginThread again.
-		 */
-		static BS_UTILITY_EXPORT void endThread();
-
-		/** @copydoc MemoryStackInternal::alloc() */
-		static BS_UTILITY_EXPORT UINT8* alloc(UINT32 numBytes);
-
-		/** @copydoc MemoryStackInternal::dealloc() */
-		static BS_UTILITY_EXPORT void deallocLast(UINT8* data);
-
-	private:
-		static BS_THREADLOCAL MemStackInternal<1024 * 1024>* ThreadMemStack;
-	};
-
-	/** @endcond */
-
-	/** @copydoc MemoryStackInternal::alloc() */
-	BS_UTILITY_EXPORT inline void* bs_stack_alloc(UINT32 numBytes);
-
-	/**
-	 * Allocates enough memory to hold the specified type, on the stack, but does not initialize the object. 
-	 *
-	 * @see	MemoryStackInternal::alloc()
-	 */
-	template<class T>
-	T* bs_stack_alloc()
-	{
-		return (T*)MemStack::alloc(sizeof(T));
-	}
-
-	/**
-	 * Allocates enough memory to hold N objects of the specified type, on the stack, but does not initialize the objects. 
-	 *
-	 * @see	MemoryStackInternal::alloc()
-	 */
-	template<class T>
-	T* bs_stack_alloc(UINT32 count)
-	{
-		return (T*)MemStack::alloc(sizeof(T) * count);
-	}
-
-	/**
-	 * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
-	 *
-	 * @see	MemoryStackInternal::alloc()
-	 */
-	template<class T>
-	T* bs_stack_new(UINT32 count = 0)
-	{
-		T* data = bs_stack_alloc<T>(count);
-
-		for(unsigned int i = 0; i < count; i++)
-			new ((void*)&data[i]) T;
-
-		return data;
-	}
-
-	/**
-	 * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
-	 *
-	 * @see MemoryStackInternal::alloc()
-	 */
-	template<class T, class... Args>
-	T* bs_stack_new(Args &&...args, UINT32 count = 0)
-	{
-		T* data = bs_stack_alloc<T>(count);
-
-		for(unsigned int i = 0; i < count; i++)
-			new ((void*)&data[i]) T(std::forward<Args>(args)...);
-
-		return data;
-	}
-
-	/**
-	 * Destructs and deallocates last allocated entry currently located on stack.
-	 *
-	 * @see MemoryStackInternal::dealloc()
-	 */
-	template<class T>
-	void bs_stack_delete(T* data)
-	{
-		data->~T();
-
-		MemStack::deallocLast((UINT8*)data);
-	}
-
-	/**
-	 * Destructs an array of objects and deallocates last allocated entry currently located on stack.
-	 *
-	 * @see	MemoryStackInternal::dealloc()
-	 */
-	template<class T>
-	void bs_stack_delete(T* data, UINT32 count)
-	{
-		for(unsigned int i = 0; i < count; i++)
-			data[i].~T();
-
-		MemStack::deallocLast((UINT8*)data);
-	}
-
-	/** @copydoc MemoryStackInternal::dealloc() */
-	BS_UTILITY_EXPORT inline void bs_stack_free(void* data);
-
-	/** @cond INTERNAL */
-
-	/**
-	 * Allows use of a stack allocator by using normal new/delete/free/dealloc operators.
-	 * 			
-	 * @see	MemStack
-	 */
-	class StackAlloc
-	{ };
-
-	/**
-	* Specialized memory allocator implementations that allows use of a stack allocator in normal new/delete/free/dealloc 
-	* operators.
-	* 			
-	* @see MemStack
-	*/
-	template<>
-	class MemoryAllocator<StackAlloc> : public MemoryAllocatorBase
-	{
-	public:
-		static void* allocate(size_t bytes)
-		{
-			return bs_stack_alloc((UINT32)bytes);
-		}
-
-		static void free(void* ptr)
-		{
-			bs_stack_free(ptr);
-		}
-	};
-
-	/** @endcond */
-	/** @} */
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include <stack>
+#include <assert.h>
+#include "BsStdHeaders.h"
+#include "BsThreadDefines.h"
+
+namespace BansheeEngine
+{
+	/** @addtogroup Memory
+	 *  @{
+	 */
+
+	 /** @cond INTERNAL */
+
+	/**
+	 * Describes a memory stack of a certain block capacity. See ::MemStack for more information.
+	 *
+	 * @tparam	BlockCapacity Minimum size of a block. Larger blocks mean less memory allocations, but also potentially
+	 *						  more wasted memory. If an allocation requests more bytes than BlockCapacity, first largest 
+	 *						  multiple is used instead.
+	 */
+	template <int BlockCapacity = 1024 * 1024>
+	class MemStackInternal
+	{
+	private:
+		/** A single block of memory of BlockCapacity size. A pointer to the first free address is stored, and a remaining 
+		 *  size. 
+		 */
+		class MemBlock
+		{
+		public:
+			MemBlock(UINT32 size)
+				:mData(nullptr), mFreePtr(0), mSize(size), 
+				mNextBlock(nullptr), mPrevBlock(nullptr)
+			{ }
+
+			~MemBlock()
+			{ }
+
+			/**
+			 * Returns the first free address and increments the free pointer. Caller needs to ensure the remaining block 
+			 * size is adequate before calling.
+			 */
+			UINT8* alloc(UINT32 amount)
+			{
+				UINT8* freePtr = &mData[mFreePtr];
+				mFreePtr += amount;
+
+				return freePtr;
+			}
+
+			/**
+			 * Deallocates the provided pointer. Deallocation must happen in opposite order from allocation otherwise 
+			 * corruption will occur.
+			 * 			
+			 * @note	Pointer to @p data isn't actually needed, but is provided for debug purposes in order to more
+			 * 			easily track out-of-order deallocations.
+			 */
+			void dealloc(UINT8* data, UINT32 amount)
+			{
+				mFreePtr -= amount;
+				assert((&mData[mFreePtr]) == data && "Out of order stack deallocation detected. Deallocations need to happen in order opposite of allocations.");
+			}
+
+			UINT8* mData;
+			UINT32 mFreePtr;
+			UINT32 mSize;
+			MemBlock* mNextBlock;
+			MemBlock* mPrevBlock;
+		};
+
+	public:
+		MemStackInternal()
+			:mFreeBlock(nullptr)
+		{
+			mFreeBlock = allocBlock(BlockCapacity);
+		}
+
+		~MemStackInternal()
+		{
+			assert(mFreeBlock->mFreePtr == 0 && "Not all blocks were released before shutting down the stack allocator.");
+
+			MemBlock* curBlock = mFreeBlock;
+			while (curBlock != nullptr)
+			{
+				MemBlock* nextBlock = curBlock->mNextBlock;
+				deallocBlock(curBlock);
+
+				curBlock = nextBlock;
+			}
+		}
+
+		/**
+		 * Allocates the given amount of memory on the stack.
+		 *
+		 * @param[in]	amount	The amount to allocate in bytes.
+		 *
+		 * @note	
+		 * Allocates the memory in the currently active block if it is large enough, otherwise a new block is allocated. 
+		 * If the allocation is larger than default block size a separate block will be allocated only for that allocation,
+		 * making it essentially a slower heap allocator.
+		 * @note			
+		 * Each allocation comes with a 4 byte overhead.
+		 */
+		UINT8* alloc(UINT32 amount)
+		{
+			amount += sizeof(UINT32);
+
+			UINT32 freeMem = mFreeBlock->mSize - mFreeBlock->mFreePtr;
+			if(amount > freeMem)
+				allocBlock(amount);
+
+			UINT8* data = mFreeBlock->alloc(amount);
+
+			UINT32* storedSize = reinterpret_cast<UINT32*>(data);
+			*storedSize = amount;
+
+			return data + sizeof(UINT32);
+		}
+
+		/** Deallocates the given memory. Data must be deallocated in opposite order then when it was allocated. */
+		void dealloc(UINT8* data)
+		{
+			data -= sizeof(UINT32);
+
+			UINT32* storedSize = reinterpret_cast<UINT32*>(data);
+			mFreeBlock->dealloc(data, *storedSize);
+
+			if (mFreeBlock->mFreePtr == 0)
+			{
+				MemBlock* emptyBlock = mFreeBlock;
+
+				if (emptyBlock->mPrevBlock != nullptr)
+					mFreeBlock = emptyBlock->mPrevBlock;
+
+				// Merge with next block
+				if (emptyBlock->mNextBlock != nullptr)
+				{
+					UINT32 totalSize = emptyBlock->mSize + emptyBlock->mNextBlock->mSize;
+
+					if (emptyBlock->mPrevBlock != nullptr)
+						emptyBlock->mPrevBlock->mNextBlock = nullptr;
+					else
+						mFreeBlock = nullptr;
+
+					deallocBlock(emptyBlock->mNextBlock);
+					deallocBlock(emptyBlock);
+
+					allocBlock(totalSize);
+				}
+			}
+		}
+
+	private:
+		MemBlock* mFreeBlock;
+
+		/** 
+		 * Allocates a new block of memory using a heap allocator. Block will never be smaller than BlockCapacity no matter 
+		 * the @p wantedSize.
+		 */
+		MemBlock* allocBlock(UINT32 wantedSize)
+		{
+			UINT32 blockSize = BlockCapacity;
+			if(wantedSize > blockSize)
+				blockSize = wantedSize;
+
+			MemBlock* newBlock = nullptr;
+			MemBlock* curBlock = mFreeBlock;
+
+			while (curBlock != nullptr)
+			{
+				MemBlock* nextBlock = curBlock->mNextBlock;
+				if (nextBlock != nullptr && nextBlock->mSize >= blockSize)
+				{
+					newBlock = nextBlock;
+					break;
+				}
+
+				curBlock = nextBlock;
+			}
+
+			if (newBlock == nullptr)
+			{
+				UINT8* data = (UINT8*)reinterpret_cast<UINT8*>(bs_alloc(blockSize + sizeof(MemBlock)));
+				newBlock = new (data)MemBlock(blockSize);
+				data += sizeof(MemBlock);
+
+				newBlock->mData = data;
+				newBlock->mPrevBlock = mFreeBlock;
+
+				if (mFreeBlock != nullptr)
+				{
+					if(mFreeBlock->mNextBlock != nullptr)
+						mFreeBlock->mNextBlock->mPrevBlock = newBlock;
+
+					newBlock->mNextBlock = mFreeBlock->mNextBlock;
+					mFreeBlock->mNextBlock = newBlock;
+				}
+			}
+
+			mFreeBlock = newBlock;
+			return newBlock;
+		}
+
+		/** Deallocates a block of memory. */
+		void deallocBlock(MemBlock* block)
+		{
+			block->~MemBlock();
+			bs_free(block);
+		}
+	};
+
+	/**
+	 * One of the fastest, but also very limiting type of allocator. All deallocations must happen in opposite order from 
+	 * allocations. 
+	 * 			
+	 * @note	
+	 * It's mostly useful when you need to allocate something temporarily on the heap, usually something that gets 
+	 * allocated and freed within the same method.
+	 * @note
+	 * Each allocation comes with a pretty hefty 4 byte memory overhead, so don't use it for small allocations. 
+	 * @note
+	 * Thread safe. But you cannot allocate on one thread and deallocate on another. Threads will keep
+	 * separate stacks internally. Make sure to call beginThread()/endThread() for any thread this stack is used on.
+	 */
+	class MemStack
+	{
+	public:
+		/**
+		 * Sets up the stack with the currently active thread. You need to call this on any thread before doing any 
+		 * allocations or deallocations.
+		 */
+		static BS_UTILITY_EXPORT void beginThread();
+
+		/**
+		 * Cleans up the stack for the current thread. You may not perform any allocations or deallocations after this is 
+		 * called, unless you call beginThread again.
+		 */
+		static BS_UTILITY_EXPORT void endThread();
+
+		/** @copydoc MemStackInternal::alloc() */
+		static BS_UTILITY_EXPORT UINT8* alloc(UINT32 numBytes);
+
+		/** @copydoc MemStackInternal::dealloc() */
+		static BS_UTILITY_EXPORT void deallocLast(UINT8* data);
+
+	private:
+		static BS_THREADLOCAL MemStackInternal<1024 * 1024>* ThreadMemStack;
+	};
+
+	/** @endcond */
+
+	/** @copydoc MemStackInternal::alloc() */
+	BS_UTILITY_EXPORT inline void* bs_stack_alloc(UINT32 numBytes);
+
+	/**
+	 * Allocates enough memory to hold the specified type, on the stack, but does not initialize the object. 
+	 *
+	 * @see	MemStackInternal::alloc()
+	 */
+	template<class T>
+	T* bs_stack_alloc()
+	{
+		return (T*)MemStack::alloc(sizeof(T));
+	}
+
+	/**
+	 * Allocates enough memory to hold N objects of the specified type, on the stack, but does not initialize the objects. 
+	 *
+	 * @see	MemStackInternal::alloc()
+	 */
+	template<class T>
+	T* bs_stack_alloc(UINT32 count)
+	{
+		return (T*)MemStack::alloc(sizeof(T) * count);
+	}
+
+	/**
+	 * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
+	 *
+	 * @see	MemStackInternal::alloc()
+	 */
+	template<class T>
+	T* bs_stack_new(UINT32 count = 0)
+	{
+		T* data = bs_stack_alloc<T>(count);
+
+		for(unsigned int i = 0; i < count; i++)
+			new ((void*)&data[i]) T;
+
+		return data;
+	}
+
+	/**
+	 * Allocates enough memory to hold the specified type, on the stack, and constructs the object.
+	 *
+	 * @see MemStackInternal::alloc()
+	 */
+	template<class T, class... Args>
+	T* bs_stack_new(Args &&...args, UINT32 count = 0)
+	{
+		T* data = bs_stack_alloc<T>(count);
+
+		for(unsigned int i = 0; i < count; i++)
+			new ((void*)&data[i]) T(std::forward<Args>(args)...);
+
+		return data;
+	}
+
+	/**
+	 * Destructs and deallocates last allocated entry currently located on stack.
+	 *
+	 * @see MemStackInternal::dealloc()
+	 */
+	template<class T>
+	void bs_stack_delete(T* data)
+	{
+		data->~T();
+
+		MemStack::deallocLast((UINT8*)data);
+	}
+
+	/**
+	 * Destructs an array of objects and deallocates last allocated entry currently located on stack.
+	 *
+	 * @see	MemStackInternal::dealloc()
+	 */
+	template<class T>
+	void bs_stack_delete(T* data, UINT32 count)
+	{
+		for(unsigned int i = 0; i < count; i++)
+			data[i].~T();
+
+		MemStack::deallocLast((UINT8*)data);
+	}
+
+	/** @copydoc MemStackInternal::dealloc() */
+	BS_UTILITY_EXPORT inline void bs_stack_free(void* data);
+
+	/** @cond INTERNAL */
+
+	/**
+	 * Allows use of a stack allocator by using normal new/delete/free/dealloc operators.
+	 * 			
+	 * @see	MemStack
+	 */
+	class StackAlloc
+	{ };
+
+	/**
+	* Specialized memory allocator implementations that allows use of a stack allocator in normal new/delete/free/dealloc 
+	* operators.
+	* 			
+	* @see MemStack
+	*/
+	template<>
+	class MemoryAllocator<StackAlloc> : public MemoryAllocatorBase
+	{
+	public:
+		static void* allocate(size_t bytes)
+		{
+			return bs_stack_alloc((UINT32)bytes);
+		}
+
+		static void free(void* ptr)
+		{
+			bs_stack_free(ptr);
+		}
+	};
+
+	/** @endcond */
+	/** @} */
 }
 }

+ 2 - 2
Source/MBansheeEditor/Scene/Gizmos/ColliderGizmos.cs

@@ -22,7 +22,7 @@ namespace BansheeEditor
             Gizmos.Transform = so.WorldTransform;
             Gizmos.Transform = so.WorldTransform;
 
 
             Vector3 scaledExtents = collider.Extents*so.Scale;
             Vector3 scaledExtents = collider.Extents*so.Scale;
-            Gizmos.DrawWireCube(so.WorldTransform.MultiplyAffine(collider.Center), scaledExtents);
+            Gizmos.DrawWireCube(collider.Center, scaledExtents);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -40,7 +40,7 @@ namespace BansheeEditor
             Vector3 scale = so.Scale;
             Vector3 scale = so.Scale;
             float scaledRadius = collider.Radius * MathEx.Max(scale.x, scale.y, scale.z);
             float scaledRadius = collider.Radius * MathEx.Max(scale.x, scale.y, scale.z);
 
 
-            Gizmos.DrawWireSphere(so.WorldTransform.MultiplyAffine(collider.Center), scaledRadius);
+            Gizmos.DrawWireSphere(collider.Center, scaledRadius);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 73 - 71
Source/MBansheeEngine/Physics/BoxCollider.cs

@@ -1,71 +1,73 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Collider with box geometry.
-    /// </summary>
-    public sealed class BoxCollider : Collider
-    {
-        [SerializeField]
-        private Vector3 extents = Vector3.One;
-
-        /// <summary>
-        /// Extents (half size) of the geometry of the box.
-        /// </summary>
-        public Vector3 Extents
-        {
-            get { return extents; }
-            set
-            {
-                if (extents == value)
-                    return;
-
-                extents = value;
-
-                if (Native != null)
-                {
-                    Native.Extents = value;
-
-                    if (parent != null)
-                        parent.UpdateMassDistribution();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Position of the box shape, relative to the component's scene object.
-        /// </summary>
-        public Vector3 Center
-        {
-            get { return serializableData.localPosition; }
-            set
-            {
-                if (serializableData.localPosition == value)
-                    return;
-
-                serializableData.localPosition = value;
-
-                if (Native != null)
-                    UpdateTransform();
-            }
-        }
-
-        /// <summary>
-        /// Returns the native box collider wrapped by this component.
-        /// </summary>
-        private NativeBoxCollider Native
-        {
-            get { return (NativeBoxCollider)native; }
-        }
-
-        /// <inheritdoc/>
-        internal override NativeCollider CreateCollider()
-        {
-            NativeBoxCollider boxCollider = new NativeBoxCollider();
-            boxCollider.Extents = extents;
-
-            return boxCollider;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Collider with box geometry.
+    /// </summary>
+    public sealed class BoxCollider : Collider
+    {
+        [SerializeField]
+        private Vector3 extents = Vector3.One;
+
+        /// <summary>
+        /// Extents (half size) of the geometry of the box.
+        /// </summary>
+        public Vector3 Extents
+        {
+            get { return extents; }
+            set
+            {
+                value = new Vector3(MathEx.Max(value.x, 0.01f), MathEx.Max(value.y, 0.01f), MathEx.Max(value.z, 0.01f));
+
+                if (extents == value)
+                    return;
+
+                extents = value;
+
+                if (Native != null)
+                {
+                    Native.Extents = value;
+
+                    if (parent != null)
+                        parent.UpdateMassDistribution();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Position of the box shape, relative to the component's scene object.
+        /// </summary>
+        public Vector3 Center
+        {
+            get { return serializableData.localPosition; }
+            set
+            {
+                if (serializableData.localPosition == value)
+                    return;
+
+                serializableData.localPosition = value;
+
+                if (Native != null)
+                    UpdateTransform();
+            }
+        }
+
+        /// <summary>
+        /// Returns the native box collider wrapped by this component.
+        /// </summary>
+        private NativeBoxCollider Native
+        {
+            get { return (NativeBoxCollider)native; }
+        }
+
+        /// <inheritdoc/>
+        internal override NativeCollider CreateCollider()
+        {
+            NativeBoxCollider boxCollider = new NativeBoxCollider();
+            boxCollider.Extents = extents;
+
+            return boxCollider;
+        }
+    }
+}

+ 122 - 120
Source/MBansheeEngine/Physics/CapsuleCollider.cs

@@ -1,120 +1,122 @@
-//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
-//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
-namespace BansheeEngine
-{
-    /// <summary>
-    /// Collider with capsule geometry.
-    /// </summary>
-    public sealed class CapsuleCollider : Collider
-    {
-        [SerializeField]
-        private float radius = 0.2f;
-
-        [SerializeField]
-        private float halfHeight = 0.5f;
-
-        [SerializeField]
-        private Vector3 normal = Vector3.YAxis;
-
-        /// <summary>
-        /// Radius of the capsule.
-        /// </summary>
-        public float Radius
-        {
-            get { return radius; }
-            set
-            {
-                if (radius == value)
-                    return;
-
-                radius = value;
-
-                if (Native != null)
-                {
-                    Native.Radius = value;
-
-                    if (parent != null)
-                        parent.UpdateMassDistribution();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Half height of the capsule, from the origin to one of the hemispherical centers, along the normal vector.
-        /// </summary>
-        public float HalfHeight
-        {
-            get { return halfHeight; }
-            set
-            {
-                if (halfHeight == value)
-                    return;
-
-                halfHeight = value;
-
-                if (Native != null)
-                {
-                    Native.HalfHeight = value;
-
-                    if (parent != null)
-                        parent.UpdateMassDistribution();
-                }
-            }
-        }
-        
-        /// <summary>
-        /// Position of the capsule shape, relative to the component's scene object.
-        /// </summary>
-        public Vector3 Center
-        {
-            get { return serializableData.localPosition; }
-            set
-            {
-                if (serializableData.localPosition == value)
-                    return;
-
-                serializableData.localPosition = value;
-
-                if (Native != null)
-                    UpdateTransform();
-            }
-        }
-
-        /// <summary>
-        /// Normal vector of the capsule. It determines how is the capsule oriented.
-        /// </summary>
-        public Vector3 Normal
-        {
-            get { return normal; }
-            set
-            {
-                if (normal == value)
-                    return;
-
-                normal = value.Normalized;
-                serializableData.localRotation = Quaternion.FromToRotation(Vector3.XAxis, normal);
-
-                if (Native != null)
-                    UpdateTransform();
-            }
-        }
-
-        /// <summary>
-        /// Returns the native capsule collider wrapped by this component.
-        /// </summary>
-        private NativeCapsuleCollider Native
-        {
-            get { return (NativeCapsuleCollider)native; }
-        }
-
-        /// <inheritdoc/>
-        internal override NativeCollider CreateCollider()
-        {
-            NativeCapsuleCollider capsuleCollider = new NativeCapsuleCollider();
-            capsuleCollider.Radius = radius;
-            capsuleCollider.HalfHeight = halfHeight;
-
-            return capsuleCollider;
-        }
-    }
-}
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+namespace BansheeEngine
+{
+    /// <summary>
+    /// Collider with capsule geometry.
+    /// </summary>
+    public sealed class CapsuleCollider : Collider
+    {
+        [SerializeField]
+        private float radius = 0.2f;
+
+        [SerializeField]
+        private float halfHeight = 0.5f;
+
+        [SerializeField]
+        private Vector3 normal = Vector3.YAxis;
+
+        /// <summary>
+        /// Radius of the capsule.
+        /// </summary>
+        public float Radius
+        {
+            get { return radius; }
+            set
+            {
+                value = MathEx.Max(value, 0.01f);
+                if (radius == value)
+                    return;
+
+                radius = value;
+
+                if (Native != null)
+                {
+                    Native.Radius = value;
+
+                    if (parent != null)
+                        parent.UpdateMassDistribution();
+                }
+            }
+        }
+
+        /// <summary>
+        /// Half height of the capsule, from the origin to one of the hemispherical centers, along the normal vector.
+        /// </summary>
+        public float HalfHeight
+        {
+            get { return halfHeight; }
+            set
+            {
+                value = MathEx.Max(value, 0.01f);
+                if (halfHeight == value)
+                    return;
+
+                halfHeight = value;
+
+                if (Native != null)
+                {
+                    Native.HalfHeight = value;
+
+                    if (parent != null)
+                        parent.UpdateMassDistribution();
+                }
+            }
+        }
+        
+        /// <summary>
+        /// Position of the capsule shape, relative to the component's scene object.
+        /// </summary>
+        public Vector3 Center
+        {
+            get { return serializableData.localPosition; }
+            set
+            {
+                if (serializableData.localPosition == value)
+                    return;
+
+                serializableData.localPosition = value;
+
+                if (Native != null)
+                    UpdateTransform();
+            }
+        }
+
+        /// <summary>
+        /// Normal vector of the capsule. It determines how is the capsule oriented.
+        /// </summary>
+        public Vector3 Normal
+        {
+            get { return normal; }
+            set
+            {
+                if (normal == value)
+                    return;
+
+                normal = value.Normalized;
+                serializableData.localRotation = Quaternion.FromToRotation(Vector3.XAxis, normal);
+
+                if (Native != null)
+                    UpdateTransform();
+            }
+        }
+
+        /// <summary>
+        /// Returns the native capsule collider wrapped by this component.
+        /// </summary>
+        private NativeCapsuleCollider Native
+        {
+            get { return (NativeCapsuleCollider)native; }
+        }
+
+        /// <inheritdoc/>
+        internal override NativeCollider CreateCollider()
+        {
+            NativeCapsuleCollider capsuleCollider = new NativeCapsuleCollider();
+            capsuleCollider.Radius = radius;
+            capsuleCollider.HalfHeight = halfHeight;
+
+            return capsuleCollider;
+        }
+    }
+}

+ 1 - 1
Source/MBansheeEngine/Physics/NativeRigidbody.cs

@@ -190,7 +190,7 @@ namespace BansheeEngine
 
 
         public void AddCollider(Collider collider)
         public void AddCollider(Collider collider)
         {
         {
-            if (collider == null)
+            if (collider == null || collider.native == null)
                 return;
                 return;
 
 
             IntPtr colliderPtr = collider.native.GetCachedPtr();
             IntPtr colliderPtr = collider.native.GetCachedPtr();

+ 3 - 1
Source/MBansheeEngine/Physics/Rigidbody.cs

@@ -583,7 +583,9 @@ namespace BansheeEngine
                             continue;
                             continue;
 
 
                         entry.SetRigidbody(this, true);
                         entry.SetRigidbody(this, true);
-                        entry.native.Rigidbody = native;
+
+                        if(entry.native != null)
+                            entry.native.Rigidbody = native;
 
 
                         children.Add(entry);
                         children.Add(entry);
                         native.AddCollider(entry);
                         native.AddCollider(entry);

+ 1 - 0
Source/MBansheeEngine/Physics/SphereCollider.cs

@@ -18,6 +18,7 @@ namespace BansheeEngine
             get { return radius; }
             get { return radius; }
             set
             set
             {
             {
+                value = MathEx.Max(value, 0.01f);
                 if (radius == value)
                 if (radius == value)
                     return;
                     return;