BearishSun 9 лет назад
Родитель
Сommit
693d2e21ca

+ 18 - 1
Source/BansheeCore/Include/BsAnimation.h

@@ -8,6 +8,7 @@
 #include "BsSkeleton.h"
 #include "BsSkeletonMask.h"
 #include "BsVector2.h"
+#include "BsAABox.h"
 
 namespace BansheeEngine
 {
@@ -28,7 +29,8 @@ namespace BansheeEngine
 		Clean = 0,
 		Value = 1 << 0,
 		Layout = 1 << 1,
-		Skeleton = 1 << 2
+		Skeleton = 1 << 2,
+		Culling = 1 << 3
 	};
 
 	typedef Flags<AnimDirtyStateFlag> AnimDirtyState;
@@ -195,6 +197,10 @@ namespace BansheeEngine
 		AnimatedSceneObjectInfo* sceneObjectInfos;
 		Matrix4* sceneObjectTransforms;
 
+		// Culling
+		AABox mBounds;
+		bool mCullEnabled;
+
 		// Evaluation results
 		LocalSkeletonPose skeletonPose;
 		LocalSkeletonPose sceneObjectPose;
@@ -236,6 +242,15 @@ namespace BansheeEngine
 		/** Changes the speed for all animations. The default value is 1.0f. Use negative values to play-back in reverse. */
 		void setSpeed(float speed);
 
+		/** Sets bounds that will be used for animation culling, if enabled. Bounds must be in world space. */
+		void setBounds(const AABox& bounds);
+
+		/** 
+		 * When enabled, animation that is not in a view of any camera will not be evaluated. View determination is done by
+		 * checking the bounds provided in setBounds().
+		 */
+		void setCulling(bool cull);
+
 		/** 
 		 * Plays the specified animation clip. 
 		 *
@@ -411,6 +426,8 @@ namespace BansheeEngine
 		UINT64 mId;
 		AnimWrapMode mDefaultWrapMode;
 		float mDefaultSpeed;
+		AABox mBounds;
+		bool mCull;
 		AnimDirtyState mDirty;
 
 		SPtr<Skeleton> mSkeleton;

+ 3 - 1
Source/BansheeCore/Include/BsAnimationManager.h

@@ -5,6 +5,7 @@
 #include "BsCorePrerequisites.h"
 #include "BsModule.h"
 #include "BsCoreThread.h"
+#include "BsConvexVolume.h"
 
 namespace BansheeEngine
 {
@@ -62,7 +63,7 @@ namespace BansheeEngine
 		 * Synchronizes animation data to the animation thread, advances animation time and queues new animation evaluation
 		 * task.
 		 */
-		void postUpdate();
+		void postUpdate(const Vector<ConvexVolume>& cullFrustums);
 
 		/** 
 		 * Gets skeleton poses required by the renderer to display all the animations. This will block the animation thread
@@ -102,6 +103,7 @@ namespace BansheeEngine
 
 		// Animation thread
 		Vector<SPtr<AnimationProxy>> mProxies;
+		Vector<ConvexVolume> mCullFrustums;
 		RendererAnimationData mAnimData[CoreThread::NUM_SYNC_BUFFERS];
 
 		UINT32 mPoseReadBufferIdx;

+ 22 - 2
Source/BansheeCore/Source/BsAnimation.cpp

@@ -31,7 +31,7 @@ namespace BansheeEngine
 
 	AnimationProxy::AnimationProxy(UINT64 id)
 		: id(id), layers(nullptr), numLayers(0), numSceneObjects(0), sceneObjectInfos(nullptr)
-		, sceneObjectTransforms(nullptr), genericCurveOutputs(nullptr)
+		, sceneObjectTransforms(nullptr), mCullEnabled(true), numGenericCurves(0), genericCurveOutputs(nullptr)
 	{ }
 
 	AnimationProxy::~AnimationProxy()
@@ -517,7 +517,7 @@ namespace BansheeEngine
 	}
 
 	Animation::Animation()
-		: mDefaultWrapMode(AnimWrapMode::Loop), mDefaultSpeed(1.0f), mDirty(AnimDirtyStateFlag::Skeleton)
+		: mDefaultWrapMode(AnimWrapMode::Loop), mDefaultSpeed(1.0f), mCull(true), mDirty(AnimDirtyStateFlag::Skeleton)
 		, mGenericCurveValuesValid(false)
 	{
 		mId = AnimationManager::instance().registerAnimation(this);
@@ -565,6 +565,20 @@ namespace BansheeEngine
 		mDirty |= AnimDirtyStateFlag::Value;
 	}
 
+	void Animation::setBounds(const AABox& bounds)
+	{
+		mBounds = bounds;
+
+		mDirty |= AnimDirtyStateFlag::Culling;
+	}
+
+	void Animation::setCulling(bool cull)
+	{
+		mCull = cull;
+
+		mDirty |= AnimDirtyStateFlag::Culling;
+	}
+
 	void Animation::play(const HAnimationClip& clip)
 	{
 		AnimationClipInfo* clipInfo = addClip(clip, (UINT32)-1);
@@ -1045,6 +1059,12 @@ namespace BansheeEngine
 			else if (mDirty.isSet(AnimDirtyStateFlag::Value))
 				mAnimProxy->updateValues(mClipInfos);
 
+			if(mDirty.isSet(AnimDirtyStateFlag::Culling))
+			{
+				mAnimProxy->mCullEnabled = mCull;
+				mAnimProxy->mBounds = mBounds;
+			}
+
 			// Check if there are dirty transforms
 			if(!didFullRebuild)
 			{

+ 20 - 1
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -5,6 +5,7 @@
 #include "BsAnimationClip.h"
 #include "BsTaskScheduler.h"
 #include "BsTime.h"
+#include "BsCoreSceneManager.h"
 
 namespace BansheeEngine
 {
@@ -49,7 +50,7 @@ namespace BansheeEngine
 		mWorkerRunning = false;
 	}
 
-	void AnimationManager::postUpdate()
+	void AnimationManager::postUpdate(const Vector<ConvexVolume>& cullFrustums)
 	{
 		if (mPaused)
 			return;
@@ -73,6 +74,8 @@ namespace BansheeEngine
 			mProxies.push_back(anim.second->mAnimProxy);
 		}
 
+		mCullFrustums = cullFrustums;
+
 		// Make sure thread finishes writing all changes to the anim proxies as they will be read by the animation thread
 		std::atomic_thread_fence(std::memory_order_release);
 		
@@ -108,6 +111,22 @@ namespace BansheeEngine
 		UINT32 curBoneIdx = 0;
 		for(auto& anim : mProxies)
 		{
+			if(anim->mCullEnabled)
+			{
+				bool isVisible = false;
+				for(auto& frustum : mCullFrustums)
+				{
+					if(frustum.intersects(anim->mBounds))
+					{
+						isVisible = true;
+						break;
+					}
+				}
+
+				if (!isVisible)
+					continue;
+			}
+
 			if (anim->skeleton != nullptr)
 			{
 				UINT32 numBones = anim->skeleton->getNumBones();

+ 1 - 1
Source/BansheeCore/Source/BsCoreApplication.cpp

@@ -224,7 +224,7 @@ namespace BansheeEngine
 			PROFILE_CALL(gCoreSceneManager()._update(), "SceneManager");
 			gAudio()._update();
 			gPhysics().update();
-			AnimationManager::instance().postUpdate();
+			AnimationManager::instance().postUpdate({}); // TODO
 
 			// Update plugins
 			for (auto& pluginUpdateFunc : mPluginUpdateFunctions)

+ 44 - 1
Source/BansheeEngine/Include/BsCAnimation.h

@@ -74,6 +74,27 @@ namespace BansheeEngine
 		/** @copydoc Animation::setState */
 		void setState(const HAnimationClip& clip, AnimationClipState state);
 
