Torque3D Documentation / _generateds / sceneObject.cpp

sceneObject.cpp

Engine/source/scene/sceneObject.cpp

More...

Public Functions

ConsoleDocClass(SceneObject , "@brief A networkable object that exists in the 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n\n</a>" "The <a href="/coding/class/classsceneobject/">SceneObject</a> class provides the foundation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> 3D objects in the Engine. It " "exposes the functionality <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for:\n\n</a>" "<ul><li> Position, rotation and scale within the world.</li >" "< li >Working with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scene  graph, allowing efficient " "and robust rendering of the game scene.</li >" "< li >Various helper functions, including functions <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get bounding information " "and momentum/velocity.</li >" "< li >Mounting one <a href="/coding/class/classsceneobject/">SceneObject</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another.</li >" "< li >An interface <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision detection, as well as ray casting.</li >" "< li >Lighting. SceneObjects can register lights both at lightmap generation " " time, and dynamic lights at  runtime, but does not <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the client on its own. The " "same is true of collision detection beyond that of the bounding box. Instead you " "use one of the many classes that derrive from SceneObject, such as <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TSStatic.\n\n</a>" " @section SceneObject_Hiding Difference Between setHidden() and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">isRenderEnabled\n\n</a>" "When it comes time <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> decide <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsceneobject/">SceneObject</a> should <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> or not, there are two " "methods that can stop the <a href="/coding/class/classsceneobject/">SceneObject</a> from rendering at all. You need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be aware of " "the differences between these two methods as they impact how the <a href="/coding/class/classsceneobject/">SceneObject</a> is networked " "from the server <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n\n</a>" "The first method of manually controlling <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsceneobject/">SceneObject</a> is rendered is through its " "<a href="/coding/class/classsceneobject/#classsceneobject_1ae41e1b7a91fa4be04cb7fa0e7b9901d9">SceneObject::isRenderEnabled</a> property. When set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> false the <a href="/coding/class/classsceneobject/">SceneObject</a> is considered invisible but " "still present within the scene. This means it still takes part in collisions and continues " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">networked.\n\n</a>" "The second method is using the setHidden() method. This will actually remove <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsceneobject/">SceneObject</a> " "from the scene and it will no longer be networked from the server <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the cleint. Any client-side " "ghost of the object will be deleted as the server no longer considers the object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scope.\n\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )
DefineEngineMethod(SceneObject , attachChild , bool , (const char *_subObject) , "(SceneObject subObject)" "attach an object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this one, preserving its present transform." )
DefineEngineMethod(SceneObject , attachChildAt , bool , (SceneObject *_subObject, MatrixF _offset, S32 _node) , (nullAsType< SceneObject * >(), MatrixF::Identity, 0) , "(SceneObject subObject, <a href="/coding/class/classmatrixf/">MatrixF</a> offset, <a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> offset)" "Mount object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this one with the specified offset expressed in our coordinate space." )
DefineEngineMethod(SceneObject , attachToParent , bool , (const char *_sceneObject) , "attachToParent(<a href="/coding/class/classsceneobject/">SceneObject</a>)" "specify <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> null or non-null parent" )
DefineEngineMethod(SceneObject , detachChild , bool , (const char *_subObject) , "SceneObject subObject" )
DefineEngineMethod(SceneObject , getChild , S32 , (S32 _index) , (0) , "getChild(<a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> index) -- returns child <a href="/coding/class/classsceneobject/">SceneObject</a> at given index" )
DefineEngineMethod(SceneObject , getEulerRotation , Point3F , () , "Get Euler rotation of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return the orientation of the object in the form of rotations around the " " X, Y and Z axes in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">degrees.\n</a>" )
DefineEngineMethod(SceneObject , getForwardVector , VectorF , () , "Get the direction this object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">facing.\n</a>" "@return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vector indicating the direction this object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">facing.\n</a>" "@note This is the object's y axis." )
DefineEngineMethod(SceneObject , getInverseTransform , TransformF , () , "Get the object's inverse <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform.\n</a>" "@return the inverse transform of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
DefineEngineMethod(SceneObject , getMountedObject , S32 , (S32 slot) , "Get the object mounted at <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> particular <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot.\n</a>" "@param slot mount slot index <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">query\n</a>" "@return ID of the object mounted in the slot, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no object." )
DefineEngineMethod(SceneObject , getMountedObjectCount , S32 , () , "Get the number of objects mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">us.\n</a>" "@return the number of mounted objects." )
DefineEngineMethod(SceneObject , getMountedObjectNode , S32 , (S32 slot) , "@brief Get the mount node index of the object mounted at our given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot.\n\n</a>" "@param slot mount slot index <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">query\n</a>" "@return index of the mount node used by the object mounted in this slot." )
DefineEngineMethod(SceneObject , getMountNodeObject , S32 , (S32 node) , "@brief Get the object mounted at our given node <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">index.\n\n</a>" "@param node mount node index <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">query\n</a>" "@return ID of the first object mounted at the node, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> none found." )
DefineEngineMethod(SceneObject , getNumChildren , S32 , () , "returns number of direct child objects" )
DefineEngineMethod(SceneObject , getNumProgeny , S32 , () , "returns number of recursively-nested child objects" )
DefineEngineMethod(SceneObject , getObjectBox , Box3F , () , "Get the object's bounding box (relative <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the object's origin).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return six fields, two Point3Fs, containing the min and max points of the " "objectbox." )
DefineEngineMethod(SceneObject , getObjectMount , S32 , () , "@brief Get the object we are mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@return the SimObjectID of the object we're mounted to, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not mounted." )
DefineEngineMethod(SceneObject , getParent , S32 , () , "returns ID of parent <a href="/coding/class/classsceneobject/">SceneObject</a>" )
DefineEngineMethod(SceneObject , getPosition , Point3F , () , "Get the object's world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n</a>" "@return the current world position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
DefineEngineMethod(SceneObject , getRightVector , VectorF , () , "Get the right vector of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vector indicating the right direction of this object." "@note This is the object's x axis." )
DefineEngineMethod(SceneObject , getScale , Point3F , () , "Get the object's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scale.\n</a>" "@return object scale as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classpoint3f/">Point3F</a>" )
DefineEngineMethod(SceneObject , getTransform , TransformF , () , "Get the object's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform.\n</a>" "@return the current transform of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
DefineEngineMethod(SceneObject , getType , S32 , () , "Return the type mask <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return The numeric type mask <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the object." )
DefineEngineMethod(SceneObject , getUpVector , VectorF , () , "Get the up vector of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vector indicating the up direction of this object." "@note This is the object's z axis." )
DefineEngineMethod(SceneObject , getWorldBox , Box3F , () , "Get the object's world bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box.\n</a>" "@return six fields, two Point3Fs, containing the min and max points of the " "worldbox." )
DefineEngineMethod(SceneObject , getWorldBoxCenter , Point3F , () , "Get the center of the object's world bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box.\n</a>" "@return the center of the world bounding box <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this object." )
DefineEngineMethod(SceneObject , isGlobalBounds , bool , () , "Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> this object has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> global bounds <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set.\n</a>" "If global bounds are set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be true, then the object is assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have an " "infinitely large bounding box <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision and rendering <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">purposes.\n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the object has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> global bounds." )
DefineEngineMethod(SceneObject , isMounted , bool , () , "@brief Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> we are mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another object, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not mounted." )
DefineEngineMethod(SceneObject , mountObject , bool , (SceneObject *objB, S32 slot, TransformF txfm) , (TransformF::Identity) , "@brief Mount objB <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this object at the desired slot with optional <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform.\n\n</a>" "@param objB <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount onto <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">us\n</a>" "@param slot Mount slot <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ID\n</a>" "@param txfm (optional) mount offset <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> failed(objB is not valid)" )
DefineEngineMethod(SceneObject , setForwardVector , void , (VectorF newForward, VectorF upVector) , (VectorF(0, 0, 0), VectorF(0, 0, 1)) , "Sets the forward vector of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scene object, making it face Y+along the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" " @param The <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> forward vector <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set.\n</a>" " @param(Optional) The up vector <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> help orient the rotation." )
DefineEngineMethod(SceneObject , setPosition , void , (Point3F pos) , "Set the object's world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n</a>" "@param pos the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> world position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )
DefineEngineMethod(SceneObject , setScale , void , (Point3F scale) , "Set the object's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scale.\n</a>" "@param scale object scale <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set\n</a>" )
DefineEngineMethod(SceneObject , setTransform , void , (TransformF txfm) , "Set the object's transform (orientation and position)." "@param txfm object transform <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set" )
DefineEngineMethod(SceneObject , unmount , void , () , "Unmount us from the currently mounted object <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">any.\n</a>" )
DefineEngineMethod(SceneObject , unmountObject , bool , (SceneObject *target) , "@brief Unmount an object from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ourselves.\n\n</a>" "@param target object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">unmount\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n</a>" )

Detailed Description

Public Functions

ConsoleDocClass(SceneObject , "@brief A networkable object that exists in the 3D <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n\n</a>" "The <a href="/coding/class/classsceneobject/">SceneObject</a> class provides the foundation <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> 3D objects in the Engine. It " "exposes the functionality <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">for:\n\n</a>" "<ul><li> Position, rotation and scale within the world.</li >" "< li >Working with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scene  graph, allowing efficient " "and robust rendering of the game scene.</li >" "< li >Various helper functions, including functions <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get bounding information " "and momentum/velocity.</li >" "< li >Mounting one <a href="/coding/class/classsceneobject/">SceneObject</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another.</li >" "< li >An interface <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision detection, as well as ray casting.</li >" "< li >Lighting. SceneObjects can register lights both at lightmap generation " " time, and dynamic lights at  runtime, but does not <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the client on its own. The " "same is true of collision detection beyond that of the bounding box. Instead you " "use one of the many classes that derrive from SceneObject, such as <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">TSStatic.\n\n</a>" " @section SceneObject_Hiding Difference Between setHidden() and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">isRenderEnabled\n\n</a>" "When it comes time <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> decide <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsceneobject/">SceneObject</a> should <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> or not, there are two " "methods that can stop the <a href="/coding/class/classsceneobject/">SceneObject</a> from rendering at all. You need <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be aware of " "the differences between these two methods as they impact how the <a href="/coding/class/classsceneobject/">SceneObject</a> is networked " "from the server <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">client.\n\n</a>" "The first method of manually controlling <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsceneobject/">SceneObject</a> is rendered is through its " "<a href="/coding/class/classsceneobject/#classsceneobject_1ae41e1b7a91fa4be04cb7fa0e7b9901d9">SceneObject::isRenderEnabled</a> property. When set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> false the <a href="/coding/class/classsceneobject/">SceneObject</a> is considered invisible but " "still present within the scene. This means it still takes part in collisions and continues " "<a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">networked.\n\n</a>" "The second method is using the setHidden() method. This will actually remove <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsceneobject/">SceneObject</a> " "from the scene and it will no longer be networked from the server <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the cleint. Any client-side " "ghost of the object will be deleted as the server no longer considers the object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scope.\n\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">gameObjects\n</a>" )

DefineEngineMethod(SceneObject , attachChild , bool , (const char *_subObject) , "(SceneObject subObject)" "attach an object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this one, preserving its present transform." )

DefineEngineMethod(SceneObject , attachChildAt , bool , (SceneObject *_subObject, MatrixF _offset, S32 _node) , (nullAsType< SceneObject * >(), MatrixF::Identity, 0) , "(SceneObject subObject, <a href="/coding/class/classmatrixf/">MatrixF</a> offset, <a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> offset)" "Mount object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this one with the specified offset expressed in our coordinate space." )

DefineEngineMethod(SceneObject , attachToParent , bool , (const char *_sceneObject) , "attachToParent(<a href="/coding/class/classsceneobject/">SceneObject</a>)" "specify <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> null or non-null parent" )

DefineEngineMethod(SceneObject , detachChild , bool , (const char *_subObject) , "SceneObject subObject" )

DefineEngineMethod(SceneObject , getChild , S32 , (S32 _index) , (0) , "getChild(<a href="/coding/file/types_8h/#types_8h_1a57a2244776e01ad620c556de58eb7880">S32</a> index) -- returns child <a href="/coding/class/classsceneobject/">SceneObject</a> at given index" )

DefineEngineMethod(SceneObject , getEulerRotation , Point3F , () , "Get Euler rotation of this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return the orientation of the object in the form of rotations around the " " X, Y and Z axes in <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">degrees.\n</a>" )

DefineEngineMethod(SceneObject , getForwardVector , VectorF , () , "Get the direction this object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">facing.\n</a>" "@return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vector indicating the direction this object is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">facing.\n</a>" "@note This is the object's y axis." )

DefineEngineMethod(SceneObject , getInverseTransform , TransformF , () , "Get the object's inverse <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform.\n</a>" "@return the inverse transform of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

DefineEngineMethod(SceneObject , getMountedObject , S32 , (S32 slot) , "Get the object mounted at <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> particular <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot.\n</a>" "@param slot mount slot index <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">query\n</a>" "@return ID of the object mounted in the slot, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no object." )

DefineEngineMethod(SceneObject , getMountedObjectCount , S32 , () , "Get the number of objects mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">us.\n</a>" "@return the number of mounted objects." )

DefineEngineMethod(SceneObject , getMountedObjectNode , S32 , (S32 slot) , "@brief Get the mount node index of the object mounted at our given <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot.\n\n</a>" "@param slot mount slot index <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">query\n</a>" "@return index of the mount node used by the object mounted in this slot." )

DefineEngineMethod(SceneObject , getMountNodeObject , S32 , (S32 node) , "@brief Get the object mounted at our given node <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">index.\n\n</a>" "@param node mount node index <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">query\n</a>" "@return ID of the first object mounted at the node, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> none found." )

DefineEngineMethod(SceneObject , getNumChildren , S32 , () , "returns number of direct child objects" )

DefineEngineMethod(SceneObject , getNumProgeny , S32 , () , "returns number of recursively-nested child objects" )

DefineEngineMethod(SceneObject , getObjectBox , Box3F , () , "Get the object's bounding box (relative <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the object's origin).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@return six fields, two Point3Fs, containing the min and max points of the " "objectbox." )

DefineEngineMethod(SceneObject , getObjectMount , S32 , () , "@brief Get the object we are mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@return the SimObjectID of the object we're mounted to, or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not mounted." )

DefineEngineMethod(SceneObject , getParent , S32 , () , "returns ID of parent <a href="/coding/class/classsceneobject/">SceneObject</a>" )

DefineEngineMethod(SceneObject , getPosition , Point3F , () , "Get the object's world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n</a>" "@return the current world position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

DefineEngineMethod(SceneObject , getRightVector , VectorF , () , "Get the right vector of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vector indicating the right direction of this object." "@note This is the object's x axis." )

DefineEngineMethod(SceneObject , getScale , Point3F , () , "Get the object's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scale.\n</a>" "@return object scale as <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classpoint3f/">Point3F</a>" )

DefineEngineMethod(SceneObject , getTransform , TransformF , () , "Get the object's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform.\n</a>" "@return the current transform of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

DefineEngineMethod(SceneObject , getType , S32 , () , "Return the type mask <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return The numeric type mask <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the object." )

DefineEngineMethod(SceneObject , getUpVector , VectorF , () , "Get the up vector of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> vector indicating the up direction of this object." "@note This is the object's z axis." )

DefineEngineMethod(SceneObject , getWorldBox , Box3F , () , "Get the object's world bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box.\n</a>" "@return six fields, two Point3Fs, containing the min and max points of the " "worldbox." )

DefineEngineMethod(SceneObject , getWorldBoxCenter , Point3F , () , "Get the center of the object's world bounding <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">box.\n</a>" "@return the center of the world bounding box <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this object." )

DefineEngineMethod(SceneObject , isGlobalBounds , bool , () , "Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> this object has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> global bounds <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set.\n</a>" "If global bounds are set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be true, then the object is assumed <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> have an " "infinitely large bounding box <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> collision and rendering <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">purposes.\n</a>" " @return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the object has <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> global bounds." )

DefineEngineMethod(SceneObject , isMounted , bool , () , "@brief Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> we are mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> mounted <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> another object, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not mounted." )

DefineEngineMethod(SceneObject , mountObject , bool , (SceneObject *objB, S32 slot, TransformF txfm) , (TransformF::Identity) , "@brief Mount objB <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> this object at the desired slot with optional <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform.\n\n</a>" "@param objB <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount onto <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">us\n</a>" "@param slot Mount slot <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ID\n</a>" "@param txfm (optional) mount offset <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transform\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> failed(objB is not valid)" )

DefineEngineMethod(SceneObject , setForwardVector , void , (VectorF newForward, VectorF upVector) , (VectorF(0, 0, 0), VectorF(0, 0, 1)) , "Sets the forward vector of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> scene object, making it face Y+along the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">vector.\n</a>" " @param The <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> forward vector <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set.\n</a>" " @param(Optional) The up vector <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> help orient the rotation." )

DefineEngineMethod(SceneObject , setPosition , void , (Point3F pos) , "Set the object's world <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position.\n</a>" "@param pos the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> world position of the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object\n</a>" )

DefineEngineMethod(SceneObject , setScale , void , (Point3F scale) , "Set the object's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">scale.\n</a>" "@param scale object scale <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">set\n</a>" )

DefineEngineMethod(SceneObject , setTransform , void , (TransformF txfm) , "Set the object's transform (orientation and position)." "@param txfm object transform <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> set" )

DefineEngineMethod(SceneObject , unmount , void , () , "Unmount us from the currently mounted object <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">any.\n</a>" )

DefineEngineMethod(SceneObject , unmountObject , bool , (SceneObject *target) , "@brief Unmount an object from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">ourselves.\n\n</a>" "@param target object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">unmount\n</a>" "@return true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> successful, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">failed\n</a>" )

IMPLEMENT_CONOBJECT(SceneObject )

scopeCallback(SceneObject * obj, void * conPtr)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2012 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  25// Arcane-FX for MIT Licensed Open Source version of Torque 3D from GarageGames
  26// Copyright (C) 2015 Faust Logic, Inc.
  27//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~//~~~~~~~~~~~~~~~~~~~~~//
  28
  29#include "platform/platform.h"
  30#include "scene/sceneObject.h"
  31
  32#include "platform/profiler.h"
  33#include "console/consoleTypes.h"
  34#include "console/engineAPI.h"
  35#include "console/simPersistID.h"
  36#include "sim/netConnection.h"
  37#include "core/stream/bitStream.h"
  38#include "scene/sceneManager.h"
  39#include "scene/sceneTracker.h"
  40#include "scene/sceneRenderState.h"
  41#include "scene/zones/sceneZoneSpace.h"
  42#include "collision/extrudedPolyList.h"
  43#include "collision/earlyOutPolyList.h"
  44#include "collision/optimizedPolyList.h"
  45#include "math/mPolyhedron.h"
  46#include "gfx/bitmap/gBitmap.h"
  47#include "math/util/frustum.h"
  48#include "math/mathIO.h"
  49#include "math/mTransform.h"
  50#include "T3D/gameBase/gameProcess.h"
  51#include "T3D/gameBase/gameConnection.h"
  52#include "T3D/accumulationVolume.h"
  53
  54IMPLEMENT_CONOBJECT(SceneObject);
  55
  56ConsoleDocClass( SceneObject,
  57   "@brief A networkable object that exists in the 3D world.\n\n"
  58
  59   "The SceneObject class provides the foundation for 3D objects in the Engine.  It "
  60   "exposes the functionality for:\n\n"
  61
  62   "<ul><li>Position, rotation and scale within the world.</li>"
  63   "<li>Working with a scene graph (in the Zone and Portal sections), allowing efficient "
  64   "and robust rendering of the game scene.</li>"
  65   "<li>Various helper functions, including functions to get bounding information "
  66   "and momentum/velocity.</li>"
  67   "<li>Mounting one SceneObject to another.</li>"
  68   "<li>An interface for collision detection, as well as ray casting.</li>"
  69   "<li>Lighting. SceneObjects can register lights both at lightmap generation "
  70   "time, and dynamic lights at runtime (for special effects, such as from flame "
  71   "or a projectile, or from an explosion).</li></ul>\n\n"
  72
  73   "You do not typically work with SceneObjects themselves.  The SceneObject provides a reference "
  74   "within the game world (the scene), but does not render to the client on its own.  The "
  75   "same is true of collision detection beyond that of the bounding box.  Instead you "
  76   "use one of the many classes that derrive from SceneObject, such as TSStatic.\n\n"
  77
  78   "@section SceneObject_Hiding Difference Between setHidden() and isRenderEnabled\n\n"
  79
  80   "When it comes time to decide if a SceneObject should render or not, there are two "
  81   "methods that can stop the SceneObject from rendering at all.  You need to be aware of "
  82   "the differences between these two methods as they impact how the SceneObject is networked "
  83   "from the server to the client.\n\n"
  84
  85   "The first method of manually controlling if a SceneObject is rendered is through its "
  86   "SceneObject::isRenderEnabled property.  When set to false the SceneObject is considered invisible but "
  87   "still present within the scene.  This means it still takes part in collisions and continues "
  88   "to be networked.\n\n"
  89
  90   "The second method is using the setHidden() method.  This will actually remove a SceneObject "
  91   "from the scene and it will no longer be networked from the server to the cleint.  Any client-side "
  92   "ghost of the object will be deleted as the server no longer considers the object to be in scope.\n\n"
  93
  94   "@ingroup gameObjects\n"
  95);
  96
  97#ifdef TORQUE_TOOLS
  98extern bool gEditingMission;
  99#endif
 100
 101Signal< void( SceneObject* ) > SceneObject::smSceneObjectAdd;
 102Signal< void( SceneObject* ) > SceneObject::smSceneObjectRemove;
 103
 104
 105//-----------------------------------------------------------------------------
 106
 107SceneObject::SceneObject()
 108{
 109   mContainer = 0;
 110   mTypeMask = DefaultObjectType;
 111   mCollisionCount = 0;
 112   mGlobalBounds = false;
 113
 114   mObjScale.set(1,1,1);
 115   mObjToWorld.identity();
 116   mWorldToObj.identity();
 117
 118   mObjBox      = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0));
 119   mWorldBox    = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0));
 120   mWorldSphere = SphereF(Point3F(0, 0, 0), 0);
 121
 122   mRenderObjToWorld.identity();
 123   mRenderWorldToObj.identity();
 124   mRenderWorldBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0));
 125   mRenderWorldSphere = SphereF(Point3F(0, 0, 0), 0);
 126
 127   mContainerSeqKey = 0;
 128
 129   mBinRefHead = NULL;
 130
 131   mSceneManager = NULL;
 132
 133   mNumCurrZones = 0;
 134   mZoneRefHead = NULL;
 135   mZoneRefDirty = false;
 136
 137   mBinMinX = 0xFFFFFFFF;
 138   mBinMaxX = 0xFFFFFFFF;
 139   mBinMinY = 0xFFFFFFFF;
 140   mBinMaxY = 0xFFFFFFFF;
 141   mLightPlugin = NULL;
 142
 143   mMount.object = NULL;
 144   mMount.link = NULL;
 145   mMount.list = NULL;
 146   mMount.node = -1;
 147   mMount.xfm = MatrixF::Identity;
 148   mMountPID = NULL;
 149
 150   mSceneObjectLinks = NULL;
 151
 152   mObjectFlags.set( RenderEnabledFlag | SelectionEnabledFlag );
 153// PATHSHAPE
 154   // init the scenegraph relationships to indicate no parent, no children, and no siblings
 155   mGraph.parent = NULL; 
 156   mGraph.nextSibling = NULL;
 157   mGraph.firstChild = NULL;
 158   mGraph.objToParent.identity();
 159// PATHSHAPE END
 160   mIsScopeAlways = false;
 161
 162   mAccuTex = NULL;
 163   mSelectionFlags = 0;
 164   mPathfindingIgnore = false;
 165
 166   mGameObjectAssetId = StringTable->insert("");
 167
 168   mDirtyGameObject = false;
 169}
 170
 171//-----------------------------------------------------------------------------
 172
 173SceneObject::~SceneObject()
 174{
 175   AssertFatal( mZoneRefHead == NULL && mBinRefHead == NULL,
 176      "SceneObject::~SceneObject - Object still linked in reference lists!");
 177   AssertFatal( !mSceneObjectLinks,
 178      "SceneObject::~SceneObject() - object is still linked to SceneTrackers" );
 179
 180   mAccuTex = NULL;
 181   unlink();
 182}
 183
 184//-----------------------------------------------------------------------------
 185
 186bool SceneObject::castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info)
 187{
 188   // By default, all ray checking against the rendered mesh will be passed
 189   // on to the collision mesh.  This saves having to define both methods
 190   // for simple objects.
 191   return castRay( start, end, info );
 192}
 193
 194//-----------------------------------------------------------------------------
 195
 196bool SceneObject::containsPoint( const Point3F& point )
 197{
 198   // If it's not in the AABB, then it can't be in the OBB either,
 199   // so early out.
 200
 201   if( !mWorldBox.isContained( point ) )
 202      return false;
 203
 204   // Transform point into object space and test it against
 205   // our object space bounding box.
 206
 207   Point3F objPoint( 0, 0, 0 );
 208   getWorldTransform().mulP( point, &objPoint );
 209   objPoint.convolveInverse( getScale() );
 210
 211   return ( mObjBox.isContained( objPoint ) );
 212}
 213
 214//-----------------------------------------------------------------------------
 215
 216bool SceneObject::collideBox(const Point3F &start, const Point3F &end, RayInfo *info)
 217{
 218   const F32 * pStart = (const F32*)start;
 219   const F32 * pEnd = (const F32*)end;
 220   const F32 * pMin = (const F32*)mObjBox.minExtents;
 221   const F32 * pMax = (const F32*)mObjBox.maxExtents;
 222
 223   F32 maxStartTime = -1;
 224   F32 minEndTime = 1;
 225   F32 startTime;
 226   F32 endTime;
 227
 228   // used for getting normal
 229   U32 hitIndex = 0xFFFFFFFF;
 230   U32 side;
 231
 232   // walk the axis
 233   for(U32 i = 0; i < 3; i++)
 234   {
 235      //
 236      if(pStart[i] < pEnd[i])
 237      {
 238         if(pEnd[i] < pMin[i] || pStart[i] > pMax[i])
 239            return(false);
 240
 241         F32 dist = pEnd[i] - pStart[i];
 242
 243         startTime = (pStart[i] < pMin[i]) ? (pMin[i] - pStart[i]) / dist : -1;
 244         endTime = (pEnd[i] > pMax[i]) ? (pMax[i] - pStart[i]) / dist : 1;
 245         side = 1;
 246      }
 247      else
 248      {
 249         if(pStart[i] < pMin[i] || pEnd[i] > pMax[i])
 250            return(false);
 251
 252         F32 dist = pStart[i] - pEnd[i];
 253         startTime = (pStart[i] > pMax[i]) ? (pStart[i] - pMax[i]) / dist : -1;
 254         endTime = (pEnd[i] < pMin[i]) ? (pStart[i] - pMin[i]) / dist : 1;
 255         side = 0;
 256      }
 257
 258      //
 259      if(startTime > maxStartTime)
 260      {
 261         maxStartTime = startTime;
 262         hitIndex = i * 2 + side;
 263      }
 264      if(endTime < minEndTime)
 265         minEndTime = endTime;
 266      if(minEndTime < maxStartTime)
 267         return(false);
 268   }
 269
 270   // fail if inside
 271   if(maxStartTime < 0.f)
 272      return(false);
 273
 274   //
 275   static Point3F boxNormals[] = {
 276      Point3F( 1, 0, 0),
 277      Point3F(-1, 0, 0),
 278      Point3F( 0, 1, 0),
 279      Point3F( 0,-1, 0),
 280      Point3F( 0, 0, 1),
 281      Point3F( 0, 0,-1),
 282   };
 283
 284   //
 285   AssertFatal(hitIndex != 0xFFFFFFFF, "SceneObject::collideBox");
 286   info->t = maxStartTime;
 287   info->object = this;
 288   mObjToWorld.mulV(boxNormals[hitIndex], &info->normal);
 289   info->material = 0;
 290   return(true);
 291}
 292
 293//-----------------------------------------------------------------------------
 294
 295void SceneObject::disableCollision()
 296{
 297   for (SceneObject* ptr = getMountList(); ptr; ptr = ptr->getMountLink())
 298      ptr->disableCollision();
 299   mCollisionCount++;
 300   AssertFatal(mCollisionCount < 50, "SceneObject::disableCollision called 50 times on the same object. Is this inside a circular loop?" );
 301}
 302
 303//-----------------------------------------------------------------------------
 304
 305void SceneObject::enableCollision()
 306{
 307   for (SceneObject* ptr = getMountList(); ptr; ptr = ptr->getMountLink())
 308      ptr->enableCollision();
 309   if (mCollisionCount)
 310      --mCollisionCount;
 311}
 312
 313//-----------------------------------------------------------------------------
 314
 315bool SceneObject::onAdd()
 316{
 317   if ( !Parent::onAdd() )
 318      return false;
 319
 320   mIsScopeAlways = mNetFlags.test( ScopeAlways );
 321
 322   mWorldToObj = mObjToWorld;
 323   mWorldToObj.affineInverse();
 324   resetWorldBox();
 325
 326   setRenderTransform(mObjToWorld);
 327
 328   resolveMountPID();
 329
 330   smSceneObjectAdd.trigger(this);
 331
 332   return true;
 333}
 334
 335//-----------------------------------------------------------------------------
 336
 337void SceneObject::onRemove()
 338{
 339   smSceneObjectRemove.trigger(this);
 340
 341   unmount();
 342   plUnlink();
 343
 344   Parent::onRemove();
 345// PATHSHAPE
 346   if ( getParent() != NULL)   attachToParent( NULL);
 347// PATHSHAPE END
 348}
 349
 350//-----------------------------------------------------------------------------
 351
 352void SceneObject::addToScene()
 353{
 354   if( mSceneManager )
 355      return;
 356
 357   if( isClientObject() )
 358      gClientSceneGraph->addObjectToScene( this );
 359   else
 360      gServerSceneGraph->addObjectToScene( this );
 361}
 362
 363//-----------------------------------------------------------------------------
 364
 365void SceneObject::removeFromScene()
 366{
 367   if( !mSceneManager )
 368      return;
 369
 370   mSceneManager->removeObjectFromScene( this );
 371}
 372
 373//-----------------------------------------------------------------------------
 374
 375void SceneObject::onDeleteNotify( SimObject *obj )
 376{      
 377   // We are comparing memory addresses so even if obj really is not a 
 378   // ProcessObject this cast shouldn't break anything.
 379   if ( obj == mAfterObject )
 380      mAfterObject = NULL;
 381
 382   if ( obj == mMount.object )
 383      unmount();
 384
 385   Parent::onDeleteNotify( obj );   
 386}
 387
 388//-----------------------------------------------------------------------------
 389
 390void SceneObject::inspectPostApply()
 391{
 392   if( isServerObject() )
 393      setMaskBits( MountedMask );
 394   Parent::inspectPostApply();
 395}
 396
 397//-----------------------------------------------------------------------------
 398
 399void SceneObject::setGlobalBounds()
 400{ 
 401   mGlobalBounds = true;
 402   mObjBox.minExtents.set( -1e10, -1e10, -1e10 );
 403   mObjBox.maxExtents.set(  1e10,  1e10,  1e10 );
 404
 405   if( mSceneManager )
 406      mSceneManager->notifyObjectDirty( this );
 407}
 408
 409//-----------------------------------------------------------------------------
 410
 411void SceneObject::setTransform( const MatrixF& mat )
 412{
 413   // This test is a bit expensive so turn it off in release.   
 414#ifdef TORQUE_DEBUG
 415   //AssertFatal( mat.isAffine(), "SceneObject::setTransform() - Bad transform (non affine)!" );
 416#endif
 417
 418   PROFILE_SCOPE( SceneObject_setTransform );
 419// PATHSHAPE
 420   PerformUpdatesForChildren(mat);
 421// PATHSHAPE END
 422
 423   // Update the transforms.
 424
 425   mObjToWorld = mWorldToObj = mat;
 426   mWorldToObj.affineInverse();
 427
 428   // Update the world-space AABB.
 429
 430   resetWorldBox();
 431
 432   // If we're in a SceneManager, sync our scene state.
 433
 434   if( mSceneManager != NULL )
 435      mSceneManager->notifyObjectDirty( this );
 436
 437   setRenderTransform( mat );
 438}
 439
 440//-----------------------------------------------------------------------------
 441
 442void SceneObject::setScale( const VectorF &scale )
 443{
 444   AssertFatal( !mIsNaN( scale ), "SceneObject::setScale() - The scale is NaN!" );
 445
 446   // Avoid unnecessary scaling operations.
 447   if ( mObjScale.equal( scale ) )
 448      return;
 449
 450   mObjScale = scale;
 451   setTransform(MatrixF(mObjToWorld));
 452
 453   // Make sure that any subclasses of me get a chance to react to the
 454   // scale being changed.
 455   onScaleChanged();
 456
 457   setMaskBits( ScaleMask );
 458}
 459
 460void SceneObject::setForwardVector(VectorF newForward, VectorF upVector)
 461{
 462   MatrixF mat = getTransform();
 463
 464   VectorF up(0.0f, 0.0f, 1.0f);
 465   VectorF axisX;
 466   VectorF axisY = newForward;
 467   VectorF axisZ;
 468
 469   if (upVector != VectorF::Zero)
 470      up = upVector;
 471
 472   // Validate and normalize input:  
 473   F32 lenSq;
 474   lenSq = axisY.lenSquared();
 475   if (lenSq < 0.000001f)
 476   {
 477      axisY.set(0.0f, 1.0f, 0.0f);
 478      Con::errorf("SceneObject::setForwardVector() - degenerate forward vector");
 479   }
 480   else
 481   {
 482      axisY /= mSqrt(lenSq);
 483   }
 484
 485
 486   lenSq = up.lenSquared();
 487   if (lenSq < 0.000001f)
 488   {
 489      up.set(0.0f, 0.0f, 1.0f);
 490      Con::errorf("SceneObject::setForwardVector() - degenerate up vector - too small");
 491   }
 492   else
 493   {
 494      up /= mSqrt(lenSq);
 495   }
 496
 497   if (fabsf(mDot(up, axisY)) > 0.9999f)
 498   {
 499      Con::errorf("SceneObject::setForwardVector() - degenerate up vector - same as forward");
 500      // I haven't really tested this, but i think it generates something which should be not parallel to the previous vector:  
 501      F32 tmp = up.x;
 502      up.x = -up.y;
 503      up.y = up.z;
 504      up.z = tmp;
 505   }
 506
 507   // construct the remaining axes:  
 508   mCross(axisY, up, &axisX);
 509   mCross(axisX, axisY, &axisZ);
 510
 511   mat.setColumn(0, axisX);
 512   mat.setColumn(1, axisY);
 513   mat.setColumn(2, axisZ);
 514
 515   setTransform(mat);
 516}
 517
 518//-----------------------------------------------------------------------------
 519
 520void SceneObject::resetWorldBox()
 521{
 522   AssertFatal(mObjBox.isValidBox(), "SceneObject::resetWorldBox - Bad object box!");
 523
 524   mWorldBox = mObjBox;
 525   mWorldBox.minExtents.convolve(mObjScale);
 526   mWorldBox.maxExtents.convolve(mObjScale);
 527   mObjToWorld.mul(mWorldBox);
 528
 529   AssertFatal(mWorldBox.isValidBox(), "SceneObject::resetWorldBox - Bad world box!");
 530
 531   // Create mWorldSphere from mWorldBox
 532   mWorldBox.getCenter(&mWorldSphere.center);
 533   mWorldSphere.radius = (mWorldBox.maxExtents - mWorldSphere.center).len();
 534
 535   // Update tracker links.
 536   
 537   for( SceneObjectLink* link = mSceneObjectLinks; link != NULL; 
 538        link = link->getNextLink() )
 539      link->update();
 540}
 541
 542//-----------------------------------------------------------------------------
 543
 544void SceneObject::resetObjectBox()
 545{
 546   AssertFatal( mWorldBox.isValidBox(), "SceneObject::resetObjectBox - Bad world box!" );
 547
 548   mObjBox = mWorldBox;
 549   mWorldToObj.mul( mObjBox );
 550
 551   Point3F objScale( mObjScale );
 552   objScale.setMax( Point3F( (F32)POINT_EPSILON, (F32)POINT_EPSILON, (F32)POINT_EPSILON ) );
 553   mObjBox.minExtents.convolveInverse( objScale );
 554   mObjBox.maxExtents.convolveInverse( objScale );
 555
 556   AssertFatal( mObjBox.isValidBox(), "SceneObject::resetObjectBox - Bad object box!" );
 557
 558   // Update the mWorldSphere from mWorldBox
 559   mWorldBox.getCenter( &mWorldSphere.center );
 560   mWorldSphere.radius = ( mWorldBox.maxExtents - mWorldSphere.center ).len();
 561
 562   // Update scene managers.
 563   
 564   for( SceneObjectLink* link = mSceneObjectLinks; link != NULL; 
 565        link = link->getNextLink() )
 566      link->update();
 567}
 568
 569//-----------------------------------------------------------------------------
 570
 571void SceneObject::setRenderTransform(const MatrixF& mat)
 572{
 573   PROFILE_START(SceneObj_setRenderTransform);
 574   mRenderObjToWorld = mRenderWorldToObj = mat;
 575   mRenderWorldToObj.affineInverse();
 576
 577   AssertFatal(mObjBox.isValidBox(), "Bad object box!");
 578   resetRenderWorldBox();
 579   PROFILE_END();
 580}
 581
 582//-----------------------------------------------------------------------------
 583
 584void SceneObject::resetRenderWorldBox()
 585{
 586   AssertFatal( mObjBox.isValidBox(), "Bad object box!" );
 587
 588   mRenderWorldBox = mObjBox;
 589   mRenderWorldBox.minExtents.convolve( mObjScale );
 590   mRenderWorldBox.maxExtents.convolve( mObjScale );
 591   mRenderObjToWorld.mul( mRenderWorldBox );
 592
 593   AssertFatal( mRenderWorldBox.isValidBox(), "Bad world box!" );
 594
 595   // Create mRenderWorldSphere from mRenderWorldBox.
 596
 597   mRenderWorldBox.getCenter( &mRenderWorldSphere.center );
 598   mRenderWorldSphere.radius = ( mRenderWorldBox.maxExtents - mRenderWorldSphere.center ).len();
 599}
 600
 601//-----------------------------------------------------------------------------
 602
 603void SceneObject::setHidden( bool hidden )
 604{
 605   if( hidden != isHidden() )
 606   {
 607      // Add/remove the object from the scene.  Removing it
 608      // will also cause the NetObject to go out of scope since
 609      // the container query will not find it anymore.  However,
 610      // ScopeAlways objects need to be treated separately as we
 611      // do next.
 612
 613      if( !hidden )
 614         addToScene();
 615      else
 616         removeFromScene();
 617
 618      // ScopeAlways objects stay in scope no matter what, i.e. even
 619      // if they aren't in the scene query anymore.  So, to force ghosts
 620      // to go away, we need to clear ScopeAlways while we are hidden.
 621
 622      if( hidden && mIsScopeAlways )
 623         clearScopeAlways();
 624      else if( !hidden && mIsScopeAlways )
 625         setScopeAlways();
 626
 627      Parent::setHidden( hidden );
 628   }
 629}
 630
 631//-----------------------------------------------------------------------------
 632
 633void SceneObject::initPersistFields()
 634{
 635   addGroup("GameObject");
 636   addField("GameObject", TypeGameObjectAssetPtr, Offset(mGameObjectAsset, SceneObject), "The asset Id used for the game object this entity is based on.");
 637
 638   addField("dirtyGameObject", TypeBool, Offset(mDirtyGameObject, SceneObject), "If this entity is a GameObject, it flags if this instance delinates from the template.",
 639      AbstractClassRep::FieldFlags::FIELD_HideInInspectors);
 640   endGroup("GameObject");
 641
 642   addGroup( "Transform" );
 643
 644      addProtectedField( "position", TypeMatrixPosition, Offset( mObjToWorld, SceneObject ),
 645         &_setFieldPosition, &defaultProtectedGetFn,
 646         "Object world position." );
 647      addProtectedField( "rotation", TypeMatrixRotation, Offset( mObjToWorld, SceneObject ),
 648         &_setFieldRotation, &defaultProtectedGetFn,
 649         "Object world orientation." );
 650      addProtectedField( "scale", TypePoint3F, Offset( mObjScale, SceneObject ),
 651         &_setFieldScale, &defaultProtectedGetFn,
 652         "Object world scale." );
 653
 654   endGroup( "Transform" );
 655
 656   addGroup( "Editing" );
 657
 658      addProtectedField( "isRenderEnabled", TypeBool, Offset( mObjectFlags, SceneObject ),
 659         &_setRenderEnabled, &_getRenderEnabled,
 660         "Controls client-side rendering of the object.\n"
 661         "@see isRenderable()\n" );
 662
 663      addProtectedField( "isSelectionEnabled", TypeBool, Offset( mObjectFlags, SceneObject ),
 664         &_setSelectionEnabled, &_getSelectionEnabled,
 665         "Determines if the object may be selected from wihin the Tools.\n"
 666         "@see isSelectable()\n" );
 667
 668   endGroup( "Editing" );
 669
 670   addGroup( "Mounting" );
 671
 672      addProtectedField( "mountPID", TypePID, Offset( mMountPID, SceneObject ), &_setMountPID, &defaultProtectedGetFn,
 673         "@brief PersistentID of object we are mounted to.\n\n"
 674         "Unlike the SimObjectID that is determined at run time, the PersistentID of an object is saved with the level/mission and "
 675         "may be used to form a link between objects." );
 676      addField( "mountNode", TypeS32, Offset( mMount.node, SceneObject ), "Node we are mounted to." );
 677      addField( "mountPos", TypeMatrixPosition, Offset( mMount.xfm, SceneObject ), "Position we are mounted at ( object space of our mount object )." );
 678      addField( "mountRot", TypeMatrixRotation, Offset( mMount.xfm, SceneObject ), "Rotation we are mounted at ( object space of our mount object )." );
 679
 680   endGroup( "Mounting" );
 681
 682   Parent::initPersistFields();
 683}
 684
 685bool SceneObject::_setGameObject(void* object, const char* index, const char* data)
 686{
 687   // Sanity!
 688   AssertFatal(data != NULL, "Cannot use a NULL asset Id.");
 689
 690   return true; //rbI->setMeshAsset(data);
 691}
 692
 693//-----------------------------------------------------------------------------
 694
 695bool SceneObject::_setFieldPosition( void *object, const char *index, const char *data )
 696{
 697   SceneObject* so = static_cast<SceneObject*>( object );
 698   if ( so )
 699   {
 700      MatrixF txfm( so->getTransform() );
 701      Con::setData( TypeMatrixPosition, &txfm, 0, 1, &data );
 702      so->setTransform( txfm );
 703   }
 704   return false;
 705}
 706
 707//-----------------------------------------------------------------------------
 708
 709bool SceneObject::_setFieldRotation( void *object, const char *index, const char *data )
 710{
 711   SceneObject* so = static_cast<SceneObject*>( object );
 712   if ( so )
 713   {
 714      MatrixF txfm( so->getTransform() );
 715      Con::setData( TypeMatrixRotation, &txfm, 0, 1, &data );
 716      so->setTransform( txfm );
 717   }
 718   return false;
 719}
 720
 721//-----------------------------------------------------------------------------
 722
 723bool SceneObject::_setFieldScale( void *object, const char *index, const char *data )
 724{
 725   SceneObject* so = static_cast<SceneObject*>( object );
 726   if ( so )
 727   {
 728      Point3F scale;
 729      Con::setData( TypePoint3F, &scale, 0, 1, &data );
 730      so->setScale( scale );
 731   }
 732   return false;
 733}
 734
 735//-----------------------------------------------------------------------------
 736
 737bool SceneObject::writeField( StringTableEntry fieldName, const char* value )
 738{
 739   if( !Parent::writeField( fieldName, value ) )
 740      return false;
 741      
 742   static StringTableEntry sIsRenderEnabled = StringTable->insert( "isRenderEnabled" );
 743   static StringTableEntry sIsSelectionEnabled = StringTable->insert( "isSelectionEnabled" );
 744   static StringTableEntry sMountNode = StringTable->insert( "mountNode" );
 745   static StringTableEntry sMountPos = StringTable->insert( "mountPos" );
 746   static StringTableEntry sMountRot = StringTable->insert( "mountRot" );
 747   
 748   // Don't write flag fields if they are at their default values.
 749   
 750   if( fieldName == sIsRenderEnabled && dAtob( value ) )
 751      return false;
 752   else if( fieldName == sIsSelectionEnabled && dAtob( value ) )
 753      return false;
 754   else if ( mMountPID == NULL && ( fieldName == sMountNode || 
 755                                    fieldName == sMountPos || 
 756                                    fieldName == sMountRot ) )
 757   {
 758      return false;
 759   }
 760
 761      
 762   return true;
 763}
 764
 765//-----------------------------------------------------------------------------
 766
 767static void scopeCallback( SceneObject* obj, void* conPtr )
 768{
 769   NetConnection* ptr = reinterpret_cast< NetConnection* >( conPtr );
 770   if( obj->isScopeable() )
 771      ptr->objectInScope(obj);
 772}
 773
 774void SceneObject::onCameraScopeQuery( NetConnection* connection, CameraScopeQuery* query )
 775{
 776   SceneManager* sceneManager = getSceneManager();
 777   GameConnection* conn  = dynamic_cast<GameConnection*> (connection);
 778   if (conn && (query->visibleDistance = conn->getVisibleGhostDistance()) == 0.0f)
 779      if ((query->visibleDistance = sceneManager->getVisibleGhostDistance()) == 0.0f)
 780         query->visibleDistance = sceneManager->getVisibleDistance();
 781
 782   // Object itself is in scope.
 783
 784   if( this->isScopeable() )
 785      connection->objectInScope( this );
 786
 787   // If we're mounted to something, that object is in scope too.
 788
 789   if( isMounted() )
 790      connection->objectInScope( mMount.object );
 791
 792   // If we're added to a scene graph, let the graph do the scene scoping.
 793   // Otherwise just put everything in the server container in scope.
 794
 795   if( getSceneManager() )
 796      getSceneManager()->scopeScene( query, connection );
 797   else
 798      gServerContainer.findObjects( 0xFFFFFFFF, scopeCallback, connection );
 799}
 800
 801//-----------------------------------------------------------------------------
 802
 803bool SceneObject::isRenderEnabled() const
 804{
 805#ifdef TORQUE_TOOLS
 806   if (gEditingMission)
 807   {
 808      AbstractClassRep *classRep = getClassRep();
 809      return (mObjectFlags.test(RenderEnabledFlag) && classRep->isRenderEnabled());
 810   }
 811#endif
 812   return (mObjectFlags.test(RenderEnabledFlag));
 813}
 814
 815//-----------------------------------------------------------------------------
 816
 817void SceneObject::setRenderEnabled( bool value )
 818{
 819   if( value )
 820      mObjectFlags.set( RenderEnabledFlag );
 821   else
 822      mObjectFlags.clear( RenderEnabledFlag );
 823      
 824   setMaskBits( FlagMask );
 825}
 826
 827//-----------------------------------------------------------------------------
 828
 829const char* SceneObject::_getRenderEnabled( void* object, const char* data )
 830{
 831   SceneObject* obj = reinterpret_cast< SceneObject* >( object );
 832   if( obj->mObjectFlags.test( RenderEnabledFlag ) )
 833      return "1";
 834   else
 835      return "0";
 836}
 837
 838//-----------------------------------------------------------------------------
 839
 840bool SceneObject::_setRenderEnabled( void *object, const char *index, const char *data )
 841{
 842   SceneObject* obj = reinterpret_cast< SceneObject* >( object );
 843   obj->setRenderEnabled( dAtob( data ) );
 844   return false;
 845}
 846
 847//-----------------------------------------------------------------------------
 848
 849bool SceneObject::isSelectionEnabled() const
 850{
 851   AbstractClassRep *classRep = getClassRep();
 852   return ( mObjectFlags.test( SelectionEnabledFlag ) && classRep->isSelectionEnabled() );
 853}
 854
 855//-----------------------------------------------------------------------------
 856
 857void SceneObject::setSelectionEnabled( bool value )
 858{
 859   if( value )
 860      mObjectFlags.set( SelectionEnabledFlag );
 861   else
 862      mObjectFlags.clear( SelectionEnabledFlag );
 863      
 864   // Not synchronized on network so don't set dirty bit.
 865}
 866
 867//-----------------------------------------------------------------------------
 868
 869const char* SceneObject::_getSelectionEnabled( void* object, const char* data )
 870{
 871   SceneObject* obj = reinterpret_cast< SceneObject* >( object );
 872   if( obj->mObjectFlags.test( SelectionEnabledFlag ) )
 873      return "true";
 874   else
 875      return "false";
 876}
 877
 878//-----------------------------------------------------------------------------
 879
 880bool SceneObject::_setSelectionEnabled( void *object, const char *index, const char *data )
 881{
 882   SceneObject* obj = reinterpret_cast< SceneObject* >( object );
 883   obj->setSelectionEnabled( dAtob( data ) );
 884   return false;
 885}
 886
 887//--------------------------------------------------------------------------
 888
 889U32 SceneObject::packUpdate( NetConnection* conn, U32 mask, BitStream* stream )
 890{
 891   U32 retMask = Parent::packUpdate( conn, mask, stream );
 892
 893   if ( stream->writeFlag( mask & FlagMask ) )
 894      stream->writeRangedU32( (U32)mObjectFlags, 0, getObjectFlagMax() );
 895
 896   // PATHSHAPE
 897   //Begin attachment
 898   retMask = 0; //retry mask
 899
 900   if (stream->writeFlag(getParent() != NULL))   {
 901      stream->writeAffineTransform(mGraph.objToParent);
 902   }
 903   if (stream->writeFlag(mask & MountedMask))
 904   {
 905      // Check to see if we need to write an object ID      
 906      if (stream->writeFlag(mGraph.parent))      {
 907         S32 t = conn->getGhostIndex(mGraph.parent);
 908         // Check to see if we can actually ghost this...         
 909         if (t == -1)         {
 910            // Cant, try again later            
 911            retMask |= MountedMask;
 912            stream->writeFlag(false);
 913         }
 914         else {
 915            // Can, write it.            
 916            stream->writeFlag(true);
 917            stream->writeRangedU32(U32(t), 0, NetConnection::MaxGhostCount);
 918            stream->writeAffineTransform(mGraph.objToParent);
 919            //Con::errorf("%d: sent mounted on %d", getId(), mGraph.parent->getId());         
 920         }
 921      }
 922   }
 923   // End of Attachment   
 924   // PATHSHAPE END
 925
 926   if ( mask & MountedMask ) 
 927   {                  
 928      if ( mMount.object ) 
 929      {
 930         S32 gIndex = conn->getGhostIndex( mMount.object );
 931
 932         if ( stream->writeFlag( gIndex != -1 ) ) 
 933         {
 934            stream->writeFlag( true );
 935            stream->writeInt( gIndex, NetConnection::GhostIdBitSize );
 936            if ( stream->writeFlag( mMount.node != -1 ) )
 937               stream->writeInt( mMount.node, NumMountPointBits );
 938            mathWrite( *stream, mMount.xfm );
 939         }
 940         else
 941            // Will have to try again later
 942            retMask |= MountedMask;
 943      }
 944      else
 945         // Unmount if this isn't the initial packet
 946         if ( stream->writeFlag( !(mask & InitialUpdateMask) ) )
 947            stream->writeFlag( false );
 948   }
 949   else
 950      stream->writeFlag( false );
 951   
 952   return retMask;
 953}
 954
 955//-----------------------------------------------------------------------------
 956
 957void SceneObject::unpackUpdate( NetConnection* conn, BitStream* stream )
 958{
 959   Parent::unpackUpdate( conn, stream );
 960   
 961   // FlagMask
 962   if ( stream->readFlag() )      
 963      mObjectFlags = stream->readRangedU32( 0, getObjectFlagMax() );
 964
 965   // PATHSHAPE  
 966   // begin of attachment
 967   if (stream->readFlag())
 968   {
 969      MatrixF m;
 970      stream->readAffineTransform(&m);
 971      mGraph.objToParent = m;
 972   }
 973   if (stream->readFlag())
 974   {
 975      // Check to see if we need to read an object ID      
 976      if (stream->readFlag())
 977      {
 978         // Check to see if we can actually ghost this...         
 979         if (stream->readFlag())
 980         {
 981            GameBase *newParent = static_cast<GameBase*>(conn->resolveGhost(stream->readRangedU32(0, NetConnection::MaxGhostCount)));
 982            MatrixF m;
 983            stream->readAffineTransform(&m);
 984
 985            if (getParent() != newParent)
 986            {
 987               clearProcessAfter();
 988               processAfter(newParent);
 989            }
 990
 991            attachToParent(newParent, &m);
 992            //Con::errorf("%d: got mounted on %d", getId(), mParentObject->getId());         
 993         }
 994      }
 995      else
 996      {
 997         attachToParent(NULL);
 998      }
 999   }
1000   // End of attachment
1001   // PATHSHAPE END
1002
1003   // MountedMask
1004   if ( stream->readFlag() ) 
1005   {
1006      if ( stream->readFlag() ) 
1007      {
1008         S32 gIndex = stream->readInt( NetConnection::GhostIdBitSize );
1009         SceneObject* obj = dynamic_cast<SceneObject*>( conn->resolveGhost( gIndex ) );
1010         S32 node = -1;
1011         if ( stream->readFlag() ) // node != -1
1012            node = stream->readInt( NumMountPointBits );
1013         MatrixF xfm;
1014         mathRead( *stream, &xfm );
1015         if ( !obj )
1016         {
1017            conn->setLastError( "Invalid packet from server." );
1018            return;
1019         }
1020         obj->mountObject( this, node, xfm );
1021      }
1022      else
1023         unmount();
1024   }
1025}
1026
1027//-----------------------------------------------------------------------------
1028
1029void SceneObject::_updateZoningState() const
1030{
1031   if( mZoneRefDirty )
1032   {
1033      SceneZoneSpaceManager* manager = getSceneManager()->getZoneManager();
1034      if( manager )
1035         manager->updateObject( const_cast< SceneObject* >( this ) );
1036      else
1037         mZoneRefDirty = false;
1038   }
1039}
1040
1041//-----------------------------------------------------------------------------
1042
1043U32 SceneObject::getCurrZone( const U32 index ) const
1044{
1045   _updateZoningState();
1046
1047   // Not the most efficient way to do this, walking the list,
1048   //  but it's an uncommon call...
1049   ZoneRef* walk = mZoneRefHead;
1050   for( U32 i = 0; i < index; ++ i )
1051   {
1052      walk = walk->nextInObj;
1053      AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" );
1054   }
1055   AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" );
1056
1057   return walk->zone;
1058}
1059
1060//-----------------------------------------------------------------------------
1061
1062Point3F SceneObject::getPosition() const
1063{
1064   Point3F pos;
1065   mObjToWorld.getColumn(3, &pos);
1066   return pos;
1067}
1068
1069//-----------------------------------------------------------------------------
1070
1071Point3F SceneObject::getRenderPosition() const
1072{
1073   Point3F pos;
1074   mRenderObjToWorld.getColumn(3, &pos);
1075   return pos;
1076}
1077
1078//-----------------------------------------------------------------------------
1079
1080void SceneObject::setPosition(const Point3F &pos)
1081{
1082   AssertFatal( !mIsNaN( pos ), "SceneObject::setPosition() - The position is NaN!" );
1083
1084   MatrixF xform = mObjToWorld;
1085   xform.setColumn(3, pos);
1086   setTransform(xform);
1087}
1088
1089//-----------------------------------------------------------------------------
1090
1091F32 SceneObject::distanceTo(const Point3F &pnt) const
1092{
1093   return mWorldBox.getDistanceToPoint( pnt );   
1094}
1095
1096//-----------------------------------------------------------------------------
1097
1098void SceneObject::processAfter( ProcessObject *obj )
1099{
1100   AssertFatal( dynamic_cast<SceneObject*>( obj ), "SceneObject::processAfter - Got non-SceneObject!" );
1101
1102   mAfterObject = (SceneObject*)obj;
1103   if ( mAfterObject->mAfterObject == this )
1104      mAfterObject->mAfterObject = NULL;
1105
1106   getProcessList()->markDirty();
1107}
1108
1109//-----------------------------------------------------------------------------
1110
1111void SceneObject::clearProcessAfter()
1112{
1113   mAfterObject = NULL;
1114}
1115
1116//-----------------------------------------------------------------------------
1117
1118void SceneObject::setProcessTick( bool t )
1119{
1120   if ( t == mProcessTick )
1121      return;
1122
1123   if ( mProcessTick )
1124   {
1125      if ( !getMountedObjectCount() )
1126         plUnlink(); // Only unlink if there is nothing mounted to us
1127      mProcessTick = false;
1128   }
1129   else
1130   {
1131      // Just to be sure...
1132      plUnlink();
1133
1134      getProcessList()->addObject( this );
1135
1136      mProcessTick = true;  
1137   }   
1138}
1139
1140//-----------------------------------------------------------------------------
1141
1142ProcessList* SceneObject::getProcessList() const
1143{
1144   if ( isClientObject() )      
1145      return ClientProcessList::get();
1146   else
1147      return ServerProcessList::get();
1148}
1149
1150//-------------------------------------------------------------------------
1151
1152bool SceneObject::isMounted()
1153{
1154   resolveMountPID();
1155
1156   return mMount.object != NULL;
1157}
1158
1159//-----------------------------------------------------------------------------
1160
1161S32 SceneObject::getMountedObjectCount()
1162{
1163   S32 count = 0;
1164   for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link)
1165      count++;
1166   return count;
1167}
1168
1169//-----------------------------------------------------------------------------
1170
1171SceneObject* SceneObject::getMountedObject(S32 idx)
1172{
1173   if (idx >= 0) {
1174      S32 count = 0;
1175      for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link)
1176         if (count++ == idx)
1177            return itr;
1178   }
1179   return NULL;
1180}
1181
1182//-----------------------------------------------------------------------------
1183
1184S32 SceneObject::getMountedObjectNode(S32 idx)
1185{
1186   if (idx >= 0) {
1187      S32 count = 0;
1188      for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link)
1189         if (count++ == idx)
1190            return itr->mMount.node;
1191   }
1192   return -1;
1193}
1194
1195//-----------------------------------------------------------------------------
1196
1197SceneObject* SceneObject::getMountNodeObject(S32 node)
1198{
1199   for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link)
1200      if (itr->mMount.node == node)
1201         return itr;
1202   return NULL;
1203}
1204
1205//-----------------------------------------------------------------------------
1206
1207bool SceneObject::_setMountPID( void* object, const char* index, const char* data )
1208{
1209   SceneObject* so = static_cast<SceneObject*>( object );
1210   if ( so )
1211   {
1212      // Unmount old object (PID reference is released even if it had been resolved yet)
1213      if ( so->mMountPID )
1214      {
1215         so->mMountPID->decRefCount();
1216         so->mMountPID = NULL;
1217      }
1218      so->unmount();
1219
1220      // Get the new PID (new object will be mounted on demand)
1221      Con::setData( TypePID, &so->mMountPID, 0, 1, &data );
1222      if ( so->mMountPID )
1223         so->mMountPID->incRefCount();    // Prevent PID from being deleted out from under us!
1224   }
1225   return false;
1226}
1227
1228void SceneObject::resolveMountPID()
1229{
1230   if ( mMountPID && !mMount.object )
1231   {
1232      SceneObject *obj = dynamic_cast< SceneObject* >( mMountPID->getObject() );
1233      if ( obj )
1234         obj->mountObject( this, mMount.node, mMount.xfm );
1235   }
1236}
1237
1238//-----------------------------------------------------------------------------
1239
1240void SceneObject::mountObject( SceneObject *obj, S32 node, const MatrixF &xfm )
1241{
1242   if ( obj->mMount.object == this )
1243   {
1244      // Already mounted to this
1245      // So update our node and xfm which may have changed.
1246      obj->mMount.node = node;
1247      obj->mMount.xfm = xfm;
1248   }
1249   else
1250   {
1251      if ( obj->mMount.object )
1252         obj->unmount();
1253
1254      obj->mMount.object = this;
1255      obj->mMount.node = node;
1256      obj->mMount.link = mMount.list;
1257      obj->mMount.xfm = xfm;
1258      mMount.list = obj;
1259
1260      // Assign PIDs to both objects
1261      if ( isServerObject() )
1262      {
1263         obj->getOrCreatePersistentId();
1264         if ( !obj->mMountPID )
1265         {
1266            obj->mMountPID = getOrCreatePersistentId();
1267            obj->mMountPID->incRefCount();
1268         }
1269      }
1270
1271      obj->onMount( this, node );
1272   }
1273}
1274
1275//-----------------------------------------------------------------------------
1276
1277void SceneObject::unmountObject( SceneObject *obj )
1278{
1279   if ( obj->mMount.object == this ) 
1280   {
1281      // Find and unlink the object
1282      for ( SceneObject **ptr = &mMount.list; *ptr; ptr = &(*ptr)->mMount.link )
1283      {
1284         if ( *ptr == obj )
1285         {
1286            *ptr = obj->mMount.link;
1287            break;
1288         }
1289      }
1290
1291      obj->mMount.object = NULL;
1292      obj->mMount.link = NULL;
1293
1294      if( obj->mMountPID != NULL ) // Only on server.
1295      {
1296         obj->mMountPID->decRefCount();
1297         obj->mMountPID = NULL;
1298      }
1299
1300      obj->onUnmount( this, obj->mMount.node );
1301   }
1302}
1303
1304//-----------------------------------------------------------------------------
1305
1306void SceneObject::unmount()
1307{
1308   if (mMount.object)
1309      mMount.object->unmountObject(this);
1310}
1311
1312//-----------------------------------------------------------------------------
1313
1314void SceneObject::onMount( SceneObject *obj, S32 node )
1315{   
1316   deleteNotify( obj );
1317
1318   if ( !isGhost() ) 
1319   {      
1320      setMaskBits( MountedMask );      
1321      //onMount_callback( node );
1322   }
1323}
1324
1325//-----------------------------------------------------------------------------
1326
1327void SceneObject::onUnmount( SceneObject *obj, S32 node )
1328{
1329   clearNotify(obj);
1330
1331   if ( !isGhost() ) 
1332   {           
1333      setMaskBits( MountedMask );      
1334      //onUnmount_callback( node );
1335   }
1336}
1337
1338//-----------------------------------------------------------------------------
1339
1340void SceneObject::getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat )
1341{
1342   MatrixF mountTransform( xfm );
1343   const Point3F &scale = getScale();
1344   Point3F position = mountTransform.getPosition();
1345   position.convolve( scale );
1346   mountTransform.setPosition( position );
1347
1348   outMat->mul( mObjToWorld, mountTransform );
1349}
1350
1351//-----------------------------------------------------------------------------
1352
1353void SceneObject::getRenderMountTransform( F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat )
1354{
1355   MatrixF mountTransform( xfm );
1356   const Point3F &scale = getScale();
1357   Point3F position = mountTransform.getPosition();
1358   position.convolve( scale );
1359   mountTransform.setPosition( position );
1360
1361   outMat->mul( mRenderObjToWorld, mountTransform );
1362}
1363
1364//=============================================================================
1365//    Console API.
1366//=============================================================================
1367// MARK: ---- Console API ----
1368
1369//-----------------------------------------------------------------------------
1370
1371DefineEngineMethod( SceneObject, getType, S32, (),,
1372   "Return the type mask for this object.\n"
1373   "@return The numeric type mask for the object." )
1374{
1375   return object->getTypeMask();
1376}
1377
1378//-----------------------------------------------------------------------------
1379
1380DefineEngineMethod( SceneObject, mountObject, bool,
1381   ( SceneObject* objB, S32 slot, TransformF txfm ), ( TransformF::Identity ),
1382   "@brief Mount objB to this object at the desired slot with optional transform.\n\n"
1383
1384   "@param objB  Object to mount onto us\n"
1385   "@param slot  Mount slot ID\n"
1386   "@param txfm (optional) mount offset transform\n"
1387   "@return true if successful, false if failed (objB is not valid)" )
1388{
1389   if ( objB )
1390   {
1391      object->mountObject( objB, slot, txfm.getMatrix() );
1392      return true;
1393   }
1394   return false;
1395}
1396
1397//-----------------------------------------------------------------------------
1398
1399DefineEngineMethod( SceneObject, unmountObject, bool, ( SceneObject* target ),,
1400   "@brief Unmount an object from ourselves.\n\n"
1401
1402   "@param target object to unmount\n"
1403   "@return true if successful, false if failed\n" )
1404{
1405   if ( target )
1406   {
1407      object->unmountObject(target);
1408      return true;
1409   }
1410   return false;
1411}
1412
1413//-----------------------------------------------------------------------------
1414
1415DefineEngineMethod( SceneObject, unmount, void, (),,
1416   "Unmount us from the currently mounted object if any.\n" )
1417{
1418   object->unmount();
1419}
1420
1421//-----------------------------------------------------------------------------
1422
1423DefineEngineMethod( SceneObject, isMounted, bool, (),,
1424   "@brief Check if we are mounted to another object.\n\n"
1425   "@return true if mounted to another object, false if not mounted." )
1426{
1427   return object->isMounted();
1428}
1429
1430//-----------------------------------------------------------------------------
1431
1432DefineEngineMethod( SceneObject, getObjectMount, S32, (),,
1433   "@brief Get the object we are mounted to.\n\n"
1434   "@return the SimObjectID of the object we're mounted to, or 0 if not mounted." )
1435{
1436   return object->isMounted()? object->getObjectMount()->getId(): 0;
1437}
1438
1439//-----------------------------------------------------------------------------
1440
1441DefineEngineMethod( SceneObject, getMountedObjectCount, S32, (),,
1442   "Get the number of objects mounted to us.\n"
1443   "@return the number of mounted objects." )
1444{
1445   return object->getMountedObjectCount();
1446}
1447
1448//-----------------------------------------------------------------------------
1449
1450DefineEngineMethod( SceneObject, getMountedObject, S32, ( S32 slot ),,
1451   "Get the object mounted at a particular slot.\n"
1452   "@param slot mount slot index to query\n"
1453   "@return ID of the object mounted in the slot, or 0 if no object." )
1454{
1455   SceneObject* mobj = object->getMountedObject( slot );
1456   return mobj? mobj->getId(): 0;
1457}
1458
1459//-----------------------------------------------------------------------------
1460
1461DefineEngineMethod( SceneObject, getMountedObjectNode, S32, ( S32 slot ),,
1462   "@brief Get the mount node index of the object mounted at our given slot.\n\n"
1463   "@param slot mount slot index to query\n"
1464   "@return index of the mount node used by the object mounted in this slot." )
1465{
1466   return object->getMountedObjectNode( slot );
1467}
1468
1469//-----------------------------------------------------------------------------
1470
1471DefineEngineMethod( SceneObject, getMountNodeObject, S32, ( S32 node ),,
1472   "@brief Get the object mounted at our given node index.\n\n"
1473   "@param node mount node index to query\n"
1474   "@return ID of the first object mounted at the node, or 0 if none found." )
1475{
1476   SceneObject* mobj = object->getMountNodeObject( node );
1477   return mobj? mobj->getId(): 0;
1478}
1479
1480//-----------------------------------------------------------------------------
1481
1482DefineEngineMethod( SceneObject, getTransform, TransformF, (),,
1483   "Get the object's transform.\n"
1484   "@return the current transform of the object\n" )
1485{
1486   return object->getTransform();
1487}
1488
1489//-----------------------------------------------------------------------------
1490
1491DefineEngineMethod( SceneObject, getInverseTransform, TransformF, (),,
1492   "Get the object's inverse transform.\n"
1493   "@return the inverse transform of the object\n" )
1494{
1495   return object->getWorldTransform();
1496}
1497
1498//-----------------------------------------------------------------------------
1499
1500DefineEngineMethod( SceneObject, getPosition, Point3F, (),,
1501   "Get the object's world position.\n"
1502   "@return the current world position of the object\n" )
1503{
1504   return object->getTransform().getPosition();
1505}
1506
1507DefineEngineMethod( SceneObject, setPosition, void, (Point3F pos),,
1508   "Set the object's world position.\n"
1509   "@param pos the new world position of the object\n" )
1510{
1511   return object->setPosition(pos);
1512}
1513
1514//-----------------------------------------------------------------------------
1515
1516DefineEngineMethod( SceneObject, getEulerRotation, Point3F, (),,
1517   "Get Euler rotation of this object.\n"
1518   "@return the orientation of the object in the form of rotations around the "
1519   "X, Y and Z axes in degrees.\n" )
1520{
1521   Point3F euler = object->getTransform().toEuler();
1522   
1523   // Convert to degrees.
1524   euler.x = mRadToDeg( euler.x );
1525   euler.y = mRadToDeg( euler.y );
1526   euler.z = mRadToDeg( euler.z );
1527   
1528   return euler;
1529}
1530
1531//-----------------------------------------------------------------------------
1532
1533DefineEngineMethod( SceneObject, getForwardVector, VectorF, (),,
1534   "Get the direction this object is facing.\n"
1535   "@return a vector indicating the direction this object is facing.\n"
1536   "@note This is the object's y axis." )
1537{
1538   return object->getTransform().getForwardVector();
1539}
1540
1541//-----------------------------------------------------------------------------
1542
1543DefineEngineMethod( SceneObject, getRightVector, VectorF, (),,
1544   "Get the right vector of the object.\n"
1545   "@return a vector indicating the right direction of this object."
1546   "@note This is the object's x axis." )
1547{
1548   return object->getTransform().getRightVector();
1549}
1550
1551//-----------------------------------------------------------------------------
1552
1553DefineEngineMethod( SceneObject, getUpVector, VectorF, (),,
1554   "Get the up vector of the object.\n"
1555   "@return a vector indicating the up direction of this object."
1556   "@note This is the object's z axis." )
1557{
1558   return object->getTransform().getUpVector();
1559}
1560
1561//-----------------------------------------------------------------------------
1562
1563DefineEngineMethod( SceneObject, setTransform, void, ( TransformF txfm ),,
1564   "Set the object's transform (orientation and position)."
1565   "@param txfm object transform to set" )
1566{
1567// PATHSHAPE
1568   object->PerformUpdatesForChildren(txfm.getMatrix());
1569// PATHSHAPE END
1570   if ( !txfm.hasRotation() )
1571      object->setPosition( txfm.getPosition() );
1572   else
1573      object->setTransform( txfm.getMatrix() );
1574}
1575
1576//-----------------------------------------------------------------------------
1577
1578DefineEngineMethod( SceneObject, getScale, Point3F, (),,
1579   "Get the object's scale.\n"
1580   "@return object scale as a Point3F" )
1581{
1582   return object->getScale();
1583}
1584
1585//-----------------------------------------------------------------------------
1586
1587DefineEngineMethod( SceneObject, setScale, void, ( Point3F scale ),,
1588   "Set the object's scale.\n"
1589   "@param scale object scale to set\n" )
1590{
1591   object->setScale( scale );
1592}
1593
1594//-----------------------------------------------------------------------------
1595
1596DefineEngineMethod( SceneObject, getWorldBox, Box3F, (),,
1597   "Get the object's world bounding box.\n"
1598   "@return six fields, two Point3Fs, containing the min and max points of the "
1599   "worldbox." )
1600{
1601   return object->getWorldBox();
1602}
1603
1604//-----------------------------------------------------------------------------
1605
1606DefineEngineMethod( SceneObject, getWorldBoxCenter, Point3F, (),,
1607   "Get the center of the object's world bounding box.\n"
1608   "@return the center of the world bounding box for this object." )
1609{
1610   Point3F center;
1611   object->getWorldBox().getCenter( &center );
1612   return center;
1613}
1614
1615//-----------------------------------------------------------------------------
1616
1617DefineEngineMethod( SceneObject, getObjectBox, Box3F, (),,
1618   "Get the object's bounding box (relative to the object's origin).\n"
1619   "@return six fields, two Point3Fs, containing the min and max points of the "
1620   "objectbox." )
1621{
1622   return object->getObjBox();
1623}
1624
1625//-----------------------------------------------------------------------------
1626
1627DefineEngineMethod( SceneObject, isGlobalBounds, bool, (),,
1628   "Check if this object has a global bounds set.\n"
1629   "If global bounds are set to be true, then the object is assumed to have an "
1630   "infinitely large bounding box for collision and rendering purposes.\n"
1631   "@return true if the object has a global bounds." )
1632{
1633   return object->isGlobalBounds();
1634}
1635
1636DefineEngineMethod(SceneObject, setForwardVector, void, (VectorF newForward, VectorF upVector), (VectorF(0, 0, 0), VectorF(0, 0, 1)),
1637   "Sets the forward vector of a scene object, making it face Y+ along the new vector.\n"
1638   "@param The new forward vector to set.\n"
1639   "@param (Optional) The up vector to use to help orient the rotation.")
1640{
1641   object->setForwardVector(newForward, upVector);
1642}
1643
1644// PATHSHAPE
1645// Move RenderTransform by set amount
1646// no longer used
1647
1648void SceneObject::moveRender(const Point3F &delta) 
1649{
1650   Point3F pos;
1651
1652   const MatrixF& tmat = getRenderTransform();
1653   tmat.getColumn(3,&pos);
1654   AngAxisF aa(tmat);
1655   pos += delta;
1656
1657   MatrixF mat;
1658   aa.setMatrix(&mat);
1659   mat.setColumn(3,pos);
1660   setRenderTransform(mat);
1661}
1662
1663void SceneObject::PerformUpdatesForChildren(MatrixF mat){
1664       UpdateXformChange(mat);
1665      for (U32 i=0; i < getNumChildren(); i++) {
1666         SceneObject *o = getChild(i);
1667         o->updateChildTransform(); //update the position of the child object
1668      }
1669}
1670
1671
1672
1673
1674
1675// This function will move the players based on how much it's
1676// parent have moved
1677void SceneObject::updateChildTransform(){
1678   if (getParent() != NULL){
1679      MatrixF one;
1680      MatrixF two;
1681      MatrixF three;
1682      MatrixF four;
1683      MatrixF mat;
1684      one= getTransform();
1685      two = getParent()->getTransform();
1686      one.affineInverse();
1687      four.mul(two,one);
1688      mat.mul(getParent()->mLastXform,getTransform());
1689      setTransform(mat);
1690   }
1691}
1692
1693// This function will move the rendered image based on how much it's
1694// parent have moved since the processtick.
1695// For some reason the player object must be updated via it's GetRenderTransform seen below,
1696// Other objects seem to require getTransform() only
1697void SceneObject::updateRenderChangesByParent(){
1698   if (getParent() != NULL){
1699      MatrixF renderXform = getParent()->getRenderTransform();
1700      MatrixF xform = getParent()->getTransform();
1701      xform.affineInverse();
1702
1703      MatrixF offset;
1704      offset.mul(renderXform, xform);
1705
1706          MatrixF mat;
1707      
1708      //add the "offset" caused by the parents change, and add it to it's own
1709      // This is needed by objects that update their own render transform thru interpolate tick
1710      // Mostly for stationary objects.
1711
1712          if (getClassName() == StringTable->insert("Player"))
1713         mat.mul(offset,getRenderTransform());  
1714      else                             
1715         mat.mul(offset,getTransform());   
1716         setRenderTransform(mat);
1717   }
1718}
1719
1720
1721
1722
1723
1724//Ramen - Move Transform by set amount
1725//written by  Anthony Lovell
1726void SceneObject::move(F32 x, F32 y, F32 z) 
1727{
1728    Point3F delta;
1729    delta.x = x;
1730    delta.y = y;
1731    delta.z = z;
1732    move(delta);
1733}
1734// move by a specified delta in root coordinate space
1735void SceneObject::move(const Point3F &delta) 
1736{
1737   Point3F pos;
1738
1739   const MatrixF& tmat = getTransform();
1740   tmat.getColumn(3,&pos);
1741   AngAxisF aa(tmat);
1742 
1743   pos += delta;
1744
1745   MatrixF mat;
1746   aa.setMatrix(&mat);
1747   mat.setColumn(3,pos);
1748   setTransform(mat);
1749}
1750
1751
1752
1753//written by  Anthony Lovell ----------------------------------------------------------
1754U32
1755SceneObject::getNumChildren() const
1756{   
1757    U32 num = 0;
1758    for (SceneObject *cur = mGraph.firstChild; cur; cur = cur->mGraph.nextSibling)
1759        num++;
1760    return num;
1761}
1762//written by  Anthony Lovell ----------------------------------------------------------
1763SceneObject *
1764SceneObject::getChild(U32 index) const
1765{       
1766    SceneObject *cur = mGraph.firstChild;
1767    for (U32 i = 0; 
1768        cur && i < index; 
1769        i++)
1770        cur = cur->mGraph.nextSibling;
1771    return cur;
1772}
1773
1774
1775
1776
1777void SceneObject::UpdateXformChange(const MatrixF &mat){
1778// This function gets the difference between the Transform and current Render transform
1779// Used for Interpolation matching with the child objects who rely on this data.
1780
1781   MatrixF oldxform = getTransform();
1782
1783   oldxform.affineInverse();
1784   mLastXform.mul(mat,oldxform);
1785
1786}
1787
1788
1789//----------------------------------------------------------
1790bool
1791SceneObject::attachChildAt(SceneObject *subObject, MatrixF atThisOffset, S32 node)
1792{   
1793    AssertFatal(subObject, "attaching a null subObject");
1794    AssertFatal(!isChildOf(subObject), "cyclic attachChild()");
1795    bool b = subObject->attachToParent(this, &atThisOffset, node);    
1796    if (!b) 
1797        return false;
1798    
1799    return true;
1800}
1801
1802//----------------------------------------------------------
1803bool
1804SceneObject::attachChildAt(SceneObject *subObject, Point3F atThisPosition)
1805{   
1806    AssertFatal(subObject, "attaching a null subObject");
1807    AssertFatal(!isChildOf(subObject), "cyclic attachChild()");
1808    bool b = subObject->attachToParent(this);
1809    if (!b) 
1810        return false;
1811        
1812    subObject->mGraph.objToParent.setColumn(3, atThisPosition);
1813//    calcTransformFromLocalTransform();
1814
1815    return true;
1816}
1817
1818//----------------------------------------------------------
1819bool
1820SceneObject::attachChild(SceneObject *child)
1821{   
1822   AssertFatal(child, "attaching a null subObject");
1823    AssertFatal(!isChildOf(child), "cyclic attachChild()");
1824   
1825    return  child->attachToParent(this);        
1826}
1827
1828
1829//----------------------------------------------------------
1830/// returns a count of children plus their children, recursively
1831U32
1832SceneObject::getNumProgeny() const
1833{   
1834    U32 num = 0;
1835    for (SceneObject *cur = mGraph.firstChild; cur; cur = cur->mGraph.nextSibling) {
1836        num += 1 + cur->getNumProgeny();
1837    }
1838    return num;
1839}
1840
1841DefineEngineMethod(SceneObject, getNumChildren, S32, (),, "returns number of direct child objects")
1842{
1843    return object->getNumChildren();
1844}
1845
1846DefineEngineMethod(SceneObject, getNumProgeny, S32, (),, "returns number of recursively-nested child objects")
1847{
1848    return object->getNumProgeny();
1849}
1850
1851DefineEngineMethod(SceneObject, getChild, S32, (S32 _index), (0), "getChild(S32 index) -- returns child SceneObject at given index")
1852{
1853    SceneObject *s = object->getChild(_index);
1854    return s ? s->getId() : 0;
1855}
1856
1857DefineEngineMethod(SceneObject, attachChildAt, bool, (SceneObject* _subObject, MatrixF _offset, S32 _node), (nullAsType<SceneObject*>(), MatrixF::Identity, 0), "(SceneObject subObject, MatrixF offset, S32 offset)"
1858              "Mount object to this one with the specified offset expressed in our coordinate space.")
1859{   
1860   if (_subObject != nullptr)
1861   {
1862      return object->attachChildAt(_subObject, _offset, _node);
1863   }
1864   else
1865   {
1866      Con::errorf("Couldn't addObject()!");
1867      return false;
1868   }
1869}
1870
1871DefineEngineMethod(SceneObject, attachToParent, bool, (const char*_sceneObject), ,"attachToParent(SceneObject)"
1872              "specify a null or non-null parent")
1873{   
1874    SceneObject * t;   
1875    
1876    if(Sim::findObject(_sceneObject, t))
1877    {
1878        return object->attachToParent(t);
1879    }
1880    else
1881    {      
1882        if ((!String::compare("0", _sceneObject))|| (!String::compare("", _sceneObject)))
1883            return object->attachToParent(NULL);
1884        else
1885        {
1886            Con::errorf("Couldn't setParent()!");   
1887            return false;
1888        }
1889    }
1890}
1891
1892DefineEngineMethod(SceneObject, getParent, S32, (),, "returns ID of parent SceneObject")
1893{
1894    SceneObject *p = object->getParent();
1895    return p ? p->getId() : -1;
1896}
1897
1898DefineEngineMethod(SceneObject, attachChild, bool, (const char*_subObject),, "(SceneObject subObject)"
1899              "attach an object to this one, preserving its present transform.")
1900{   
1901    SceneObject * t;   
1902    MatrixF m;   
1903    if(Sim::findObject(_subObject, t)) 
1904        return object->attachChild(t);
1905
1906    Con::errorf("Couldn't addObject()!");
1907    return false;
1908}
1909
1910bool SceneObject::isChildOf(SceneObject *so)
1911{    
1912    SceneObject *p = mGraph.parent;
1913    if (p) {
1914        if (p == so) 
1915            return true;
1916        else
1917            return p->isChildOf(so);
1918    } else
1919        return false;
1920}
1921
1922
1923
1924bool SceneObject::attachToParent(SceneObject *newParent, MatrixF *atThisOffset/* = NULL */, S32 node )
1925{   
1926   SceneObject *oldParent = mGraph.parent;
1927
1928    if (oldParent == newParent)
1929        return true;
1930
1931    // cycles in the scene hierarchy are forbidden!
1932    // that is:  a SceneObject cannot be a child of its progeny
1933    if (newParent && newParent->isChildOf(this))
1934        return false;
1935      
1936    mGraph.parent = newParent;
1937
1938    if (oldParent) {
1939
1940        clearNotify(oldParent); 
1941
1942        // remove this SceneObject from the list of children of oldParent
1943        SceneObject *cur = oldParent->mGraph.firstChild;
1944        if (cur == this) { // if we are the first child, this is easy
1945            oldParent->mGraph.firstChild = mGraph.nextSibling;
1946        } else {
1947            while (cur->mGraph.nextSibling != this) {
1948                cur = cur->mGraph.nextSibling;
1949                // ASSERT cur != NULL;
1950            }
1951            cur->mGraph.nextSibling = mGraph.nextSibling;
1952        }
1953        oldParent->onLostChild(this);
1954    }
1955     
1956    if (newParent) {
1957
1958        deleteNotify(newParent); // if we are deleted, inform our parent
1959
1960        // add this SceneObject to the list of children of oldParent
1961        mGraph.nextSibling = newParent->mGraph.firstChild;
1962        newParent->mGraph.firstChild = this;
1963        mGraph.parent = newParent;
1964        
1965        newParent->onNewChild(this);
1966
1967        if (atThisOffset)
1968            mGraph.objToParent = *atThisOffset;
1969    } else {
1970        mGraph.parent = NULL;
1971        mGraph.nextSibling = NULL;
1972        mGraph.objToParent = mObjToWorld;
1973    }
1974
1975    onLostParent(oldParent);
1976    onNewParent(newParent);
1977
1978    setMaskBits(MountedMask);
1979    return true;
1980}
1981
1982DefineEngineMethod(SceneObject, detachChild, bool, (const char*_subObject),, "SceneObject subObject")
1983{   
1984    SceneObject * t;       
1985    if(Sim::findObject(_subObject, t))   {
1986        return t->attachToParent(NULL);
1987    } else 
1988        return false;  
1989}
1990
1991// subclasses can do something with these if they care to
1992void SceneObject::onNewParent(SceneObject *newParent) {}  
1993void SceneObject::onLostParent(SceneObject *oldParent){}    
1994void SceneObject::onNewChild(SceneObject *newKid){}   
1995void SceneObject::onLostChild(SceneObject *lostKid){}
1996