Torque3D Documentation / _generateds / guiShapeEdPreview.cpp

guiShapeEdPreview.cpp

Engine/source/gui/editor/guiShapeEdPreview.cpp

More...

Public Functions

ConsoleDocClass(GuiShapeEdPreview , "@brief This <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> provides the 3D view <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the Shape Editor tool, and is " "not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> general purpose <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">use.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControls\n</a>" " @internal" )
DefineEngineMethod(GuiShapeEdPreview , addThread , void , () , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> thread (initially without any sequence set)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , computeShapeBounds , Box3F , () , "Compute the bounding box of the shape using the current detail and node <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transforms\n\n</a>" "@return the bounding box \"min.x min.y min.z max.x max.y max.z\"" )
DefineEngineMethod(GuiShapeEdPreview , exportToCollada , void , (const char *path) , "Export the current shape and all mounted objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> COLLADA (.dae).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "Note that animation is not exported, and all geometry is combined into <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "single <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mesh.\n\n</a>" " @param path Destination <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">filename\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , fitToShape , void , () , "Adjust the camera position and zoom <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> fit the shape within the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">view.\n\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , getMeshHidden , bool , (const char *name) , "Return whether the named object is currently <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">hidden\n\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , getMountThreadDir , F32 , (S32 slot) , "Get the playback direction of the sequence playing on this mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@return direction of the sequence (-1=reverse, 0=paused, 1=forward)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(GuiShapeEdPreview , getMountThreadPos , F32 , (S32 slot) , "Get the playback position of the sequence playing on this mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@return playback position of the sequence (0-1)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(GuiShapeEdPreview , getMountThreadSequence , const char * , (S32 slot) , "Get the name of the sequence playing on this mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@return name of the sequence (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(GuiShapeEdPreview , getThreadCount , S32 , () , "Get the number of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">threads\n\n</a>" "@return the number of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">threads\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , getThreadSequence , const char * , () , "Get the name of the sequence assigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the active thread" )
DefineEngineMethod(GuiShapeEdPreview , mountShape , bool , (const char *shapePath, const char *nodeName, const char *type, S32 slot) , "Mount <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> shape onto the <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> shape at the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">node\n\n</a>" "@param shapePath path <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the shape <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mount\n</a>" "@param nodeName name of the node on the <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> shape <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@param type type of mounting <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use (<a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a>, Image or Wheel)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param slot mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , refreshShape , void , () , "Refresh the shape (used when the shape meshes or nodes have been added or removed)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , refreshThreadSequences , void , () )
DefineEngineMethod(GuiShapeEdPreview , removeThread , void , (S32 slot) , "Removes the specifed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">thread\n\n</a>" "@param slot index of the thread <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">remove\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setAllMeshesHidden , void , (bool hidden) , "Show or hide all objects in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setMeshHidden , void , (const char *name, bool hidden) , "Show or hide the named object in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setModel , bool , (const char *shapePath) , "Sets the model <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be displayed in this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n\n</a>" "@param shapeName Name of the model <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">display.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the model was loaded successfully, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise.\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setMountNode , void , (S32 slot, const char *nodeName) , "Set the node <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> shape is mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param nodename name of the node <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setMountThreadDir , void , (S32 slot, F32 dir) , "Set the playback direction of the shape mounted in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param dir playback direction (-1=backwards, 0=paused, 1=forwards)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setMountThreadPos , void , (S32 slot, F32 pos) , "Set the sequence position of the shape mounted in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param pos sequence position (0-1)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setMountThreadSequence , void , (S32 slot, const char *name) , "Set the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the shape mounted in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param name name of the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">play\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setOrbitPos , void , (Point3F pos) , "Set the camera orbit <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n\n</a>" "@param pos Position in the form \"x y z\"\n" )
DefineEngineMethod(GuiShapeEdPreview , setThreadSequence , void , (const char *name, F32 duration, F32 pos, bool play) , (0, 0, false) , "Sets the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">thread.\n\n</a>" "@param name name of the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">play\n</a>" "@param duration transition duration (0 <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> no transition)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param pos position in the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transition <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@param play <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> true, the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> sequence will play during the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transition\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , setTimeScale , void , (F32 scale) , "Set the time scale of all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">threads\n\n</a>" "@param scale <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> time scale <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , unmountAll , void , () , "Unmount all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shapes\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , unmountShape , void , (S32 slot) , "Unmount the shape in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" )
DefineEngineMethod(GuiShapeEdPreview , updateNodeTransforms , void , () , "Refresh the shape node transforms (used when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> node transform has been modified externally)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )
IMPLEMENT_CALLBACK(GuiShapeEdPreview , onThreadPosChanged , void , (F32 pos, bool inTransition) , (pos, inTransition) , "Called when the position of the active thread has changed, such as during " "playback." )

Detailed Description

Public Variables

const F32 sMoveScaler 
const S32 sNodeRectSize 
const F32 sZoomScaler 

Public Functions

ConsoleDocClass(GuiShapeEdPreview , "@brief This <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> provides the 3D view <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the Shape Editor tool, and is " "not intended <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> general purpose <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">use.\n</a>" " @ingroup <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiControls\n</a>" " @internal" )

DefineEngineMethod(GuiShapeEdPreview , addThread , void , () , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> thread (initially without any sequence set)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , computeShapeBounds , Box3F , () , "Compute the bounding box of the shape using the current detail and node <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transforms\n\n</a>" "@return the bounding box \"min.x min.y min.z max.x max.y max.z\"" )

DefineEngineMethod(GuiShapeEdPreview , exportToCollada , void , (const char *path) , "Export the current shape and all mounted objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> COLLADA (.dae).\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "Note that animation is not exported, and all geometry is combined into <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> " "single <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mesh.\n\n</a>" " @param path Destination <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">filename\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , fitToShape , void , () , "Adjust the camera position and zoom <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> fit the shape within the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">view.\n\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , getMeshHidden , bool , (const char *name) , "Return whether the named object is currently <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">hidden\n\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , getMountThreadDir , F32 , (S32 slot) , "Get the playback direction of the sequence playing on this mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@return direction of the sequence (-1=reverse, 0=paused, 1=forward)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(GuiShapeEdPreview , getMountThreadPos , F32 , (S32 slot) , "Get the playback position of the sequence playing on this mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@return playback position of the sequence (0-1)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(GuiShapeEdPreview , getMountThreadSequence , const char * , (S32 slot) , "Get the name of the sequence playing on this mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@return name of the sequence (<a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> any)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(GuiShapeEdPreview , getThreadCount , S32 , () , "Get the number of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">threads\n\n</a>" "@return the number of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">threads\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , getThreadSequence , const char * , () , "Get the name of the sequence assigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the active thread" )

DefineEngineMethod(GuiShapeEdPreview , mountShape , bool , (const char *shapePath, const char *nodeName, const char *type, S32 slot) , "Mount <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> shape onto the <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> shape at the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">node\n\n</a>" "@param shapePath path <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the shape <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mount\n</a>" "@param nodeName name of the node on the <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> shape <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@param type type of mounting <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use (<a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a>, Image or Wheel)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param slot mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , refreshShape , void , () , "Refresh the shape (used when the shape meshes or nodes have been added or removed)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , refreshThreadSequences , void , () )

DefineEngineMethod(GuiShapeEdPreview , removeThread , void , (S32 slot) , "Removes the specifed <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">thread\n\n</a>" "@param slot index of the thread <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">remove\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setAllMeshesHidden , void , (bool hidden) , "Show or hide all objects in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setMeshHidden , void , (const char *name, bool hidden) , "Show or hide the named object in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shape\n\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setModel , bool , (const char *shapePath) , "Sets the model <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be displayed in this <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control\n\n</a>" "@param shapeName Name of the model <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">display.\n</a>" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> the model was loaded successfully, false <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">otherwise.\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setMountNode , void , (S32 slot, const char *nodeName) , "Set the node <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> shape is mounted <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to.\n\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param nodename name of the node <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setMountThreadDir , void , (S32 slot, F32 dir) , "Set the playback direction of the shape mounted in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param dir playback direction (-1=backwards, 0=paused, 1=forwards)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setMountThreadPos , void , (S32 slot, F32 pos) , "Set the sequence position of the shape mounted in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param pos sequence position (0-1)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setMountThreadSequence , void , (S32 slot, const char *name) , "Set the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the shape mounted in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param name name of the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">play\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setOrbitPos , void , (Point3F pos) , "Set the camera orbit <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">position\n\n</a>" "@param pos Position in the form \"x y z\"\n" )

DefineEngineMethod(GuiShapeEdPreview , setThreadSequence , void , (const char *name, F32 duration, F32 pos, bool play) , (0, 0, false) , "Sets the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> play <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the active <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">thread.\n\n</a>" "@param name name of the sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">play\n</a>" "@param duration transition duration (0 <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> no transition)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param pos position in the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> sequence <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transition <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">to\n</a>" "@param play <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> true, the <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> sequence will play during the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">transition\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , setTimeScale , void , (F32 scale) , "Set the time scale of all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">threads\n\n</a>" "@param scale <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> time scale <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">value\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , unmountAll , void , () , "Unmount all <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">shapes\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , unmountShape , void , (S32 slot) , "Unmount the shape in the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" "@param slot mounted shape <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">slot\n</a>" )

DefineEngineMethod(GuiShapeEdPreview , updateNodeTransforms , void , () , "Refresh the shape node transforms (used when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> node transform has been modified externally)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" )

IMPLEMENT_CALLBACK(GuiShapeEdPreview , onThreadPosChanged , void , (F32 pos, bool inTransition) , (pos, inTransition) , "Called when the position of the active thread has changed, such as during " "playback." )

IMPLEMENT_CONOBJECT(GuiShapeEdPreview )

   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#include "console/consoleTypes.h"
  25#include "console/console.h"
  26#include "console/engineAPI.h"
  27#include "gui/core/guiCanvas.h"
  28#include "gui/editor/guiShapeEdPreview.h"
  29#include "renderInstance/renderPassManager.h"
  30#include "lighting/lightManager.h"
  31#include "lighting/lightInfo.h"
  32#include "core/resourceManager.h"
  33#include "scene/sceneManager.h"
  34#include "scene/sceneRenderState.h"
  35#include "gfx/primBuilder.h"
  36#include "gfx/gfxDrawUtil.h"
  37#include "collision/concretePolyList.h"
  38
  39#ifdef TORQUE_COLLADA
  40   #include "collision/optimizedPolyList.h"
  41   #include "ts/collada/colladaUtils.h"
  42#endif
  43
  44static const F32 sMoveScaler = 50.0f;
  45static const F32 sZoomScaler = 200.0f;
  46static const S32 sNodeRectSize = 16;
  47
  48IMPLEMENT_CONOBJECT( GuiShapeEdPreview );
  49
  50ConsoleDocClass( GuiShapeEdPreview,
  51   "@brief This control provides the 3D view for the Shape Editor tool, and is "
  52   "not intended for general purpose use.\n"
  53
  54   "@ingroup GuiControls\n"
  55   "@internal"
  56);
  57
  58IMPLEMENT_CALLBACK( GuiShapeEdPreview, onThreadPosChanged, void, ( F32 pos, bool inTransition ), ( pos, inTransition),
  59   "Called when the position of the active thread has changed, such as during "
  60   "playback." );
  61
  62
  63GuiShapeEdPreview::GuiShapeEdPreview()
  64:  mOrbitDist( 5.0f ),
  65   mMoveSpeed ( 1.0f ),
  66   mZoomSpeed ( 1.0f ),
  67   mGridDimension( 30, 30 ),
  68   mModel( NULL ),
  69   mRenderGhost( false ),
  70   mRenderNodes( false ),
  71   mRenderBounds( false ),
  72   mRenderObjBox( false ),
  73   mRenderColMeshes( false ),
  74   mRenderMounts( true ),
  75   mSunDiffuseColor( 255, 255, 255, 255 ),
  76   mSelectedNode( -1 ),
  77   mSunAmbientColor( 140, 140, 140, 255 ),
  78   mHoverNode( -1 ),
  79   mSelectedObject( -1 ),
  80   mUsingAxisGizmo( false ),
  81   mSelectedObjDetail( 0 ),
  82   mEditingSun( false ),
  83   mGizmoDragID( 0 ),
  84   mTimeScale( 1.0f ),
  85   mActiveThread( -1 ),
  86   mFakeSun( NULL ),
  87   mLastRenderTime( 0 ),
  88   mCameraRot( 0, 0, 3.9f ),
  89   mSunRot( 45.0f, 0, 135.0f ),
  90   mRenderCameraAxes( false ),
  91   mOrbitPos( 0, 0, 0 ),
  92   mFixedDetail( true ),
  93   mCurrentDL( 0 ),
  94   mDetailSize( 0 ),
  95   mDetailPolys( 0 ),
  96   mPixelSize( 0 ),
  97   mNumMaterials( 0 ),
  98   mNumDrawCalls( 0 ),
  99   mNumBones( 0 ),
 100   mNumWeights( 0 ),
 101   mColMeshes( 0 ),
 102   mColPolys( 0 )
 103{
 104   mActive = true;
 105
 106   // By default don't do dynamic reflection
 107   // updates for this viewport.
 108   mReflectPriority = 0.0f;
 109}
 110
 111GuiShapeEdPreview::~GuiShapeEdPreview()
 112{
 113   SAFE_DELETE( mModel );
 114   SAFE_DELETE( mFakeSun );
 115}
 116
 117void GuiShapeEdPreview::initPersistFields()
 118{
 119   addGroup( "Rendering" );
 120   addField( "editSun",        TypeBool,         Offset( mEditingSun, GuiShapeEdPreview ),
 121      "If true, dragging the gizmo will rotate the sun direction" );
 122   addField( "selectedNode",   TypeS32,          Offset( mSelectedNode, GuiShapeEdPreview ),
 123      "Index of the selected node, or -1 if none" );
 124   addField( "selectedObject", TypeS32,          Offset( mSelectedObject, GuiShapeEdPreview ),
 125      "Index of the selected object, or -1 if none" );
 126   addField( "selectedObjDetail", TypeS32,       Offset( mSelectedObjDetail, GuiShapeEdPreview ),
 127      "Index of the selected object detail mesh, or 0 if none" );
 128   addField( "gridDimension",  TypePoint2I,      Offset( mGridDimension, GuiShapeEdPreview ),
 129      "Grid dimensions (number of rows and columns) in the form \"rows cols\"" );
 130   addField( "renderGrid",     TypeBool,         Offset( mRenderGridPlane, EditTSCtrl ),
 131      "Flag indicating whether to draw the grid" );
 132   addField( "renderNodes",    TypeBool,         Offset( mRenderNodes, GuiShapeEdPreview ),
 133      "Flag indicating whether to render the shape nodes" );
 134   addField( "renderGhost",    TypeBool,         Offset( mRenderGhost, GuiShapeEdPreview ),
 135      "Flag indicating whether to render the shape in 'ghost' mode (transparent)" );
 136   addField( "renderBounds",   TypeBool,         Offset( mRenderBounds, GuiShapeEdPreview ),
 137      "Flag indicating whether to render the shape bounding box" );
 138   addField( "renderObjBox",   TypeBool,         Offset( mRenderObjBox, GuiShapeEdPreview ),
 139      "Flag indicating whether to render the selected object's bounding box" );
 140   addField( "renderColMeshes", TypeBool,        Offset( mRenderColMeshes, GuiShapeEdPreview ),
 141      "Flag indicating whether to render the shape's collision geometry" );
 142   addField( "renderMounts",   TypeBool,         Offset( mRenderMounts, GuiShapeEdPreview ),
 143      "Flag indicating whether to render mounted objects" );
 144   endGroup( "Rendering" );
 145
 146   addGroup( "Sun" );
 147   addProtectedField( "sunDiffuse", TypeColorI, Offset( mSunDiffuseColor, GuiShapeEdPreview ), &setFieldSunDiffuse, &defaultProtectedGetFn,
 148      "Ambient color for the sun" );
 149   addProtectedField( "sunAmbient", TypeColorI, Offset( mSunAmbientColor, GuiShapeEdPreview ), &setFieldSunAmbient, &defaultProtectedGetFn,
 150      "Diffuse color for the sun" );
 151   addProtectedField( "sunAngleX",  TypeF32,    Offset( mSunRot.x, GuiShapeEdPreview ), &setFieldSunAngleX, &defaultProtectedGetFn,
 152      "X-axis rotation angle for the sun" );
 153   addProtectedField( "sunAngleZ",  TypeF32,    Offset( mSunRot.z, GuiShapeEdPreview ), &setFieldSunAngleZ, &defaultProtectedGetFn,
 154      "Z-axis rotation angle for the sun" );
 155   endGroup( "Sun" );
 156
 157   addGroup( "Animation" );
 158   addField( "activeThread",              TypeS32,    Offset( mActiveThread, GuiShapeEdPreview ),
 159      "Index of the active thread, or -1 if none" );
 160   addProtectedField( "threadPos",        TypeF32,    NULL, &setFieldThreadPos, &getFieldThreadPos,
 161      "Current position of the active thread (0-1)" );
 162   addProtectedField( "threadDirection",  TypeS32,    NULL, &setFieldThreadDir, &getFieldThreadDir,
 163      "Playback direction of the active thread" );
 164   addProtectedField( "threadPingPong",   TypeBool,   NULL, &setFieldThreadPingPong, &getFieldThreadPingPong,
 165      "'PingPong' mode of the active thread" );
 166   endGroup( "Animation" );
 167
 168   addGroup( "Detail Stats" );
 169   addField( "fixedDetail",            TypeBool, Offset( mFixedDetail, GuiShapeEdPreview ),
 170      "If false, the current detail is selected based on camera distance" );
 171   addField( "orbitDist",              TypeF32, Offset( mOrbitDist, GuiShapeEdPreview ),
 172      "The current distance from the camera to the model" );
 173   addProtectedField( "currentDL",     TypeS32, Offset( mCurrentDL, GuiShapeEdPreview ),     &setFieldCurrentDL,     &defaultProtectedGetFn,
 174      "The current detail level" );
 175   addProtectedField( "detailSize",    TypeS32, Offset( mDetailSize, GuiShapeEdPreview ),    &defaultProtectedSetFn, &defaultProtectedGetFn,
 176      "The size of the current detail" );
 177   addProtectedField( "detailPolys",   TypeS32, Offset( mDetailPolys, GuiShapeEdPreview ),   &defaultProtectedSetFn, &defaultProtectedGetFn,
 178      "Number of polygons in the current detail" );
 179   addProtectedField( "pixelSize",     TypeF32, Offset( mPixelSize, GuiShapeEdPreview ),     &defaultProtectedSetFn, &defaultProtectedGetFn,
 180      "The current pixel size of the model" );
 181   addProtectedField( "numMaterials",  TypeS32, Offset( mNumMaterials, GuiShapeEdPreview ),  &defaultProtectedSetFn, &defaultProtectedGetFn,
 182      "The number of materials in the current detail level" );
 183   addProtectedField( "numDrawCalls",  TypeS32, Offset( mNumDrawCalls, GuiShapeEdPreview ),  &defaultProtectedSetFn, &defaultProtectedGetFn,
 184      "The number of draw calls in the current detail level" );
 185   addProtectedField( "numBones",      TypeS32, Offset( mNumBones, GuiShapeEdPreview ),      &defaultProtectedSetFn, &defaultProtectedGetFn,
 186      "The number of bones in the current detail level (skins only)" );
 187   addProtectedField( "numWeights",    TypeS32, Offset( mNumWeights, GuiShapeEdPreview ),    &defaultProtectedSetFn, &defaultProtectedGetFn,
 188      "The number of vertex weights in the current detail level (skins only)" );
 189   addProtectedField( "colMeshes",     TypeS32, Offset( mColMeshes, GuiShapeEdPreview ),     &defaultProtectedSetFn, &defaultProtectedGetFn,
 190      "The number of collision meshes in the shape" );
 191   addProtectedField( "colPolys",      TypeS32, Offset( mColPolys, GuiShapeEdPreview ),      &defaultProtectedSetFn, &defaultProtectedGetFn,
 192      "The total number of collision polygons (all meshes) in the shape" );
 193   endGroup( "Detail Stats" );
 194
 195   Parent::initPersistFields();
 196}
 197
 198bool GuiShapeEdPreview::setFieldCurrentDL( void *object, const char *index, const char *data )
 199{
 200   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 201   if ( gui )
 202      gui->setCurrentDetail( mFloor( dAtof( data ) + 0.5f ) );
 203   return false;
 204}
 205
 206bool GuiShapeEdPreview::setFieldSunDiffuse( void *object, const char *index, const char *data )
 207{
 208   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 209   if ( gui )
 210   {
 211      Con::setData( TypeColorI, &gui->mSunDiffuseColor, 0, 1, &data );
 212      gui->updateSun();
 213   }
 214   return false;
 215}
 216
 217bool GuiShapeEdPreview::setFieldSunAmbient( void *object, const char *index, const char *data )
 218{
 219   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 220   if ( gui )
 221   {
 222      Con::setData( TypeColorI, &gui->mSunAmbientColor, 0, 1, &data );
 223      gui->updateSun();
 224   }
 225   return false;
 226}
 227
 228bool GuiShapeEdPreview::setFieldSunAngleX( void *object, const char *index, const char *data )
 229{
 230   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 231   if ( gui )
 232   {
 233      Con::setData( TypeF32, &gui->mSunRot.x, 0, 1, &data );
 234      gui->updateSun();
 235   }
 236   return false;
 237}
 238
 239bool GuiShapeEdPreview::setFieldSunAngleZ( void *object, const char *index, const char *data )
 240{
 241   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 242   if ( gui )
 243   {
 244      Con::setData( TypeF32, &gui->mSunRot.z, 0, 1, &data );
 245      gui->updateSun();
 246   }
 247   return false;
 248}
 249
 250bool GuiShapeEdPreview::setFieldThreadPos( void *object, const char *index, const char *data )
 251{
 252   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 253   if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key )
 254      gui->mModel->setPos( gui->mThreads[gui->mActiveThread].key, dAtof( data ) );
 255   return false;
 256}
 257
 258const char *GuiShapeEdPreview::getFieldThreadPos( void *object, const char *data )
 259{
 260   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 261   if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key )
 262      return Con::getFloatArg( gui->mModel->getPos( gui->mThreads[gui->mActiveThread].key ) );
 263   else
 264      return "0";
 265}
 266
 267bool GuiShapeEdPreview::setFieldThreadDir( void *object, const char *index, const char *data )
 268{
 269   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 270   if ( gui && ( gui->mActiveThread >= 0 ) )
 271   {
 272      Thread& thread = gui->mThreads[gui->mActiveThread];
 273      Con::setData( TypeS32, &(thread.direction), 0, 1, &data );
 274      if ( thread.key )
 275         gui->mModel->setTimeScale( thread.key, gui->mTimeScale * thread.direction );
 276   }
 277   return false;
 278}
 279
 280const char *GuiShapeEdPreview::getFieldThreadDir( void *object, const char *data )
 281{
 282   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 283   if ( gui && ( gui->mActiveThread >= 0 ) )
 284      return Con::getIntArg( gui->mThreads[gui->mActiveThread].direction );
 285   else
 286      return "0";
 287}
 288
 289bool GuiShapeEdPreview::setFieldThreadPingPong( void *object, const char *index, const char *data )
 290{
 291   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 292   if ( gui && ( gui->mActiveThread >= 0 ) )
 293      Con::setData( TypeBool, &(gui->mThreads[gui->mActiveThread].pingpong), 0, 1, &data );
 294   return false;
 295}
 296
 297const char *GuiShapeEdPreview::getFieldThreadPingPong( void *object, const char *data )
 298{
 299   GuiShapeEdPreview* gui = static_cast<GuiShapeEdPreview*>( object );
 300   if ( gui && ( gui->mActiveThread >= 0 ) )
 301      return Con::getIntArg( gui->mThreads[gui->mActiveThread].pingpong );
 302   else
 303      return "0";
 304}
 305
 306
 307bool GuiShapeEdPreview::onWake()
 308{
 309   if (!Parent::onWake())
 310      return false;
 311
 312   if (!mFakeSun )
 313      mFakeSun = LIGHTMGR->createLightInfo();
 314
 315   mFakeSun->setRange( 2000000.0f );
 316   updateSun();
 317
 318   mGizmoProfile->mode = MoveMode;
 319
 320   return( true );
 321}
 322
 323void GuiShapeEdPreview::setDisplayType( S32 type )
 324{
 325   Parent::setDisplayType( type );
 326   mOrthoCamTrans.set( 0, 0, 0 );
 327}
 328
 329//-----------------------------------------------------------------------------
 330
 331void GuiShapeEdPreview::setCurrentDetail(S32 dl)
 332{
 333   if ( mModel )
 334   {
 335      TSShape* shape = mModel->getShape();
 336      S32 smallest = shape->mSmallestVisibleDL;
 337      shape->mSmallestVisibleDL = shape->details.size() - 1;
 338      mModel->setCurrentDetail( dl );
 339      shape->mSmallestVisibleDL = smallest;
 340
 341      // Match the camera distance to this detail if necessary
 342      //@todo if ( !gui->mFixedDetail )
 343   }
 344}
 345
 346bool GuiShapeEdPreview::setObjectModel(const char* modelName)
 347{
 348   SAFE_DELETE( mModel );
 349   unmountAll();
 350   mThreads.clear();
 351   mActiveThread = -1;
 352
 353   if (modelName && modelName[0])
 354   {
 355      Resource<TSShape> model = ResourceManager::get().load( modelName );
 356      if (! bool( model ))
 357      {
 358         Con::warnf( avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName ));
 359         return false;
 360      }
 361
 362      mModel = new TSShapeInstance( model, true );
 363      AssertFatal( mModel, avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName ));
 364
 365      TSShape* shape = mModel->getShape();
 366
 367      // Initialize camera values:
 368      mOrbitPos = shape->center;
 369
 370      // Set camera move and zoom speed according to model size
 371      mMoveSpeed = shape->mRadius / sMoveScaler;
 372      mZoomSpeed = shape->mRadius / sZoomScaler;
 373
 374      // Reset node selection
 375      mHoverNode = -1;
 376      mSelectedNode = -1;
 377      mSelectedObject = -1;
 378      mSelectedObjDetail = 0;
 379      mProjectedNodes.setSize( shape->nodes.size() );
 380
 381      // Reset detail stats
 382      mCurrentDL = 0;
 383
 384      // the first time recording
 385      mLastRenderTime = Platform::getVirtualMilliseconds();
 386   }
 387
 388   return true;
 389}
 390
 391void GuiShapeEdPreview::addThread()
 392{
 393   if ( mModel )
 394   {
 395      mThreads.increment();
 396      if ( mActiveThread == -1 )
 397         mActiveThread = 0;
 398   }
 399}
 400
 401void GuiShapeEdPreview::removeThread(S32 slot)
 402{
 403   if ( slot < mThreads.size() )
 404   {
 405      if ( mThreads[slot].key )
 406         mModel->destroyThread( mThreads[slot].key );
 407      mThreads.erase( slot );
 408
 409      if ( mActiveThread >= mThreads.size() )
 410         mActiveThread = mThreads.size() - 1;
 411   }
 412}
 413
 414void GuiShapeEdPreview::setTimeScale( F32 scale )
 415{
 416   // Update time scale for all threads
 417   mTimeScale = scale;
 418   for ( S32 i = 0; i < mThreads.size(); i++ )
 419   {
 420      if ( mThreads[i].key )
 421         mModel->setTimeScale( mThreads[i].key, mTimeScale * mThreads[i].direction );
 422   }
 423}
 424
 425void GuiShapeEdPreview::setActiveThreadSequence(const char* seqName, F32 duration, F32 pos, bool play)
 426{
 427   if ( mActiveThread == -1 )
 428      return;
 429
 430   setThreadSequence(mThreads[mActiveThread], mModel, seqName, duration, pos, play);
 431}
 432
 433void GuiShapeEdPreview::setThreadSequence(GuiShapeEdPreview::Thread& thread, TSShapeInstance* shape, const char* seqName, F32 duration, F32 pos, bool play)
 434{
 435   thread.seqName = seqName;
 436
 437   S32 seq = shape->getShape()->findSequence( thread.seqName );
 438   if ( thread.key && ( shape->getSequence(thread.key) == seq ) )
 439      return;
 440
 441   if ( seq == -1 )
 442   {
 443      // This thread is now set to an invalid sequence, so the key must be
 444      // removed, but we keep the thread info around in case the user changes
 445      // back to a valid sequence
 446      if ( thread.key )
 447      {
 448         shape->destroyThread( thread.key );
 449         thread.key = NULL;
 450      }
 451   }
 452   else
 453   {
 454      // Add a TSThread key if one does not already exist
 455      if ( !thread.key )
 456      {
 457         thread.key = shape->addThread();
 458         shape->setTimeScale( thread.key, mTimeScale * thread.direction );
 459      }
 460
 461      // Transition to slider or synched position?
 462      if ( pos == -1.0f )
 463         pos = shape->getPos( thread.key );
 464
 465      if ( duration == 0.0f )
 466      {
 467         // No transition => go straight to new sequence
 468         shape->setSequence( thread.key, seq, pos );
 469      }
 470      else
 471      {
 472         // Get the current position if transitioning to the sync position
 473         shape->setTimeScale( thread.key, thread.direction >= 0 ? 1 : -1 );
 474         shape->transitionToSequence( thread.key, seq, pos, duration, play );
 475         shape->setTimeScale( thread.key, mTimeScale * thread.direction );
 476      }
 477   }
 478}
 479
 480const char* GuiShapeEdPreview::getThreadSequence() const
 481{
 482   return ( mActiveThread >= 0 ) ? mThreads[mActiveThread].seqName : "";
 483}
 484
 485void GuiShapeEdPreview::refreshThreadSequences()
 486{
 487   S32 oldActive = mActiveThread;
 488
 489   for ( S32 i = 0; i < mThreads.size(); i++ )
 490   {
 491      Thread& thread = mThreads[i];
 492      if ( !thread.key )
 493         continue;
 494
 495      // Detect changed (or removed) sequence indices
 496      if ( mModel->getSequence(thread.key) != mModel->getShape()->findSequence( thread.seqName ) )
 497      {
 498         mActiveThread = i;
 499         setThreadSequence( thread, mModel, thread.seqName, 0.0f, mModel->getPos( thread.key ), false );
 500      }
 501   }
 502
 503   mActiveThread = oldActive;
 504}
 505
 506//-----------------------------------------------------------------------------
 507// MOUNTING
 508
 509bool GuiShapeEdPreview::mountShape(const char* modelName, const char* nodeName, const char* mountType, S32 slot)
 510{
 511   if ( !modelName || !modelName[0] )
 512      return false;
 513
 514   Resource<TSShape> model = ResourceManager::get().load( modelName );
 515   if ( !bool( model ) )
 516      return false;
 517
 518   TSShapeInstance* tsi = new TSShapeInstance( model, true );
 519
 520   if ( slot == -1 )
 521   {
 522      slot = mMounts.size();
 523      mMounts.push_back( new MountedShape );
 524   }
 525   else
 526   {
 527      // Check if we are switching shapes
 528      if ( mMounts[slot]->mShape->getShape() != tsi->getShape() )
 529      {
 530         delete mMounts[slot]->mShape;
 531         mMounts[slot]->mShape = NULL;
 532         mMounts[slot]->mThread.init();
 533      }
 534      else
 535      {
 536         // Keep using the existing shape
 537         delete tsi;
 538         tsi = mMounts[slot]->mShape;
 539      }
 540   }
 541
 542   MountedShape* mount = mMounts[slot];
 543   mount->mShape = tsi;
 544
 545   if ( dStrEqual( mountType, "Wheel" ) )
 546      mount->mType = MountedShape::Wheel;
 547   else if ( dStrEqual( mountType, "Image" ) )
 548      mount->mType = MountedShape::Image;
 549   else
 550      mount->mType = MountedShape::Object;
 551
 552   setMountNode( slot, nodeName);
 553
 554   return true;
 555}
 556
 557void GuiShapeEdPreview::setMountNode(S32 mountSlot, const char* nodeName)
 558{
 559   if ( mountSlot < mMounts.size() )
 560   {
 561      MountedShape* mount = mMounts[mountSlot];
 562
 563      mount->mNode = mModel ? mModel->getShape()->findNode( nodeName ) : -1;
 564      mount->mTransform.identity();
 565
 566      switch ( mount->mType )
 567      {
 568      case MountedShape::Image:
 569         {
 570            // Mount point is either the node called 'mountPoint' or the origin
 571            S32 node = mount->mShape->getShape()->findNode( "mountPoint" );
 572            if ( node != -1 )
 573            {
 574               mount->mShape->getShape()->getNodeWorldTransform( node, &mount->mTransform );
 575               mount->mTransform.inverse();
 576            }
 577         }
 578         break;
 579
 580      case MountedShape::Wheel:
 581         // Rotate shape according to node's x position (left or right)
 582         {
 583            F32 rotAngle = M_PI_F/2;
 584            if ( mount->mNode != -1 )
 585            {
 586               MatrixF hubMat;
 587               mModel->getShape()->getNodeWorldTransform( mount->mNode, &hubMat );
 588               if ( hubMat.getPosition().x < 0 )
 589                  rotAngle = -M_PI_F/2;
 590            }
 591            mount->mTransform.set( EulerF( 0, 0, rotAngle ) );
 592         }
 593         break;
 594
 595      default:
 596         // No mount transform (use origin)
 597         break;
 598      }
 599   }
 600}
 601
 602const char* GuiShapeEdPreview::getMountThreadSequence(S32 mountSlot) const
 603{
 604   if ( mountSlot < mMounts.size() )
 605   {
 606      MountedShape* mount = mMounts[mountSlot];
 607      return mount->mThread.seqName;
 608   }
 609   else
 610      return "";
 611}
 612
 613void GuiShapeEdPreview::setMountThreadSequence(S32 mountSlot, const char* seqName)
 614{
 615   if ( mountSlot < mMounts.size() )
 616   {
 617      MountedShape* mount = mMounts[mountSlot];
 618      setThreadSequence( mount->mThread, mount->mShape, seqName );
 619   }
 620}
 621
 622F32 GuiShapeEdPreview::getMountThreadPos(S32 mountSlot) const
 623{
 624   if ( mountSlot < mMounts.size() )
 625   {
 626      MountedShape* mount = mMounts[mountSlot];
 627      if ( mount->mThread.key )
 628         return mount->mShape->getPos( mount->mThread.key );
 629   }
 630   return 0;
 631}
 632
 633void GuiShapeEdPreview::setMountThreadPos(S32 mountSlot, F32 pos)
 634{
 635   if ( mountSlot < mMounts.size() )
 636   {
 637      MountedShape* mount = mMounts[mountSlot];
 638      if ( mount->mThread.key )
 639         mount->mShape->setPos( mount->mThread.key, pos );
 640   }
 641}
 642
 643F32 GuiShapeEdPreview::getMountThreadDir(S32 mountSlot) const
 644{
 645   if ( mountSlot < mMounts.size() )
 646   {
 647      MountedShape* mount = mMounts[mountSlot];
 648      return mount->mThread.direction;
 649   }
 650   return 0;
 651}
 652
 653void GuiShapeEdPreview::setMountThreadDir(S32 mountSlot, F32 dir)
 654{
 655   if ( mountSlot < mMounts.size() )
 656   {
 657      MountedShape* mount = mMounts[mountSlot];
 658      mount->mThread.direction = dir;
 659      if ( mount->mThread.key )
 660         mount->mShape->setTimeScale( mount->mThread.key, mTimeScale * mount->mThread.direction );
 661   }
 662}
 663
 664void GuiShapeEdPreview::unmountShape(S32 mountSlot)
 665{
 666   if ( mountSlot < mMounts.size() )
 667   {
 668      delete mMounts[mountSlot];
 669      mMounts.erase( mountSlot );
 670   }
 671}
 672
 673void GuiShapeEdPreview::unmountAll()
 674{
 675   for ( S32 i = 0; i < mMounts.size(); i++)
 676      delete mMounts[i];
 677   mMounts.clear();
 678}
 679
 680void GuiShapeEdPreview::refreshShape()
 681{
 682   if ( mModel )
 683   {
 684      // Nodes or details may have changed => refresh the shape instance
 685      mModel->setMaterialList( mModel->mMaterialList );
 686      mModel->initNodeTransforms();
 687      mModel->initMeshObjects();
 688
 689      TSShape* shape = mModel->getShape();
 690
 691      mProjectedNodes.setSize( shape->nodes.size() );
 692
 693      if ( mSelectedObject >= shape->objects.size() )
 694      {
 695         mSelectedObject = -1;
 696         mSelectedObjDetail = 0;
 697      }
 698
 699      // Re-compute the collision mesh stats
 700      mColMeshes = 0;
 701      mColPolys = 0;
 702      for ( S32 i = 0; i < shape->details.size(); i++ )
 703      {
 704         const TSShape::Detail& det = shape->details[i];
 705         const String& detName = shape->getName( det.nameIndex );
 706         if ( ( det.subShapeNum < 0 ) || !detName.startsWith( "collision-" ) )
 707            continue;
 708
 709         mColPolys += det.polyCount;
 710
 711         S32 od = det.objectDetailNum;
 712         S32 start = shape->subShapeFirstObject[det.subShapeNum];
 713         S32 end   = start + shape->subShapeNumObjects[det.subShapeNum];
 714         for ( S32 j = start; j < end; j++ )
 715         {
 716            const TSShape::Object &obj = shape->objects[j];
 717            const TSMesh* mesh = ( od < obj.numMeshes ) ? shape->meshes[obj.startMeshIndex + od] : NULL;
 718            if ( mesh )
 719               mColMeshes++;
 720         }
 721      }
 722   }
 723}
 724
 725void GuiShapeEdPreview::updateSun()
 726{
 727   if ( mFakeSun )
 728   {
 729      // Update sun colors
 730      mFakeSun->setColor( mSunDiffuseColor );
 731      mFakeSun->setAmbient( mSunAmbientColor );
 732
 733      // Determine the new sun direction and position
 734      Point3F vec;
 735      MatrixF xRot, zRot;
 736      xRot.set( EulerF( mDegToRad(mSunRot.x), 0.0f, 0.0f ));
 737      zRot.set( EulerF( 0.0f, 0.0f, mDegToRad(mSunRot.z) ));
 738
 739      zRot.mul( xRot );
 740      zRot.getColumn( 1, &vec );
 741
 742      mFakeSun->setDirection( vec );
 743      //mFakeSun->setPosition( vec * -10000.0f );
 744   }
 745}
 746
 747void GuiShapeEdPreview::updateNodeTransforms()
 748{
 749   if ( mModel )
 750      mModel->mDirtyFlags[0] |= TSShapeInstance::TransformDirty;
 751}
 752
 753bool GuiShapeEdPreview::getMeshHidden( const char* name ) const
 754{
 755   if ( mModel )
 756   {
 757      S32 objIndex = mModel->getShape()->findObject( name );
 758      if ( objIndex != -1 )
 759         return mModel->mMeshObjects[objIndex].forceHidden;
 760   }
 761   return false;
 762}
 763
 764void GuiShapeEdPreview::setMeshHidden( const char* name, bool hidden )
 765{
 766   if ( mModel )
 767   {
 768      S32 objIndex = mModel->getShape()->findObject( name );
 769      if ( objIndex != -1 )
 770         mModel->setMeshForceHidden( objIndex, hidden );
 771   }
 772}
 773
 774void GuiShapeEdPreview::setAllMeshesHidden( bool hidden )
 775{
 776   if ( mModel )
 777   {
 778      for ( S32 i = 0; i < mModel->mMeshObjects.size(); i++ )
 779         mModel->setMeshForceHidden( i, hidden );
 780   }
 781}
 782
 783void GuiShapeEdPreview::get3DCursor( GuiCursor *&cursor, 
 784                                       bool &visible, 
 785                                       const Gui3DMouseEvent &event_ )
 786{
 787   cursor = NULL;
 788   visible = false;
 789
 790   GuiCanvas *root = getRoot();
 791   if ( !root )
 792      return;
 793
 794   S32 currCursor = PlatformCursorController::curArrow;
 795
 796   if ( root->mCursorChanged == currCursor )
 797      return;
 798
 799   PlatformWindow *window = root->getPlatformWindow();
 800   PlatformCursorController *controller = window->getCursorController();
 801
 802   // We've already changed the cursor, 
 803   // so set it back before we change it again.
 804   if ( root->mCursorChanged != -1 )
 805      controller->popCursor();
 806
 807   // Now change the cursor shape
 808   controller->pushCursor( currCursor );
 809   root->mCursorChanged = currCursor;
 810}
 811
 812void GuiShapeEdPreview::fitToShape()
 813{
 814   if ( !mModel )
 815      return;
 816
 817   // Determine the shape bounding box given the current camera rotation
 818   MatrixF camRotMatrix( smCamMatrix );
 819   camRotMatrix.setPosition( Point3F::Zero );
 820   camRotMatrix.inverse();
 821
 822   Box3F bounds;
 823   computeSceneBounds( bounds );
 824   mOrbitPos = bounds.getCenter();
 825
 826   camRotMatrix.mul( bounds );
 827
 828   // Estimate the camera distance to fill the view by comparing the radii
 829   // of the box and the viewport
 830   F32 len_x = bounds.len_x();
 831   F32 len_z = bounds.len_z();
 832   F32 shapeRadius = mSqrt( len_x*len_x + len_z*len_z ) / 2;
 833   F32 viewRadius = 0.45f * getMin( getExtent().x, getExtent().y );
 834
 835   // Set camera parameters
 836   if ( mDisplayType == DisplayTypePerspective )
 837   {
 838      mOrbitDist = ( shapeRadius / viewRadius ) * mSaveWorldToScreenScale.y;
 839   }
 840   else
 841   {
 842      mOrthoCamTrans.set( 0, 0, 0 );
 843      mOrthoFOV = shapeRadius * viewRadius / 320;
 844   }
 845}
 846
 847void GuiShapeEdPreview::setOrbitPos( const Point3F& pos )
 848{
 849   mOrbitPos = pos;
 850}
 851
 852void GuiShapeEdPreview::exportToCollada( const String& path )
 853{
 854#ifdef TORQUE_COLLADA
 855   if ( mModel )
 856   {
 857      MatrixF orientation( true );
 858      orientation.setPosition( mModel->getShape()->mBounds.getCenter() );
 859      orientation.inverse();
 860
 861      OptimizedPolyList polyList;
 862      polyList.setBaseTransform( orientation );
 863
 864      mModel->buildPolyList( &polyList, mCurrentDL );
 865      for ( S32 i = 0; i < mMounts.size(); i++ )
 866      {
 867         MountedShape* mount = mMounts[i];
 868
 869         MatrixF mat( true );
 870         if ( mount->mNode != -1 )
 871         {
 872            mat = mModel->mNodeTransforms[ mount->mNode ];
 873            mat *= mount->mTransform;
 874         }
 875
 876         polyList.setTransform( &mat, Point3F::One );
 877         mount->mShape->buildPolyList( &polyList, 0 );
 878      }
 879
 880      // Use a ColladaUtils function to do the actual export to a Collada file
 881      ColladaUtils::exportToCollada( path, polyList );
 882   }
 883#endif
 884}
 885
 886//-----------------------------------------------------------------------------
 887// Camera control and Node editing
 888// - moving the mouse over a node will highlight (but not select) it
 889// - left clicking on a node will select it, the gizmo will appear
 890// - left clicking on no node will unselect the current node
 891// - left dragging the gizmo will translate/rotate the node
 892// - middle drag translates the view
 893// - right drag rotates the view
 894// - mouse wheel zooms the view
 895// - holding shift while changing the view speeds them up
 896
 897void GuiShapeEdPreview::handleMouseDown(const GuiEvent& event, GizmoMode mode)
 898{
 899   if (!mActive || !mVisible || !mAwake )
 900      return;
 901
 902   mouseLock();
 903   mLastMousePos = event.mousePoint;
 904
 905   if ( mRenderNodes && ( mode == NoneMode ) )
 906   {
 907      mGizmoDragID++;
 908      make3DMouseEvent( mLastEvent, event );
 909
 910      // Check gizmo first
 911      mUsingAxisGizmo = false;
 912      if ( mSelectedNode != -1 )
 913      {
 914         mGizmo->on3DMouseDown( mLastEvent );
 915         if ( mGizmo->getSelection() != Gizmo::None )
 916         {
 917            mUsingAxisGizmo = true;
 918            return;
 919         }
 920      }
 921
 922      // Check if we have clicked on a node
 923      S32 selected = collideNode( mLastEvent );
 924      if ( selected != mSelectedNode )
 925      {
 926         mSelectedNode = selected;
 927         Con::executef( this, "onNodeSelected", Con::getIntArg( mSelectedNode ));
 928      }
 929   }
 930
 931   //if ( mode == RotateMode )
 932   //   mRenderCameraAxes = true;
 933}
 934
 935void GuiShapeEdPreview::handleMouseUp(const GuiEvent& event, GizmoMode mode)
 936{
 937   mouseUnlock();
 938   mUsingAxisGizmo = false;
 939
 940   if ( mRenderNodes && ( mode == NoneMode ) )
 941   {
 942      make3DMouseEvent( mLastEvent, event );
 943      mGizmo->on3DMouseUp( mLastEvent );
 944   }
 945
 946   //if ( mode == RotateMode )
 947   //   mRenderCameraAxes = false;
 948}
 949
 950void GuiShapeEdPreview::handleMouseMove(const GuiEvent& event, GizmoMode mode)
 951{
 952   if ( mRenderNodes && ( mode == NoneMode ) )
 953   {
 954      make3DMouseEvent( mLastEvent, event );
 955      if ( mSelectedNode != -1 )
 956      {
 957         // Check if the mouse is hovering over an axis
 958         mGizmo->on3DMouseMove( mLastEvent );
 959         if ( mGizmo->getSelection() != Gizmo::None )
 960            return;
 961      }
 962
 963      // Check if we are over another node
 964      mHoverNode = collideNode( mLastEvent );
 965   }
 966}
 967
 968void GuiShapeEdPreview::handleMouseDragged(const GuiEvent& event, GizmoMode mode)
 969{
 970   // For non-perspective views, ignore rotation, and let EditTSCtrl handle
 971   // translation
 972   if ( mDisplayType != DisplayTypePerspective )
 973   {
 974      if ( mode == MoveMode )
 975      {
 976         Parent::onRightMouseDragged( event );
 977         return;
 978      }
 979      else if ( mode == RotateMode )
 980         return;
 981   }
 982
 983   Point2F delta( event.mousePoint.x - mLastMousePos.x, event.mousePoint.y - mLastMousePos.y );
 984   mLastMousePos = event.mousePoint;
 985
 986   // Use shift to increase speed
 987   delta.x *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f;
 988   delta.y *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f;
 989
 990   if ( mode == NoneMode )
 991   {
 992      if ( mEditingSun )
 993      {
 994         mSunRot.x += mRadToDeg( delta.y );
 995         mSunRot.z += mRadToDeg( delta.x );
 996         updateSun();
 997      }
 998      else if ( mRenderNodes )
 999      {
1000         make3DMouseEvent( mLastEvent, event );
1001
1002         if ( mUsingAxisGizmo )
1003         {
1004            // Use gizmo to modify the transform of the selected node
1005            mGizmo->on3DMouseDragged( mLastEvent );
1006            switch ( mGizmoProfile->mode )
1007            {
1008            case MoveMode:
1009               // Update node transform
1010               if ( mSelectedNode != -1 )
1011               {
1012                  Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition() + mGizmo->getOffset();
1013                  mModel->mNodeTransforms[mSelectedNode].setPosition( pos );
1014               }
1015               break;
1016
1017            case RotateMode:
1018               // Update node transform
1019               if ( mSelectedNode != -1 )
1020               {
1021                  EulerF rot = mGizmo->getDeltaRot();
1022                  mModel->mNodeTransforms[mSelectedNode].mul( MatrixF( rot ) );
1023               }
1024               break;
1025            default:
1026               break;
1027            }
1028
1029            // Notify the change in node transform
1030            const char* name = mModel->getShape()->getNodeName(mSelectedNode).c_str();
1031            const Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition();
1032            AngAxisF aa(mModel->mNodeTransforms[mSelectedNode]);
1033            char buffer[256];
1034            dSprintf(buffer, sizeof(buffer), "%g %g %g %g %g %g %g",
1035               pos.x, pos.y, pos.z, aa.axis.x, aa.axis.y, aa.axis.z, aa.angle);
1036
1037            Con::executef(this, "onEditNodeTransform", name, buffer, Con::getIntArg(mGizmoDragID));
1038         }
1039      }
1040   }
1041   else
1042   {
1043      switch ( mode )
1044      {
1045      case MoveMode:
1046         {
1047            VectorF offset(-delta.x, 0, delta.y );
1048            smCamMatrix.mulV( offset );
1049            mOrbitPos += offset * mMoveSpeed;
1050         }
1051         break;
1052
1053      case RotateMode:
1054         mCameraRot.x += delta.y;
1055         mCameraRot.z += delta.x;
1056         break;
1057
1058      default:
1059         break;
1060      }
1061   }
1062}
1063
1064void GuiShapeEdPreview::on3DMouseWheelUp(const Gui3DMouseEvent& event)
1065{
1066   if ( mDisplayType == DisplayTypePerspective )
1067   {
1068      // Use shift and ctrl to increase speed
1069      F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f;
1070      mOrbitDist -= mFabs(event.fval) * mZoomSpeed * mod;
1071   }
1072}
1073
1074void GuiShapeEdPreview::on3DMouseWheelDown(const Gui3DMouseEvent& event)
1075{
1076   if ( mDisplayType == DisplayTypePerspective )
1077   {
1078      // Use shift and ctrl to increase speed
1079      F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f;
1080      mOrbitDist += mFabs(event.fval) * mZoomSpeed * mod;
1081   }
1082}
1083
1084//-----------------------------------------------------------------------------
1085// NODE PICKING
1086void GuiShapeEdPreview::updateProjectedNodePoints()
1087{
1088   if ( mModel )
1089   {
1090      // Project the 3D node position to get the 2D screen coordinates
1091      for ( S32 i = 0; i < mModel->mNodeTransforms.size(); i++)
1092         project( mModel->mNodeTransforms[i].getPosition(), &mProjectedNodes[i] );
1093   }
1094}
1095
1096S32 GuiShapeEdPreview::collideNode(const Gui3DMouseEvent& event) const
1097{
1098   // Check if the given position is inside the screen rectangle of
1099   // any shape node
1100   S32 nodeIndex = -1;
1101   F32 minZ = 0;
1102   for ( S32 i = 0; i < mProjectedNodes.size(); i++)
1103   {
1104      const Point3F& pt = mProjectedNodes[i];
1105      if ( pt.z > 1.0f )
1106         continue;
1107
1108      RectI rect( pt.x - sNodeRectSize/2, pt.y - sNodeRectSize/2, sNodeRectSize, sNodeRectSize );
1109      if ( rect.pointInRect( event.mousePoint ) )
1110      {
1111         if ( ( nodeIndex == -1 ) || ( pt.z < minZ ) )
1112         {
1113            nodeIndex = i;
1114            minZ = pt.z;
1115         }
1116      }
1117   }
1118
1119   return nodeIndex;
1120}
1121
1122//-----------------------------------------------------------------------------
1123// RENDERING
1124bool GuiShapeEdPreview::getCameraTransform(MatrixF* cameraMatrix)
1125{
1126   // Adjust the camera so that we are still facing the model
1127   if ( mDisplayType == DisplayTypePerspective )
1128   {
1129      Point3F vec;
1130      MatrixF xRot, zRot;
1131      xRot.set( EulerF( mCameraRot.x, 0.0f, 0.0f ));
1132      zRot.set( EulerF( 0.0f, 0.0f, mCameraRot.z ));
1133
1134      cameraMatrix->mul( zRot, xRot );
1135      cameraMatrix->getColumn( 1, &vec );
1136      cameraMatrix->setColumn( 3, mOrbitPos - vec*mOrbitDist );
1137   }
1138   else
1139   {
1140      cameraMatrix->identity();
1141      if ( mModel )
1142      {
1143         Point3F camPos = mModel->getShape()->mBounds.getCenter();
1144         F32 offset = mModel->getShape()->mBounds.len();
1145
1146         switch (mDisplayType)
1147         {
1148            case DisplayTypeTop:       camPos.z += offset;    break;
1149            case DisplayTypeBottom:    camPos.z -= offset;    break;
1150            case DisplayTypeFront:     camPos.y += offset;    break;
1151            case DisplayTypeBack:      camPos.y -= offset;    break;
1152            case DisplayTypeRight:     camPos.x += offset;    break;
1153            case DisplayTypeLeft:      camPos.x -= offset;    break;
1154            default:
1155               break;
1156         }
1157
1158         cameraMatrix->setColumn( 3, camPos );
1159      }
1160   }
1161
1162   return true;
1163}
1164
1165void GuiShapeEdPreview::computeSceneBounds(Box3F& bounds)
1166{
1167   if ( mModel )
1168      mModel->computeBounds( mCurrentDL, bounds );
1169
1170   if (bounds.getExtents().x < POINT_EPSILON || bounds.getExtents().y < POINT_EPSILON || bounds.getExtents().z < POINT_EPSILON)
1171   {
1172      bounds.set(Point3F::Zero);
1173
1174      //We probably don't have any actual meshes in this model, so compute using the bones if we have them
1175      for (S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
1176      {
1177         Point3F nodePos = mModel->mNodeTransforms[i].getPosition();
1178
1179         bounds.extend(nodePos);
1180      }
1181   }
1182}
1183
1184void GuiShapeEdPreview::updateDetailLevel(const SceneRenderState* state)
1185{
1186   // Make sure current detail is valid
1187   if ( !mModel->getShape()->details.size() )
1188      return;
1189
1190   if ( mModel->getCurrentDetail() >= mModel->getShape()->details.size() )
1191      setCurrentDetail( mModel->getShape()->details.size() - 1 );
1192
1193   // Convert between FOV and distance so zoom is consistent between Perspective
1194   // and Orthographic views (conversion factor found by trial and error)
1195   const F32 fov2dist = 1.3f;
1196   if ( mDisplayType == DisplayTypePerspective )
1197      mOrthoFOV = mOrbitDist / fov2dist;
1198   else
1199      mOrbitDist = mOrthoFOV * fov2dist;
1200
1201   // Use fixed distance in orthographic view (value found by trial + error)
1202   F32 dist = ( mDisplayType == DisplayTypePerspective ) ? mOrbitDist : 0.1f;
1203
1204   // Select the appropriate detail level, and update the detail stats
1205   S32 currentDetail = mModel->getCurrentDetail();
1206   mModel->setDetailFromDistance( state, dist );   // need to call this to update smLastPixelSize
1207   if ( mFixedDetail )
1208      setCurrentDetail( currentDetail );
1209
1210   if ( mModel->getCurrentDetail() < 0 )
1211      setCurrentDetail( 0 );
1212
1213   currentDetail = mModel->getCurrentDetail();
1214   const TSShape::Detail& det = mModel->getShape()->details[ currentDetail ];
1215
1216   mDetailPolys = det.polyCount;
1217   mDetailSize = det.size;
1218   mPixelSize = TSShapeInstance::smLastPixelSize;
1219
1220   mNumMaterials = 0;
1221   mNumDrawCalls = 0;
1222   mNumBones = 0;
1223   mNumWeights = 0;
1224
1225   if ( det.subShapeNum < 0 )
1226   {
1227      mNumMaterials = 1;
1228      mNumDrawCalls = 1;
1229   }
1230   else
1231   {
1232      Vector<U32> usedMaterials;
1233
1234      S32 start = mModel->getShape()->subShapeFirstObject[det.subShapeNum];
1235      S32 end = start + mModel->getShape()->subShapeNumObjects[det.subShapeNum];
1236
1237      for ( S32 iObj = start; iObj < end; iObj++ )
1238      {
1239         const TSShape::Object& obj = mModel->getShape()->objects[iObj];
1240
1241         if ( obj.numMeshes <= currentDetail )
1242            continue;
1243
1244         const TSMesh* mesh = mModel->getShape()->meshes[ obj.startMeshIndex + currentDetail ];
1245         if ( !mesh )
1246            continue;
1247
1248         // Count the number of draw calls and materials
1249         mNumDrawCalls += mesh->mPrimitives.size();
1250         for ( S32 iPrim = 0; iPrim < mesh->mPrimitives.size(); iPrim++ )
1251            usedMaterials.push_back_unique( mesh->mPrimitives[iPrim].matIndex & TSDrawPrimitive::MaterialMask );
1252
1253         // For skinned meshes, count the number of bones and weights
1254         if ( mesh->getMeshType() == TSMesh::SkinMeshType )
1255         {
1256            const TSSkinMesh* skin = dynamic_cast<const TSSkinMesh*>(mesh);
1257            mNumBones += skin->batchData.initialTransforms.size();
1258            mNumWeights += skin->weight.size();
1259         }
1260      }
1261
1262      mNumMaterials = usedMaterials.size();
1263   }
1264
1265   // Detect changes in detail level
1266   if ( mCurrentDL != currentDetail )
1267   {
1268      mCurrentDL = currentDetail;
1269      Con::executef( this, "onDetailChanged");
1270   }
1271}
1272
1273void GuiShapeEdPreview::updateThreads(F32 delta)
1274{
1275   // Advance time on all threads
1276   for ( S32 i = 0; i < mThreads.size(); i++ )
1277   {
1278      Thread& thread = mThreads[i];
1279      if ( !thread.key || !thread.direction )
1280         continue;
1281
1282      // Make sure thread priority matches sequence priority (which may have changed)
1283      mModel->setPriority( thread.key, mModel->getShape()->sequences[mModel->getSequence( thread.key )].priority );
1284
1285      // Handle ping-pong
1286      if ( thread.pingpong && !mModel->isInTransition( thread.key ) )
1287      {
1288         // Determine next position, then adjust if needed
1289         F32 threadPos = mModel->getPos( thread.key );
1290         F32 nextPos = threadPos + ( mModel->getTimeScale( thread.key ) * delta / mModel->getDuration( thread.key ) );
1291
1292         if ( nextPos < 0 )
1293         {
1294            // Reflect position and swap playback direction
1295            nextPos = -nextPos;
1296            mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) );
1297            mModel->setPos( thread.key, nextPos );
1298         }
1299         else if ( nextPos > 1.0f )
1300         {
1301            // Reflect position and swap playback direction
1302            nextPos = 2.0f - nextPos;
1303            mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) );
1304            mModel->setPos( thread.key, nextPos );
1305         }
1306         else
1307         {
1308            // Advance time normally
1309            mModel->advanceTime( delta, thread.key );
1310         }
1311      }
1312      else
1313      {
1314         // Advance time normally
1315         mModel->advanceTime( delta, thread.key );
1316      }
1317
1318      // Invoke script callback if active thread position has changed
1319      if ( i == mActiveThread )
1320      {
1321         F32 threadPos = mModel->getPos( thread.key );
1322         bool inTransition = mModel->isInTransition( thread.key );
1323         onThreadPosChanged_callback( threadPos, inTransition );
1324      }
1325   }
1326
1327   // Mark threads as dirty so they will be re-sorted, in case the user changed
1328   // sequence priority or blend flags
1329   mModel->setDirty( TSShapeInstance::ThreadDirty );
1330
1331   // Advance time on all mounted shape threads
1332   for ( S32 i = 0; i < mMounts.size(); i++ )
1333   {
1334      MountedShape* mount = mMounts[i];
1335      if ( mount->mThread.key )
1336         mount->mShape->advanceTime( delta, mount->mThread.key );
1337   }
1338}
1339
1340void GuiShapeEdPreview::renderWorld(const RectI &updateRect)
1341{
1342   if ( !mModel )
1343      return;
1344
1345   mSaveFrustum = GFX->getFrustum();
1346   mSaveFrustum.setFarDist( 100000.0f );
1347   GFX->setFrustum( mSaveFrustum );
1348   mSaveFrustum.setTransform( smCamMatrix );
1349
1350   mSaveProjection = GFX->getProjectionMatrix();
1351   mSaveWorldToScreenScale = GFX->getWorldToScreenScale();
1352
1353   FogData savedFogData = gClientSceneGraph->getFogData();
1354   gClientSceneGraph->setFogData( FogData() );  // no fog in preview window
1355
1356   SceneRenderState state
1357   (
1358      gClientSceneGraph,
1359      SPT_Diffuse,
1360      SceneCameraState( GFX->getViewport(), mSaveFrustum,
1361                        GFX->getWorldMatrix(), GFX->getProjectionMatrix() )
1362   );
1363
1364   // Set up pass transforms
1365   RenderPassManager *renderPass = state.getRenderPass();
1366   renderPass->assignSharedXform( RenderPassManager::View, GFX->getWorldMatrix() );
1367   renderPass->assignSharedXform( RenderPassManager::Projection, GFX->getProjectionMatrix() );
1368
1369   // Set up our TS render state here.
1370   TSRenderState rdata;
1371   rdata.setSceneState(&state);
1372
1373   LIGHTMGR->unregisterAllLights();
1374   LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mFakeSun );
1375
1376   // We might have some forward lit materials
1377   // so pass down a query to gather lights.
1378   LightQuery query;
1379   query.init( SphereF( Point3F::Zero, 1 ) );
1380   rdata.setLightQuery( &query );
1381
1382   // Update projected node points (for mouse picking)
1383   updateProjectedNodePoints();
1384
1385   // Determine time elapsed since last render (for animation playback)
1386   S32 time = Platform::getVirtualMilliseconds();
1387   S32 dt = time - mLastRenderTime;
1388   mLastRenderTime = time;
1389
1390   if ( mModel )
1391   {
1392      updateDetailLevel( &state );
1393
1394      // Render the grid
1395      renderGrid();
1396
1397      // Animate the model
1398      updateThreads( (F32)dt / 1000.f );
1399      mModel->animate();
1400
1401      // Render the shape
1402      GFX->setStateBlock( mDefaultGuiSB );
1403
1404      if ( mRenderGhost )
1405         rdata.setFadeOverride( 0.5f );
1406
1407      GFX->pushWorldMatrix();
1408      GFX->setWorldMatrix( MatrixF::Identity );
1409
1410      mModel->render( rdata );
1411
1412      // Render mounted objects
1413      if ( mRenderMounts )
1414      {
1415         for ( S32 i = 0; i < mMounts.size(); i++ )
1416         {
1417            MountedShape* mount = mMounts[i];
1418
1419            GFX->pushWorldMatrix();
1420
1421            if ( mount->mNode != -1 )
1422            {
1423               GFX->multWorld( mModel->mNodeTransforms[ mount->mNode ] );
1424               GFX->multWorld( mount->mTransform );
1425            }
1426
1427            mount->mShape->animate();
1428            mount->mShape->render( rdata );
1429
1430            GFX->popWorldMatrix();
1431         }
1432      }
1433
1434      GFX->popWorldMatrix();
1435
1436      renderPass->renderPass( &state );
1437
1438      // @todo: Model and other elements (bounds, grid etc) use different
1439      // zBuffers, so at the moment, draw order determines what is on top
1440
1441      // Render collision volumes
1442      renderCollisionMeshes();
1443
1444      // Render the shape bounding box
1445      if ( mRenderBounds )
1446      {
1447         Point3F boxSize = mModel->getShape()->mBounds.maxExtents - mModel->getShape()->mBounds.minExtents;
1448
1449         GFXStateBlockDesc desc;
1450         desc.fillMode = GFXFillWireframe;
1451         GFX->getDrawUtil()->drawCube( desc, boxSize, mModel->getShape()->center, ColorI::WHITE );
1452      }
1453
1454      // Render the selected object bounding box
1455      if ( mRenderObjBox && ( mSelectedObject != -1 ) )
1456      {
1457         const TSShape::Object& obj = mModel->getShape()->objects[mSelectedObject];
1458         const TSMesh* mesh = ( mCurrentDL < obj.numMeshes ) ? mModel->getShape()->meshes[obj.startMeshIndex + mSelectedObjDetail] : NULL;
1459         if ( mesh )
1460         {
1461            GFX->pushWorldMatrix();
1462            if ( obj.nodeIndex != -1 )
1463               GFX->multWorld( mModel->mNodeTransforms[ obj.nodeIndex ] );
1464
1465            const Box3F& bounds = mesh->getBounds();
1466            GFXStateBlockDesc desc;
1467            desc.fillMode = GFXFillWireframe;
1468            GFX->getDrawUtil()->drawCube( desc, bounds.getExtents(), bounds.getCenter(), ColorI::RED );
1469
1470            GFX->popWorldMatrix();
1471         }
1472      }
1473
1474      // Render the sun direction if currently editing it
1475      renderSunDirection();
1476
1477      // render the nodes in the model
1478      renderNodes();
1479
1480      // use the gizmo to render the camera axes
1481      if ( mRenderCameraAxes )
1482      {
1483         GizmoMode savedMode = mGizmoProfile->mode;
1484         mGizmoProfile->mode = MoveMode;
1485
1486         Point3F pos;
1487         Point2I screenCenter( updateRect.point + updateRect.extent/2 );
1488         unproject( Point3F( screenCenter.x, screenCenter.y, 0.5 ), &pos );
1489
1490         mGizmo->set( MatrixF::Identity, pos, Point3F::One);
1491         mGizmo->renderGizmo( smCamMatrix );
1492
1493         mGizmoProfile->mode = savedMode;
1494      }
1495   }
1496
1497   gClientSceneGraph->setFogData( savedFogData );         // restore fog setting
1498}
1499
1500void GuiShapeEdPreview::renderGui(Point2I offset, const RectI& updateRect)
1501{
1502   // Render the 2D stuff here
1503
1504   // Render the names of the hovered and selected nodes
1505   if ( mModel )
1506   {
1507      if ( mRenderNodes && mHoverNode != -1 )
1508         renderNodeName( mHoverNode, LinearColorF::WHITE );
1509      if ( mSelectedNode != -1 )
1510         renderNodeName( mSelectedNode, LinearColorF::WHITE );
1511   }
1512}
1513
1514void GuiShapeEdPreview::renderGrid()
1515{
1516   if ( mRenderGridPlane )
1517   {
1518      // Use EditTSCtrl to render the grid in non-perspective views
1519      if ( mDisplayType != DisplayTypePerspective )
1520      {
1521         Parent::renderGrid();
1522         return;
1523      }
1524
1525      // Round grid dimension up to a multiple of the minor ticks
1526      Point2I dim(mGridDimension.x + mGridPlaneMinorTicks, mGridDimension.y + mGridPlaneMinorTicks);
1527      dim /= ( mGridPlaneMinorTicks + 1 );
1528      dim *= ( mGridPlaneMinorTicks + 1 );
1529
1530      Point2F minorStep( mGridPlaneSize, mGridPlaneSize );
1531      Point2F size( minorStep.x * dim.x, minorStep.y * dim.y );
1532      Point2F majorStep( minorStep * ( mGridPlaneMinorTicks + 1 ) );
1533
1534      GFXStateBlockDesc desc;
1535      desc.setBlend( true );
1536      desc.setZReadWrite( true, false );
1537
1538      GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, minorStep, mGridPlaneMinorTickColor );
1539      GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, majorStep, mGridPlaneColor );
1540   }
1541}
1542
1543void GuiShapeEdPreview::renderSunDirection() const
1544{
1545   if ( mEditingSun )
1546   {
1547      // Render four arrows aiming in the direction of the sun's light
1548      ColorI color = LinearColorF( mFakeSun->getColor()).toColorI();
1549      F32 length = mModel->getShape()->mBounds.len() * 0.8f;
1550
1551      // Get the sun's vectors
1552      Point3F fwd = mFakeSun->getTransform().getForwardVector();
1553      Point3F up = mFakeSun->getTransform().getUpVector() * length / 8;
1554      Point3F right = mFakeSun->getTransform().getRightVector() * length / 8;
1555
1556      // Calculate the start and end points of the first arrow (bottom left)
1557      Point3F start = mModel->getShape()->center - fwd * length - up/2 - right/2;
1558      Point3F end = mModel->getShape()->center - fwd * length / 3 - up/2 - right/2;
1559
1560      GFXStateBlockDesc desc;
1561      desc.setZReadWrite( true, true );
1562
1563      GFXDrawUtil* drawUtil = GFX->getDrawUtil();
1564
1565      drawUtil->drawArrow( desc, start, end, color );
1566      drawUtil->drawArrow( desc, start + up, end + up, color );
1567      drawUtil->drawArrow( desc, start + right, end + right, color );
1568      drawUtil->drawArrow( desc, start + up + right, end + up + right, color );
1569   }
1570}
1571
1572void GuiShapeEdPreview::renderNodes() const
1573{
1574   if ( mRenderNodes )
1575   {
1576      // Render links between nodes
1577      GFXStateBlockDesc desc;
1578      desc.setZReadWrite( false, true );
1579      desc.setCullMode( GFXCullNone );
1580      GFX->setStateBlockByDesc( desc );
1581
1582      PrimBuild::color( ColorI::WHITE );
1583      PrimBuild::begin( GFXLineList, mModel->getShape()->nodes.size() * 2 );
1584      for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
1585      {
1586         const TSShape::Node& node = mModel->getShape()->nodes[i];
1587         if (node.parentIndex >= 0)
1588         {
1589            Point3F start(mModel->mNodeTransforms[i].getPosition());
1590            Point3F end(mModel->mNodeTransforms[node.parentIndex].getPosition());
1591
1592            PrimBuild::vertex3f( start.x, start.y, start.z );
1593            PrimBuild::vertex3f( end.x, end.y, end.z );
1594         }
1595      }
1596      PrimBuild::end();
1597
1598      // Render the node axes
1599      for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++)
1600      {
1601         // Render the selected and hover nodes last (so they are on top)
1602         if ( ( i == mSelectedNode ) || ( i == mHoverNode ) )
1603            continue;   
1604
1605         renderNodeAxes( i, LinearColorF::WHITE );
1606      }
1607
1608      // Render the hovered node
1609      if ( mHoverNode != -1 )
1610         renderNodeAxes( mHoverNode, LinearColorF::GREEN );
1611   }
1612
1613   // Render the selected node (even if mRenderNodes is false)
1614   if ( mSelectedNode != -1 )
1615   {
1616      renderNodeAxes( mSelectedNode, LinearColorF::GREEN );
1617
1618      const MatrixF& nodeMat = mModel->mNodeTransforms[mSelectedNode];
1619      mGizmo->set( nodeMat, nodeMat.getPosition(), Point3F::One);
1620      mGizmo->renderGizmo( smCamMatrix );
1621   }
1622}
1623
1624void GuiShapeEdPreview::renderNodeAxes(S32 index, const LinearColorF& nodeColor) const
1625{
1626   if(mModel->mNodeTransforms.size() <= index || index < 0)
1627      return;
1628   const Point3F xAxis( 1.0f,  0.15f, 0.15f );
1629   const Point3F yAxis( 0.15f, 1.0f,  0.15f );
1630   const Point3F zAxis( 0.15f, 0.15f, 1.0f  );
1631
1632   GFXStateBlockDesc desc;
1633   desc.setZReadWrite( false, true );
1634   desc.setCullMode( GFXCullNone );
1635
1636   // Render nodes the same size regardless of zoom
1637   F32 scale = mOrbitDist / 60;
1638
1639   GFX->pushWorldMatrix();
1640   GFX->multWorld( mModel->mNodeTransforms[index] );
1641   const ColorI color = LinearColorF(nodeColor).toColorI();
1642   GFX->getDrawUtil()->drawCube( desc, xAxis * scale, Point3F::Zero, color );
1643   GFX->getDrawUtil()->drawCube( desc, yAxis * scale, Point3F::Zero, color );
1644   GFX->getDrawUtil()->drawCube( desc, zAxis * scale, Point3F::Zero, color );
1645
1646   GFX->popWorldMatrix();
1647}
1648
1649void GuiShapeEdPreview::renderNodeName(S32 index, const LinearColorF& textColor) const
1650{
1651   if(index < 0 || index >= mModel->getShape()->nodes.size() || index >= mProjectedNodes.size())
1652      return;
1653   const TSShape::Node& node = mModel->getShape()->nodes[index];
1654   const String& nodeName = mModel->getShape()->getName( node.nameIndex );
1655
1656   Point2I pos( mProjectedNodes[index].x, mProjectedNodes[index].y + sNodeRectSize + 6 );
1657
1658   GFX->getDrawUtil()->setBitmapModulation( LinearColorF(textColor).toColorI());
1659   GFX->getDrawUtil()->drawText( mProfile->mFont, pos, nodeName.c_str() );
1660}
1661
1662void GuiShapeEdPreview::renderCollisionMeshes() const
1663{
1664   if ( mRenderColMeshes )
1665   {
1666      ConcretePolyList polylist;
1667      polylist.setTransform( &MatrixF::Identity, Point3F::One );
1668      for ( S32 iDet = 0; iDet < mModel->getShape()->details.size(); iDet++ )
1669      {
1670         const TSShape::Detail& det = mModel->getShape()->details[iDet];
1671         const String& detName = mModel->getShape()->getName( det.nameIndex );
1672
1673         // Ignore non-collision details
1674         if ( detName.startsWith( "Collision-" ) )
1675            mModel->buildPolyList( &polylist, iDet );
1676      }
1677
1678      polylist.render();
1679   }
1680}
1681
1682//-----------------------------------------------------------------------------
1683// Console methods (GuiShapeEdPreview)
1684//-----------------------------------------------------------------------------
1685
1686DefineEngineMethod( GuiShapeEdPreview, setOrbitPos, void, ( Point3F pos ),,
1687   "Set the camera orbit position\n\n"
1688   "@param pos Position in the form \"x y z\"\n" )
1689{
1690   object->setOrbitPos( pos );
1691}
1692
1693DefineEngineMethod( GuiShapeEdPreview, setModel, bool, ( const char* shapePath ),,
1694   "Sets the model to be displayed in this control\n\n"
1695   "@param shapeName Name of the model to display.\n"
1696   "@return True if the model was loaded successfully, false otherwise.\n" )
1697{
1698   return object->setObjectModel( shapePath );
1699}
1700
1701DefineEngineMethod( GuiShapeEdPreview, fitToShape, void, (),,
1702   "Adjust the camera position and zoom to fit the shape within the view.\n\n" )
1703{
1704   object->fitToShape();
1705}
1706
1707DefineEngineMethod( GuiShapeEdPreview, refreshShape, void, (),,
1708   "Refresh the shape (used when the shape meshes or nodes have been added or removed)\n\n" )
1709{
1710   object->refreshShape();
1711}
1712
1713DefineEngineMethod( GuiShapeEdPreview, updateNodeTransforms, void, (),,
1714   "Refresh the shape node transforms (used when a node transform has been modified externally)\n\n" )
1715{
1716   object->updateNodeTransforms();
1717}
1718
1719DefineEngineMethod( GuiShapeEdPreview, computeShapeBounds, Box3F, (),,
1720   "Compute the bounding box of the shape using the current detail and node transforms\n\n"
1721   "@return the bounding box \"min.x min.y min.z max.x max.y max.z\"" )
1722{
1723   Box3F bounds;
1724   object->computeSceneBounds(bounds);
1725   return bounds;
1726}
1727
1728DefineEngineMethod( GuiShapeEdPreview, getMeshHidden, bool, ( const char* name ),,
1729   "Return whether the named object is currently hidden\n\n" )
1730{
1731   return object->getMeshHidden( name );
1732}
1733
1734DefineEngineMethod( GuiShapeEdPreview, setMeshHidden, void, ( const char* name, bool hidden ),,
1735   "Show or hide the named object in the shape\n\n" )
1736{
1737   object->setMeshHidden( name, hidden );
1738}
1739
1740DefineEngineMethod( GuiShapeEdPreview, setAllMeshesHidden, void, ( bool hidden ),,
1741   "Show or hide all objects in the shape\n\n" )
1742{
1743   object->setAllMeshesHidden( hidden );
1744}
1745
1746DefineEngineMethod( GuiShapeEdPreview, exportToCollada, void, ( const char* path ),,
1747   "Export the current shape and all mounted objects to COLLADA (.dae).\n"
1748   "Note that animation is not exported, and all geometry is combined into a "
1749   "single mesh.\n\n"
1750   "@param path Destination filename\n" )
1751{
1752   object->exportToCollada( path );
1753}
1754
1755//-----------------------------------------------------------------------------
1756// THREADS
1757DefineEngineMethod( GuiShapeEdPreview, addThread, void, (),,
1758   "Add a new thread (initially without any sequence set)\n\n" )
1759{
1760   object->addThread();
1761}
1762
1763DefineEngineMethod( GuiShapeEdPreview, removeThread, void, ( S32 slot ),,
1764   "Removes the specifed thread\n\n"
1765   "@param slot index of the thread to remove\n" )
1766{
1767   object->removeThread( slot );
1768}
1769
1770DefineEngineMethod( GuiShapeEdPreview, getThreadCount, S32, (),,
1771   "Get the number of threads\n\n"
1772   "@return the number of threads\n" )
1773{
1774   return object->getThreadCount();
1775}
1776
1777DefineEngineMethod( GuiShapeEdPreview, setTimeScale, void, ( F32 scale ),,
1778   "Set the time scale of all threads\n\n"
1779   "@param scale new time scale value\n" )
1780{
1781   object->setTimeScale( scale );
1782}
1783
1784DefineEngineMethod( GuiShapeEdPreview, setThreadSequence, void, ( const char* name, F32 duration, F32 pos, bool play ), ( 0, 0, false ),
1785   "Sets the sequence to play for the active thread.\n\n"
1786   "@param name name of the sequence to play\n"
1787   "@param duration transition duration (0 for no transition)\n"
1788   "@param pos position in the new sequence to transition to\n"
1789   "@param play if true, the new sequence will play during the transition\n" )
1790{
1791   object->setActiveThreadSequence( name, duration, pos, play );
1792}
1793
1794DefineEngineMethod( GuiShapeEdPreview, getThreadSequence, const char*, (),,
1795   "Get the name of the sequence assigned to the active thread" )
1796{
1797   return object->getThreadSequence();
1798}
1799
1800DefineEngineMethod( GuiShapeEdPreview, refreshThreadSequences, void, (),,
1801   "Refreshes thread sequences (in case of removed/renamed sequences" )
1802{
1803   object->refreshThreadSequences();
1804}
1805
1806//-----------------------------------------------------------------------------
1807// Mounting
1808DefineEngineMethod( GuiShapeEdPreview, mountShape, bool, ( const char* shapePath, const char* nodeName, const char* type, S32 slot ),,
1809   "Mount a shape onto the main shape at the specified node\n\n"
1810   "@param shapePath path to the shape to mount\n"
1811   "@param nodeName name of the node on the main shape to mount to\n"
1812   "@param type type of mounting to use (Object, Image or Wheel)\n"
1813   "@param slot mount slot\n" )
1814{
1815   return object->mountShape( shapePath, nodeName, type, slot );
1816}
1817
1818DefineEngineMethod( GuiShapeEdPreview, setMountNode, void, ( S32 slot, const char* nodeName ),,
1819   "Set the node a shape is mounted to.\n\n"
1820   "@param slot mounted shape slot\n"
1821   "@param nodename name of the node to mount to\n" )
1822{
1823   object->setMountNode( slot, nodeName );
1824}
1825
1826DefineEngineMethod( GuiShapeEdPreview, getMountThreadSequence, const char*, ( S32 slot ),,
1827   "Get the name of the sequence playing on this mounted shape\n"
1828   "@param slot mounted shape slot\n"
1829   "@return name of the sequence (if any)\n" )
1830{
1831   return object->getMountThreadSequence( slot );
1832}
1833
1834DefineEngineMethod( GuiShapeEdPreview, setMountThreadSequence, void, ( S32 slot, const char* name ),,
1835   "Set the sequence to play for the shape mounted in the specified slot\n"
1836   "@param slot mounted shape slot\n"
1837   "@param name name of the sequence to play\n" )
1838{
1839   object->setMountThreadSequence( slot, name );
1840}
1841
1842DefineEngineMethod( GuiShapeEdPreview, getMountThreadPos, F32, ( S32 slot ),,
1843   "Get the playback position of the sequence playing on this mounted shape\n"
1844   "@param slot mounted shape slot\n"
1845   "@return playback position of the sequence (0-1)\n" )
1846{
1847   return object->getMountThreadPos( slot );
1848}
1849
1850DefineEngineMethod( GuiShapeEdPreview, setMountThreadPos, void, ( S32 slot, F32 pos ),,
1851   "Set the sequence position of the shape mounted in the specified slot\n"
1852   "@param slot mounted shape slot\n"
1853   "@param pos sequence position (0-1)\n" )
1854{
1855   object->setMountThreadPos( slot, pos );
1856}
1857
1858DefineEngineMethod( GuiShapeEdPreview, getMountThreadDir, F32, ( S32 slot ),,
1859   "Get the playback direction of the sequence playing on this mounted shape\n"
1860   "@param slot mounted shape slot\n"
1861   "@return direction of the sequence (-1=reverse, 0=paused, 1=forward)\n" )
1862{
1863   return object->getMountThreadDir( slot );
1864}
1865
1866DefineEngineMethod( GuiShapeEdPreview, setMountThreadDir, void, ( S32 slot, F32 dir ),,
1867   "Set the playback direction of the shape mounted in the specified slot\n"
1868   "@param slot mounted shape slot\n"
1869   "@param dir playback direction (-1=backwards, 0=paused, 1=forwards)\n" )
1870{
1871   object->setMountThreadDir( slot, dir );
1872}
1873
1874DefineEngineMethod( GuiShapeEdPreview, unmountShape, void, ( S32 slot ),,
1875   "Unmount the shape in the specified slot\n"
1876   "@param slot mounted shape slot\n" )
1877{
1878   return object->unmountShape( slot );
1879}
1880
1881DefineEngineMethod( GuiShapeEdPreview, unmountAll, void, (),,
1882   "Unmount all shapes\n" )
1883{
1884   return object->unmountAll();
1885}
1886