+		/** Sets bounds that will be used for animation and mesh culling. Only relevant if setUseBounds() is set to true. */
+		void setBounds(const AABox& bounds);
+
+		/** Gets animation bounds. @see setBounds. */
+		const AABox& getBounds() const { return mBounds; }
+
+		/** 
+		 * Determines should animation bounds be used for visibility determination (culling). If false the bounds of the
+         * mesh attached to the relevant CRenderable component will be used instead.
+		 */
+		void setUseBounds(bool enable);
+
+		/** Checks whether animation bounds are enabled. @see setUseBounds. */
+		bool getUseBounds() const { return mUseBounds; }
+
+		/** Enables or disables culling of the animation when out of view. Culled animation will not be evaluated. */
+		void setEnableCull(bool enable);
+
+		/** Checks whether the animation will be evaluated when it is out of view. */
+		bool getEnableCull() const { return mEnableCull; }
+
 		/** Triggered whenever an animation event is reached. */
 		Event<void(const HAnimationClip&, const String&)> onEventTriggered;
 
@@ -96,6 +117,21 @@ namespace BansheeEngine
 		/** Called whenever the bone name the Bone component points to changes. */
 		void _notifyBoneChanged(const HBone& bone);
 
+		/** 
+		 * Registers a Renderable component with the animation, should be called whenever a Renderable component is added
+		 * to the same scene object as this component.
+		 */
+		void _registerRenderable(const HRenderable& renderable);
+
+		/** 
+		 * Removes renderable from the animation component. Should be called when a Renderable component is removed from
+		 * this scene object.
+		 */
+		void _unregisterRenderable();
+
+		/** Re-applies the bounds to the internal animation object, and the relevant renderable object if one exists. */
+		void _updateBounds(bool updateRenderable = true);
+
 		/** @} */
 
 		/************************************************************************/
@@ -115,6 +151,9 @@ namespace BansheeEngine
 
 		/** @copydoc Component::onEnabled() */
 		void onEnabled() override;
+
+		/** @copydoc Component::onTransformChanged() */
+		void onTransformChanged(TransformChangedFlags flags) override;
     protected:
 		using Component::destroyInternal;
 
@@ -137,10 +176,14 @@ namespace BansheeEngine
 		Vector<HBone> findChildBones();
 
 		SPtr<Animation> mInternal;
+		HRenderable mAnimatedRenderable;
 
 		HAnimationClip mDefaultClip;
 		AnimWrapMode mWrapMode;
 		float mSpeed;
+		bool mEnableCull;
+		bool mUseBounds;
+		AABox mBounds;
 
 		Vector<SceneObjectMappingInfo> mMappingInfos;
 
@@ -153,7 +196,7 @@ namespace BansheeEngine
 		RTTITypeBase* getRTTI() const override;
 
 	protected:
-		CAnimation() {} // Serialization only
+		CAnimation(); // Serialization only
      };
 
 	 /** @} */

+ 3 - 0
Source/BansheeEngine/Include/BsCAnimationRTTI.h

@@ -20,6 +20,9 @@ namespace BansheeEngine
 			BS_RTTI_MEMBER_REFL(mDefaultClip, 0)
 			BS_RTTI_MEMBER_PLAIN(mWrapMode, 1)
 			BS_RTTI_MEMBER_PLAIN(mSpeed, 2)
+			BS_RTTI_MEMBER_PLAIN(mEnableCull, 3)
+			BS_RTTI_MEMBER_PLAIN(mUseBounds, 4)
+			BS_RTTI_MEMBER_PLAIN(mBounds, 5)
 		BS_END_RTTI_MEMBERS
 	public:
 		CAnimationRTTI()

+ 7 - 3
Source/BansheeEngine/Include/BsCRenderable.h

