Jelajahi Sumber

Ability to set scene object mobility, ensuring static and immovable objects can be better optimized

BearishSun 8 tahun lalu
induk
melakukan
6c825a8337

+ 17 - 0
Source/BansheeCore/Include/BsCommonTypes.h

@@ -546,6 +546,23 @@ namespace bs
 	typedef Flags<RenderSurfaceMaskBits> RenderSurfaceMask;
 	BS_FLAGS_OPERATORS(RenderSurfaceMaskBits);
 
+	/** 
+	 * Controls what kind of mobility restrictions a scene object has. This is used primarily as a performance hint to
+	 * other systems. Generally the more restricted the mobility the higher performance can be achieved.
+	 */
+	enum class BS_SCRIPT_EXPORT() ObjectMobility
+	{
+		/** Scene object can be moved and has no mobility restrictions. */
+		Movable,
+		/** 
+		 * Scene object isn't allowed to be moved but is allowed to be visually changed in other ways (e.g. changing the
+		 * displayed mesh or light intensity (depends on attached components).
+		 */
+		Immovable,
+		/** Scene object isn't allowed to be moved nor is it allowed to be visually changed. Object must be fully static. */
+		Static
+	};
+
 	/**	Texture addressing mode, per component. */
 	struct UVWAddressingMode
 	{

+ 2 - 1
Source/BansheeCore/Include/BsGameObject.h

@@ -16,7 +16,8 @@ namespace bs
 	{
 		TCF_None = 0x00, /**< Component will not be notified about any events relating to the transform. */
 		TCF_Transform = 0x01, /**< Component will be notified when the its position, rotation or scale has changed. */
-		TCF_Parent = 0x02 /**< Component will be notified when its parent changes. */
+		TCF_Parent = 0x02, /**< Component will be notified when its parent changes. */
+		TCF_Mobility = 0x04 /**< Component will be notified when mobility state changes. */
 	};
 
 	/** @} */

+ 16 - 1
Source/BansheeCore/Include/BsLight.h

@@ -28,7 +28,8 @@ namespace bs
 	enum class LightDirtyFlag
 	{
 		Transform = 0x01,
-		Everything = 0x02
+		Everything = 0x02,
+		Mobility = 0x04
 	};
 
 	/** @} */
@@ -161,6 +162,19 @@ namespace bs
 		/**	Sets whether the light should be rendered or not. */
 		void setIsActive(bool active) { mIsActive = active; _markCoreDirty(); }
 
+		/**
+		 * Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+		 * with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+		 * the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+		 * set. By default scene object's mobility is unrestricted.
+		 */
+		void setMobility(ObjectMobility mobility);
+
+		/** 
+		 * Gets the mobility setting for this scene object. See setMobility(); 
+		 */
+		ObjectMobility getMobility() const { return mMobility; }
+
 		/** 
 		 * Marks the simulation thread object as dirty and notifies the system its data should be synced with its core 
 		 * thread counterpart. 
@@ -188,6 +202,7 @@ namespace bs
 		bool mIsActive; /**< Whether the light should be rendered or not. */
 		Sphere mBounds; /**< Sphere that bounds the light area of influence. */
 		bool mAutoAttenuation; /**< Determines is attenuation radius is automatically determined. */
+		ObjectMobility mMobility; /**< Determines if there are any restrictions placed on light movement. */
 	};
 
 	/** @} */

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

@@ -21,7 +21,8 @@ namespace bs
 	enum class RenderableDirtyFlag
 	{
 		Transform = 0x01,
-		Everything = 0x02
+		Everything = 0x02,
+		Mobility = 0x04
 	};
 
 	/** Type of animation that can be applied to a renderable object. */
@@ -93,6 +94,19 @@ namespace bs
 		/**	Sets whether the object should be rendered or not. */
 		void setIsActive(bool active);
 