@@ -22,7 +22,7 @@ namespace BansheeEngine
 	{
 	public:
 		/** @copydoc Renderable::setMesh */
-		void setMesh(HMesh mesh) { mInternal->setMesh(mesh); }
+		void setMesh(HMesh mesh);
 
 		/** @copydoc Renderable::setMaterial */
 		void setMaterial(UINT32 idx, HMaterial material) { mInternal->setMaterial(idx, material); }
@@ -55,13 +55,17 @@ namespace BansheeEngine
 		/** Returns the internal renderable that is used for majority of operations by this component. */
 		SPtr<Renderable> _getRenderable() const { return mInternal; }
 
-		/** Attaches an animation that will be used for animating the renderable's mesh. */
-		void _setAnimation(const SPtr<Animation>& animation);
+		/** Registers an Animation component that will be used for animating the renderable's mesh. */
+		void _registerAnimation(const HAnimation& animation);
+
+		/** Removes the Animation component, making the renderable rendered as a static object. */
+		void _unregisterAnimation();
 
 		/** @} */
 
 	private:
 		mutable SPtr<Renderable> mInternal;
+		HAnimation mAnimation;
 
 		/************************************************************************/
 		/* 							COMPONENT OVERRIDES                    		*/

+ 16 - 1
Source/BansheeEngine/Include/BsRenderable.h

@@ -81,6 +81,20 @@ namespace BansheeEngine
 		/**	Sets whether the object should be rendered or not. */
 		void setIsActive(bool active);
 
+		/** 
+		 * Sets bounds that will be used when determining if object is visible. Only relevant if setUseOverrideBounds() is
+		 * set to true.
+		 *
+		 * @param[in]	bounds	Bounds in local space.
+		 */
+		void setOverrideBounds(const AABox& bounds);
+
+		/**
+		 * Enables or disables override bounds. When enabled the bounds provided to setOverrideBounds() will be used for
+		 * determining object visibility, otherwise the bounds from the object's mesh will be used. Disabled by default.
+		 */
+		void setUseOverrideBounds(bool enable);
+
 		/**
 		 * Gets the layer bitfield that controls whether a renderable is considered visible in a specific camera. 
 		 * Renderable layer must match camera layer in order for the camera to render the component.
@@ -131,7 +145,8 @@ namespace BansheeEngine
 		MeshType mMesh;
 		Vector<MaterialType> mMaterials;
 		UINT64 mLayer;
-		Vector<AABox> mWorldBounds;
+		AABox mOverrideBounds;
+		bool mUseOverrideBounds;
 		Vector3 mPosition;
 		Matrix4 mTransform;
 		Matrix4 mTransformNoScale;

+ 113 - 14
Source/BansheeEngine/Source/BsCAnimation.cpp

@@ -10,9 +10,15 @@ using namespace std::placeholders;
 
 namespace BansheeEngine
 {
+	CAnimation::CAnimation()
+		:mWrapMode(AnimWrapMode::Loop), mSpeed(1.0f), mEnableCull(true), mUseBounds(false)
+	{ }
+
 	CAnimation::CAnimation(const HSceneObject& parent)
-		: Component(parent), mWrapMode(AnimWrapMode::Loop), mSpeed(1.0f)
+		: Component(parent), mWrapMode(AnimWrapMode::Loop), mSpeed(1.0f), mEnableCull(true), mUseBounds(false)
 	{
+		mNotifyFlags = TCF_Transform;
+
 		setName("Animation");
 	}
 
@@ -106,6 +112,44 @@ namespace BansheeEngine
 			return mInternal->setState(clip, state);
 	}
 
+	void CAnimation::setBounds(const AABox& bounds)
+	{
+		mBounds = bounds;
+
+		if(mUseBounds)
+		{
+			if(mAnimatedRenderable != nullptr)
+			{
+				SPtr<Renderable> renderable = mAnimatedRenderable->_getRenderable();
+				if (renderable != nullptr)
+					renderable->setOverrideBounds(bounds);
+
+				if(mInternal != nullptr)
+				{
+					AABox bounds = mBounds;
+					bounds.transformAffine(SO()->getWorldTfrm());
+
+					mInternal->setBounds(bounds);
+				}
+			}
+		}
+	}
+
+	void CAnimation::setUseBounds(bool enable)
+	{
+		mUseBounds = enable;
+
+		_updateBounds();
+	}
+
+	void CAnimation::setEnableCull(bool enable)
+	{
+		mEnableCull = enable;
+
+		if (mInternal != nullptr)
+			mInternal->setCulling(enable);
+	}
+
 	void CAnimation::onInitialized()
 	{
 		
@@ -126,6 +170,15 @@ namespace BansheeEngine
 		restoreInternal();
 	}
 
+	void CAnimation::onTransformChanged(TransformChangedFlags flags)
+	{
+		if (!SO()->getActive())
+			return;
+
+		if ((flags & (TCF_Transform)) != 0)
+			_updateBounds(false);
+	}
+
 	void CAnimation::restoreInternal()
 	{
 		if (mInternal == nullptr)
@@ -134,31 +187,27 @@ namespace BansheeEngine
 			mInternal->onEventTriggered.connect(std::bind(&CAnimation::eventTriggered, this, _1, _2));
 		}
 
+		mAnimatedRenderable = SO()->getComponent<CRenderable>();
+
 		mInternal->setWrapMode(mWrapMode);
 		mInternal->setSpeed(mSpeed);
+		mInternal->setCulling(mEnableCull);
+
+		_updateBounds();
 
 		if (mDefaultClip.isLoaded())
 			mInternal->play(mDefaultClip);
 
 		setBoneMappings();
 
-		HRenderable renderableComponent = SO()->getComponent<CRenderable>();
-		if (renderableComponent == nullptr)
-			return;
-
-		renderableComponent->_setAnimation(mInternal);
+		if (mAnimatedRenderable != nullptr)
+			mAnimatedRenderable->_registerAnimation(mThisHandle);
 	}
 
 	void CAnimation::destroyInternal()
 	{
-		HRenderable renderableComponent = SO()->getComponent<CRenderable>();
-		if (renderableComponent == nullptr)
-			return;
-
-		SPtr<Renderable> renderable = renderableComponent->_getRenderable();
-
-		if (renderable != nullptr)
-			renderable->setAnimation(nullptr);
+		if (mAnimatedRenderable != nullptr)
+			mAnimatedRenderable->_unregisterAnimation();
 
 		// This should release the last reference and destroy the internal listener
 		mInternal = nullptr;
@@ -213,6 +262,56 @@ namespace BansheeEngine
 		}
 	}
 
+	void CAnimation::_registerRenderable(const HRenderable& renderable)
+	{
+		mAnimatedRenderable = renderable;
+
+		_updateBounds();
+	}
+
+	void CAnimation::_unregisterRenderable()
+	{
+		mAnimatedRenderable = nullptr;
+	}
+
+	void CAnimation::_updateBounds(bool updateRenderable)
+	{
+		SPtr<Renderable> renderable;
+		if (updateRenderable && mAnimatedRenderable != nullptr)
+			renderable = mAnimatedRenderable->_getRenderable();
+
+		if (mUseBounds)
+		{
+			if (renderable != nullptr)
+			{
+				renderable->setUseOverrideBounds(true);
+				renderable->setOverrideBounds(mBounds);
+			}
+
+			if (mInternal != nullptr)
+			{
+				AABox bounds = mBounds;
+				bounds.transformAffine(SO()->getWorldTfrm());
+
+				mInternal->setBounds(bounds);
+			}
+		}
+		else
+		{
+			if (renderable != nullptr)
+				renderable->setUseOverrideBounds(false);
+
+			if (mInternal != nullptr)
+			{
+				AABox bounds;
+				if (mAnimatedRenderable != nullptr)
+					bounds = mAnimatedRenderable->getBounds().getBox();
+
+				mInternal->setBounds(bounds);
+			}
+		}
+	}
+
 	void CAnimation::setBoneMappings()
 	{
 		mMappingInfos.clear();

+ 38 - 8
Source/BansheeEngine/Source/BsCRenderable.cpp

@@ -17,6 +17,14 @@ namespace BansheeEngine
 		setName("Renderable");
 	}
 
+	void CRenderable::setMesh(HMesh mesh)
+	{
+		mInternal->setMesh(mesh);
+
+		if (mAnimation != nullptr)
+			mAnimation->_updateBounds(false);
+	}
+
 	void CRenderable::onInitialized()
 	{
 		// If mInternal already exists this means this object was deserialized,
@@ -28,9 +36,12 @@ namespace BansheeEngine
 
 		gSceneManager()._registerRenderable(mInternal, sceneObject());
 
-		HAnimation animationComponent = SO()->getComponent<CAnimation>();
-		if (animationComponent != nullptr)
-			_setAnimation(animationComponent->_getInternal());
+		mAnimation = SO()->getComponent<CAnimation>();
+		if (mAnimation != nullptr)
+		{
+			_registerAnimation(mAnimation);
+			mAnimation->_registerRenderable(mThisHandle);
+		}
 	}
 
 	Bounds CRenderable::getBounds() const
@@ -46,15 +57,31 @@ namespace BansheeEngine
 		return true;
 	}
 
-	void CRenderable::_setAnimation(const SPtr<Animation>& animation)
+	void CRenderable::_registerAnimation(const HAnimation& animation)
 	{
+		mAnimation = animation;
+
 		if (mInternal != nullptr)
 		{
-			mInternal->setAnimation(animation);
+			mInternal->setAnimation(animation->_getInternal());
 
-			// Need to update transform because animated renderables handle local transforms through bones, so it shouldn't
-			// be included in the renderable's transform.
-			mInternal->_updateTransform(mThisHandle, true);
+			// Need to update transform because animated renderables handle local transforms through bones, so it
+			// shouldn't be included in the renderable's transform.
+			mInternal->_updateTransform(SO(), true);
+		}
+	}
+
+	void CRenderable::_unregisterAnimation()
+	{
+		mAnimation = nullptr;
+
+		if(mInternal != nullptr)
+		{
+			mInternal->setAnimation(nullptr);
+
+			// Need to update transform because animated renderables handle local transforms through bones, so it
+			// shouldn't be included in the renderable's transform.
+			mInternal->_updateTransform(SO(), true);
 		}
 	}
 
@@ -65,6 +92,9 @@ namespace BansheeEngine
 
 	void CRenderable::onDestroyed()
 	{
+		if (mAnimation != nullptr)
+			mAnimation->_unregisterRenderable();
+
 		gSceneManager()._unregisterRenderable(mInternal);
 	}
 

+ 47 - 5
Source/BansheeEngine/Source/BsRenderable.cpp

@@ -26,7 +26,8 @@ namespace BansheeEngine
 
 	template<bool Core>
 	TRenderable<Core>::TRenderable()
-		:mLayer(1), mTransform(Matrix4::IDENTITY), mTransformNoScale(Matrix4::IDENTITY), mIsActive(true)
+		: mLayer(1), mUseOverrideBounds(false), mTransform(Matrix4::IDENTITY), mTransformNoScale(Matrix4::IDENTITY)
+		, mIsActive(true)
 	{
 		mMaterials.resize(1);
 	}
@@ -123,6 +124,25 @@ namespace BansheeEngine
 		_markCoreDirty();
 	}
 
+	template<bool Core>
+	void TRenderable<Core>::setOverrideBounds(const AABox& bounds)
+	{
+		mOverrideBounds = bounds;
+
+		if(mUseOverrideBounds)
+			_markCoreDirty();
+	}
+
+	template<bool Core>
+	void TRenderable<Core>::setUseOverrideBounds(bool enable)
+	{
+		if (mUseOverrideBounds == enable)
+			return;
+
+		mUseOverrideBounds = enable;
+		_markCoreDirty();
+	}
+
 	template class TRenderable < false >;
 	template class TRenderable < true >;
 
@@ -146,6 +166,16 @@ namespace BansheeEngine
 
 	Bounds RenderableCore::getBounds() const
 	{
+		if (mUseOverrideBounds)
+		{
+			Sphere sphere(mOverrideBounds.getCenter(), mOverrideBounds.getRadius());
+
+			Bounds bounds(mOverrideBounds, sphere);
+			bounds.transformAffine(mTransform);
+
+			return bounds;
+		}
+
 		SPtr<MeshCore> mesh = getMesh();
 
 		if (mesh == nullptr)
@@ -168,7 +198,6 @@ namespace BansheeEngine
 	{
 		char* dataPtr = (char*)data.getBuffer();
 
-		mWorldBounds.clear();
 		mMaterials.clear();
 
 		UINT32 numMaterials = 0;
@@ -176,7 +205,8 @@ namespace BansheeEngine
 		bool oldIsActive = mIsActive;
 
 		dataPtr = rttiReadElem(mLayer, dataPtr);
-		dataPtr = rttiReadElem(mWorldBounds, dataPtr);
+		dataPtr = rttiReadElem(mOverrideBounds, dataPtr);
+		dataPtr = rttiReadElem(mUseOverrideBounds, dataPtr);
 		dataPtr = rttiReadElem(numMaterials, dataPtr);
 		dataPtr = rttiReadElem(mTransform, dataPtr);
 		dataPtr = rttiReadElem(mTransformNoScale, dataPtr);
@@ -238,6 +268,16 @@ namespace BansheeEngine
 
 	Bounds Renderable::getBounds() const
 	{
+		if(mUseOverrideBounds)
+		{
+			Sphere sphere(mOverrideBounds.getCenter(), mOverrideBounds.getRadius());
+
+			Bounds bounds(mOverrideBounds, sphere);
+			bounds.transformAffine(mTransform);
+
+			return bounds;
+		}
+
 		HMesh mesh = getMesh();
 
 		if (!mesh.isLoaded())
@@ -335,7 +375,8 @@ namespace BansheeEngine
 			animationId = (UINT64)-1;
 
 		UINT32 size = rttiGetElemSize(mLayer) + 
-			rttiGetElemSize(mWorldBounds) + 
+			rttiGetElemSize(mOverrideBounds) + 
+			rttiGetElemSize(mUseOverrideBounds) +
 			rttiGetElemSize(numMaterials) + 
 			rttiGetElemSize(mTransform) +
 			rttiGetElemSize(mTransformNoScale) +
@@ -349,7 +390,8 @@ namespace BansheeEngine
 		UINT8* data = allocator->alloc(size);
 		char* dataPtr = (char*)data;
 		dataPtr = rttiWriteElem(mLayer, dataPtr);
-		dataPtr = rttiWriteElem(mWorldBounds, dataPtr);
+		dataPtr = rttiWriteElem(mOverrideBounds, dataPtr);
+		dataPtr = rttiWriteElem(mUseOverrideBounds, dataPtr);
 		dataPtr = rttiWriteElem(numMaterials, dataPtr);
 		dataPtr = rttiWriteElem(mTransform, dataPtr);
 		dataPtr = rttiWriteElem(mTransformNoScale, dataPtr);

+ 44 - 1
Source/MBansheeEditor/Inspectors/AnimationInspector.cs

@@ -17,6 +17,10 @@ namespace BansheeEditor
         private GUIResourceField animationClipField = new GUIResourceField(typeof(AnimationClip), new LocEdString("Clip"));
         private GUIEnumField wrapModeField = new GUIEnumField(typeof(AnimWrapMode), new LocEdString("Wrap mode"));
         private GUIFloatField speedField = new GUIFloatField(new LocEdString("Speed"));
+        private GUIToggleField cullingField = new GUIToggleField(new LocEdString("Culling"));
+        private GUIToggleField overrideBoundsField = new GUIToggleField(new LocEdString("Override bounds"));
+        private GUIVector3Field centerField = new GUIVector3Field(new LocEdString("Center"));
+        private GUIVector3Field sizeField = new GUIVector3Field(new LocEdString("Size"));
         private InspectableState modifyState;
 
         /// <inheritdoc/>
@@ -35,6 +39,10 @@ namespace BansheeEditor
             animationClipField.Value = animation.DefaultClip;
             wrapModeField.Value = (ulong)animation.WrapMode;
             speedField.Value = animation.Speed;
+            cullingField.Value = animation.Cull;
+            overrideBoundsField.Value = animation.UseBounds;
+            centerField.Value = animation.Bounds.Center;
+            sizeField.Value = animation.Bounds.Size;
 
             InspectableState oldState = modifyState;
             if (modifyState.HasFlag(InspectableState.Modified))
@@ -73,10 +81,45 @@ namespace BansheeEditor
             speedField.OnChanged += x => { animation.Speed = x; MarkAsModified(); };
             speedField.OnConfirmed += ConfirmModify;
             speedField.OnFocusLost += ConfirmModify;
-            
+
+            cullingField.OnChanged += x => { animation.Cull = x; MarkAsModified(); ConfirmModify(); };
+            overrideBoundsField.OnChanged += x => { animation.UseBounds = x; MarkAsModified(); ConfirmModify(); };
+            centerField.OnChanged += x =>
+            {
+                AABox bounds = animation.Bounds;
+                Vector3 min = x - bounds.Size*0.5f;
+                Vector3 max = x + bounds.Size*0.5f;
+
+                animation.Bounds = new AABox(min, max);
+                MarkAsModified();
+            };
+            centerField.OnConfirmed += ConfirmModify;
+            centerField.OnFocusLost += ConfirmModify;
+
+            sizeField.OnChanged += x =>
+            {
+                AABox bounds = animation.Bounds;
+                Vector3 min = bounds.Center - x * 0.5f;
+                Vector3 max = bounds.Center + x * 0.5f;
+
+                animation.Bounds = new AABox(min, max);
+                MarkAsModified();
+            };
+            sizeField.OnConfirmed += ConfirmModify;
+            sizeField.OnFocusLost += ConfirmModify;
+
             Layout.AddElement(animationClipField);
             Layout.AddElement(wrapModeField);
             Layout.AddElement(speedField);
+            Layout.AddElement(cullingField);
+            Layout.AddElement(overrideBoundsField);
+
+            GUILayoutX boundsLayout = Layout.AddLayoutX();
+            boundsLayout.AddElement(new GUILabel(new LocEdString("Bounds"), GUIOption.FixedWidth(100)));
+
+            GUILayoutY boundsContent = boundsLayout.AddLayoutY();
+            boundsContent.AddElement(centerField);
+            boundsContent.AddElement(sizeField);
         }
 
         /// <summary>

+ 1 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -52,6 +52,7 @@
     <Compile Include="Utility\SerializedObject.cs" />
     <Compile Include="Windows\AboutBox.cs" />
     <Compile Include="Windows\AnimationWindow.cs" />
+    <Compile Include="Windows\Animation\AnimationGizmo.cs" />
     <Compile Include="Windows\Animation\EditorAnimInfo.cs" />
     <Compile Include="Windows\Animation\FieldSelectionWindow.cs" />
     <Compile Include="Windows\Animation\GUIAnimEvents.cs" />

+ 35 - 0
Source/MBansheeEditor/Windows/Animation/AnimationGizmo.cs

@@ -0,0 +1,35 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup Gizmos
+     *  @{
+     */
+
+    /// <summary>
+    /// Handles drawing of gizmos for the <see cref="Animation"/> component.
+    /// </summary>
+    internal class AnimationGizmo
+    {
+        /// <summary>
+        /// Method called by the runtime when gizmos are meant to be drawn.
+        /// </summary>
+        /// <param name="animation">Animation component to draw gizmo for.</param>
+        [DrawGizmo(DrawGizmoFlags.Selected | DrawGizmoFlags.ParentSelected)]
+        private static void DrawGizmo(Animation animation)
+        {
+            SceneObject so = animation.SceneObject;
+
+            Gizmos.Color = Color.Green;
+            Gizmos.Transform = Matrix4.TRS(so.Position, so.Rotation, Vector3.One);
+
+            AABox bounds = animation.Bounds;
+            Vector3 scaledSize = bounds.Size * so.Scale;
+            Gizmos.DrawWireCube(bounds.Center, scaledSize * 0.5f);
+        }
+    }
+
+    /** @} */
+}

+ 231 - 99
Source/MBansheeEngine/Animation/Animation.cs

@@ -11,91 +11,6 @@ namespace BansheeEngine
      *  @{
      */
 
-    /// <summary>
-    /// Determines how an animation clip behaves when it reaches the end.
-    /// </summary>
-    public enum AnimWrapMode // Note: Must match C++ enum AnimWrapMode
-    {
-        /// <summary>
-        /// Loop around to the beginning/end when the last/first frame is reached.
-        /// </summary>
-        Loop,
-        /// <summary>
-        /// Clamp to end/beginning, keeping the last/first frame active.
-        /// </summary>
-	    Clamp
-    }
-
-    /// <summary>
-    /// Represents an animation clip used in 1D blending. Each clip has a position on the number line.
-    /// </summary>
-    public class BlendClipInfo
-    {
-        public AnimationClip clip;
-        public float position;
-    }
-
-    /// <summary>
-    /// Defines a 1D blend where two animation clips are blended between each other using linear interpolation.
-    /// </summary>
-    public class Blend1DInfo
-    {
-        public BlendClipInfo[] clips;
-    }
-
-    /// <summary>
-    /// Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation.
-    /// </summary>
-    public class Blend2DInfo
-    {
-        public AnimationClip topLeftClip;
-        public AnimationClip topRightClip;
-        public AnimationClip botLeftClip;
-        public AnimationClip botRightClip;
-    }
-
-    /// <summary>
-    /// Contains information about a currently playing animation clip.
-    /// </summary>
-    [StructLayout(LayoutKind.Sequential), SerializeObject]
-    public struct AnimationClipState // Note: Must match C++ struct AnimationClipState
-    {
-        /// <summary>
-        /// Layer the clip is playing on. Multiple clips can be played simulatenously on different layers.
-        /// </summary>
-        public int layer;
-
-        /// <summary>
-        /// Current time the animation is playing from.
-        /// </summary>
-        public float time;
-
-        /// <summary>
-        /// Speed at which the animation is playing.
-        /// </summary>
-        public float speed;
-
-        /// <summary>
-        /// Determines how much of an influence does the clip have on the final pose.
-        /// </summary>
-        public float weight;
-
-        /// <summary>
-        /// Determines what happens to other animation clips when a new clip starts playing.
-        /// </summary>
-        public AnimWrapMode wrapMode;
-
-        /// <summary>
-        /// Initializes the state with default values.
-        /// </summary>
-        public void InitDefault()
-        {
-            speed = 1.0f;
-            weight = 1.0f;
-            wrapMode = AnimWrapMode.Loop;
-        }
-    }
-
     /// <summary>
     /// Handles animation playback. Takes one or multiple animation clips as input and evaluates them every animation update
     /// tick depending on set properties.The evaluated data is used by the core thread for skeletal animation, by the sim
@@ -111,6 +26,7 @@ namespace BansheeEngine
         private FloatCurvePropertyInfo[] floatProperties;
         private List<SceneObjectMappingInfo> mappingInfo = new List<SceneObjectMappingInfo>();
         private AnimationClip primaryClip;
+        private Renderable animatedRenderable;
 
         /// <summary>
         /// Contains mapping for a suffix used by property paths used for curve identifiers, to their index and type.
@@ -200,6 +116,63 @@ namespace BansheeEngine
             }
         }
 