+		/**
+		 * Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+		 * with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+		 * the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+		 * set. By default scene object's mobility is unrestricted.
+		 */
+		void setMobility(ObjectMobility mobility);
+
+		/** 
+		 * Gets the mobility setting for this scene object. See setMobility(); 
+		 */
+		ObjectMobility getMobility() const { return mMobility; }
+	
 		/** 
 		 * Sets bounds that will be used when determining if object is visible. Only relevant if setUseOverrideBounds() is
 		 * set to true.
@@ -164,6 +178,7 @@ namespace bs
 		Matrix4 mTransformNoScale;
 		bool mIsActive;
 		RenderableAnimType mAnimType;
+		ObjectMobility mMobility;
 	};
 
 	/** @} */

+ 15 - 1
Source/BansheeCore/Include/BsSceneObject.h

@@ -361,7 +361,7 @@ namespace bs
 		/** 
 		 * Notifies components and child scene object that a transform has been changed.  
 		 * 
-		 * @param	flags	Specifies in what way was the transform changed.
+		 * @param	flags		Specifies in what way was the transform changed.
 		 */
 		void notifyTransformChanged(TransformChangedFlags flags) const;
 
@@ -455,6 +455,19 @@ namespace bs
 		 */
 		bool getActive(bool self = false);
 