+        /// <summary>
+        /// Sets bounds that will be used for animation and mesh culling. Only relevant if <see cref="UseBounds"/> is set
+        /// to true.
+        /// </summary>
+        public AABox Bounds
+        {
+            get { return serializableData.bounds; }
+            set
+            {
+                serializableData.bounds = value;
+
+                if (serializableData.useBounds)
+                {
+                    if (animatedRenderable != null && animatedRenderable.Native != null)
+                        animatedRenderable.Native.OverrideBounds = value;
+
+                    if (_native != null)
+                    {
+                        AABox bounds = serializableData.bounds;
+                        bounds.TransformAffine(SceneObject.WorldTransform);
+
+                        _native.Bounds = bounds;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        /// Determines should animation bounds be used for visibility determination (culling). If false the bounds of the
+        /// mesh attached to the relevant <see cref="Renderable"/> component will be used instead.
+        /// </summary>
+        public bool UseBounds
+        {
+            get { return serializableData.useBounds; }
+            set
+            {
+                serializableData.useBounds = value;
+
+                UpdateBounds();
+            }
+        }
+
+        /// <summary>
+        /// If true, the animation will not be evaluated when it is out of view.
+        /// </summary>
+        public bool Cull
+        {
+            get { return serializableData.cull; }
+            set
+            {
+                serializableData.cull = value;
+
+                if (_native != null)
+                    _native.Cull = value;
+            }
+        }
+
         /// <summary>
         /// Plays the specified animation clip. 
         /// </summary>
@@ -539,6 +512,11 @@ namespace BansheeEngine
             }
         }
 
+        private void OnInitialize()
+        {
+            NotifyFlags = TransformChangedFlags.Transform;
+        }
+
         private void OnEnable()
         {
             RestoreNative();
@@ -565,9 +543,14 @@ namespace BansheeEngine
             _native = new NativeAnimation();
             _native.OnEventTriggered += EventTriggered;
 
+            animatedRenderable = SceneObject.GetComponent<Renderable>();
+
             // Restore saved values after reset
             _native.WrapMode = serializableData.wrapMode;
             _native.Speed = serializableData.speed;
+            _native.Cull = serializableData.cull;
+
+            UpdateBounds();
 
             if (serializableData.defaultClip != null)
                 _native.Play(serializableData.defaultClip);
@@ -579,11 +562,8 @@ namespace BansheeEngine
             SetBoneMappings();
             UpdateSceneObjectMapping();
 
-            Renderable renderable = SceneObject.GetComponent<Renderable>();
-            if (renderable == null)
-                return;
-
-            renderable.NativeAnimation = _native;
+            if(animatedRenderable != null)
+                animatedRenderable.RegisterAnimation(this);
         }
 
         /// <summary>
@@ -591,14 +571,8 @@ namespace BansheeEngine
         /// </summary>
         private void DestroyNative()
         {
-            Renderable renderableComponent = SceneObject.GetComponent<Renderable>();
-            if (renderableComponent != null)
-            {
-                NativeRenderable renderable = renderableComponent.Native;
-
-                if (renderable != null)
-                    renderable.Animation = null;
-            }
+            if (animatedRenderable != null)
+                animatedRenderable.UnregisterAnimation();
 
             if (_native != null)
             {
@@ -611,6 +585,15 @@ namespace BansheeEngine
             floatProperties = null;
         }
 
+        private void OnTransformChanged(TransformChangedFlags flags)
+        {
+            if (!SceneObject.Active)
+                return;
+
+            if ((flags & (TransformChangedFlags.Transform)) != 0)
+                UpdateBounds(false);
+        }
+
         /// <summary>
         /// Finds any curves that affect a transform of a specific scene object, and ensures that animation properly updates
         /// those transforms. This does not include curves referencing bones.
@@ -701,7 +684,6 @@ namespace BansheeEngine
             if (_native == null)
                 return;
 
-            SceneObject newSO = null;
             for (int i = 0; i < mappingInfo.Count; i++)
             {
                 if (mappingInfo[i].bone == bone)
@@ -787,6 +769,67 @@ namespace BansheeEngine
             return bones.ToArray();
         }
 
+
+        /// <summary>
+        /// Re-applies the bounds to the internal animation object, and the relevant renderable object if one exists.
+        /// </summary>
+        internal void UpdateBounds(bool updateRenderable = true)
+        {
+            NativeRenderable renderable = null;
+            if (updateRenderable && animatedRenderable != null)
+                renderable = animatedRenderable.Native;
+
+            if (serializableData.useBounds)
+            {
+                if (renderable != null)
+                {
+                    renderable.UseOverrideBounds = true;
+                    renderable.OverrideBounds = serializableData.bounds;
+                }
+
+                if (_native != null)
+                {
+                    AABox bounds = serializableData.bounds;
+                    bounds.TransformAffine(SceneObject.WorldTransform);
+
+                    _native.Bounds = bounds;
+                }
+            }
+            else
+            {
+                if (renderable != null)
+                    renderable.UseOverrideBounds = false;
+
+                if (_native != null)
+                {
+                    AABox bounds = new AABox();
+                    if (animatedRenderable != null)
+                        bounds = animatedRenderable.Bounds.Box;
+
+                    _native.Bounds = bounds;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Registers an <see cref="Renderable"/> component with the animation. Rendering will be affected by the animation.
+        /// </summary>
+        /// <param name="renderable">Component that was added</param>
+        internal void RegisterRenderable(Renderable renderable)
+        {
+            animatedRenderable = renderable;
+
+            UpdateBounds();
+        }
+
+        /// <summary>
+        /// Removes renderable from the animation component. Rendering will no longer be affected by animation.
+        /// </summary>
+        internal void UnregisterRenderable()
+        {
+            animatedRenderable = null;
+        }
+
         /// <summary>
         /// Builds a list of properties that will be animated using float animation curves.
         /// </summary>
@@ -949,6 +992,9 @@ namespace BansheeEngine
             public AnimationClip defaultClip;
             public AnimWrapMode wrapMode = AnimWrapMode.Loop;
             public float speed = 1.0f;
+            public AABox bounds;
+            public bool useBounds;
+            public bool cull = true;
         }
 
         /// <summary>
@@ -986,5 +1032,91 @@ namespace BansheeEngine
         }
     }
 
+
+    /// <summary>
+    /// Determines how an animation clip behaves when it reaches the end.
+    /// </summary>
+    public enum AnimWrapMode // Note: Must match C++ enum AnimWrapMode
+    {
+        /// <summary>
+        /// Loop around to the beginning/end when the last/first frame is reached.
+        /// </summary>
+        Loop,
+        /// <summary>
+        /// Clamp to end/beginning, keeping the last/first frame active.
+        /// </summary>
+	    Clamp
+    }
+
+    /// <summary>
+    /// Represents an animation clip used in 1D blending. Each clip has a position on the number line.
+    /// </summary>
+    public class BlendClipInfo
+    {
+        public AnimationClip clip;
+        public float position;
+    }
+
+    /// <summary>
+    /// Defines a 1D blend where two animation clips are blended between each other using linear interpolation.
+    /// </summary>
+    public class Blend1DInfo
+    {
+        public BlendClipInfo[] clips;
+    }
+
+    /// <summary>
+    /// Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation.
+    /// </summary>
+    public class Blend2DInfo
+    {
+        public AnimationClip topLeftClip;
+        public AnimationClip topRightClip;
+        public AnimationClip botLeftClip;
+        public AnimationClip botRightClip;
+    }
+
+    /// <summary>
+    /// Contains information about a currently playing animation clip.
+    /// </summary>
+    [StructLayout(LayoutKind.Sequential), SerializeObject]
+    public struct AnimationClipState // Note: Must match C++ struct AnimationClipState
+    {
+        /// <summary>
+        /// Layer the clip is playing on. Multiple clips can be played simulatenously on different layers.
+        /// </summary>
+        public int layer;
+
+        /// <summary>
+        /// Current time the animation is playing from.
+        /// </summary>
+        public float time;
+
+        /// <summary>
+        /// Speed at which the animation is playing.
+        /// </summary>
+        public float speed;
+
+        /// <summary>
+        /// Determines how much of an influence does the clip have on the final pose.
+        /// </summary>
+        public float weight;
+
+        /// <summary>
+        /// Determines what happens to other animation clips when a new clip starts playing.
+        /// </summary>
+        public AnimWrapMode wrapMode;
+
+        /// <summary>
+        /// Initializes the state with default values.
+        /// </summary>
+        public void InitDefault()
+        {
+            speed = 1.0f;
+            weight = 1.0f;
+            wrapMode = AnimWrapMode.Loop;
+        }
+    }
+
     /** @} */
 }

+ 16 - 0
Source/MBansheeEngine/Animation/Interop/NativeAnimation.cs

@@ -28,6 +28,16 @@ namespace BansheeEngine
             get { return Internal_GetNumClips(mCachedPtr); }
         }
 
+        public AABox Bounds
+        {
+            set { Internal_SetBounds(mCachedPtr, ref value); }
+        }
+
+        public bool Cull
+        {
+            set { Internal_SetCull(mCachedPtr, value); }
+        }
+
         public Action<AnimationClip, string> OnEventTriggered;
 
         public void Play(AnimationClip clip)
@@ -204,6 +214,12 @@ namespace BansheeEngine
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_UnmapSceneObject(IntPtr thisPtr, IntPtr sceneObjectPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetBounds(IntPtr thisPtr, ref AABox bounds);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetCull(IntPtr thisPtr, bool cull);
     }
 
     /** @endcond */

+ 16 - 0
Source/MBansheeEngine/Interop/NativeRenderable.cs

@@ -42,6 +42,16 @@ namespace BansheeEngine
             return new Bounds(box, sphere);
         }
 
+        internal AABox OverrideBounds
+        {
+            set { Internal_SetOverrideBounds(mCachedPtr, ref value); }
+        }
+
+        internal bool UseOverrideBounds
+        {
+            set { Internal_SetUseOverrideBounds(mCachedPtr, value); }
+        }
+
         internal UInt64 Layers
         {
             get { return Internal_GetLayers(mCachedPtr); }
@@ -146,6 +156,12 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetMaterials(IntPtr thisPtr, Material[] materials);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetOverrideBounds(IntPtr thisPtr, ref AABox box);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetUseOverrideBounds(IntPtr thisPtr, bool enable);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_OnDestroy(IntPtr thisPtr);
     }

+ 52 - 22
Source/MBansheeEngine/Rendering/Renderable.cs

@@ -16,6 +16,7 @@ namespace BansheeEngine
     public sealed class Renderable : Component
     {
         private NativeRenderable _native;
+        private Animation animation;
 
         [SerializeField]
         private SerializableData serializableData = new SerializableData();
@@ -28,24 +29,6 @@ namespace BansheeEngine
             get { return _native; }
         }
 
-        /// <summary>
-        /// Attaches an animation that will be used for animating the renderable's mesh.
-        /// </summary>
-        internal NativeAnimation NativeAnimation
-        {
-            set
-            {
-                if (_native != null)
-                {
-                    _native.Animation = value;
-
-                    // Need to update transform because animated renderables handle local transforms through bones, so it
-                    // shouldn't be included in the renderable's transform.
-                    _native.UpdateTransform(SceneObject, true);
-                }
-            }
-        }
-
         /// <summary>
         /// Mesh to render. 
         /// </summary>