+		/**
+		 * Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+		 * with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+		 * the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+		 * set. By default scene object's mobility is unrestricted.
+		 */
+		void setMobility(ObjectMobility mobility);
+
+		/** 
+		 * Gets the mobility setting for this scene object. See setMobility(); 
+		 */
+		ObjectMobility getMobility() const { return mMobility; }
+
 		/**
 		 * Makes a deep copy of this object.
 		 * 			
@@ -468,6 +481,7 @@ namespace bs
 		Vector<HSceneObject> mChildren;
 		bool mActiveSelf;
 		bool mActiveHierarchy;
+		ObjectMobility mMobility;
 
 		/**
 		 * Internal version of setParent() that allows you to set a null parent.

+ 4 - 0
Source/BansheeCore/Include/BsSceneObjectRTTI.h

@@ -78,6 +78,9 @@ namespace bs
 
 		UINT32& getPrefabHash(SceneObject* obj) { return obj->mPrefabHash; }
 		void setPrefabHash(SceneObject* obj, UINT32& value) { obj->mPrefabHash = value; }
+
+		ObjectMobility& getMobility(SceneObject* obj) { return obj->mMobility; }
+		void setMobility(SceneObject* obj, ObjectMobility& value) { obj->mMobility = value; }
 	public:
 		SceneObjectRTTI()
 		{
@@ -93,6 +96,7 @@ namespace bs
 			addPlainField("mRotation", 7, &SceneObjectRTTI::getRotation, &SceneObjectRTTI::setRotation);
 			addPlainField("mScale", 8, &SceneObjectRTTI::getScale, &SceneObjectRTTI::setScale);
 			addPlainField("mActiveSelf", 9, &SceneObjectRTTI::getActive, &SceneObjectRTTI::setActive);
+			addPlainField("mMobility", 10, &SceneObjectRTTI::getMobility, &SceneObjectRTTI::setMobility);
 		}
 
 		void onDeserializationStarted(IReflectable* obj, const UnorderedMap<String, UINT64>& params) override

+ 23 - 9
Source/BansheeCore/Source/BsLight.cpp

@@ -12,7 +12,7 @@ namespace bs
 	LightBase::LightBase()
 		: mPosition(BsZero), mRotation(BsIdentity), mType(LightType::Radial), mCastsShadows(false), mColor(Color::White)
 		, mAttRadius(10.0f), mSourceRadius(0.0f), mIntensity(5.0f), mSpotAngle(45), mSpotFalloffAngle(35.0f)
-		, mIsActive(true), mAutoAttenuation(true)
+		, mIsActive(true), mAutoAttenuation(true), mMobility(ObjectMobility::Movable)
 	{
 		updateAttenuationRange();
 	}
@@ -21,7 +21,7 @@ namespace bs
 		Degree spotAngle, Degree spotFalloffAngle)
 		: mPosition(BsZero), mRotation(BsIdentity), mType(type), mCastsShadows(castsShadows), mColor(color)
 		, mAttRadius(attRadius), mSourceRadius(srcRadius), mIntensity(intensity), mSpotAngle(spotAngle)
-		, mSpotFalloffAngle(spotFalloffAngle), mIsActive(true), mAutoAttenuation(true)
+		, mSpotFalloffAngle(spotFalloffAngle), mIsActive(true), mAutoAttenuation(true), mMobility(ObjectMobility::Movable)
 	{
 		updateAttenuationRange();
 	}
@@ -165,6 +165,13 @@ namespace bs
 		}
 	}
 
+	void LightBase::setMobility(ObjectMobility mobility)
+	{
+		mMobility = mobility;
+
+		_markCoreDirty(LightDirtyFlag::Mobility);
+	}
+
 	Light::Light()
 		:mLastUpdateHash(0)
 	{
@@ -233,6 +240,7 @@ namespace bs
 		size += rttiGetElemSize(mIsActive);
 		size += rttiGetElemSize(getCoreDirtyFlags());
 		size += rttiGetElemSize(mBounds);
+		size += rttiGetElemSize(mMobility);
 
 		UINT8* buffer = allocator->alloc(size);
 
@@ -251,6 +259,7 @@ namespace bs
 		dataPtr = rttiWriteElem(mIsActive, dataPtr);
 		dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
 		dataPtr = rttiWriteElem(mBounds, dataPtr);
+		dataPtr = rttiWriteElem(mMobility, dataPtr);
 
 		return CoreSyncData(buffer, size);
 	}
@@ -328,15 +337,11 @@ namespace bs
 		dataPtr = rttiReadElem(mIsActive, dataPtr);
 		dataPtr = rttiReadElem(dirtyFlags, dataPtr);
 		dataPtr = rttiReadElem(mBounds, dataPtr);
+		dataPtr = rttiReadElem(mMobility, dataPtr);
 
 		updateBounds();
 
-		if (dirtyFlags == (UINT32)LightDirtyFlag::Transform)
-		{
-			if (mIsActive)
-				gRenderer()->notifyLightUpdated(this);
-		}
-		else
+		if((dirtyFlags & (UINT32)LightDirtyFlag::Everything) != 0)
 		{
 			if (oldIsActive != mIsActive)
 			{
@@ -360,6 +365,15 @@ namespace bs
 				gRenderer()->notifyLightAdded(this);
 			}
 		}
+		else if((dirtyFlags & (UINT32)LightDirtyFlag::Mobility) != 0)
+		{
+			gRenderer()->notifyLightRemoved(this);
+			gRenderer()->notifyLightAdded(this);
+		}
+		else if ((dirtyFlags & (UINT32)LightDirtyFlag::Transform) != 0)
+		{
+			if (mIsActive)
+				gRenderer()->notifyLightUpdated(this);
+		}
 	}
-
 }}

+ 23 - 7
Source/BansheeCore/Source/BsRenderable.cpp

@@ -27,7 +27,7 @@ namespace bs
 	template<bool Core>
 	TRenderable<Core>::TRenderable()
 		: mLayer(1), mUseOverrideBounds(false), mPosition(BsZero), mTransform(BsIdentity), mTransformNoScale(BsIdentity)
-		, mIsActive(true), mAnimType(RenderableAnimType::None)
+		, mIsActive(true), mAnimType(RenderableAnimType::None), mMobility(ObjectMobility::Movable)
 	{
 		mMaterials.resize(1);
 	}
@@ -124,6 +124,14 @@ namespace bs
 		_markCoreDirty();
 	}
 
+	template <bool Core>
+	void TRenderable<Core>::setMobility(ObjectMobility mobility)
+	{
+		mMobility = mobility;
+
+		_markCoreDirty(RenderableDirtyFlag::Mobility);
+	}
+
 	template<bool Core>
 	void TRenderable<Core>::setOverrideBounds(const AABox& bounds)
 	{
@@ -314,6 +322,7 @@ namespace bs
 			rttiGetElemSize(mIsActive) +
 			rttiGetElemSize(animationId) +
 			rttiGetElemSize(mAnimType) + 
+			rttiGetElemSize(mMobility) +
 			rttiGetElemSize(getCoreDirtyFlags()) +
 			sizeof(SPtr<ct::Mesh>) +
 			numMaterials * sizeof(SPtr<ct::Material>);
@@ -330,6 +339,7 @@ namespace bs
 		dataPtr = rttiWriteElem(mIsActive, dataPtr);
 		dataPtr = rttiWriteElem(animationId, dataPtr);
 		dataPtr = rttiWriteElem(mAnimType, dataPtr);
+		dataPtr = rttiWriteElem(mMobility, dataPtr);
 		dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
 
 		SPtr<ct::Mesh>* mesh = new (dataPtr) SPtr<ct::Mesh>();
@@ -600,6 +610,7 @@ namespace bs
 		dataPtr = rttiReadElem(mIsActive, dataPtr);
 		dataPtr = rttiReadElem(mAnimationId, dataPtr);
 		dataPtr = rttiReadElem(mAnimType, dataPtr);
+		dataPtr = rttiReadElem(mMobility, dataPtr);
 		dataPtr = rttiReadElem(dirtyFlags, dataPtr);
 
 		SPtr<Mesh>* mesh = (SPtr<Mesh>*)dataPtr;
@@ -615,12 +626,7 @@ namespace bs
 			dataPtr += sizeof(SPtr<Material>);
 		}
 
-		if (dirtyFlags == (UINT32)RenderableDirtyFlag::Transform)
-		{
-			if (mIsActive)
-				gRenderer()->notifyRenderableUpdated(this);
-		}
-		else
+		if((dirtyFlags & (UINT32)RenderableDirtyFlag::Everything) != 0)
 		{
 			createAnimationBuffers();
 
@@ -651,6 +657,16 @@ namespace bs
 				gRenderer()->notifyRenderableAdded(this);
 			}
 		}
+		else if((dirtyFlags & (UINT32)RenderableDirtyFlag::Mobility) != 0)
+		{
+				gRenderer()->notifyRenderableRemoved(this);
+				gRenderer()->notifyRenderableAdded(this);
+		}
+		else if ((dirtyFlags & (UINT32)RenderableDirtyFlag::Transform) != 0)
+		{
+			if (mIsActive)
+				gRenderer()->notifyRenderableUpdated(this);
+		}
 	}
 	}
 }

+ 6 - 0
Source/BansheeCore/Source/BsSceneManager.cpp

@@ -166,6 +166,9 @@ namespace bs
 			SPtr<Renderable> renderable = renderablePair.second.renderable;
 			HSceneObject so = renderablePair.second.sceneObject;
 
+			if (so->getMobility() != renderable->getMobility())
+				renderable->setMobility(so->getMobility());
+
 			renderable->_updateTransform(so);
 
 			if (so->getActive() != renderable->getIsActive())
@@ -197,6 +200,9 @@ namespace bs
 			SPtr<Light> handler = lightPair.second.light;
 			HSceneObject so = lightPair.second.sceneObject;
 
+			if (so->getMobility() != handler->getMobility())
+				handler->setMobility(so->getMobility());
+
 			UINT32 curHash = so->getTransformHash();
 			if (curHash != handler->_getLastModifiedHash())
 			{

+ 66 - 16
Source/BansheeCore/Source/BsSceneObject.cpp

@@ -19,6 +19,7 @@ namespace bs
 		, mScale(Vector3::ONE), mWorldPosition(Vector3::ZERO), mWorldRotation(Quaternion::IDENTITY)
 		, mWorldScale(Vector3::ONE), mCachedLocalTfrm(Matrix4::IDENTITY), mCachedWorldTfrm(Matrix4::IDENTITY)
 		, mDirtyFlags(0xFFFFFFFF), mDirtyHash(0), mActiveSelf(true), mActiveHierarchy(true)
+		, mMobility(ObjectMobility::Movable)
 	{
 		setName(name);
 	}
@@ -235,24 +236,36 @@ namespace bs
 
 	void SceneObject::setPosition(const Vector3& position)
 	{
-		mPosition = position;
-		notifyTransformChanged(TCF_Transform);
+		if (mMobility == ObjectMobility::Movable)
+		{
+			mPosition = position;
+			notifyTransformChanged(TCF_Transform);
+		}
 	}
 
 	void SceneObject::setRotation(const Quaternion& rotation)
 	{
-		mRotation = rotation;
-		notifyTransformChanged(TCF_Transform);
+		if (mMobility == ObjectMobility::Movable)
+		{
+			mRotation = rotation;
+			notifyTransformChanged(TCF_Transform);
+		}
 	}
 
 	void SceneObject::setScale(const Vector3& scale)
 	{
-		mScale = scale;
-		notifyTransformChanged(TCF_Transform);
+		if (mMobility == ObjectMobility::Movable)
+		{
+			mScale = scale;
+			notifyTransformChanged(TCF_Transform);
+		}
 	}
 
 	void SceneObject::setWorldPosition(const Vector3& position)
 	{
+		if(mMobility != ObjectMobility::Movable)
+			return;
+
 		if (mParent != nullptr)
 		{
 			Vector3 invScale = mParent->getWorldScale();
@@ -272,6 +285,9 @@ namespace bs
 
 	void SceneObject::setWorldRotation(const Quaternion& rotation)
 	{
+		if (mMobility != ObjectMobility::Movable)
+			return;
+
 		if (mParent != nullptr)
 		{
 			Quaternion invRotation = mParent->getWorldRotation().inverse();
@@ -286,6 +302,9 @@ namespace bs
 
 	void SceneObject::setWorldScale(const Vector3& scale)
 	{
+		if (mMobility != ObjectMobility::Movable)
+			return;
+
 		if (mParent != nullptr)
 		{
 			Matrix3 rotScale;
@@ -433,26 +452,43 @@ namespace bs
 
 	void SceneObject::notifyTransformChanged(TransformChangedFlags flags) const
 	{
-		mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty;
-		mDirtyHash++;
+		// If object is immovable, don't send transform changed events nor mark the transform dirty
+		TransformChangedFlags componentFlags = flags;
+		if (mMobility != ObjectMobility::Movable)
+			componentFlags = (TransformChangedFlags)(componentFlags & ~TCF_Transform);
+		else
+		{
+			mDirtyFlags |= DirtyFlags::LocalTfrmDirty | DirtyFlags::WorldTfrmDirty;
+			mDirtyHash++;
+		}
 
-		for(auto& entry : mComponents)
+		// Only send component flags if we haven't removed them all
+		if (componentFlags != 0)
 		{
-			if (entry->supportsNotify(flags))
+			for (auto& entry : mComponents)
 			{
-				bool alwaysRun = entry->hasFlag(ComponentFlag::AlwaysRun);
-				if(alwaysRun || gSceneManager().isRunning())
-					entry->onTransformChanged(flags);
+				if (entry->supportsNotify(flags))
+				{
+					bool alwaysRun = entry->hasFlag(ComponentFlag::AlwaysRun);
+					if (alwaysRun || gSceneManager().isRunning())
+						entry->onTransformChanged(componentFlags);
+				}
 			}
 		}
 
-		for (auto& entry : mChildren)
-			entry->notifyTransformChanged(flags);
+		// Mobility flag is only relevant for this scene object
+		flags = (TransformChangedFlags)(flags & ~TCF_Mobility);
+		if (flags != 0)
+		{
+			for (auto& entry : mChildren)
+				entry->notifyTransformChanged(flags);
+		}
 	}
 
 	void SceneObject::updateWorldTfrm() const
 	{
-		if(mParent != nullptr)
+		// Don't allow movement from parent when not movable
+		if (mParent != nullptr && mMobility == ObjectMobility::Movable)
 		{
 			// Update orientation
 			const Quaternion& parentOrientation = mParent->getWorldRotation();
@@ -683,6 +719,20 @@ namespace bs
 			return mActiveHierarchy;
 	}
 
+	void SceneObject::setMobility(ObjectMobility mobility)
+	{
+		if(mMobility != mobility)
+		{
+			mMobility = mobility;
+
+			// If mobility changed to movable, update both the mobility flag and transform, otherwise just mobility
+			if (mMobility == ObjectMobility::Movable)
+				notifyTransformChanged((TransformChangedFlags)(TCF_Transform | TCF_Mobility));
+			else
+				notifyTransformChanged(TCF_Mobility);
+		}
+	}
+
 	HSceneObject SceneObject::clone(bool instantiate)
 	{
 		bool isInstantiated = !hasFlag(SOF_DontInstantiate);

+ 12 - 1
Source/MBansheeEditor/Windows/Inspector/InspectorWindow.cs

@@ -69,6 +69,7 @@ namespace BansheeEditor
         private int undoCommandIdx = -1;
         private GUITextBox soNameInput;
         private GUIToggle soActiveToggle;
+        private GUIEnumField soMobility;
         private GUILayout soPrefabLayout;
         private bool soHasPrefab;
         private GUIFloatField soPosX;
@@ -256,6 +257,14 @@ namespace BansheeEditor
             nameLayout.AddElement(soNameInput);
             nameLayout.AddFlexibleSpace();
 
+            GUILayoutX mobilityLayout = sceneObjectLayout.AddLayoutX();
+            GUILabel mobilityLbl = new GUILabel(new LocEdString("Mobility"), GUIOption.FixedWidth(50));
+            soMobility = new GUIEnumField(typeof(ObjectMobility), "", 0, GUIOption.FixedWidth(85));
+            soMobility.Value = (ulong)activeSO.Mobility;
+            soMobility.OnSelectionChanged += value => activeSO.Mobility = (ObjectMobility) value;
+            mobilityLayout.AddElement(mobilityLbl);
+            mobilityLayout.AddElement(soMobility);
+
             soPrefabLayout = sceneObjectLayout.AddLayoutX();
 
             GUILayoutX positionLayout = sceneObjectLayout.AddLayoutX();
@@ -360,6 +369,7 @@ namespace BansheeEditor
 
             soNameInput.Text = activeSO.Name;
             soActiveToggle.Value = activeSO.Active;
+            soMobility.Value = (ulong) activeSO.Mobility;
 
             SceneObject prefabParent = PrefabUtility.GetPrefabParent(activeSO);
 
@@ -724,6 +734,7 @@ namespace BansheeEditor
             activeSO = null;
             soNameInput = null;
             soActiveToggle = null;
+            soMobility = null;
             soPrefabLayout = null;
             soHasPrefab = false;
             soPosX = null;
@@ -747,7 +758,7 @@ namespace BansheeEditor
         /// <returns>Area of the title bar, relative to the window.</returns>
         private Rect2I GetTitleBounds()
         {
-            return new Rect2I(0, 0, Width, 115);
+            return new Rect2I(0, 0, Width, 135);
         }
 
         /// <summary>

+ 7 - 3
Source/MBansheeEngine/Scene/GameObject.cs

@@ -43,8 +43,12 @@ namespace BansheeEngine
         /// <summary>
         /// Component will be notified when its parent changes.
         /// </summary>
-        Parent = 0x02
-    }
+        Parent = 0x02,
+        /// <summary>
+        /// Component will be notified when its scene object's mobility state changes.
+        /// </summary>
+        Mobility = 0x04
+}
 
-    /** @} */
+/** @} */
 }