@@ -65,6 +48,9 @@ namespace BansheeEngine
                 int numToCopy = MathEx.Min(newMaterials.Length, serializableData.materials.Length);
                 Array.Copy(serializableData.materials, newMaterials, numToCopy);
                 serializableData.materials = newMaterials;
+
+                if (animation != null)
+                    animation.UpdateBounds(false);
             }
         }
 
@@ -133,6 +119,16 @@ namespace BansheeEngine
             get { return _native.GetBounds(SceneObject); }
         }
 
+        private void OnInitialize()
+        {
+            animation = SceneObject.GetComponent<Animation>();
+            if (animation != null)
+            {
+                RegisterAnimation(animation);
+                animation.RegisterRenderable(this);
+            }
+        }
+
         private void OnReset()
         {
             if (_native != null)
@@ -147,10 +143,6 @@ namespace BansheeEngine
                 _native.Materials = serializableData.materials;
 
             _native.Layers = serializableData.layers;
-
-            Animation animation = SceneObject.GetComponent<Animation>();
-            if (animation != null)
-                NativeAnimation = animation.Native;
         }
 
         private void OnUpdate()
@@ -160,9 +152,47 @@ namespace BansheeEngine
 
         private void OnDestroy()
         {
+            if (animation != null)
+                animation.UnregisterRenderable();
+
             _native.OnDestroy();
         }
 