+ 18 - 0
Source/MBansheeEngine/Scene/SceneObject.cs

@@ -42,6 +42,18 @@ namespace BansheeEngine
             set { Internal_SetActive(mCachedPtr, value); }
         }
 
+        /// <summary>
+        /// Sets the mobility of a scene object. This is used primarily as a performance hint to engine systems. Objects
+        /// with more restricted mobility will result in higher performance. Some mobility constraints will be enforced by
+        /// the engine itself, while for others the caller must be sure not to break the promise he made when mobility was
+        /// set. By default scene object's mobility is unrestricted.
+        /// </summary>
+        public ObjectMobility Mobility
+        {
+            get { return (ObjectMobility)Internal_GetMobility(mCachedPtr); }
+            set { Internal_SetMobility(mCachedPtr, (int)value); }
+        }
+
         /// <summary>
         /// World position. This includes local position of this object, plus position offset of any parents.
         /// </summary>
@@ -468,6 +480,12 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_GetActive(IntPtr nativeInstance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetMobility(IntPtr nativeInstance, int value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetMobility(IntPtr nativeInstance);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetParent(IntPtr nativeInstance, SceneObject parent);
 

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

@@ -58,6 +58,9 @@ namespace bs
 		static void internal_setActive(ScriptSceneObject* nativeInstance, bool value);
 		static bool internal_getActive(ScriptSceneObject* nativeInstance);
 
+		static void internal_setMobility(ScriptSceneObject* nativeInstance, int value);
+		static int internal_getMobility(ScriptSceneObject* nativeInstance);
+
 		static void internal_setParent(ScriptSceneObject* nativeInstance, MonoObject* parent);
 		static MonoObject* internal_getParent(ScriptSceneObject* nativeInstance);
 

+ 18 - 0
Source/SBansheeEngine/Source/BsScriptSceneObject.cpp

@@ -25,6 +25,9 @@ namespace bs
 		metaData.scriptClass->addInternalCall("Internal_SetName", &ScriptSceneObject::internal_setName);
 		metaData.scriptClass->addInternalCall("Internal_GetActive", &ScriptSceneObject::internal_getActive);
 		metaData.scriptClass->addInternalCall("Internal_SetActive", &ScriptSceneObject::internal_setActive);
+		metaData.scriptClass->addInternalCall("Internal_GetMobility", &ScriptSceneObject::internal_getMobility);
+		metaData.scriptClass->addInternalCall("Internal_SetMobility", &ScriptSceneObject::internal_setMobility);
+		metaData.scriptClass->addInternalCall("Internal_GetParent", &ScriptSceneObject::internal_getParent);
 		metaData.scriptClass->addInternalCall("Internal_GetParent", &ScriptSceneObject::internal_getParent);
 		metaData.scriptClass->addInternalCall("Internal_SetParent", &ScriptSceneObject::internal_setParent);
 		metaData.scriptClass->addInternalCall("Internal_GetNumChildren", &ScriptSceneObject::internal_getNumChildren);
@@ -102,6 +105,21 @@ namespace bs
 		return nativeInstance->mSceneObject->getActive(true);
 	}
 
+	void ScriptSceneObject::internal_setMobility(ScriptSceneObject* nativeInstance, int value)
+	{
+		if (checkIfDestroyed(nativeInstance))
+			return;
+
+		nativeInstance->mSceneObject->setMobility((ObjectMobility)value);
+	}
+
+	int ScriptSceneObject::internal_getMobility(ScriptSceneObject* nativeInstance)
+	{
+		if (checkIfDestroyed(nativeInstance))
+			return false;
+
+		return (int)nativeInstance->mSceneObject->getMobility();
+	}
 	void ScriptSceneObject::internal_setParent(ScriptSceneObject* nativeInstance, MonoObject* parent)
 	{
 		if (checkIfDestroyed(nativeInstance))