+        /// <summary>
+        /// Registers an <see cref="Animation"/> component with the renderable. Rendering will be affected by the animation.
+        /// </summary>
+        /// <param name="animation">Component that was added</param>
+        internal void RegisterAnimation(Animation animation)
+        {
+            this.animation = animation;
+
+            if (_native != null)
+            {
+                _native.Animation = animation.Native;
+
+                // Need to update transform because animated renderables handle local transforms through bones, so it
+                // shouldn't be included in the renderable's transform.
+                _native.UpdateTransform(SceneObject, true);
+            }
+        }
+
+        /// <summary>
+        /// Removes animation from the renderable. Rendering will no longer be affected by animation.
+        /// </summary>
+        internal void UnregisterAnimation()
+        {
+            animation = null;
+
+            if (_native != null)
+            {
+                _native.Animation = null;
+
+                // Need to update transform because animated renderables handle local transforms through bones, so it
+                // shouldn't be included in the renderable's transform.
+                _native.UpdateTransform(SceneObject, true);
+            }
+        }
+
         /// <inheritdoc/>
         protected internal override bool CalculateBounds(out AABox box, out Sphere sphere)
         {

+ 3 - 0
Source/SBansheeEngine/Include/BsScriptAnimation.h

@@ -60,6 +60,9 @@ namespace BansheeEngine
 		static void internal_MapCurveToSceneObject(ScriptAnimation* thisPtr, MonoString* curve, ScriptSceneObject* so);
 		static void internal_UnmapSceneObject(ScriptAnimation* thisPtr, ScriptSceneObject* so);
 
+		static void internal_SetBounds(ScriptAnimation* thisPtr, AABox* bounds);
+		static void internal_SetCull(ScriptAnimation* thisPtr, bool cull);
+
 		static bool internal_GetGenericCurveValue(ScriptAnimation* thisPtr, UINT32 curveIdx, float* value);
 
 		typedef void(__stdcall *OnEventTriggeredThunkDef) (MonoObject*, MonoObject*, MonoString*, MonoException**);

+ 2 - 0
Source/SBansheeEngine/Include/BsScriptRenderable.h

@@ -49,6 +49,8 @@ namespace BansheeEngine
 		static void internal_SetLayers(ScriptRenderable* thisPtr, UINT64 layers);
 		static void internal_SetMaterial(ScriptRenderable* thisPtr, ScriptMaterial* material, int index);
 		static void internal_SetMaterials(ScriptRenderable* thisPtr, MonoArray* materials);
+		static void internal_SetOverrideBounds(ScriptRenderable* thisPtr, AABox* box);
+		static void internal_SetUseOverrideBounds(ScriptRenderable* thisPtr, bool enable);
 		static void internal_OnDestroy(ScriptRenderable* thisPtr);
 	};
 

+ 13 - 0
Source/SBansheeEngine/Source/BsScriptAnimation.cpp

@@ -54,6 +54,9 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_MapCurveToSceneObject", &ScriptAnimation::internal_MapCurveToSceneObject);
 		metaData.scriptClass->addInternalCall("Internal_UnmapSceneObject", &ScriptAnimation::internal_UnmapSceneObject);
 
+		metaData.scriptClass->addInternalCall("Internal_SetBounds", &ScriptAnimation::internal_SetBounds);
+		metaData.scriptClass->addInternalCall("Internal_SetCull", &ScriptAnimation::internal_SetCull);
+
 		metaData.scriptClass->addInternalCall("Internal_GetGenericCurveValue", &ScriptAnimation::internal_GetGenericCurveValue);
 
 		sOnEventTriggeredThunk = (OnEventTriggeredThunkDef)metaData.scriptClass->getMethod("Internal_OnEventTriggered", 2)->getThunk();
@@ -198,6 +201,16 @@ namespace BansheeEngine
 		thisPtr->getInternal()->unmapSceneObject(soHandle);
 	}
 
+	void ScriptAnimation::internal_SetBounds(ScriptAnimation* thisPtr, AABox* bounds)
+	{
+		thisPtr->getInternal()->setBounds(*bounds);
+	}
+
+	void ScriptAnimation::internal_SetCull(ScriptAnimation* thisPtr, bool cull)
+	{
+		thisPtr->getInternal()->setCulling(cull);
+	}
+
 	MonoField* ScriptBlendClipInfo::clipField = nullptr;
 	MonoField* ScriptBlendClipInfo::positionField = nullptr;
 

+ 12 - 0
Source/SBansheeEngine/Source/BsScriptRenderable.cpp

@@ -38,6 +38,8 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetLayers", &ScriptRenderable::internal_SetLayers);
 		metaData.scriptClass->addInternalCall("Internal_SetMaterial", &ScriptRenderable::internal_SetMaterial);
 		metaData.scriptClass->addInternalCall("Internal_SetMaterials", &ScriptRenderable::internal_SetMaterials);
+		metaData.scriptClass->addInternalCall("Internal_SetOverrideBounds", &ScriptRenderable::internal_SetOverrideBounds);
+		metaData.scriptClass->addInternalCall("Internal_SetUseOverrideBounds", &ScriptRenderable::internal_SetUseOverrideBounds);
 		metaData.scriptClass->addInternalCall("Internal_OnDestroy", &ScriptRenderable::internal_OnDestroy);
 	}
 
@@ -137,6 +139,16 @@ namespace BansheeEngine
 		thisPtr->getInternal()->setMaterial(index, nativeMaterial);
 	}
 
+	void ScriptRenderable::internal_SetOverrideBounds(ScriptRenderable* thisPtr, AABox* box)
+	{
+		thisPtr->getInternal()->setOverrideBounds(*box);
+	}
+
+	void ScriptRenderable::internal_SetUseOverrideBounds(ScriptRenderable* thisPtr, bool enable)
+	{
+		thisPtr->getInternal()->setUseOverrideBounds(enable);
+	}
+
 	void ScriptRenderable::internal_OnDestroy(ScriptRenderable* thisPtr)
 	{
 		thisPtr->destroy();