Torque3D Documentation / _generateds / worldEditor.cpp

worldEditor.cpp

Engine/source/gui/worldEditor/worldEditor.cpp

More...

Classes:

Public Functions

ConsoleDocClass(WorldEditor , "@brief The <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> Editor tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">class\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(WorldEditor , addUndoState , void , () , "Adds/Submits an undo state <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the undo manager." )
DefineEngineMethod(WorldEditor , alignByAxis , void , (S32 axis) , "Align all selected objects along the given axis." "@param axis Axis <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> align all selected objects along." )
DefineEngineMethod(WorldEditor , alignByBounds , void , (S32 boundsAxis) , "Align all selected objects against the given bounds axis." "@param boundsAxis Bounds axis <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> align all selected objects against." )
DefineEngineMethod(WorldEditor , canPasteSelection , bool , () , "Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> we can paste the current selection." "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> we can paste the current selection, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not." )
DefineEngineMethod(WorldEditor , clearIgnoreList , void , () , "Clear the ignore class <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n</a>" )
DefineEngineMethod(WorldEditor , clearSelection , void , () , "Clear the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selection.\n</a>" )
DefineEngineMethod(WorldEditor , colladaExportSelection , void , (const char *path) , "Export the combined geometry of all selected objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified path in collada format." "@param path Path <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> export collada format to." )
DefineEngineMethod(WorldEditor , copySelection , void , () , "Copy the current selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be pasted later." )
DefineEngineMethod(WorldEditor , createConvexShapeFrom , ConvexShape * , (SceneObject *polyObject) , "Create <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classconvexshape/">ConvexShape</a> from the given polyhedral object." )
DefineEngineMethod(WorldEditor , createPolyhedralObject , SceneObject * , (const char *className, SceneObject *geometryProvider) , "Grab the geometry from @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> geometryProvider, create <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> className object, and assign it the extracted geometry." )
DefineEngineMethod(WorldEditor , cutSelection , void , () , "Cut the current selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be pasted later." )
DefineEngineMethod(WorldEditor , dropSelection , void , (bool skipUndo) , (false) , "Drop the current selection." "@param skipUndo True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> skip creating undo's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this action, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> create an undo." )
DefineEngineMethod(WorldEditor , explodeSelectedPrefab , void , () , "Replace selected <a href="/coding/class/classprefab/">Prefab</a> objects with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsimgroup/">SimGroup</a> containing all children objects defined in the .prefab." )
DefineEngineMethod(WorldEditor , getActiveEditorTool , EditorTool * , () , "Gets the active Editor Tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the world editor." )
DefineEngineMethod(WorldEditor , getActiveSelection , S32 , () , "Return the currently active <a href="/coding/class/classworldeditorselection/">WorldEditorSelection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return currently active <a href="/coding/class/classworldeditorselection/">WorldEditorSelection</a> object or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no selection set is available." )
DefineEngineMethod(WorldEditor , getSelectedObject , S32 , (S32 index) , "Return the selected object and the given index." "@param index Index of selected object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get." "@return selected object at given index or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> index is incorrect." )
DefineEngineMethod(WorldEditor , getSelectionCentroid , Point3F , () , "Get centroid of the selection." "@return centroid of the selection." )
DefineEngineMethod(WorldEditor , getSelectionExtent , Point3F , () , "Get extent of the selection." "@return extent of the selection." )
DefineEngineMethod(WorldEditor , getSelectionRadius , F32 , () , "Get the radius of the current selection." "@return radius of the current selection." )
DefineEngineMethod(WorldEditor , getSelectionSize , S32 , () , "Return the number of objects currently selected in the editor." "@return number of objects currently selected in the editor." )
DefineEngineMethod(WorldEditor , getSoftSnap , bool , () , "Is soft snapping always on?" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> soft snap is on, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not." )
DefineEngineMethod(WorldEditor , getSoftSnapAlignment , WorldEditor::AlignmentType , () , "Get the soft snap alignment." "@return soft snap alignment." )
DefineEngineMethod(WorldEditor , getSoftSnapBackfaceTolerance , F32 , () , "Get the fraction of the soft snap radius that backfaces may be included." "@return fraction of the soft snap radius that backfaces may be included." )
DefineEngineMethod(WorldEditor , getSoftSnapSize , F32 , () , "Get the absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." "@return absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." )
DefineEngineMethod(WorldEditor , getTerrainSnapAlignment , WorldEditor::AlignmentType , () , "Get the terrain snap alignment." "@return terrain snap alignment type." )
DefineEngineMethod(WorldEditor , hideObject , void , (SceneObject *obj, bool hide) , "Hide/show the given object." "@param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> hide/show." "@param hide True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> hide the object, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> show it." )
DefineEngineMethod(WorldEditor , hideSelection , void , (bool hide) , "Hide/show the selection." "@param hide True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> hide the selection, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> show it." )
DefineEngineMethod(WorldEditor , invalidateSelectionCentroid , void , () , "Invalidate the selection sets centroid." )
DefineEngineMethod(WorldEditor , lockSelection , void , (bool lock) , "Lock/unlock the selection." "@param lock True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> lock the selection, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> unlock it." )
DefineEngineMethod(WorldEditor , makeSelectionAMesh , bool , (const char *filename) , "Save selected objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> .dae collada <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and replace them in the level with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classtsstatic/">TSStatic</a> object." "@param filename collada <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the selected objects to." )
DefineEngineMethod(WorldEditor , makeSelectionPrefab , void , (const char *filename, bool dontDeleteOriginals) , (false) , "Save selected objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> .prefab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and replace them in the level with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classprefab/">Prefab</a> object." "@param filename <a href="/coding/class/classprefab/">Prefab</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the selected objects to." )
DefineEngineMethod(WorldEditor , mountRelative , void , (SceneObject *objA, SceneObject *objB) , "Mount object B relatively <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> object A." "@param objA <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount to." "@param objB <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount." )
DefineEngineMethod(WorldEditor , pasteSelection , void , () , "Paste the current selection." )
DefineEngineMethod(WorldEditor , redirectConsole , void , (S32 objID) , "Redirect console." "@param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> id." )
DefineEngineMethod(WorldEditor , resetSelectedRotation , void , () , "Reset the rotation of the selection." )
DefineEngineMethod(WorldEditor , resetSelectedScale , void , () , "Reset the scale of the selection." )
DefineEngineMethod(WorldEditor , selectObject , void , (SimObject *obj) , "Selects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object." "@param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> select." )
DefineEngineMethod(WorldEditor , setActiveSelection , void , (WorldEditorSelection *selection) , "Set the currently active <a href="/coding/class/classworldeditorselection/">WorldEditorSelection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param selection A WorldEditorSelectionSet object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the selection container." )
DefineEngineMethod(WorldEditor , setEditorTool , void , (EditorTool *newEditorTool) , (nullAsType< EditorTool * >()) , "Sets the active Editor Tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the world editor." )
DefineEngineMethod(WorldEditor , setSoftSnap , void , (bool softSnap) , "Allow soft snapping all of the time." "@param softSnap True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn soft snap on, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn it off." )
DefineEngineMethod(WorldEditor , setSoftSnapAlignment , void , (WorldEditor::AlignmentType type) , "Set the soft snap alignment." "@param type Soft snap alignment type." )
DefineEngineMethod(WorldEditor , setSoftSnapBackfaceTolerance , void , (F32 tolerance) , "Set the fraction of the soft snap radius that backfaces may be included." "@param tolerance Fraction of the soft snap radius that backfaces may be included (range of 0..1)." )
DefineEngineMethod(WorldEditor , setSoftSnapSize , void , (F32 size) , "Set the absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." "@param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> Absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." )
DefineEngineMethod(WorldEditor , setTerrainSnapAlignment , void , (WorldEditor::AlignmentType alignment) , "Set the terrain snap alignment." "@param alignment New terrain snap alignment type." )
DefineEngineMethod(WorldEditor , softSnapDebugRender , void , (F32 debugRender) , "Toggle soft snapping debug rendering." "@param debugRender True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn on soft snapping debug rendering, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn it off." )
DefineEngineMethod(WorldEditor , softSnapRender , void , (F32 render) , "Render the soft snapping bounds." "@param <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the soft snapping bounds, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not." )
DefineEngineMethod(WorldEditor , softSnapRenderTriangle , void , (F32 renderTriangle) , "Render the soft snapped triangle." "@param renderTriangle True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the soft snapped triangle, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not." )
DefineEngineMethod(WorldEditor , softSnapSizeByBounds , void , (bool useBounds) , "Use selection bounds <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> as soft snap bounds." "@param useBounds True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use selection bounds <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> as soft snap bounds, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not." )
DefineEngineMethod(WorldEditor , transformSelection , void , (bool position, Point3F point, bool relativePos, bool rotate, Point3F rotation, bool relativeRot, bool rotLocal, S32 scaleType, Point3F scale, bool sRelative, bool sLocal) , "Transform selection by given parameters." "@param position True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform the selection's position." "@param point Position <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform by." "@param relativePos True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use relative position." "@param rotate True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform the selection's rotation." "@param rotation Rotation <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform by." "@param relativeRot True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the relative rotation." "@param rotLocal True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the local rotation." "@param scaleType Scale type <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use." "@param scale Scale <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform by." "@param sRelative True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> relative scale." "@param sLocal True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> local scale." )
DefineEngineMethod(WorldEditor , unselectObject , void , (SimObject *obj) , "Unselects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object." "@param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> unselect." )
DefineEngineStringlyVariadicMethod(WorldEditor , ignoreObjClass , void , 3 , 0 , "(string class_name, ...)" )
ImplementEnumType(WorldEditorAlignmentType , "How <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> snap when snapping is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">enabled.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n\n</a>" )
ImplementEnumType(WorldEditorDropType , "How <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> drop objects when placed or dropped in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n\n</a>" )

Detailed Description

Public Variables

 EndImplementEnumType 
Frustum gDragFrustum 

Public Functions

ConsoleDocClass(WorldEditor , "@brief The <a href="/coding/file/x86unixmain_8cpp/#x86unixmain_8cpp_1a217dbf8b442f20279ea00b898af96f52">main</a> <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a81f4537631c9ab219ec74de554483adc">World</a> Editor tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">class\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )

DefineEngineMethod(WorldEditor , addUndoState , void , () , "Adds/Submits an undo state <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the undo manager." )

DefineEngineMethod(WorldEditor , alignByAxis , void , (S32 axis) , "Align all selected objects along the given axis." "@param axis Axis <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> align all selected objects along." )

DefineEngineMethod(WorldEditor , alignByBounds , void , (S32 boundsAxis) , "Align all selected objects against the given bounds axis." "@param boundsAxis Bounds axis <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> align all selected objects against." )

DefineEngineMethod(WorldEditor , canPasteSelection , bool , () , "Check <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> we can paste the current selection." "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> we can paste the current selection, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not." )

DefineEngineMethod(WorldEditor , clearIgnoreList , void , () , "Clear the ignore class <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">list.\n</a>" )

DefineEngineMethod(WorldEditor , clearSelection , void , () , "Clear the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selection.\n</a>" )

DefineEngineMethod(WorldEditor , colladaExportSelection , void , (const char *path) , "Export the combined geometry of all selected objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified path in collada format." "@param path Path <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> export collada format to." )

DefineEngineMethod(WorldEditor , copySelection , void , () , "Copy the current selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be pasted later." )

DefineEngineMethod(WorldEditor , createConvexShapeFrom , ConvexShape * , (SceneObject *polyObject) , "Create <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classconvexshape/">ConvexShape</a> from the given polyhedral object." )

DefineEngineMethod(WorldEditor , createPolyhedralObject , SceneObject * , (const char *className, SceneObject *geometryProvider) , "Grab the geometry from @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> geometryProvider, create <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> className object, and assign it the extracted geometry." )

DefineEngineMethod(WorldEditor , cutSelection , void , () , "Cut the current selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> be pasted later." )

DefineEngineMethod(WorldEditor , dropSelection , void , (bool skipUndo) , (false) , "Drop the current selection." "@param skipUndo True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> skip creating undo's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> this action, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> create an undo." )

DefineEngineMethod(WorldEditor , explodeSelectedPrefab , void , () , "Replace selected <a href="/coding/class/classprefab/">Prefab</a> objects with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classsimgroup/">SimGroup</a> containing all children objects defined in the .prefab." )

DefineEngineMethod(WorldEditor , getActiveEditorTool , EditorTool * , () , "Gets the active Editor Tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the world editor." )

DefineEngineMethod(WorldEditor , getActiveSelection , S32 , () , "Return the currently active <a href="/coding/class/classworldeditorselection/">WorldEditorSelection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@return currently active <a href="/coding/class/classworldeditorselection/">WorldEditorSelection</a> object or 0 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no selection set is available." )

DefineEngineMethod(WorldEditor , getSelectedObject , S32 , (S32 index) , "Return the selected object and the given index." "@param index Index of selected object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> get." "@return selected object at given index or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> index is incorrect." )

DefineEngineMethod(WorldEditor , getSelectionCentroid , Point3F , () , "Get centroid of the selection." "@return centroid of the selection." )

DefineEngineMethod(WorldEditor , getSelectionExtent , Point3F , () , "Get extent of the selection." "@return extent of the selection." )

DefineEngineMethod(WorldEditor , getSelectionRadius , F32 , () , "Get the radius of the current selection." "@return radius of the current selection." )

DefineEngineMethod(WorldEditor , getSelectionSize , S32 , () , "Return the number of objects currently selected in the editor." "@return number of objects currently selected in the editor." )

DefineEngineMethod(WorldEditor , getSoftSnap , bool , () , "Is soft snapping always on?" "@return True <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> soft snap is on, false <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> not." )

DefineEngineMethod(WorldEditor , getSoftSnapAlignment , WorldEditor::AlignmentType , () , "Get the soft snap alignment." "@return soft snap alignment." )

DefineEngineMethod(WorldEditor , getSoftSnapBackfaceTolerance , F32 , () , "Get the fraction of the soft snap radius that backfaces may be included." "@return fraction of the soft snap radius that backfaces may be included." )

DefineEngineMethod(WorldEditor , getSoftSnapSize , F32 , () , "Get the absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." "@return absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." )

DefineEngineMethod(WorldEditor , getTerrainSnapAlignment , WorldEditor::AlignmentType , () , "Get the terrain snap alignment." "@return terrain snap alignment type." )

DefineEngineMethod(WorldEditor , hideObject , void , (SceneObject *obj, bool hide) , "Hide/show the given object." "@param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> hide/show." "@param hide True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> hide the object, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> show it." )

DefineEngineMethod(WorldEditor , hideSelection , void , (bool hide) , "Hide/show the selection." "@param hide True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> hide the selection, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> show it." )

DefineEngineMethod(WorldEditor , invalidateSelectionCentroid , void , () , "Invalidate the selection sets centroid." )

DefineEngineMethod(WorldEditor , lockSelection , void , (bool lock) , "Lock/unlock the selection." "@param lock True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> lock the selection, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> unlock it." )

DefineEngineMethod(WorldEditor , makeSelectionAMesh , bool , (const char *filename) , "Save selected objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> .dae collada <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and replace them in the level with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classtsstatic/">TSStatic</a> object." "@param filename collada <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the selected objects to." )

DefineEngineMethod(WorldEditor , makeSelectionPrefab , void , (const char *filename, bool dontDeleteOriginals) , (false) , "Save selected objects <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> .prefab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> and replace them in the level with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/class/classprefab/">Prefab</a> object." "@param filename <a href="/coding/class/classprefab/">Prefab</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> save the selected objects to." )

DefineEngineMethod(WorldEditor , mountRelative , void , (SceneObject *objA, SceneObject *objB) , "Mount object B relatively <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> object A." "@param objA <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount to." "@param objB <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> mount." )

DefineEngineMethod(WorldEditor , pasteSelection , void , () , "Paste the current selection." )

DefineEngineMethod(WorldEditor , redirectConsole , void , (S32 objID) , "Redirect console." "@param objID <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> id." )

DefineEngineMethod(WorldEditor , resetSelectedRotation , void , () , "Reset the rotation of the selection." )

DefineEngineMethod(WorldEditor , resetSelectedScale , void , () , "Reset the scale of the selection." )

DefineEngineMethod(WorldEditor , selectObject , void , (SimObject *obj) , "Selects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object." "@param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> select." )

DefineEngineMethod(WorldEditor , setActiveSelection , void , (WorldEditorSelection *selection) , "Set the currently active <a href="/coding/class/classworldeditorselection/">WorldEditorSelection</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">object.\n</a>" "@param selection A WorldEditorSelectionSet object <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the selection container." )

DefineEngineMethod(WorldEditor , setEditorTool , void , (EditorTool *newEditorTool) , (nullAsType< EditorTool * >()) , "Sets the active Editor Tool <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the world editor." )

DefineEngineMethod(WorldEditor , setSoftSnap , void , (bool softSnap) , "Allow soft snapping all of the time." "@param softSnap True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn soft snap on, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn it off." )

DefineEngineMethod(WorldEditor , setSoftSnapAlignment , void , (WorldEditor::AlignmentType type) , "Set the soft snap alignment." "@param type Soft snap alignment type." )

DefineEngineMethod(WorldEditor , setSoftSnapBackfaceTolerance , void , (F32 tolerance) , "Set the fraction of the soft snap radius that backfaces may be included." "@param tolerance Fraction of the soft snap radius that backfaces may be included (range of 0..1)." )

DefineEngineMethod(WorldEditor , setSoftSnapSize , void , (F32 size) , "Set the absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." "@param <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> Absolute <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> trigger <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> soft snap." )

DefineEngineMethod(WorldEditor , setTerrainSnapAlignment , void , (WorldEditor::AlignmentType alignment) , "Set the terrain snap alignment." "@param alignment New terrain snap alignment type." )

DefineEngineMethod(WorldEditor , softSnapDebugRender , void , (F32 debugRender) , "Toggle soft snapping debug rendering." "@param debugRender True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn on soft snapping debug rendering, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> turn it off." )

DefineEngineMethod(WorldEditor , softSnapRender , void , (F32 render) , "Render the soft snapping bounds." "@param <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the soft snapping bounds, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not." )

DefineEngineMethod(WorldEditor , softSnapRenderTriangle , void , (F32 renderTriangle) , "Render the soft snapped triangle." "@param renderTriangle True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> the soft snapped triangle, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not." )

DefineEngineMethod(WorldEditor , softSnapSizeByBounds , void , (bool useBounds) , "Use selection bounds <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> as soft snap bounds." "@param useBounds True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use selection bounds <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1ab7d671599a7b25ca99a487fa341bc33a">size</a> as soft snap bounds, false <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> not." )

DefineEngineMethod(WorldEditor , transformSelection , void , (bool position, Point3F point, bool relativePos, bool rotate, Point3F rotation, bool relativeRot, bool rotLocal, S32 scaleType, Point3F scale, bool sRelative, bool sLocal) , "Transform selection by given parameters." "@param position True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform the selection's position." "@param point Position <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform by." "@param relativePos True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use relative position." "@param rotate True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform the selection's rotation." "@param rotation Rotation <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform by." "@param relativeRot True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the relative rotation." "@param rotLocal True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use the local rotation." "@param scaleType Scale type <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use." "@param scale Scale <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> transform by." "@param sRelative True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> relative scale." "@param sLocal True <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> use <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> local scale." )

DefineEngineMethod(WorldEditor , unselectObject , void , (SimObject *obj) , "Unselects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> single object." "@param obj <a href="/coding/file/gizmo_8h/#gizmo_8h_1a10fcd3bee2ea25191e31795e36bdeba1a5df911aaca43421a25e32c3002befbc4">Object</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> unselect." )

DefineEngineStringlyVariadicMethod(WorldEditor , ignoreObjClass , void , 3 , 0 , "(string class_name, ...)" )

findDragMeshCallback(SceneObject * obj, void * data)

findObjectsCallback(SceneObject * obj, void * val)

IMPLEMENT_CONOBJECT(WorldEditor )

ImplementEnumType(WorldEditorAlignmentType , "How <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> snap when snapping is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">enabled.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n\n</a>" )

ImplementEnumType(WorldEditorDropType , "How <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> drop objects when placed or dropped in the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">world.\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">internal\n\n</a>" )

   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 "platform/platform.h"
  25#include "gui/worldEditor/worldEditor.h"
  26
  27#include "gui/worldEditor/gizmo.h"
  28#include "gui/worldEditor/undoActions.h"
  29#include "gui/worldEditor/editorIconRegistry.h"
  30#include "gui/worldEditor/worldEditorSelection.h"
  31#include "core/stream/memStream.h"
  32#include "scene/simPath.h"
  33#include "scene/mixin/scenePolyhedralObject.h"
  34#include "gui/core/guiCanvas.h"
  35#include "T3D/gameBase/gameConnection.h"
  36#include "T3D/groundPlane.h"
  37#include "collision/earlyOutPolyList.h"
  38#include "collision/concretePolyList.h"
  39#include "console/consoleInternal.h"
  40#include "console/engineAPI.h"
  41#include "T3D/shapeBase.h"
  42#include "T3D/cameraSpline.h"
  43#include "T3D/convexShape.h"
  44#include "gfx/primBuilder.h"
  45#include "gfx/gfxTransformSaver.h"
  46#include "gfx/gfxDrawUtil.h"
  47#include "gfx/gfxDebugEvent.h"
  48#include "platform/typetraits.h"
  49#include "T3D/prefab.h"
  50#include "math/mEase.h"
  51#include "T3D/tsStatic.h"
  52
  53#include "tools/editorTool.h"
  54
  55#include "T3D/Scene.h"
  56#include <T3D/notesObject.h>
  57
  58IMPLEMENT_CONOBJECT( WorldEditor );
  59
  60ConsoleDocClass( WorldEditor,
  61   "@brief The main World Editor tool class\n\n"
  62   "Editor use only.\n\n"
  63   "@internal"
  64);
  65
  66ImplementEnumType( WorldEditorDropType,
  67   "How to drop objects when placed or dropped in the world.\n"
  68   "@internal\n\n")
  69   { WorldEditor::DropAtOrigin,           "atOrigin",      "Place at the scene origin (usually 0,0,0)\n"        },
  70   { WorldEditor::DropAtCamera,           "atCamera",       "Places at the same position as the camera, without rotation.\n"        },
  71   { WorldEditor::DropAtCameraWithRot,    "atCameraRot",    "Places at the same position as the camera, with the camera's rotation.\n"     },
  72   { WorldEditor::DropBelowCamera,        "belowCamera",    "Places below the camera.\n"    },
  73   { WorldEditor::DropAtScreenCenter,     "screenCenter",   "Places at a position projected outwards from the screen's center.\n"    },
  74   { WorldEditor::DropAtCentroid,         "atCentroid",     "Places at the center position of the current centroid.\n"      },
  75   { WorldEditor::DropToTerrain,          "toTerrain",      "Places on the terrain.\n"       },
  76   { WorldEditor::DropBelowSelection,     "belowSelection", "Places at a position below the selected object.\n"  },
  77   { WorldEditor::DropAtGizmo,            "atGizmo",        "Places at the gizmo point.\n"  }
  78EndImplementEnumType;
  79
  80ImplementEnumType( WorldEditorAlignmentType,
  81   "How to snap when snapping is enabled.\n"
  82   "@internal\n\n")
  83   { WorldEditor::AlignNone,  "None", "No alignement type.\n"   },
  84   { WorldEditor::AlignPosX,  "+X", "Snap towards the higher position on the X plane.\n"   },
  85   { WorldEditor::AlignPosY,  "+Y", "Snap towards the higher position on the Y plane.\n"   },
  86   { WorldEditor::AlignPosZ,  "+Z", "Snap towards the higher position on the Z plane.\n"   },
  87   { WorldEditor::AlignNegX,  "-X", "Snap towards the lower position on the X plane.\n"    },
  88   { WorldEditor::AlignNegY,  "-Y", "Snap towards the lower position on the Y plane.\n"    },
  89   { WorldEditor::AlignNegZ,  "-Z", "Snap towards the lower position on the Z plane.\n"    },
  90EndImplementEnumType;
  91
  92
  93// unnamed namespace for static data
  94namespace {
  95
  96   static VectorF axisVector[3] = {
  97      VectorF(1.0f,0.0f,0.0f),
  98      VectorF(0.0f,1.0f,0.0f),
  99      VectorF(0.0f,0.0f,1.0f)
 100   };
 101
 102   static Point3F BoxPnts[] = {
 103      Point3F(0.0f,0.0f,0.0f),
 104      Point3F(0.0f,0.0f,1.0f),
 105      Point3F(0.0f,1.0f,0.0f),
 106      Point3F(0.0f,1.0f,1.0f),
 107      Point3F(1.0f,0.0f,0.0f),
 108      Point3F(1.0f,0.0f,1.0f),
 109      Point3F(1.0f,1.0f,0.0f),
 110      Point3F(1.0f,1.0f,1.0f)
 111   };
 112
 113   static U32 BoxVerts[][4] = {
 114      {0,2,3,1},     // -x
 115      {7,6,4,5},     // +x
 116      {0,1,5,4},     // -y
 117      {3,2,6,7},     // +y
 118      {0,4,6,2},     // -z
 119      {3,7,5,1}      // +z
 120   };
 121
 122   //
 123   U32 getBoxNormalIndex(const VectorF & normal)
 124   {
 125      const F32 * pNormal = ((const F32 *)normal);
 126
 127      F32 max = 0;
 128      S32 index = -1;
 129
 130      for(U32 i = 0; i < 3; i++)
 131         if(mFabs(pNormal[i]) >= mFabs(max))
 132         {
 133            max = pNormal[i];
 134            index = i*2;
 135         }
 136
 137      AssertFatal(index >= 0, "Failed to get best normal");
 138      if(max > 0.f)
 139         index++;
 140
 141      return(index);
 142   }
 143
 144   //
 145   Point3F getBoundingBoxCenter(SceneObject * obj)
 146   {
 147      Box3F box = obj->getObjBox();
 148      MatrixF mat = obj->getTransform();
 149      VectorF scale = obj->getScale();
 150
 151      Point3F center(0,0,0);
 152      Point3F projPnts[8];
 153
 154      for(U32 i = 0; i < 8; i++)
 155      {
 156         Point3F pnt;
 157         pnt.set(BoxPnts[i].x ? box.maxExtents.x : box.minExtents.x,
 158                 BoxPnts[i].y ? box.maxExtents.y : box.minExtents.y,
 159                 BoxPnts[i].z ? box.maxExtents.z : box.minExtents.z);
 160
 161         // scale it
 162         pnt.convolve(scale);
 163         mat.mulP(pnt, &projPnts[i]);
 164         center += projPnts[i];
 165      }
 166
 167      center /= 8;
 168      return(center);
 169   }
 170
 171   //
 172   const char * parseObjectFormat(SimObject * obj, const char * format)
 173   {
 174      static char buf[1024];
 175
 176      U32 curPos = 0;
 177      U32 len = dStrlen(format);
 178
 179      for(U32 i = 0; i < len; i++)
 180      {
 181         if(format[i] == '$')
 182         {
 183            U32 j;
 184            for(j = i+1; j < len; j++)
 185               if(format[j] == '$')
 186                  break;
 187
 188            if(j == len)
 189               break;
 190
 191            char token[80];
 192
 193            AssertFatal((j - i) < (sizeof(token) - 1), "token too long");
 194            dStrncpy(token, &format[i+1], (j - i - 1));
 195            token[j-i-1] = 0;
 196
 197            U32 remaining = sizeof(buf) - curPos - 1;
 198
 199            // look at the token
 200            if(!dStricmp(token, "id"))
 201               curPos += dSprintf(buf + curPos, remaining, "%d", obj->getId());
 202            else if( dStricmp( token, "name|class" ) == 0 )
 203            {
 204               const char* str;
 205               if( obj->getName() && obj->getName()[ 0 ] )
 206                  str = obj->getName();
 207               else
 208                  str = obj->getClassName();
 209                  
 210               curPos += dSprintf( buf + curPos, remaining, "%s", str );
 211            }
 212            else if(!dStricmp(token, "name|internal"))
 213            {
 214               if( obj->getName() || !obj->getInternalName() )
 215                  curPos += dSprintf(buf + curPos, remaining, "%s", obj->getName());
 216               else
 217                  curPos += dSprintf(buf + curPos, remaining, "[%s]", obj->getInternalName());
 218            }
 219            else if(!dStricmp(token, "name"))
 220               curPos += dSprintf(buf + curPos, remaining, "%s", obj->getName());
 221            else if(!dStricmp(token, "class"))
 222               curPos += dSprintf(buf + curPos, remaining, "%s", obj->getClassName());
 223            else if(!dStricmp(token, "namespace") && obj->getNamespace())
 224               curPos += dSprintf(buf + curPos, remaining, "%s", obj->getNamespace()->mName);
 225
 226            //
 227            i = j;
 228         }
 229         else
 230            buf[curPos++] = format[i];
 231      }
 232
 233      buf[curPos] = 0;
 234      return(buf);
 235   }
 236
 237   //
 238   F32 snapFloat(F32 val, F32 snap)
 239   {
 240      if(snap == 0.f)
 241         return(val);
 242
 243      F32 a = mFmod(val, snap);
 244
 245      if(mFabs(a) > (snap / 2))
 246         val < 0.f ? val -= snap : val += snap;
 247
 248      return(val - a);
 249   }
 250
 251   //
 252   EulerF extractEuler(const MatrixF & matrix)
 253   {
 254      const F32 * mat = (const F32*)matrix;
 255
 256      EulerF r;
 257      r.x = mAsin(mat[MatrixF::idx(2,1)]);
 258
 259      if(mCos(r.x) != 0.f)
 260      {
 261         r.y = mAtan2(-mat[MatrixF::idx(2,0)], mat[MatrixF::idx(2,2)]);
 262         r.z = mAtan2(-mat[MatrixF::idx(0,1)], mat[MatrixF::idx(1,1)]);
 263      }
 264      else
 265      {
 266         r.y = 0.f;
 267         r.z = mAtan2(mat[MatrixF::idx(1,0)], mat[MatrixF::idx(0,0)]);
 268      }
 269
 270      return(r);
 271   }
 272}
 273
 274F32 WorldEditor::smProjectDistance = 20000.0f;
 275
 276SceneObject* WorldEditor::getClientObj(SceneObject * obj)
 277{
 278   AssertFatal(obj->isServerObject(), "WorldEditor::getClientObj: not a server object!");
 279
 280   NetConnection * toServer = NetConnection::getConnectionToServer();
 281   NetConnection * toClient = NetConnection::getLocalClientConnection();
 282   if (!toServer || !toClient)
 283      return NULL;
 284
 285   S32 index = toClient->getGhostIndex(obj);
 286   if(index == -1)
 287      return(0);
 288
 289   return(dynamic_cast<SceneObject*>(toServer->resolveGhost(index)));
 290}
 291
 292void WorldEditor::markAsSelected( SimObject* object, bool state )
 293{
 294   object->setSelected( state );
 295   
 296   if( dynamic_cast< SceneObject* >( object ) )
 297   {
 298      SceneObject* clientObj = WorldEditor::getClientObj( static_cast< SceneObject* >( object ) );
 299      if( clientObj )
 300         clientObj->setSelected( state );
 301   }
 302}
 303
 304void WorldEditor::setClientObjInfo(SceneObject * obj, const MatrixF & mat, const VectorF & scale)
 305{
 306   SceneObject * clientObj = getClientObj(obj);
 307   if(!clientObj)
 308      return;
 309
 310   clientObj->setTransform(mat);
 311   clientObj->setScale(scale);
 312}
 313
 314void WorldEditor::updateClientTransforms(Selection*  sel)
 315{
 316   for( U32 i = 0; i < sel->size(); ++ i )
 317   {
 318      SceneObject* serverObj = dynamic_cast< SceneObject* >( ( *sel )[ i ] );
 319      if( !serverObj )
 320         continue;
 321         
 322      SceneObject* clientObj = getClientObj( serverObj );
 323      if(!clientObj)
 324         continue;
 325
 326      clientObj->setTransform(serverObj->getTransform());
 327      clientObj->setScale(serverObj->getScale());
 328   }
 329}
 330
 331void WorldEditor::addUndoState()
 332{
 333   submitUndo( mSelected );
 334}
 335
 336void WorldEditor::submitUndo( Selection* sel, const UTF8* label )
 337{
 338   // Grab the world editor undo manager.
 339   UndoManager *undoMan = NULL;
 340   if ( !Sim::findObject( "EUndoManager", undoMan ) )
 341   {
 342      Con::errorf( "WorldEditor::createUndo() - EUndoManager not found!" );
 343      return;           
 344   }
 345
 346   // Setup the action.
 347   WorldEditorUndoAction *action = new WorldEditorUndoAction( label );
 348   for(U32 i = 0; i < sel->size(); i++)
 349   {
 350      SceneObject* object = dynamic_cast< SceneObject* >( ( *sel )[ i ] );
 351      if( !object )
 352         continue;
 353         
 354      WorldEditorUndoAction::Entry entry;
 355
 356      entry.mMatrix = object->getTransform();
 357      entry.mScale = object->getScale();
 358      entry.mObjId = object->getId();
 359      action->mEntries.push_back( entry );
 360   }
 361
 362   // Submit it.
 363   action->mWorldEditor = this;
 364   undoMan->addAction( action );
 365   
 366   // Mark the world editor as dirty!
 367   setDirty();
 368}
 369
 370void WorldEditor::WorldEditorUndoAction::undo()
 371{
 372   // NOTE: This function also handles WorldEditorUndoAction::redo().
 373
 374   MatrixF oldMatrix;
 375   VectorF oldScale;
 376   for( U32 i = 0; i < mEntries.size(); i++ )
 377   {
 378      SceneObject *obj;
 379      if ( !Sim::findObject( mEntries[i].mObjId, obj ) )
 380         continue;
 381
 382      mWorldEditor->setClientObjInfo( obj, mEntries[i].mMatrix, mEntries[i].mScale );
 383
 384      // Grab the current state.
 385      oldMatrix = obj->getTransform();
 386      oldScale = obj->getScale();
 387
 388      // Restore the saved state.
 389      obj->setTransform( mEntries[i].mMatrix );
 390      obj->setScale( mEntries[i].mScale );
 391
 392      // Store the previous state so the next time
 393      // we're called we can restore it.
 394      mEntries[i].mMatrix = oldMatrix;
 395      mEntries[i].mScale = oldScale;
 396   }
 397
 398   // Mark the world editor as dirty!
 399   mWorldEditor->setDirty();
 400   mWorldEditor->mSelected->invalidateCentroid();
 401
 402   // Let the script get a chance at it.
 403   Con::executef( mWorldEditor, "onWorldEditorUndo" );
 404}
 405
 406//------------------------------------------------------------------------------
 407// edit stuff
 408
 409bool WorldEditor::cutSelection(Selection*  sel)
 410{
 411   if ( !sel->size() )
 412      return false;
 413
 414   // First copy the selection.
 415   copySelection( sel );
 416
 417   // Grab the world editor undo manager.
 418   UndoManager *undoMan = NULL;
 419   if ( !Sim::findObject( "EUndoManager", undoMan ) )
 420   {
 421      Con::errorf( "WorldEditor::cutSelection() - EUndoManager not found!" );
 422      return false;           
 423   }
 424
 425   // Setup the action.
 426   MEDeleteUndoAction *action = new MEDeleteUndoAction();
 427   while ( sel->size() )
 428      action->deleteObject( ( *sel )[0] );
 429   undoMan->addAction( action );
 430
 431   // Mark the world editor as dirty!
 432   setDirty();
 433
 434   return true;
 435}
 436
 437bool WorldEditor::copySelection(Selection*  sel)
 438{
 439   mCopyBuffer.clear();
 440
 441   for( U32 i = 0; i < sel->size(); i++ )
 442   {
 443      mCopyBuffer.increment();
 444      mCopyBuffer.last().save( ( *sel )[i] );
 445   }
 446
 447   return true;
 448}
 449
 450bool WorldEditor::pasteSelection( bool dropSel )
 451{
 452   clearSelection();
 453
 454   // Grab the world editor undo manager.
 455   UndoManager *undoMan = NULL;
 456   if ( !Sim::findObject( "EUndoManager", undoMan ) )
 457   {
 458      Con::errorf( "WorldEditor::pasteSelection() - EUndoManager not found!" );
 459      return false;           
 460   }
 461
 462   SimGroup *targetGroup = NULL;   
 463   if( isMethod( "getNewObjectGroup" ) )
 464   {
 465      const char* targetGroupName = Con::executef( this, "getNewObjectGroup" );
 466      if( targetGroupName && targetGroupName[ 0 ] && !Sim::findObject( targetGroupName, targetGroup) )
 467         Con::errorf( "WorldEditor::pasteSelection() - no SimGroup called '%s'", targetGroupName );
 468   }
 469
 470   if( !targetGroup)
 471   {
 472      targetGroup = Scene::getRootScene();
 473      if( !targetGroup)
 474      {
 475         Con::errorf( "WorldEditor::pasteSelection() - Scene not found" );
 476         return false;
 477      }
 478   }
 479
 480   // Setup the action.
 481   MECreateUndoAction *action = new MECreateUndoAction( "Paste" );
 482
 483   for( U32 i = 0; i < mCopyBuffer.size(); i++ )
 484   {
 485      SimObject* obj = mCopyBuffer[i].restore();
 486      if ( !obj )
 487         continue;
 488
 489      if (targetGroup)
 490         targetGroup->addObject( obj );
 491
 492      action->addObject( obj );
 493
 494      if ( !mSelectionLocked )
 495      {         
 496         mSelected->addObject( obj );
 497         Con::executef( this, "onSelect", obj->getIdString() );
 498      }
 499   }
 500
 501   // Its safe to submit the action before the selection
 502   // is dropped below because the state of the objects 
 503   // are not stored until they are first undone.
 504   undoMan->addAction( action );
 505
 506   // drop it ...
 507   if ( dropSel )
 508      dropSelection( mSelected );
 509
 510   if ( mSelected->size() )
 511   {      
 512      char buf[16];
 513      dSprintf( buf, sizeof(buf), "%d", ( *mSelected )[0]->getId() );
 514
 515      SimObject *obj = NULL;
 516      if(mRedirectID)
 517         obj = Sim::findObject(mRedirectID);
 518      Con::executef(obj ? obj : this, "onClick", buf);
 519   }
 520
 521   // Mark the world editor as dirty!
 522   setDirty();
 523
 524   return true;
 525}
 526
 527//------------------------------------------------------------------------------
 528
 529WorldEditorSelection* WorldEditor::getActiveSelectionSet() const
 530{
 531   return mSelected;
 532}
 533
 534void WorldEditor::makeActiveSelectionSet( WorldEditorSelection* selection )
 535{
 536   Selection* oldSelection = getActiveSelectionSet();
 537   Selection* newSelection = selection;
 538   
 539   if( oldSelection == newSelection )
 540      return;
 541      
 542   // Unset the selection set so that calling onSelect/onUnselect callbacks
 543   // on the editor object will not affect the sets we have.
 544      
 545   mSelected = NULL;
 546      
 547   // Go through all objects in the old selection and for each
 548   // one that is not also in the new selection, signal an
 549   // unselect.
 550      
 551   if( oldSelection )
 552   {
 553      for( Selection::iterator iter = oldSelection->begin(); iter != oldSelection->end(); ++ iter )
 554         if( !newSelection || !newSelection->objInSet( *iter ) )
 555         {
 556            Con::executef( this, "onUnselect", ( *iter )->getIdString() );
 557            markAsSelected( *iter, false );
 558         }
 559            
 560      oldSelection->setAutoSelect( false );
 561   }
 562            
 563   // Go through all objects in the new selection and for each
 564   // one that is not also in the old selection, signal a
 565   // select.
 566            
 567   if( newSelection )
 568   {
 569      for( Selection::iterator iter = newSelection->begin(); iter != newSelection->end(); ++ iter )
 570         if( !oldSelection || !oldSelection->objInSet( *iter ) )
 571         {
 572            markAsSelected( *iter, true );
 573            Con::executef( this, "onSelect", ( *iter )->getIdString() );
 574         }
 575            
 576      newSelection->setAutoSelect( true );
 577   }
 578            
 579   // Install the new selection set.
 580            
 581   mSelected = newSelection;
 582   
 583   if( isMethod( "onSelectionSetChanged" ) )
 584      Con::executef( this, "onSelectionSetChanged" );
 585}
 586
 587//------------------------------------------------------------------------------
 588
 589void WorldEditor::hideObject(SceneObject* serverObj, bool hide)
 590{
 591   // client
 592   SceneObject * clientObj = getClientObj( serverObj );
 593   if( clientObj )
 594      clientObj->setHidden( hide );
 595
 596   // server
 597   serverObj->setHidden( hide );
 598}
 599
 600void WorldEditor::hideSelection(bool hide)
 601{
 602   Scene* scene = Scene::getRootScene();
 603
 604   // set server/client objects hide field
 605   for(U32 i = 0; i < mSelected->size(); i++)
 606   {
 607      SceneObject* serverObj = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] );
 608      if( !serverObj )
 609         continue;
 610
 611      // Prevent non-mission group objects (i.e. Player) from being hidden.
 612      // Otherwise it is difficult to show them again.
 613      if(!serverObj->isChildOfGroup(scene))
 614         continue;
 615
 616      hideObject(serverObj, hide);
 617   }
 618}
 619
 620void WorldEditor::lockSelection(bool lock)
 621{
 622   //
 623   for(U32 i = 0; i < mSelected->size(); i++)
 624      ( *mSelected )[i]->setLocked(lock);
 625}
 626
 627//------------------------------------------------------------------------------
 628// the centroid get's moved to the drop point...
 629void WorldEditor::dropSelection(Selection*  sel)
 630{
 631   if(!sel->size())
 632      return;
 633
 634   setDirty();
 635
 636   Point3F centroid = mObjectsUseBoxCenter ? sel->getBoxCentroid() : sel->getCentroid();
 637
 638   switch(mDropType)
 639   {
 640      case DropAtCentroid:
 641         // already there
 642         break;
 643
 644      case DropAtOrigin:
 645      {
 646         if(mDropAtBounds && !sel->containsGlobalBounds())
 647         {
 648            const Point3F& boxCenter = sel->getBoxCentroid();
 649            const Box3F& bounds = sel->getBoxBounds();
 650            Point3F offset = -boxCenter;
 651            offset.z += bounds.len_z() * 0.5f;
 652
 653            sel->offset(offset, (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 654         }
 655         else
 656            sel->offset(Point3F(-centroid), (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 657
 658         break;
 659      }
 660
 661      case DropAtCameraWithRot:
 662      {
 663         Point3F center = centroid;
 664         if(mDropAtBounds && !sel->containsGlobalBounds())
 665            center = sel->getBoxBottomCenter();
 666
 667         sel->offset(Point3F(smCamPos - center), (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 668         sel->orient(smCamMatrix, center);
 669         break;
 670      }
 671
 672      case DropAtCamera:
 673      {
 674         Point3F center = centroid;
 675         if(mDropAtBounds && !sel->containsGlobalBounds())
 676            sel->getBoxBottomCenter();
 677
 678         sel->offset(Point3F(smCamPos - center), (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 679         break;
 680      }
 681
 682      case DropBelowCamera:
 683      {
 684         Point3F center = centroid;
 685         if(mDropAtBounds && !sel->containsGlobalBounds())
 686            center = sel->getBoxBottomCenter();
 687
 688         Point3F offset = smCamPos - center;
 689         offset.z -= mDropBelowCameraOffset;
 690         sel->offset(offset, (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 691         break;
 692      }
 693
 694      case DropAtScreenCenter:
 695      {
 696         // Use the center of the selection bounds
 697         Point3F center = sel->getBoxCentroid();
 698
 699         Gui3DMouseEvent event;
 700         event.pos = smCamPos;
 701
 702         // Calculate the center of the sceen (in global screen coordinates)
 703         Point2I offset = localToGlobalCoord(Point2I(0,0));
 704         Point3F sp(F32(offset.x + F32(getExtent().x / 2)), F32(offset.y + (getExtent().y / 2)), 1.0f);
 705
 706         // Calculate the view distance to fit the selection
 707         // within the camera's view.
 708         const Box3F bounds = sel->getBoxBounds();
 709         F32 radius = bounds.len()*0.5f;
 710         F32 viewdist = calculateViewDistance(radius) * mDropAtScreenCenterScalar;
 711
 712         // Be careful of infinite sized objects, or just large ones in general.
 713         if(viewdist > mDropAtScreenCenterMax)
 714            viewdist = mDropAtScreenCenterMax;
 715
 716         // Position the selection
 717         Point3F wp;
 718         unproject(sp, &wp);
 719         event.vec = wp - smCamPos;
 720         event.vec.normalizeSafe();
 721         event.vec *= viewdist;
 722         sel->offset(Point3F(event.pos - center) += event.vec, (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 723
 724         break;
 725      }
 726
 727      case DropToTerrain:
 728      {
 729         terrainSnapSelection(sel, 0, mGizmo->getPosition(), true);
 730         break;
 731      }
 732
 733      case DropBelowSelection:
 734      {
 735         dropBelowSelection(sel, centroid, mDropAtBounds);
 736         break;
 737      }
 738
 739      case DropAtGizmo:
 740      {
 741         dropAtGizmo(sel, mGizmo->getPosition()-centroid);
 742         break;
 743      }
 744   }
 745
 746   //
 747   updateClientTransforms(sel);
 748}
 749
 750void WorldEditor::dropAtGizmo(Selection*  sel, const Point3F & gizmoPos)
 751{
 752   if (!sel->size())
 753      return;
 754
 755   sel->offset(gizmoPos, (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 756}
 757
 758void WorldEditor::dropBelowSelection(Selection*  sel, const Point3F & centroid, bool useBottomBounds)
 759{
 760   if(!sel->size())
 761      return;
 762
 763   Point3F start;
 764   if(useBottomBounds && !sel->containsGlobalBounds())
 765      start = sel->getBoxBottomCenter();
 766   else
 767      start = centroid;
 768
 769   Point3F end = start;
 770   end.z -= 4000.f;
 771      
 772   sel->disableCollision(); // Make sure we don't hit ourselves.
 773
 774   RayInfo ri;
 775   bool hit = gServerContainer.castRay(start, end, STATIC_COLLISION_TYPEMASK, &ri);
 776      
 777   sel->enableCollision();
 778
 779   if( hit )
 780      sel->offset(ri.point - start, (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 781}
 782
 783//------------------------------------------------------------------------------
 784
 785void WorldEditor::terrainSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos, bool forceStick)
 786{
 787   mStuckToGround = false;
 788
 789   if ( !mStickToGround && !forceStick )
 790      return;
 791
 792   if(!sel->size())
 793      return;
 794
 795   if(sel->containsGlobalBounds())
 796      return;
 797
 798   Point3F centroid;
 799   if(mDropAtBounds && !sel->containsGlobalBounds())
 800      centroid = sel->getBoxBottomCenter();
 801   else
 802      centroid = mObjectsUseBoxCenter ? sel->getBoxCentroid() : sel->getCentroid();
 803
 804   Point3F start = centroid;
 805   Point3F end = start;
 806   start.z -= 2000;
 807   end.z += 2000.f;
 808      
 809   sel->disableCollision(); // Make sure we don't hit ourselves.
 810
 811   RayInfo ri;
 812   bool hit;
 813   if(mBoundingBoxCollision)
 814      hit = gServerContainer.collideBox(start, end, TerrainObjectType, &ri);
 815   else
 816      hit = gServerContainer.castRay(start, end, TerrainObjectType, &ri);
 817      
 818   sel->enableCollision();
 819
 820   if( hit )
 821   {
 822      mStuckToGround = true;
 823
 824      const F32 OffsetZValueMin = 0.01f;
 825
 826      if (mTerrainSnapOffsetZ)
 827      {
 828         if (mOffsetZValue == 0.0f)
 829         {
 830            ri.point.z += OffsetZValueMin;
 831         }
 832         else
 833         {
 834            ri.point.z += mOffsetZValue;
 835         }
 836         
 837      }
 838      
 839      sel->offset(ri.point - centroid, (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
 840
 841      if(mTerrainSnapAlignment != AlignNone)
 842      {
 843         EulerF rot(0.0f, 0.0f, 0.0f); // Equivalent to AlignPosY
 844         switch(mTerrainSnapAlignment)
 845         {
 846            case AlignPosX:
 847               rot.set(0.0f, 0.0f, mDegToRad(-90.0f));
 848               break;
 849
 850            case AlignPosZ:
 851               rot.set(mDegToRad(90.0f), 0.0f, mDegToRad(180.0f));
 852               break;
 853
 854            case AlignNegX:
 855               rot.set(0.0f, 0.0f, mDegToRad(90.0f));
 856               break;
 857
 858            case AlignNegY:
 859               rot.set(0.0f, 0.0f, mDegToRad(180.0f));
 860               break;
 861
 862            case AlignNegZ:
 863               rot.set(mDegToRad(-90.0f), 0.0f, mDegToRad(180.0f));
 864               break;
 865         }
 866
 867         MatrixF mat = MathUtils::createOrientFromDir(ri.normal);
 868         MatrixF rotMat(rot);
 869
 870         sel->orient(mat.mul(rotMat), Point3F::Zero);
 871      }
 872   }
 873}
 874
 875void WorldEditor::softSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos)
 876{
 877   mSoftSnapIsStuck = false;
 878   mSoftSnapActivated = false;
 879
 880   // If soft snap is activated, holding CTRL will temporarily deactivate it.
 881   // Conversely, if soft snapping is deactivated, holding CTRL will activate it.
 882   if( (mSoftSnap && (modifier & SI_PRIMARY_CTRL)) || (!mSoftSnap && !(modifier & SI_PRIMARY_CTRL)) )
 883      return;
 884
 885   if(!sel->size())
 886      return;
 887
 888   if(sel->containsGlobalBounds())
 889      return;
 890
 891   mSoftSnapActivated = true;
 892
 893   Point3F centroid = mObjectsUseBoxCenter ? sel->getBoxCentroid() : sel->getCentroid();
 894
 895   // Find objects we may stick against
 896   Vector<SceneObject*> foundobjs;
 897
 898   SceneObject *controlObj = getControlObject();
 899   if ( controlObj )
 900      controlObj->disableCollision();
 901
 902   sel->disableCollision();
 903
 904   if(mSoftSnapSizeByBounds)
 905   {
 906      mSoftSnapBounds = sel->getBoxBounds();
 907      mSoftSnapBounds.setCenter(centroid);
 908   }
 909   else
 910   {
 911      mSoftSnapBounds.set(Point3F(mSoftSnapSize, mSoftSnapSize, mSoftSnapSize));
 912      mSoftSnapBounds.setCenter(centroid);
 913   }
 914
 915   mSoftSnapPreBounds = mSoftSnapBounds;
 916   mSoftSnapPreBounds.setCenter(gizmoPos);
 917
 918   SphereF sphere(gizmoPos, mSoftSnapPreBounds.len()*0.5f);
 919
 920   gServerContainer.findObjectList(mSoftSnapPreBounds, 0xFFFFFFFF, &foundobjs);
 921
 922   sel->enableCollision();
 923
 924   if ( controlObj )
 925      controlObj->enableCollision();
 926
 927   ConcretePolyList polys;
 928   for(S32 i=0; i<foundobjs.size(); ++i)
 929   {
 930      SceneObject* so = foundobjs[i];
 931      polys.setTransform(&(so->getTransform()), so->getScale());
 932      polys.setObject(so);
 933      so->buildPolyList(PLC_Selection, &polys, mSoftSnapPreBounds, sphere);
 934   }
 935
 936   // Calculate sticky point
 937   bool     found = false;
 938   F32      foundDist = mSoftSnapPreBounds.len();
 939   Point3F  foundPoint(0.0f, 0.0f, 0.0f);
 940   PlaneF   foundPlane;
 941   MathUtils::IntersectInfo info;
 942
 943   if(mSoftSnapDebugRender)
 944   {
 945      mSoftSnapDebugPoint.set(0.0f, 0.0f, 0.0f);
 946      mSoftSnapDebugTriangles.clear();
 947   }
 948
 949   F32 backfaceToleranceSize = mSoftSnapBackfaceTolerance*mSoftSnapSize;
 950   for(S32 i=0; i<polys.mPolyList.size(); ++i)
 951   {
 952      ConcretePolyList::Poly p = polys.mPolyList[i];
 953
 954      if(p.vertexCount >= 3)
 955      {
 956         S32 vertind[3];
 957         vertind[0] = polys.mIndexList[p.vertexStart];
 958         vertind[1] = polys.mIndexList[p.vertexStart + 1];
 959         vertind[2] = polys.mIndexList[p.vertexStart + 2];
 960
 961         // Distance to the triangle
 962         F32 d = MathUtils::mTriangleDistance(polys.mVertexList[vertind[0]], polys.mVertexList[vertind[1]], polys.mVertexList[vertind[2]], gizmoPos, &info);
 963
 964         // Cull backface polys that are not within tolerance
 965         if(p.plane.whichSide(gizmoPos) == PlaneF::Back && d > backfaceToleranceSize)
 966            continue;
 967
 968         bool changed = false;
 969         if(d < foundDist)
 970         {
 971            changed = true;
 972            found = true;
 973            foundDist = d;
 974            foundPoint = info.segment.p1;
 975            foundPlane = p.plane;
 976
 977            if(mSoftSnapRenderTriangle)
 978            {
 979               mSoftSnapTriangle.p0 = polys.mVertexList[vertind[0]];
 980               mSoftSnapTriangle.p1 = polys.mVertexList[vertind[1]];
 981               mSoftSnapTriangle.p2 = polys.mVertexList[vertind[2]];
 982            }
 983         }
 984
 985         if(mSoftSnapDebugRender)
 986         {
 987            Triangle debugTri;
 988            debugTri.p0 = polys.mVertexList[vertind[0]];
 989            debugTri.p1 = polys.mVertexList[vertind[1]];
 990            debugTri.p2 = polys.mVertexList[vertind[2]];
 991            mSoftSnapDebugTriangles.push_back(debugTri);
 992
 993            if(changed)
 994            {
 995               mSoftSnapDebugSnapTri = debugTri;
 996               mSoftSnapDebugPoint = foundPoint;
 997            }
 998         }
 999      }
1000   }
1001
1002   if(found)
1003   {
1004      // Align selection to foundPlane normal
1005      if(mSoftSnapAlignment != AlignNone)
1006      {
1007         EulerF rot(0.0f, 0.0f, 0.0f); // Equivalent to AlignPosY
1008         switch(mSoftSnapAlignment)
1009         {
1010            case AlignPosX:
1011               rot.set(0.0f, 0.0f, mDegToRad(-90.0f));
1012               break;
1013
1014            case AlignPosZ:
1015               rot.set(mDegToRad(90.0f), 0.0f, mDegToRad(180.0f));
1016               break;
1017
1018            case AlignNegX:
1019               rot.set(0.0f, 0.0f, mDegToRad(90.0f));
1020               break;
1021
1022            case AlignNegY:
1023               rot.set(0.0f, 0.0f, mDegToRad(180.0f));
1024               break;
1025
1026            case AlignNegZ:
1027               rot.set(mDegToRad(-90.0f), 0.0f, mDegToRad(180.0f));
1028               break;
1029         }
1030
1031         MatrixF mat = MathUtils::createOrientFromDir(foundPlane.getNormal());
1032         MatrixF rotMat(rot);
1033
1034         sel->orient(mat.mul(rotMat), Point3F::Zero);
1035      }
1036
1037      // Cast ray towards the selection to find how close to move it to the foundPoint
1038      F32 rayLength = mSoftSnapBounds.len() * 2;
1039      Point3F start = sel->getCentroid() - foundPlane.getNormal() * rayLength / 2;
1040      Point3F end = start + foundPlane.getNormal() * rayLength;
1041
1042      RayInfo ri;
1043      F32 minT = TypeTraits< F32 >::MAX;
1044
1045      for( U32 i = 0; i < sel->size(); ++ i )
1046      {
1047         SceneObject* object = dynamic_cast< SceneObject* >( ( *sel )[ i ] );
1048         if( !object )
1049            continue;
1050
1051         // Convert start and end points to object space
1052         Point3F s, e;
1053         MatrixF mat = object->getTransform();
1054         mat.inverse();
1055         mat.mulP( start, &s );
1056         mat.mulP( end, &e );
1057
1058         if ( object->castRayRendered( s, e, &ri ) )
1059            minT = getMin( minT, ri.t );
1060      }
1061
1062      if ( minT <= 1.0f )
1063         foundPoint += ( end - start ) * (0.5f - minT);
1064
1065      sel->offset(foundPoint - sel->getCentroid(), (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
1066   }
1067
1068   mSoftSnapIsStuck = found;
1069}
1070
1071//------------------------------------------------------------------------------
1072
1073SceneObject * WorldEditor::getControlObject()
1074{
1075   GameConnection * connection = GameConnection::getLocalClientConnection();
1076   if(connection)
1077      return(dynamic_cast<SceneObject*>(connection->getControlObject()));
1078   return(0);
1079}
1080
1081bool WorldEditor::collide( const Gui3DMouseEvent &event, SceneObject **hitObj )
1082{
1083   // Collide against the screen-space class icons...
1084
1085   S32 collidedIconIdx = -1;
1086   F32 collidedIconDist = F32_MAX;
1087
1088   for ( U32 i = 0; i < mIcons.size(); i++ )      
1089   {
1090      const IconObject &icon = mIcons[i];      
1091
1092      if ( icon.rect.pointInRect( event.mousePoint ) &&
1093           icon.dist < collidedIconDist )
1094      {
1095         collidedIconIdx = i;
1096         collidedIconDist = icon.dist;
1097      }
1098   }
1099
1100   if ( collidedIconIdx != -1 )
1101   {
1102      *hitObj = mIcons[collidedIconIdx].object;
1103      return true;
1104   }
1105
1106   if ( mBoundingBoxCollision )
1107   {
1108      // Raycast against sceneObject bounding boxes...
1109
1110      SceneObject *controlObj = getControlObject();
1111      if ( controlObj )
1112         controlObj->disableCollision();
1113
1114      Point3F startPnt = event.pos;
1115      Point3F endPnt = event.pos + event.vec * smProjectDistance;
1116      RayInfo ri;
1117
1118      bool hit = gServerContainer.collideBox(startPnt, endPnt, 0xFFFFFFFF, &ri);
1119
1120      if ( controlObj )
1121         controlObj->enableCollision();
1122
1123      if ( hit )      
1124      {
1125         // If we hit an object that is in a Prefab...
1126         // we really want to hit / select that Prefab.         
1127         Prefab *prefab = Prefab::getPrefabByChild( ri.object );
1128         
1129         if ( prefab )
1130            *hitObj = prefab;
1131         else
1132            *hitObj = ri.object;
1133
1134         return true;
1135      }
1136   }
1137
1138   // No icon hit so check against the mesh
1139   if ( mObjectMeshCollision )
1140   {
1141      SceneObject *controlObj = getControlObject();
1142      if ( controlObj )
1143         controlObj->disableCollision();
1144
1145      Point3F startPnt = event.pos;
1146      Point3F endPnt = event.pos + event.vec * smProjectDistance;
1147      RayInfo ri;
1148
1149      bool hit = gServerContainer.castRayRendered(startPnt, endPnt, 0xFFFFFFFF, &ri);
1150      if(hit && ri.object && ( ri.object->getTypeMask() & (TerrainObjectType) || dynamic_cast< GroundPlane* >( ri.object )))
1151      {
1152         // We don't want to mesh select terrain
1153         hit = false;
1154      }
1155
1156      if ( controlObj )
1157         controlObj->enableCollision();
1158
1159      if ( hit )      
1160      {
1161         // If we hit an object that is in a Prefab...
1162         // we really want to hit / select that Prefab.         
1163         Prefab *prefab = Prefab::getPrefabByChild( ri.object );
1164
1165         if ( prefab )
1166            *hitObj = prefab;
1167         else
1168            *hitObj = ri.object;
1169
1170         return true;
1171      }
1172   }
1173
1174   return false;   
1175}
1176
1177//------------------------------------------------------------------------------
1178// main render functions
1179
1180void WorldEditor::renderSelectionWorldBox(Selection*  sel)
1181{
1182   if( !mRenderSelectionBox || !sel->size() )
1183      return;
1184      
1185   // Compute the world bounds of the selection.
1186
1187   Box3F selBox( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX,
1188                 TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN );
1189
1190   for( U32 i = 0; i < sel->size(); ++ i )
1191   {
1192      SceneObject* object = dynamic_cast< SceneObject* >( ( *sel )[ i ] );
1193      if( !object )
1194         continue;
1195         
1196      const Box3F & wBox = object->getWorldBox();
1197      selBox.minExtents.setMin(wBox.minExtents);
1198      selBox.maxExtents.setMax(wBox.maxExtents);
1199   }
1200   
1201   // Set up the render state block, if we haven't done so
1202   // already.
1203
1204   if ( mRenderSelectionBoxSB.isNull() )
1205   {
1206      GFXStateBlockDesc desc;
1207      
1208      desc.setCullMode( GFXCullNone );
1209      desc.alphaDefined = true;
1210      desc.alphaTestEnable = true;
1211      desc.setZReadWrite( true, true );
1212      mRenderSelectionBoxSB = GFX->createStateBlock( desc );
1213   }
1214
1215   GFX->setStateBlock(mRenderSelectionBoxSB);
1216
1217   PrimBuild::color( mSelectionBoxColor );
1218
1219   // create the box points
1220   Point3F projPnts[8];
1221   for( U32 i = 0; i < 8; ++ i )
1222   {
1223      Point3F pnt;
1224      pnt.set(BoxPnts[i].x ? selBox.maxExtents.x : selBox.minExtents.x,
1225              BoxPnts[i].y ? selBox.maxExtents.y : selBox.minExtents.y,
1226              BoxPnts[i].z ? selBox.maxExtents.z : selBox.minExtents.z);
1227      projPnts[i] = pnt;
1228   }
1229
1230   // do the box
1231   for(U32 j = 0; j < 6; j++)
1232   {
1233      PrimBuild::begin( GFXLineStrip, 4 );
1234      for(U32 k = 0; k < 4; k++)
1235      {
1236         PrimBuild::vertex3fv( projPnts[BoxVerts[j][k]] );
1237      }
1238      PrimBuild::end();
1239   }
1240}
1241
1242void WorldEditor::renderObjectBox( SceneObject *obj, const ColorI &color )
1243{
1244   if ( mRenderObjectBoxSB.isNull() )
1245   {
1246      GFXStateBlockDesc desc;
1247      desc.setCullMode( GFXCullNone );
1248      desc.setZReadWrite( true, true );
1249      mRenderObjectBoxSB = GFX->createStateBlock( desc );
1250   }
1251
1252   GFX->setStateBlock(mRenderObjectBoxSB);
1253
1254   GFXTransformSaver saver;
1255
1256   Box3F objBox = obj->getObjBox();
1257   Point3F objScale = obj->getScale();   
1258   Point3F boxScale = objBox.getExtents();
1259   Point3F boxCenter = obj->getWorldBox().getCenter();
1260
1261   MatrixF objMat = obj->getTransform();
1262   objMat.scale(objScale);
1263   objMat.scale(boxScale);
1264   objMat.setPosition(boxCenter);
1265
1266   //GFX->multWorld( objMat );
1267
1268   PrimBuild::color( ColorI(255,255,255,255) );
1269   PrimBuild::begin( GFXLineList, 48 );
1270
1271   //Box3F objBox = obj->getObjBox();
1272   //Point3F size = objBox.getExtents();
1273   //Point3F halfSize = size * 0.5f;
1274
1275   static const Point3F cubePoints[8] = 
1276   {
1277      Point3F(-0.5, -0.5, -0.5), Point3F(-0.5, -0.5,  0.5), Point3F(-0.5,  0.5, -0.5), Point3F(-0.5,  0.5,  0.5),
1278      Point3F( 0.5, -0.5, -0.5), Point3F( 0.5, -0.5,  0.5), Point3F( 0.5,  0.5, -0.5), Point3F( 0.5,  0.5,  0.5)
1279   };
1280
1281   // 8 corner points of the box   
1282   for ( U32 i = 0; i < 8; i++ )
1283   {
1284      //const Point3F &start = cubePoints[i];  
1285
1286      // 3 lines per corner point
1287      for ( U32 j = 0; j < 3; j++ )
1288      {
1289         Point3F start = cubePoints[i];
1290         Point3F end = start;
1291         end[j] *= 0.8f;
1292
1293         objMat.mulP(start);
1294         PrimBuild::vertex3fv(start);
1295         objMat.mulP(end);
1296         PrimBuild::vertex3fv(end);            
1297      }
1298   }
1299
1300   PrimBuild::end();
1301}
1302
1303void WorldEditor::renderObjectFace(SceneObject * obj, const VectorF & normal, const ColorI & col)
1304{
1305   if ( mRenderObjectFaceSB.isNull() )
1306   {
1307      GFXStateBlockDesc desc;
1308      desc.setCullMode( GFXCullNone );
1309      desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
1310      desc.setZReadWrite( false );
1311      mRenderObjectFaceSB = GFX->createStateBlock( desc );
1312   }
1313
1314   GFX->setStateBlock(mRenderObjectFaceSB);
1315
1316   // get the normal index
1317   VectorF objNorm;
1318   obj->getWorldTransform().mulV(normal, &objNorm);
1319
1320   U32 normI = getBoxNormalIndex(objNorm);
1321
1322   //
1323   Box3F box = obj->getObjBox();
1324   MatrixF mat = obj->getTransform();
1325   VectorF scale = obj->getScale();
1326
1327   Point3F projPnts[4];
1328   for(U32 i = 0; i < 4; i++)
1329   {
1330      Point3F pnt;
1331      pnt.set(BoxPnts[BoxVerts[normI][i]].x ? box.maxExtents.x : box.minExtents.x,
1332              BoxPnts[BoxVerts[normI][i]].y ? box.maxExtents.y : box.minExtents.y,
1333              BoxPnts[BoxVerts[normI][i]].z ? box.maxExtents.z : box.minExtents.z);
1334
1335      // scale it
1336      pnt.convolve(scale);
1337      mat.mulP(pnt, &projPnts[i]);
1338   }
1339
1340   PrimBuild::color( col );
1341
1342   PrimBuild::begin( GFXTriangleStrip, 4 );
1343      for(U32 k = 0; k < 4; k++)
1344      {
1345         PrimBuild::vertex3f(projPnts[k].x, projPnts[k].y, projPnts[k].z);
1346      }
1347   PrimBuild::end();
1348}
1349
1350void WorldEditor::renderMousePopupInfo()
1351{
1352   if ( !mMouseDragged )
1353      return;
1354
1355      
1356   if ( mGizmoProfile->mode == NoneMode || !mGizmoProfile->renderInfoText )
1357      return;
1358
1359   char buf[256];
1360
1361   switch ( mGizmoProfile->mode )
1362   {
1363      case MoveMode:      
1364      {
1365         if ( !bool(mSelected)|| !mSelected->size() )
1366            return;
1367
1368         Point3F pos = getSelectionCentroid();
1369         dSprintf(buf, sizeof(buf), "x: %0.3f, y: %0.3f, z: %0.3f", pos.x, pos.y, pos.z);
1370
1371         break;
1372      }
1373
1374      case RotateMode:
1375      {
1376         if ( !bool(mHitObject) || !bool(mSelected) || (mSelected->size() != 1) )
1377            return;
1378
1379         // print out the angle-axis
1380         AngAxisF aa(mHitObject->getTransform());
1381
1382         dSprintf(buf, sizeof(buf), "x: %0.3f, y: %0.3f, z: %0.3f, a: %0.3f",
1383            aa.axis.x, aa.axis.y, aa.axis.z, mRadToDeg(aa.angle));
1384
1385         break;
1386      }
1387
1388      case ScaleMode:
1389      {
1390         if ( !bool(mHitObject) || !bool(mSelected) || (mSelected->size() != 1) )
1391            return;
1392
1393         VectorF scale = mHitObject->getScale();
1394
1395         Box3F box = mHitObject->getObjBox();
1396         box.minExtents.convolve(scale);
1397         box.maxExtents.convolve(scale);
1398
1399         box.maxExtents -= box.minExtents;
1400         dSprintf(buf, sizeof(buf), "w: %0.3f, h: %0.3f, d: %0.3f", box.maxExtents.x, box.maxExtents.y, box.maxExtents.z);
1401
1402         break;
1403      }
1404
1405      default:
1406         return;
1407   }
1408
1409   U32 width = mProfile->mFont->getStrWidth((const UTF8 *)buf);
1410   Point2I posi( mLastMouseEvent.mousePoint.x, mLastMouseEvent.mousePoint.y + 12 );
1411
1412   if ( mRenderPopupBackground )
1413   {
1414      Point2I minPt(posi.x - width / 2 - 2, posi.y - 1);
1415      Point2I maxPt(posi.x + width / 2 + 2, posi.y + mProfile->mFont->getHeight() + 1);
1416
1417      GFX->getDrawUtil()->drawRectFill(minPt, maxPt, mPopupBackgroundColor);
1418   }
1419
1420   GFX->getDrawUtil()->setBitmapModulation(mPopupTextColor);
1421   GFX->getDrawUtil()->drawText(mProfile->mFont, Point2I(posi.x - width / 2, posi.y), buf);
1422}
1423
1424void WorldEditor::renderPaths(SimObject *obj)
1425{
1426   if (obj == NULL)
1427      return;
1428   bool selected = false;
1429
1430   // Loop through subsets
1431   if (SimSet *set = dynamic_cast<SimSet*>(obj))
1432      for(SimSetIterator itr(set); *itr; ++itr) {
1433         renderPaths(*itr);
1434         if ((*itr)->isSelected())
1435            selected = true;
1436      }
1437
1438   // Render the path if it, or any of it's immediate sub-objects, is selected.
1439   if (SimPath::Path *path = dynamic_cast<SimPath::Path*>(obj))
1440      if (selected || path->isSelected())
1441         renderSplinePath(path);
1442}
1443
1444
1445void WorldEditor::renderSplinePath(SimPath::Path *path)
1446{
1447   // at the time of writing the path properties are not part of the path object
1448   // so we don't know to render it looping, splined, linear etc.
1449   // for now we render all paths splined+looping
1450
1451   Vector<Point3F> positions;
1452   Vector<QuatF>   rotations;
1453
1454   path->sortMarkers();
1455   CameraSpline spline;
1456
1457   for(SimSetIterator itr(path); *itr; ++itr)
1458   {
1459      Marker *pathmarker = dynamic_cast<Marker*>(*itr);
1460      if (!pathmarker)
1461         continue;
1462      Point3F pos;
1463      pathmarker->getTransform().getColumn(3, &pos);
1464
1465      QuatF rot;
1466      rot.set(pathmarker->getTransform());
1467      CameraSpline::Knot::Type type;
1468      switch (pathmarker->mKnotType)
1469      {
1470         case Marker::KnotTypePositionOnly:  type = CameraSpline::Knot::POSITION_ONLY; break;
1471         case Marker::KnotTypeKink:          type = CameraSpline::Knot::KINK; break;
1472         case Marker::KnotTypeNormal:
1473         default:                            type = CameraSpline::Knot::NORMAL; break;
1474
1475      }
1476
1477      CameraSpline::Knot::Path tPath;
1478      switch (pathmarker->mSmoothingType)
1479      {
1480         case Marker::SmoothingTypeLinear:   tPath = CameraSpline::Knot::LINEAR; break;
1481         case Marker::SmoothingTypeSpline:
1482         default:                            tPath = CameraSpline::Knot::SPLINE; break;
1483
1484      }
1485
1486      spline.push_back(new CameraSpline::Knot(pos, rot, 1.0f, type, tPath));
1487   }
1488
1489   F32 t = 0.0f;
1490   S32 size = spline.size();
1491   if (size <= 1)
1492      return;
1493
1494   // DEBUG
1495   //spline.renderTimeMap();
1496
1497   if (mSplineSB.isNull())
1498   {
1499      GFXStateBlockDesc desc;
1500
1501      desc.setCullMode( GFXCullNone );
1502      desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
1503      desc.samplersDefined = true;
1504
1505      mSplineSB = GFX->createStateBlock( desc );
1506   }
1507
1508   GFX->setStateBlock(mSplineSB);
1509   GFX->setupGenericShaders();
1510
1511   if (path->isLooping())
1512   {
1513      CameraSpline::Knot *front = new CameraSpline::Knot(*spline.front());
1514      CameraSpline::Knot *back  = new CameraSpline::Knot(*spline.back());
1515      spline.push_back(front);
1516      spline.push_front(back);
1517      t = 1.0f;
1518      size += 2;
1519   }
1520
1521   VectorF a(-0.45f, -0.55f, 0.0f);
1522   VectorF b( 0.0f,  0.55f, 0.0f);
1523   VectorF c( 0.45f, -0.55f, 0.0f);
1524
1525   U32 vCount=0;
1526
1527   F32 tmpT = t;
1528   while (tmpT < size - 1)
1529   {
1530      tmpT = spline.advanceDist(tmpT, 1.0f);
1531      vCount++;
1532   }
1533
1534   // Build vertex buffer
1535 
1536   U32 batchSize = vCount;
1537
1538   if(vCount > 4000)
1539      batchSize = 4000;
1540
1541   GFXVertexBufferHandle<GFXVertexPCT> vb;
1542   vb.set(GFX, 3*batchSize, GFXBufferTypeVolatile);
1543   void *lockPtr = vb.lock();
1544   if(!lockPtr) return;
1545
1546   U32 vIdx=0;
1547
1548   while (t < size - 1)
1549   {
1550      CameraSpline::Knot k;
1551      spline.value(t, &k);
1552      t = spline.advanceDist(t, 1.0f);
1553
1554      k.mRotation.mulP(a, &vb[vIdx+0].point);
1555      k.mRotation.mulP(b, &vb[vIdx+1].point);
1556      k.mRotation.mulP(c, &vb[vIdx+2].point);
1557
1558      vb[vIdx+0].point += k.mPosition;
1559      vb[vIdx+1].point += k.mPosition;
1560      vb[vIdx+2].point += k.mPosition;
1561
1562      vb[vIdx+0].color.set(0, 255, 0, 0);
1563      vb[vIdx+1].color.set(0, 255, 0, 255);
1564      vb[vIdx+2].color.set(0, 255, 0, 0);
1565
1566      // vb[vIdx+3] = vb[vIdx+1];
1567
1568      vIdx+=3;
1569
1570      // Do we have to knock it out?
1571      if(vIdx > 3 * batchSize - 10)
1572      {
1573         vb.unlock();
1574
1575         // Render the buffer
1576         GFX->setVertexBuffer(vb);
1577         GFX->drawPrimitive(GFXTriangleList,0,vIdx/3);
1578
1579         // Reset for next pass...
1580         vIdx = 0;
1581         void *nextlockPtr = vb.lock();
1582         if(!nextlockPtr) return;
1583      }
1584   }
1585
1586   vb.unlock();
1587
1588   // Render the buffer
1589   GFX->setVertexBuffer(vb);
1590   //GFX->drawPrimitive(GFXLineStrip,0,3);
1591
1592   if(vIdx)
1593      GFX->drawPrimitive(GFXTriangleList,0,vIdx/3);
1594}
1595
1596void WorldEditor::renderScreenObj( SceneObject *obj, const Point3F& projPos, const Point3F& wPos )
1597{
1598   // Do not render control objects, hidden objects,
1599   // or objects that are within a prefab.
1600   if(obj == getControlObject() || obj->isHidden() || Prefab::getPrefabByChild(obj))
1601      return;
1602
1603   GFXDrawUtil *drawer = GFX->getDrawUtil();
1604   
1605   // Lookup the ClassIcon - TextureHandle
1606   GFXTexHandle classIcon = gEditorIcons.findIcon( obj );
1607
1608   if ( classIcon.isNull() )
1609      classIcon = mDefaultClassEntry.mDefaultHandle;
1610
1611   U32 iconWidth = classIcon->getWidth();
1612   U32 iconHeight = classIcon->getHeight();
1613
1614   bool isHighlight = ( obj == mHitObject || mDragSelected->objInSet(obj) );
1615
1616   if ( isHighlight )
1617   {
1618      iconWidth += 0;
1619      iconHeight += 0;
1620   }
1621
1622   Point2I sPos( (S32)projPos.x, (S32)projPos.y );
1623   //if ( obj->isSelected() )
1624   //   sPos.y += 4;
1625   Point2I renderPos = sPos;
1626   renderPos.x -= iconWidth / 2;
1627   renderPos.y -= iconHeight / 2;  
1628
1629   Point2I iconSize( iconWidth, iconHeight );
1630
1631   RectI renderRect( renderPos, iconSize );
1632   
1633   // Render object icon, except if the object is the
1634   // only selected object.  Do render the icon when there are
1635   // multiple selection as otherwise, objects like lights are
1636   // difficult to place.
1637   
1638   if( mRenderObjHandle && ( !obj->isSelected() || getSelectionSize() > 1 ) )
1639   {
1640      // Compute icon fade.
1641
1642      S32 iconAlpha = 255;
1643      if( mFadeIcons && getDisplayType() == DisplayTypePerspective )
1644      {
1645         Point3F objDist = smCamPos - wPos;
1646         F32 dist = objDist.len();
1647         
1648         if( dist > mFadeIconsDist )
1649         {
1650            F32 iconDist = dist - mFadeIconsDist;
1651            iconAlpha = mClampF( 255 - ( 255 * ( iconDist / 10.f ) ), 0.f, 255.f );
1652         }
1653      }
1654
1655      if ( isHighlight )      
1656         drawer->setBitmapModulation( ColorI(255,255,255, mClamp(iconAlpha + 50, 0, 255)) );               
1657      else      
1658         drawer->setBitmapModulation( ColorI(255,255,255,iconAlpha) );         
1659
1660      drawer->drawBitmapStretch( classIcon, renderRect );      
1661      drawer->clearBitmapModulation();
1662
1663      if ( obj->isLocked() )      
1664         drawer->drawBitmap( mDefaultClassEntry.mLockedHandle, renderPos );      
1665
1666      // Save an IconObject for performing icon-click testing later.
1667
1668      mIcons.increment();
1669      IconObject& lastIcon = mIcons.last();
1670      lastIcon.object = obj;
1671      lastIcon.rect = renderRect;
1672      lastIcon.dist = projPos.z;
1673      lastIcon.alpha = iconAlpha;
1674   }
1675
1676   //
1677   if ( mRenderObjText && ( obj == mHitObject || obj->isSelected() ) )
1678   {      
1679      const char * str = parseObjectFormat(obj, mObjTextFormat);
1680
1681      Point2I extent(mProfile->mFont->getStrWidth((const UTF8 *)str), mProfile->mFont->getHeight());
1682
1683      Point2I pos(sPos);
1684
1685      if(mRenderObjHandle)
1686      {
1687         pos.x += (classIcon->getWidth() / 2) - (extent.x / 2);
1688         pos.y += (classIcon->getHeight() / 2) + 3;
1689      }
1690     
1691      
1692     if(mGizmoProfile->mode == NoneMode){
1693        
1694       drawer->drawBitmapStretch( classIcon, renderRect );
1695       drawer->setBitmapModulation( ColorI(255,255,255,255) ); 
1696       drawer->drawText(mProfile->mFont, pos, str); 
1697       if ( obj->isLocked() )      
1698         drawer->drawBitmap( mDefaultClassEntry.mLockedHandle, renderPos );      
1699
1700         // Save an IconObject for performing icon-click testing later.
1701      {
1702         IconObject icon;
1703         icon.object = obj;
1704         icon.rect = renderRect;
1705         icon.dist = projPos.z;             
1706         mIcons.push_back( icon );
1707      }
1708     }else{
1709        drawer->setBitmapModulation(mObjectTextColor);
1710        drawer->drawText(mProfile->mFont, pos, str);
1711     };
1712   }
1713
1714   NotesObject* noteObj = dynamic_cast<NotesObject*>(obj);
1715   if (noteObj)
1716   {
1717      Point2I pos(sPos);
1718
1719      MatrixF cameraMat = mLastCameraQuery.cameraMatrix;
1720
1721      Point3F camPos = cameraMat.getPosition();
1722      Point3F notePos = noteObj->getPosition();
1723
1724      VectorF distVec = notePos - camPos;
1725      F32 dist = distVec.len();
1726
1727      F32 maxNoteDistance = 100;
1728      F32 noteFadeStartDist = 50;
1729
1730      F32 fade = 1;
1731
1732      if(dist >= noteFadeStartDist)
1733         fade = -((dist - noteFadeStartDist) / (maxNoteDistance - noteFadeStartDist));
1734
1735      if (dist >= maxNoteDistance)
1736         return;
1737
1738      ColorI noteTextColor = mObjectTextColor;
1739      noteTextColor.alpha = 255 * fade;
1740
1741      drawer->setBitmapModulation(noteTextColor);
1742      drawer->drawText(mProfile->mFont, pos, noteObj->getNote().c_str());
1743   }
1744}
1745
1746//------------------------------------------------------------------------------
1747// ClassInfo stuff
1748
1749WorldEditor::ClassInfo::~ClassInfo()
1750{
1751   for(U32 i = 0; i < mEntries.size(); i++)
1752      delete mEntries[i];
1753}
1754
1755bool WorldEditor::objClassIgnored(const SimObject * obj)
1756{
1757   ClassInfo::Entry * entry = getClassEntry(obj);
1758   if(mToggleIgnoreList)
1759      return(!(entry ? entry->mIgnoreCollision : false));
1760   else
1761      return(entry ? entry->mIgnoreCollision : false);
1762}
1763
1764WorldEditor::ClassInfo::Entry * WorldEditor::getClassEntry(StringTableEntry name)
1765{
1766   AssertFatal(name, "WorldEditor::getClassEntry - invalid args");
1767   for(U32 i = 0; i < mClassInfo.mEntries.size(); i++)
1768      if(!dStricmp(name, mClassInfo.mEntries[i]->mName))
1769         return(mClassInfo.mEntries[i]);
1770   return(0);
1771}
1772
1773WorldEditor::ClassInfo::Entry * WorldEditor::getClassEntry(const SimObject * obj)
1774{
1775   AssertFatal(obj, "WorldEditor::getClassEntry - invalid args");
1776   return(getClassEntry(obj->getClassName()));
1777}
1778
1779bool WorldEditor::addClassEntry(ClassInfo::Entry * entry)
1780{
1781   AssertFatal(entry, "WorldEditor::addClassEntry - invalid args");
1782   if(getClassEntry(entry->mName))
1783      return(false);
1784
1785   mClassInfo.mEntries.push_back(entry);
1786   return(true);
1787}
1788
1789//------------------------------------------------------------------------------
1790// Mouse cursor stuff
1791void WorldEditor::setCursor(U32 cursor)
1792{
1793   mCurrentCursor = cursor;
1794}
1795
1796//------------------------------------------------------------------------------
1797Signal<void(WorldEditor*)> WorldEditor::smRenderSceneSignal;
1798
1799WorldEditor::WorldEditor()
1800   : mCurrentCursor(PlatformCursorController::curArrow)
1801{
1802   VECTOR_SET_ASSOCIATION( mIcons );
1803   
1804   // init the field data
1805   mDropType = DropAtScreenCenter;   
1806   mBoundingBoxCollision = true;   
1807   mObjectMeshCollision = true;
1808   mRenderPopupBackground = true;
1809   mPopupBackgroundColor.set(100,100,100);
1810   mPopupTextColor.set(255,255,0);
1811   mSelectHandle = StringTable->insert("tools/worldEditor/images/SelectHandle");
1812   mDefaultHandle = StringTable->insert("tools/worldEditor/images/DefaultHandle");
1813   mLockedHandle = StringTable->insert("tools/worldEditor/images/LockedHandle");
1814   mObjectTextColor.set(255,255,255);
1815   mObjectsUseBoxCenter = true;
1816   
1817   mObjSelectColor.set(255,0,0,200);
1818   mObjMultiSelectColor.set(128,0,0,200);
1819   mObjMouseOverSelectColor.set(0,0,255);
1820   mObjMouseOverColor.set(0,255,0);
1821   mShowMousePopupInfo = true;
1822   mDragRectColor.set(255,255,0);
1823   mRenderObjText = true;
1824   mRenderObjHandle = true;
1825   mObjTextFormat = StringTable->insert("$id$: $name|internal$");
1826   mFaceSelectColor.set(0,0,100,100);
1827   mRenderSelectionBox = true;
1828   mSelectionBoxColor.set(255,255,0,100);
1829   mSelectionLocked = false;
1830
1831   mToggleIgnoreList = false;
1832
1833   mIsDirty = false;
1834
1835   mRedirectID = 0;
1836
1837   //
1838   mHitObject = NULL;
1839
1840   //
1841   //mDefaultMode = mCurrentMode = Move;
1842   mMouseDown = false;
1843   mDragSelect = false;
1844
1845   mStickToGround = false;
1846   mStuckToGround = false;
1847   mTerrainSnapAlignment = AlignNone;
1848   mTerrainSnapOffsetZ = false;
1849   mOffsetZValue = 0.0f;
1850
1851   mDropAtBounds = false;
1852   mDropBelowCameraOffset = 15.0f;
1853   mDropAtScreenCenterScalar = 1.0f;
1854   mDropAtScreenCenterMax = 100.0f;
1855   
1856   // Create the drag selection set.
1857   
1858   mDragSelected = new Selection();
1859   mDragSelected->registerObject( "EWorldEditorDragSelection" );
1860   Sim::getRootGroup()->addObject( mDragSelected );
1861   mDragSelected->setAutoSelect(false);
1862
1863   //
1864   mSoftSnap = false;
1865   mSoftSnapActivated = false;
1866   mSoftSnapIsStuck = false;
1867   mSoftSnapAlignment = AlignNone;
1868   mSoftSnapRender = true;
1869   mSoftSnapRenderTriangle = false;
1870   mSoftSnapSizeByBounds = false;
1871   mSoftSnapSize = 2.0f;
1872   mSoftSnapBackfaceTolerance = 0.5f;
1873   mSoftSnapDebugRender = false;
1874   mSoftSnapDebugPoint.set(0.0f, 0.0f, 0.0f);
1875   
1876   mGridSnap = false;
1877   mUseGroupCenter = true;
1878   mFadeIcons = true;
1879   mFadeIconsDist = 8.f;
1880
1881   mActiveEditorTool = nullptr;
1882}
1883
1884WorldEditor::~WorldEditor()
1885{
1886}
1887
1888//------------------------------------------------------------------------------
1889
1890bool WorldEditor::onAdd()
1891{
1892   if(!Parent::onAdd())
1893      return(false);
1894
1895   // create the default class entry
1896   mDefaultClassEntry.mName = 0;
1897   mDefaultClassEntry.mIgnoreCollision = false;
1898   mDefaultClassEntry.mDefaultHandle   = GFXTexHandle(mDefaultHandle,   &GFXStaticTextureSRGBProfile, avar("%s() - mDefaultClassEntry.mDefaultHandle (line %d)", __FUNCTION__, __LINE__));
1899   mDefaultClassEntry.mSelectHandle    = GFXTexHandle(mSelectHandle,    &GFXStaticTextureSRGBProfile, avar("%s() - mDefaultClassEntry.mSelectHandle (line %d)", __FUNCTION__, __LINE__));
1900   mDefaultClassEntry.mLockedHandle    = GFXTexHandle(mLockedHandle,    &GFXStaticTextureSRGBProfile, avar("%s() - mDefaultClassEntry.mLockedHandle (line %d)", __FUNCTION__, __LINE__));
1901
1902   if(!(mDefaultClassEntry.mDefaultHandle && mDefaultClassEntry.mSelectHandle && mDefaultClassEntry.mLockedHandle))
1903      return false;
1904
1905   //mGizmo = new Gizmo();
1906   //mGizmo->registerObject("WorldEditorGizmo");   
1907   mGizmo->assignName("WorldEditorGizmo");   
1908
1909   return true;
1910}
1911
1912//------------------------------------------------------------------------------
1913
1914void WorldEditor::onEditorEnable()
1915{
1916   // go through and copy the hidden field to the client objects...
1917   for(SimSetIterator itr(Sim::getRootGroup());  *itr; ++itr)
1918   {
1919      SceneObject * obj = dynamic_cast<SceneObject *>(*itr);
1920      if(!obj)
1921         continue;
1922
1923      // only work with a server obj...
1924      if(obj->isClientObject())
1925         continue;
1926
1927      // grab the client object
1928      SceneObject * clientObj = getClientObj(obj);
1929      if(!clientObj)
1930         continue;
1931
1932      //
1933      clientObj->setHidden(obj->isHidden());
1934   }
1935}
1936
1937//------------------------------------------------------------------------------
1938
1939void WorldEditor::get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event)
1940{
1941   TORQUE_UNUSED(event);
1942   cursor = NULL;
1943   visible = false;
1944
1945   GuiCanvas *pRoot = getRoot();
1946   if( !pRoot )
1947      return Parent::get3DCursor(cursor,visible,event);
1948
1949   if(pRoot->mCursorChanged != mCurrentCursor)
1950   {
1951      PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow();
1952      AssertFatal(pWindow != NULL,"GuiControl without owning platform window!  This should not be possible.");
1953      PlatformCursorController *pController = pWindow->getCursorController();
1954      AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");
1955
1956      // We've already changed the cursor, 
1957      // so set it back before we change it again.
1958      if(pRoot->mCursorChanged != -1)
1959         pController->popCursor();
1960
1961      // Now change the cursor shape
1962      pController->pushCursor(mCurrentCursor);
1963      pRoot->mCursorChanged = mCurrentCursor;
1964   }
1965}
1966
1967//TODO: [rene 03/10 -- The entire event handling code here needs cleanup]
1968
1969void WorldEditor::on3DMouseMove(const Gui3DMouseEvent & event)
1970{   
1971   setCursor(PlatformCursorController::curArrow);
1972   mHitObject = NULL;
1973
1974   //If we have an active tool and it's intercepted our input, bail out
1975   if (mActiveEditorTool != nullptr && mActiveEditorTool->onMouseMove(event))
1976      return;
1977
1978   //
1979   mUsingAxisGizmo = false;
1980
1981   if ( bool(mSelected) && mSelected->size() > 0 )
1982   {
1983      mGizmo->on3DMouseMove( event );
1984
1985      if ( mGizmo->getSelection() != Gizmo::None )
1986      {
1987         mUsingAxisGizmo = true;
1988         mHitObject = dynamic_cast< SceneObject* >( ( *mSelected )[0] );
1989      }
1990   }
1991
1992   if ( !mHitObject )
1993   {
1994      SceneObject *hitObj = NULL;
1995      if ( collide(event, &hitObj) && hitObj->isSelectionEnabled() && !objClassIgnored(hitObj) )
1996      {
1997         mHitObject = hitObj;
1998      }
1999   }
2000   
2001   mLastMouseEvent = event;
2002}
2003
2004void WorldEditor::on3DMouseDown(const Gui3DMouseEvent & event)
2005{
2006   //If we have an active tool and it's intercepted our input, bail out
2007   if (mActiveEditorTool != nullptr && mActiveEditorTool->onMouseDown(event))
2008      return;
2009
2010   mMouseDown = true;
2011   mMouseDragged = false;
2012   mPerformedDragCopy = false;
2013   mDragGridSnapToggle = false;
2014   mLastMouseDownEvent = event;
2015
2016   mouseLock();
2017
2018   // check gizmo first
2019   mUsingAxisGizmo = false;
2020   mNoMouseDrag = false;
2021
2022   if ( bool(mSelected) && mSelected->size() > 0 )
2023   {
2024      // Update the move grid settings depending on the
2025      // bounds of the current selection.
2026
2027      const Box3F& selBounds = getActiveSelectionSet()->getBoxBounds();
2028      const F32 maxDim = getMax( selBounds.len_x(), getMax( selBounds.len_y(), selBounds.len_z() ) );
2029      const F32 size = mCeil( maxDim + 10.f );
2030      const F32 spacing = mCeil( size / 20.f );
2031
2032     if( dynamic_cast< SceneObject* >( ( *mSelected )[0] ))
2033     {
2034     
2035         if (size > 0)
2036         {
2037            mGizmo->setMoveGridSize( size );
2038            mGizmo->setMoveGridSpacing( spacing );
2039         }
2040
2041         // Let the gizmo handle the event.
2042
2043         mGizmo->on3DMouseDown( event );
2044      }
2045
2046      if ( mGizmo->getSelection() != Gizmo::None )
2047      {
2048         mUsingAxisGizmo = true;         
2049         mHitObject = dynamic_cast< SceneObject* >( ( *mSelected )[0] );
2050
2051         return;
2052      }
2053   }   
2054
2055   SceneObject *hitObj = NULL;
2056   if ( collide( event, &hitObj ) && hitObj->isSelectionEnabled() && !objClassIgnored( hitObj ) )
2057   {
2058      mPossibleHitObject = hitObj;
2059      mNoMouseDrag = true;
2060   }
2061   else if ( !mSelectionLocked )
2062   {
2063      if ( !(event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) ) )
2064         clearSelection();
2065
2066      mDragSelect = true;
2067      mDragSelected->clear();
2068      mDragRect.set( Point2I(event.mousePoint), Point2I(0,0) );
2069      mDragStart = event.mousePoint;
2070   }
2071
2072   mLastMouseEvent = event;
2073}
2074
2075void WorldEditor::on3DMouseUp( const Gui3DMouseEvent &event )
2076{
2077   //If we have an active tool and it's intercepted our input, bail out
2078   if (mActiveEditorTool != nullptr && mActiveEditorTool->onMouseUp(event))
2079      return;
2080
2081   const bool wasUsingAxisGizmo = mUsingAxisGizmo;
2082   
2083   mMouseDown = false;
2084   mStuckToGround = false;
2085   mSoftSnapIsStuck = false;
2086   mSoftSnapActivated = false;
2087   mUsingAxisGizmo = false;
2088   mGizmo->on3DMouseUp(event);
2089   
2090   // Restore grid snap if we temporarily toggled it.
2091   
2092   if( mDragGridSnapToggle )
2093   {
2094      mDragGridSnapToggle = false;
2095      const bool snapToGrid = !mGridSnap;
2096      mGridSnap = snapToGrid;
2097      mGizmo->getProfile()->snapToGrid = snapToGrid;
2098   }
2099
2100   // check if selecting objects....
2101   if ( mDragSelect )
2102   {
2103      mDragSelect = false;
2104      mPossibleHitObject = NULL;
2105      
2106      const bool addToSelection = ( event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) );
2107
2108      // add all the objects from the drag selection into the normal selection
2109      if( !addToSelection )
2110         clearSelection();
2111
2112      if ( mDragSelected->size() > 1 )
2113      {
2114         for ( U32 i = 0; i < mDragSelected->size(); i++ )                     
2115            mSelected->addObject( ( *mDragSelected )[i] );                       
2116                  
2117         Con::executef( this, "onMultiSelect", mDragSelected->getIdString(), addToSelection ? "1" : "0" );
2118         mDragSelected->clear();
2119
2120         SimObject *obj = NULL;
2121         if ( mRedirectID )
2122            obj = Sim::findObject( mRedirectID );
2123         Con::executef( obj ? obj : this, "onClick", ( *mSelected )[ 0 ]->getIdString() );
2124      }
2125      else if ( mDragSelected->size() == 1 )
2126      {         
2127         mSelected->addObject( ( *mDragSelected )[0] );    
2128         Con::executef( this, "onSelect", ( *mDragSelected )[ 0 ]->getIdString() );
2129         mDragSelected->clear();
2130         
2131         SimObject *obj = NULL;
2132         if ( mRedirectID )
2133            obj = Sim::findObject( mRedirectID );
2134         Con::executef( obj ? obj : this, "onClick", ( *mSelected )[ 0 ]->getIdString() );
2135      }
2136
2137      mouseUnlock();
2138      return;
2139   }
2140   else if( mPossibleHitObject.isValid() && !wasUsingAxisGizmo )
2141   {
2142      if ( !mSelectionLocked )
2143      {
2144         if ( event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) )
2145         {
2146            mNoMouseDrag = true;
2147            if ( mSelected->objInSet( mPossibleHitObject ) )
2148            {
2149               mSelected->removeObject( mPossibleHitObject );
2150               mSelected->storeCurrentCentroid();
2151               Con::executef( this, "onUnSelect", mPossibleHitObject->getIdString() );
2152            }
2153            else
2154            {
2155               mSelected->addObject( mPossibleHitObject );
2156               mSelected->storeCurrentCentroid();
2157               Con::executef( this, "onSelect", mPossibleHitObject->getIdString() );
2158            }
2159         }
2160         else
2161         {
2162            if ( bool(mSelected) && !mSelected->objInSet( mPossibleHitObject ) )
2163            {
2164               mNoMouseDrag = true;
2165
2166               // Call onUnSelect.  Because of the whole treeview<->selection synchronization,
2167               // this may actually cause things to disappear from mSelected so do the loop
2168               // in reverse.  This will make the loop work even if items are removed as
2169               // we go along.
2170               for (S32 i = mSelected->size() - 1; i >= 0; --i)
2171               {
2172                  //We'll explicitly inform the object of being unmarked as selected in the editor as well for outlier cases potentially not being told, such as mounted objects
2173                  WorldEditor::markAsSelected((*mSelected)[i], false);
2174
2175                  Con::executef(this, "onUnSelect", (*mSelected)[i]->getIdString());
2176               }
2177
2178               mSelected->clear();
2179               mSelected->addObject( mPossibleHitObject );
2180               mSelected->storeCurrentCentroid();
2181               Con::executef( this, "onSelect", mPossibleHitObject->getIdString() );
2182            }
2183         }
2184      }
2185
2186      if ( event.mouseClickCount > 1 )
2187      {
2188         //
2189         char buf[16];
2190         dSprintf(buf, sizeof(buf), "%d", mPossibleHitObject->getId());
2191
2192         SimObject *obj = NULL;
2193         if ( mRedirectID )
2194            obj = Sim::findObject( mRedirectID );
2195         Con::executef( obj ? obj : this, "onDblClick", buf );
2196      }
2197      else 
2198      {
2199         char buf[16];
2200         dSprintf( buf, sizeof(buf), "%d", mPossibleHitObject->getId() );
2201
2202         SimObject *obj = NULL;
2203         if ( mRedirectID )
2204            obj = Sim::findObject( mRedirectID );
2205         Con::executef( obj ? obj : this, "onClick", buf );
2206      }
2207
2208      mHitObject = mPossibleHitObject;
2209   }
2210
2211   if ( bool(mSelected) && mSelected->hasCentroidChanged() )
2212   {
2213      Con::executef( this, "onSelectionCentroidChanged");
2214   }
2215
2216   if ( mMouseDragged && bool(mSelected) && mSelected->size() )
2217   {
2218      if ( mSelected->size() )
2219      {
2220         if ( isMethod("onEndDrag") )
2221         {
2222            SimObject * obj = 0;
2223            if ( mRedirectID )
2224               obj = Sim::findObject( mRedirectID );
2225            Con::executef( obj ? obj : this, "onEndDrag", ( *mSelected )[ 0 ]->getIdString() );
2226         }
2227      }
2228   }
2229
2230   //if ( mHitObject )
2231   //   mHitObject->inspectPostApply();
2232   //mHitObject = NULL;  
2233
2234   //
2235   //mHitObject = hitObj;
2236   mouseUnlock();
2237}
2238
2239void WorldEditor::on3DMouseDragged(const Gui3DMouseEvent & event)
2240{
2241   //If we have an active tool and it's intercepted our input, bail out
2242   if (mActiveEditorTool != nullptr && mActiveEditorTool->onMouseDragged(event))
2243      return;
2244
2245   if ( !mMouseDown )
2246      return;
2247
2248   if ( mNoMouseDrag && !mUsingAxisGizmo )
2249   {
2250      // Perhaps we should start the drag after all
2251      if( mAbs(mLastMouseDownEvent.mousePoint.x - event.mousePoint.x) > 2 || mAbs(mLastMouseDownEvent.mousePoint.y - event.mousePoint.y) > 2 )
2252      {
2253         if ( !(event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) ) )
2254            clearSelection();
2255
2256         mDragSelect = true;
2257         mDragSelected->clear();
2258         mDragRect.set( Point2I(mLastMouseDownEvent.mousePoint), Point2I(0,0) );
2259         mDragStart = mLastMouseDownEvent.mousePoint;
2260
2261         mNoMouseDrag = false;
2262         mHitObject = NULL;
2263      }
2264      else
2265      {
2266         return;
2267      }
2268   }
2269
2270   //
2271   if ( !mMouseDragged )
2272   {
2273      if ( !mUsingAxisGizmo )
2274      {
2275         // vert drag on new object.. reset hit offset
2276         if ( mHitObject && bool(mSelected) &&!mSelected->objInSet( mHitObject ) )
2277         {
2278            if ( !mSelectionLocked )
2279               mSelected->addObject( mHitObject );
2280         }
2281      }
2282
2283      // create and add an undo state
2284      if ( !mDragSelect )
2285        submitUndo( mSelected );
2286
2287      mMouseDragged = true;
2288   }
2289
2290   // update the drag selection
2291   if ( mDragSelect )
2292   {
2293      // build the drag selection on the renderScene method - make sure no neg extent!
2294      mDragRect.point.x = (event.mousePoint.x < mDragStart.x) ? event.mousePoint.x : mDragStart.x;
2295      mDragRect.extent.x = (event.mousePoint.x > mDragStart.x) ? event.mousePoint.x - mDragStart.x : mDragStart.x - event.mousePoint.x;
2296      mDragRect.point.y = (event.mousePoint.y < mDragStart.y) ? event.mousePoint.y : mDragStart.y;
2297      mDragRect.extent.y = (event.mousePoint.y > mDragStart.y) ? event.mousePoint.y - mDragStart.y : mDragStart.y - event.mousePoint.y;
2298      return;
2299   }
2300
2301   if ( !mUsingAxisGizmo && ( !mHitObject || !mSelected->objInSet( mHitObject ) ) )
2302      return;
2303
2304   // anything locked?
2305   for ( U32 i = 0; i < mSelected->size(); i++ )
2306      if ( ( *mSelected )[i]->isLocked() )
2307         return;
2308
2309   if ( mUsingAxisGizmo )
2310      mGizmo->on3DMouseDragged( event );
2311
2312   switch ( mGizmoProfile->mode )
2313   {
2314   case MoveMode:
2315
2316      // grabbed axis gizmo?
2317      if ( mUsingAxisGizmo )
2318      {
2319         // Check if a copy should be made
2320         if ( event.modifier & SI_SHIFT && !mPerformedDragCopy )
2321         {
2322            mPerformedDragCopy = true;
2323            mPossibleHitObject = NULL;
2324            
2325            copySelection( mSelected );
2326            pasteSelection( false );
2327         }
2328         
2329         // Check for grid snap toggle with ALT.
2330         
2331         if( event.modifier & SI_PRIMARY_ALT )
2332         {
2333            if( !mDragGridSnapToggle )
2334            {
2335               mDragGridSnapToggle = true;
2336               const bool snapToGrid = !mGridSnap;
2337               mGridSnap = snapToGrid;
2338               mGizmo->getProfile()->snapToGrid = snapToGrid;
2339            }
2340         }
2341         else if( mDragGridSnapToggle )
2342         {
2343            mDragGridSnapToggle = false;
2344            const bool snapToGrid = !mGridSnap;
2345            mGridSnap = snapToGrid;
2346            mGizmo->getProfile()->snapToGrid = snapToGrid;
2347         }
2348
2349         mSelected->offset(mGizmo->getOffset(), (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
2350
2351         // Handle various sticking
2352         terrainSnapSelection( mSelected, event.modifier, mGizmo->getPosition() );
2353         softSnapSelection( mSelected, event.modifier, mGizmo->getPosition() );
2354
2355         updateClientTransforms( mSelected );
2356      }     
2357      break;
2358
2359   case ScaleMode:
2360      if ( mUsingAxisGizmo )
2361      {
2362         Point3F scale = mGizmo->getScale() / (mGizmo->getScale() - mGizmo->getDeltaScale());
2363
2364         // Can scale each object independently around its own origin, or scale
2365         // the selection as a group around the centroid
2366         if ( mObjectsUseBoxCenter )
2367            mSelected->scale( scale, getSelectionCentroid() );
2368         else
2369            mSelected->scale( scale );
2370
2371         updateClientTransforms(mSelected);
2372      }
2373
2374      break;
2375
2376   case RotateMode:
2377   {
2378      Point3F centroid = getSelectionCentroid();
2379      EulerF rot = mGizmo->getDeltaRot();
2380
2381      mSelected->rotate(rot, centroid);
2382      updateClientTransforms(mSelected);
2383
2384      break;  
2385   }
2386      
2387   default:
2388      break;
2389   }
2390
2391   mLastMouseEvent = event;
2392}
2393
2394void WorldEditor::on3DMouseEnter(const Gui3DMouseEvent &)
2395{
2396}
2397
2398void WorldEditor::on3DMouseLeave(const Gui3DMouseEvent &)
2399{
2400}
2401
2402void WorldEditor::on3DRightMouseDown(const Gui3DMouseEvent & event)
2403{
2404}
2405
2406void WorldEditor::on3DRightMouseUp(const Gui3DMouseEvent & event)
2407{
2408}
2409
2410//------------------------------------------------------------------------------
2411
2412void WorldEditor::updateGuiInfo()
2413{
2414   SimObject * obj = 0;
2415   if ( mRedirectID )
2416      obj = Sim::findObject( mRedirectID );
2417
2418   char buf[] = "";
2419   Con::executef( obj ? obj : this, "onGuiUpdate", buf );
2420}
2421
2422//------------------------------------------------------------------------------
2423
2424static void findObjectsCallback( SceneObject *obj, void *val )
2425{
2426   Vector<SceneObject*> * list = (Vector<SceneObject*>*)val;
2427   list->push_back(obj);
2428}
2429
2430struct DragMeshCallbackData {
2431   WorldEditor*            mWorldEditor;
2432   Box3F                   mBounds;
2433   SphereF                 mSphereBounds;
2434   Vector<SceneObject*>   mObjects;
2435   EarlyOutPolyList        mPolyList;
2436   MatrixF                 mStandardMat;
2437   Point3F                 mStandardScale;
2438
2439   DragMeshCallbackData(WorldEditor* we, Box3F &bounds, SphereF &sphereBounds)
2440   {
2441      mWorldEditor = we;
2442      mBounds = bounds;
2443      mSphereBounds = sphereBounds;
2444      mStandardMat.identity();
2445      mStandardScale.set(1.0f, 1.0f, 1.0f);
2446   }
2447};
2448
2449static Frustum gDragFrustum;
2450static void findDragMeshCallback( SceneObject *obj, void *data )
2451{
2452   DragMeshCallbackData* dragData = reinterpret_cast<DragMeshCallbackData*>(data);
2453
2454   if ( dragData->mWorldEditor->objClassIgnored( obj ) ||
2455        !obj->isSelectionEnabled() ||
2456        obj->getTypeMask() & ( TerrainObjectType | ProjectileObjectType ) ||
2457        dynamic_cast< GroundPlane* >( obj ) ||
2458        Prefab::getPrefabByChild( obj ) )
2459   {
2460      return;
2461   }
2462
2463   // Reset the poly list for us
2464   dragData->mPolyList.clear();
2465   dragData->mPolyList.setTransform(&(dragData->mStandardMat), dragData->mStandardScale);
2466
2467   // Do the work
2468   obj->buildPolyList(PLC_Selection, &(dragData->mPolyList), dragData->mBounds, dragData->mSphereBounds);
2469   if (!dragData->mPolyList.isEmpty())
2470   {
2471      dragData->mObjects.push_back(obj);
2472   }
2473}
2474
2475void WorldEditor::renderScene( const RectI &updateRect )
2476{
2477   GFXDEBUGEVENT_SCOPE( Editor_renderScene, ColorI::RED );
2478
2479   smRenderSceneSignal.trigger(this);
2480
2481   if (mActiveEditorTool != nullptr)
2482      mActiveEditorTool->render();
2483   
2484   // Grab this before anything here changes it.
2485   Frustum frustum;
2486   {
2487      F32 left, right, top, bottom, nearPlane, farPlane;
2488      bool isOrtho = false;   
2489      GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho );
2490
2491      MatrixF cameraMat = GFX->getWorldMatrix();
2492      cameraMat.inverse();
2493
2494      frustum.set( isOrtho, left, right, top, bottom, nearPlane, farPlane, cameraMat );
2495   }
2496
2497   // Render the paths
2498   renderPaths(Scene::getRootScene());
2499
2500   // walk selected
2501   Selection* selection = getActiveSelectionSet();
2502   if( selection )
2503   {
2504      bool isMultiSelection = mSelected->size() > 1;
2505      for( U32 i = 0; i < mSelected->size(); i++ )
2506      {
2507         if ( (const SceneObject *)mHitObject == ( *mSelected )[i] )
2508            continue;
2509         SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] );
2510         if( object && mRenderSelectionBox )
2511            renderObjectBox( object, isMultiSelection ? mObjMultiSelectColor : mObjSelectColor );
2512      }
2513   }
2514
2515   // do the drag selection
2516   for ( U32 i = 0; i < mDragSelected->size(); i++ )
2517   {
2518      SceneObject* object = dynamic_cast< SceneObject* >( ( *mDragSelected )[ i ] );
2519      if( object && mRenderSelectionBox )
2520         renderObjectBox(object, mObjSelectColor);
2521   }
2522
2523   if( selection )
2524   {
2525      // draw the mouse over obj
2526      if ( bool(mHitObject) && mRenderSelectionBox )
2527      {
2528         ColorI & col = selection->objInSet(mHitObject) ? mObjMouseOverSelectColor : mObjMouseOverColor;
2529         renderObjectBox(mHitObject, col);
2530      }
2531
2532      // stuff to do if there is a selection
2533      if ( selection->size() )
2534      {   
2535         if ( mRenderSelectionBox && selection->size() > 1 )
2536            renderSelectionWorldBox(selection);
2537            
2538         SceneObject* singleSelectedSceneObject = NULL;
2539         if ( selection->size() == 1 )
2540            singleSelectedSceneObject = dynamic_cast< SceneObject* >( ( *selection )[ 0 ] );
2541
2542         MatrixF objMat(true);
2543         if( singleSelectedSceneObject )
2544            objMat = singleSelectedSceneObject->getTransform();
2545
2546         Point3F worldPos = getSelectionCentroid();
2547
2548         Point3F objScale = singleSelectedSceneObject ? singleSelectedSceneObject->getScale() : Point3F(1,1,1);
2549         
2550         mGizmo->set( objMat, worldPos, objScale );
2551
2552         // Change the gizmo's centroid highlight based on soft sticking state
2553         if( mSoftSnapIsStuck || mStuckToGround )
2554            mGizmo->setCentroidHandleHighlight( true );
2555
2556         mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov );
2557
2558         // Reset any highlighting
2559         if( mSoftSnapIsStuck || mStuckToGround )
2560            mGizmo->setCentroidHandleHighlight( false );
2561
2562         // Soft snap box rendering
2563         if( (mSoftSnapRender || mSoftSnapRenderTriangle) && mSoftSnapActivated)
2564         {
2565            GFXDrawUtil *drawUtil = GFX->getDrawUtil();
2566            ColorI color;
2567
2568            GFXStateBlockDesc desc;
2569
2570            if(mSoftSnapRenderTriangle && mSoftSnapIsStuck)
2571            {
2572               desc.setBlend( false );
2573               desc.setZReadWrite( false, false );
2574               desc.fillMode = GFXFillWireframe;
2575               desc.cullMode = GFXCullNone;
2576
2577               color.set( 255, 255, 128, 255 );
2578               drawUtil->drawTriangle( desc, mSoftSnapTriangle.p0, mSoftSnapTriangle.p1, mSoftSnapTriangle.p2, color );
2579            }
2580
2581            if(mSoftSnapRender)
2582            {
2583               desc.setBlend( true );
2584               desc.blendSrc = GFXBlendOne;
2585               desc.blendDest = GFXBlendOne;
2586               desc.blendOp = GFXBlendOpAdd;
2587               desc.setZReadWrite( true, false );
2588               desc.cullMode = GFXCullCCW;
2589
2590               color.set( 64, 64, 0, 255 );
2591
2592               desc.fillMode = GFXFillWireframe;
2593               drawUtil->drawCube(desc, mSoftSnapPreBounds, color);
2594
2595               desc.fillMode = GFXFillSolid;
2596               drawUtil->drawSphere(desc, mSoftSnapPreBounds.len()*0.05f, mSoftSnapPreBounds.getCenter(), color);
2597            }
2598         }
2599      }
2600   }
2601
2602   // Debug rendering of the soft stick
2603   if(mSoftSnapDebugRender)
2604   {
2605      GFXDrawUtil *drawUtil = GFX->getDrawUtil();
2606      ColorI color( 255, 0, 0, 255 );
2607
2608      GFXStateBlockDesc desc;
2609      desc.setBlend( false );
2610      desc.setZReadWrite( false, false );
2611
2612      if(mSoftSnapIsStuck)
2613      {
2614         drawUtil->drawArrow( desc, getSelectionCentroid(), mSoftSnapDebugPoint, color );
2615
2616         color.set(255, 255, 255);
2617         desc.fillMode = GFXFillWireframe;
2618         for(S32 i=0; i<mSoftSnapDebugTriangles.size(); ++i)
2619         {
2620            drawUtil->drawTriangle( desc, mSoftSnapDebugTriangles[i].p0, mSoftSnapDebugTriangles[i].p1, mSoftSnapDebugTriangles[i].p2, color );
2621         }
2622
2623         color.set(255, 255, 0);
2624         desc.fillMode = GFXFillSolid;
2625         desc.cullMode = GFXCullNone;
2626         drawUtil->drawTriangle( desc, mSoftSnapDebugSnapTri.p0, mSoftSnapDebugSnapTri.p1, mSoftSnapDebugSnapTri.p2, color );
2627      }
2628
2629   }
2630
2631   // Now do the 2D stuff...
2632   // icons and text
2633   GFX->setClipRect(updateRect);
2634
2635   // update what is in the selection
2636   if ( mDragSelect )
2637      mDragSelected->clear();
2638
2639   // Determine selected objects based on the drag box touching
2640   // a mesh if a drag operation has begun.
2641   if( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 )
2642   {
2643      // Build the drag frustum based on the rect
2644      F32 wwidth;
2645      F32 wheight;
2646      F32 aspectRatio = F32(getWidth()) / F32(getHeight());
2647      
2648      if(!mLastCameraQuery.ortho)
2649      {
2650         wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2);
2651         wwidth = aspectRatio * wheight;
2652      }
2653      else
2654      {
2655         wheight = mLastCameraQuery.fov;
2656         wwidth = aspectRatio * wheight;
2657      }
2658
2659      F32 hscale = wwidth * 2 / F32(getWidth());
2660      F32 vscale = wheight * 2 / F32(getHeight());
2661
2662      F32 left = (mDragRect.point.x - getPosition().x) * hscale - wwidth;
2663      F32 right = (mDragRect.point.x - getPosition().x + mDragRect.extent.x) * hscale - wwidth;
2664      F32 top = wheight - vscale * (mDragRect.point.y - getPosition().y);
2665      F32 bottom = wheight - vscale * (mDragRect.point.y - getPosition().y + mDragRect.extent.y);
2666      gDragFrustum.set(mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.cameraMatrix );
2667
2668      // Create the search bounds and callback data
2669      Box3F bounds = gDragFrustum.getBounds();
2670      SphereF sphere;
2671      sphere.center = bounds.getCenter();
2672      sphere.radius = (bounds.maxExtents - sphere.center).len();
2673      DragMeshCallbackData data(this, bounds, sphere);
2674
2675      // Set up the search normal and planes
2676      Point3F vec;
2677      mLastCameraQuery.cameraMatrix.getColumn(1,&vec);
2678      vec.neg();
2679      data.mPolyList.mNormal.set(vec);
2680      const PlaneF* planes = gDragFrustum.getPlanes();
2681      for( U32 i=0; i<Frustum::PlaneCount; ++i)
2682      {
2683         data.mPolyList.mPlaneList.push_back(planes[i]);
2684
2685         // Invert the planes as the poly list routines require a different
2686         // facing from gServerContainer.findObjects().
2687         data.mPolyList.mPlaneList.last().invert();
2688      }
2689      
2690      // If we're in first-person view currently, disable
2691      // hitting the control object.
2692      
2693      const bool isFirstPerson = GameConnection::getLocalClientConnection() ? GameConnection::getLocalClientConnection()->isFirstPerson() : false;
2694      if( isFirstPerson )
2695         GameConnection::getLocalClientConnection()->getControlObject()->disableCollision();
2696         
2697      // Find objects in the region.
2698
2699      gServerContainer.findObjects( gDragFrustum, 0xFFFFFFFF, findDragMeshCallback, &data);
2700      for ( U32 i = 0; i < data.mObjects.size(); i++ )
2701      {
2702         SceneObject *obj = data.mObjects[i];
2703         
2704         // Filter out unwanted objects.
2705         
2706         if(    objClassIgnored( obj )
2707             || !obj->isSelectionEnabled()
2708             || ( obj->getTypeMask() & ( TerrainObjectType | ProjectileObjectType ) )
2709             || ( obj->getTypeMask() & StaticShapeObjectType && dynamic_cast< GroundPlane* >( obj ) ) )
2710            continue;
2711            
2712         // Add the object to the drag selection.
2713
2714         mDragSelected->addObject(obj);
2715      }
2716      
2717      // Re-enable collision on control object when in first-person view.
2718      
2719      if( isFirstPerson )
2720         GameConnection::getLocalClientConnection()->getControlObject()->enableCollision();
2721   }
2722   
2723   // Clear the vector of onscreen icons, will populate this below
2724   // Necessary for performing click testing efficiently
2725   mIcons.clear();
2726
2727   // Cull Objects and perform icon rendering
2728   Vector<SceneObject*> objects;
2729   gServerContainer.findObjects( frustum, 0xFFFFFFFF, findObjectsCallback, &objects);
2730   for ( U32 i = 0; i < objects.size(); i++ )
2731   {
2732      SceneObject *obj = objects[i];
2733      if( objClassIgnored(obj) || !obj->isSelectionEnabled() )
2734         continue;
2735
2736      Point3F wPos;
2737      if ( obj->isGlobalBounds() || !mObjectsUseBoxCenter )
2738         obj->getTransform().getColumn(3, &wPos);
2739      else      
2740         wPos = getBoundingBoxCenter(obj);      
2741
2742      Point3F sPos;
2743      if ( project(wPos, &sPos) )
2744      {
2745         Point2I sPosI( (S32)sPos.x,(S32)sPos.y );
2746         if ( !updateRect.pointInRect(sPosI) )
2747            continue;
2748
2749         // check if object needs to be added into the regions select
2750
2751         // Probably should test the entire icon screen-rect instead of just the centerpoint
2752         // but would need to move some code from renderScreenObj to here.
2753         if (mDragSelect && selection)
2754            if ( mDragRect.pointInRect(sPosI) && !selection->objInSet(obj) )
2755               mDragSelected->addObject(obj);
2756
2757         //
2758         renderScreenObj( obj, sPos, wPos );
2759      }
2760   }
2761
2762   //// Debug render rect around icons
2763   //for ( U32 i = 0; i < mIcons.size(); i++ )
2764   //   GFX->getDrawUtil()->drawRect( mIcons[i].rect, ColorI(255,255,255,255) );
2765
2766   if ( mShowMousePopupInfo && mMouseDown )
2767      renderMousePopupInfo();
2768
2769   if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 )
2770      GFX->getDrawUtil()->drawRect( mDragRect, mDragRectColor );
2771
2772   if ( selection && selection->size() )
2773      mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection );
2774}
2775
2776//------------------------------------------------------------------------------
2777// Console stuff
2778
2779void WorldEditor::initPersistFields()
2780{
2781   addGroup( "Grid" );
2782   
2783      addField( "gridSnap",               TypeBool,   Offset( mGridSnap, WorldEditor ),
2784         "If true, transform operations will snap to the grid.");
2785      addField("useGroupCenter", TypeBool, Offset(mUseGroupCenter, WorldEditor));
2786   
2787   endGroup( "Grid" );
2788   
2789   addGroup( "Dropping" );
2790   
2791      addField( "dropAtBounds",           TypeBool,   Offset(mDropAtBounds, WorldEditor) );
2792      addField( "dropBelowCameraOffset",  TypeF32,    Offset(mDropBelowCameraOffset, WorldEditor) );
2793      addField( "dropAtScreenCenterScalar",TypeF32,   Offset(mDropAtScreenCenterScalar, WorldEditor) );
2794      addField( "dropAtScreenCenterMax",  TypeF32,    Offset(mDropAtScreenCenterMax, WorldEditor) );
2795      addField( "dropType",               TYPEID< DropType >(),   Offset(mDropType, WorldEditor) );   
2796
2797   endGroup( "Dropping" );
2798   
2799   addGroup( "Colors" );
2800
2801      addField( "popupBackgroundColor",   TypeColorI, Offset(mPopupBackgroundColor, WorldEditor) );
2802      addField( "popupTextColor",         TypeColorI, Offset(mPopupTextColor, WorldEditor) );
2803      addField( "objectTextColor",        TypeColorI, Offset(mObjectTextColor, WorldEditor) );
2804      addField( "selectionBoxColor",      TypeColorI, Offset(mSelectionBoxColor, WorldEditor) );
2805      addField( "objSelectColor",         TypeColorI, Offset(mObjSelectColor, WorldEditor) );
2806      addField( "objMouseOverSelectColor",TypeColorI, Offset(mObjMouseOverSelectColor, WorldEditor) );
2807      addField( "objMouseOverColor",      TypeColorI, Offset(mObjMouseOverColor, WorldEditor) );
2808      addField( "dragRectColor",          TypeColorI, Offset(mDragRectColor, WorldEditor) );
2809      addField( "faceSelectColor",        TypeColorI, Offset(mFaceSelectColor, WorldEditor) );
2810   
2811   endGroup( "Colors" );
2812   
2813   addGroup( "Selections" );
2814   
2815      addField( "boundingBoxCollision",   TypeBool,   Offset(mBoundingBoxCollision, WorldEditor) );
2816      addField( "objectMeshCollision",    TypeBool,   Offset(mObjectMeshCollision, WorldEditor) );
2817      addField( "selectionLocked",        TypeBool,   Offset(mSelectionLocked, WorldEditor) );   
2818      addProtectedField( "objectsUseBoxCenter", TypeBool, Offset(mObjectsUseBoxCenter, WorldEditor), &setObjectsUseBoxCenter, &defaultProtectedGetFn, "" );
2819
2820   endGroup( "Selections" );
2821   
2822   addGroup( "Rendering" );
2823
2824      addField( "objTextFormat",          TypeString, Offset(mObjTextFormat, WorldEditor) );
2825      addField( "renderPopupBackground",  TypeBool,   Offset(mRenderPopupBackground, WorldEditor) );
2826      addField( "showMousePopupInfo",     TypeBool,   Offset(mShowMousePopupInfo, WorldEditor) );
2827      addField( "renderObjText",          TypeBool,   Offset(mRenderObjText, WorldEditor) );
2828      addField( "renderObjHandle",        TypeBool,   Offset(mRenderObjHandle, WorldEditor) );
2829      addField( "renderSelectionBox",     TypeBool,   Offset(mRenderSelectionBox, WorldEditor) );
2830      addField( "selectHandle",           TypeFilename, Offset(mSelectHandle, WorldEditor) );
2831      addField( "defaultHandle",          TypeFilename, Offset(mDefaultHandle, WorldEditor) );
2832      addField( "lockedHandle",           TypeFilename, Offset(mLockedHandle, WorldEditor) );
2833   
2834   endGroup( "Rendering" );
2835   
2836   addGroup( "Rendering: Icons" );
2837   
2838      addField( "fadeIcons", TypeBool, Offset( mFadeIcons, WorldEditor ),
2839         "Whether object icons should fade out with distance to camera pos." );
2840      addField( "fadeIconsDist", TypeF32, Offset( mFadeIconsDist, WorldEditor ),
2841         "Distance from camera pos at which to start fading out icons." );
2842   
2843   endGroup( "Rendering: Icons" );
2844
2845   addGroup( "Misc" );  
2846
2847      addField( "isDirty",                TypeBool,   Offset(mIsDirty, WorldEditor) );
2848      addField( "stickToGround",          TypeBool,   Offset(mStickToGround, WorldEditor) );
2849      addField("TerrainSnapOffsetZ",      TypeBool,   Offset(mTerrainSnapOffsetZ, WorldEditor));
2850      addField("OffsetZValue",            TypeF32,    Offset(mOffsetZValue, WorldEditor));
2851      //addField("sameScaleAllAxis", TypeBool, Offset(mSameScaleAllAxis, WorldEditor));
2852      addField( "toggleIgnoreList",       TypeBool,   Offset(mToggleIgnoreList, WorldEditor) );
2853
2854   endGroup( "Misc" );
2855
2856   Parent::initPersistFields();
2857}
2858
2859//------------------------------------------------------------------------------
2860// These methods are needed for the console interfaces.
2861
2862void WorldEditor::ignoreObjClass( U32 argc, ConsoleValueRef *argv )
2863{
2864   for(S32 i = 2; i < argc; i++)
2865   {
2866      ClassInfo::Entry * entry = getClassEntry(argv[i]);
2867      if(entry)
2868         entry->mIgnoreCollision = true;
2869      else
2870      {
2871         entry = new ClassInfo::Entry;
2872         entry->mName = StringTable->insert(argv[i]);
2873         entry->mIgnoreCollision = true;
2874         if(!addClassEntry(entry))
2875            delete entry;
2876      }
2877   }  
2878}
2879
2880void WorldEditor::clearIgnoreList()
2881{
2882   for(U32 i = 0; i < mClassInfo.mEntries.size(); i++)
2883      mClassInfo.mEntries[i]->mIgnoreCollision = false;  
2884}
2885
2886void WorldEditor::setObjectsUseBoxCenter(bool state)
2887{
2888   mObjectsUseBoxCenter = state;
2889   if( getActiveSelectionSet() && isMethod( "onSelectionCentroidChanged" ) )
2890      Con::executef( this, "onSelectionCentroidChanged" );
2891}
2892
2893void WorldEditor::clearSelection()
2894{
2895   if( mSelectionLocked || !mSelected )
2896      return;
2897
2898   // Call onUnSelect.  Because of the whole treeview<->selection synchronization,
2899   // this may actually cause things to disappear from mSelected so do the loop
2900   // in reverse.  This will make the loop work even if items are removed as
2901   // we go along.
2902   for (S32 i = mSelected->size() - 1; i >= 0; --i)
2903   {
2904      //We'll explicitly inform the object of being unmarked as selected in the editor as well for outlier cases potentially not being told, such as mounted objects
2905      WorldEditor::markAsSelected((*mSelected)[i], false);
2906
2907      Con::executef(this, "onUnSelect", (*mSelected)[i]->getIdString());
2908   }
2909
2910   Con::executef(this, "onClearSelection");
2911   mSelected->clear();
2912}
2913
2914void WorldEditor::selectObject( SimObject *obj )
2915{
2916   if ( mSelectionLocked || !mSelected || !obj )
2917      return;
2918
2919   // Don't check isSelectionEnabled of SceneObjects here as we
2920   // want to still allow manual selection in treeviews.
2921
2922   if ( !objClassIgnored( obj ) && !mSelected->objInSet( obj ) )
2923   {
2924      mSelected->addObject( obj );  
2925      Con::executef( this, "onSelect", obj->getIdString() );
2926   }
2927}
2928
2929void WorldEditor::selectObject( const char* obj )
2930{   
2931   SimObject *select;
2932
2933   if ( Sim::findObject( obj, select ) )
2934      selectObject( select );
2935} 
2936
2937void WorldEditor::unselectObject( SimObject *obj )
2938{
2939   if ( mSelectionLocked || !mSelected || !obj )
2940      return;
2941
2942   if ( !objClassIgnored( obj ) && mSelected->objInSet( obj ) )
2943   {
2944      mSelected->removeObject( obj );  
2945      Con::executef( this, "onUnSelect", obj->getIdString() );
2946   }
2947}
2948
2949void WorldEditor::unselectObject( const char *obj )
2950{
2951   SimObject *select;
2952
2953   if ( Sim::findObject( obj, select ) )
2954      unselectObject( select );
2955}
2956
2957S32 WorldEditor::getSelectionSize()
2958{
2959   if( !mSelected )
2960      return 0;
2961      
2962   return mSelected->size();
2963}
2964
2965S32 WorldEditor::getSelectObject(S32 index)
2966{
2967   AssertFatal( mSelected != NULL, "WorldEditor::getSelectedObject - no active selection set!" );
2968   
2969   // Return the object's id
2970   return ( *mSelected )[index]->getId(); 
2971}
2972
2973const Point3F& WorldEditor::getSelectionCentroid()
2974{
2975   if( !mSelected )
2976      return Point3F::Zero;
2977      
2978   if( mSelected->containsGlobalBounds() )
2979   {
2980      return mSelected->getCentroid();
2981   }
2982
2983   return mObjectsUseBoxCenter ? mSelected->getBoxCentroid() : mSelected->getCentroid();
2984}
2985
2986const Box3F& WorldEditor::getSelectionBounds()
2987{
2988   return mSelected->getBoxBounds();
2989}
2990
2991Point3F WorldEditor::getSelectionExtent()
2992{
2993   const Box3F& box = getSelectionBounds();
2994   return box.getExtents();
2995}
2996
2997F32 WorldEditor::getSelectionRadius()
2998{
2999   const Box3F box = getSelectionBounds();
3000   return box.len() * 0.5f;
3001}
3002
3003void WorldEditor::dropCurrentSelection( bool skipUndo )
3004{
3005   if ( !bool(mSelected) || !mSelected->size() )
3006      return;
3007
3008   if ( !skipUndo )
3009      submitUndo( mSelected );
3010
3011   dropSelection( mSelected );   
3012
3013   if ( mSelected->hasCentroidChanged() )
3014      Con::executef( this, "onSelectionCentroidChanged" );
3015}
3016
3017void WorldEditor::redirectConsole( S32 objID )
3018{
3019   mRedirectID = objID;    
3020}
3021
3022//------------------------------------------------------------------------------
3023
3024bool WorldEditor::alignByBounds( S32 boundsAxis )
3025{
3026   if(boundsAxis < 0 || boundsAxis > 5)
3027      return false;
3028
3029   if(mSelected->size() < 2)
3030      return true;
3031
3032   S32 axis = boundsAxis >= 3 ? boundsAxis-3 : boundsAxis;
3033   bool useMax = boundsAxis >= 3 ? false : true;
3034   
3035   // Find out which selected object has its bounds the farthest out
3036   F32 pos;
3037   S32 baseObj = 0;
3038   if(useMax)
3039      pos = TypeTraits< F32 >::MIN;
3040   else
3041      pos = TypeTraits< F32 >::MAX;
3042
3043   for(S32 i=1; i<mSelected->size(); ++i)
3044   {
3045      SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] );
3046      if( !object )
3047         continue;
3048         
3049      const Box3F& bounds = object->getWorldBox();
3050
3051      if(useMax)
3052      {
3053         if(bounds.maxExtents[axis] > pos)
3054         {
3055            pos = bounds.maxExtents[axis];
3056            baseObj = i;
3057         }
3058      }
3059      else
3060      {
3061         if(bounds.minExtents[axis] < pos)
3062         {
3063            pos = bounds.minExtents[axis];
3064            baseObj = i;
3065         }
3066      }
3067   }
3068
3069   submitUndo( mSelected, "Align By Bounds" );
3070
3071   // Move all selected objects to align with the calculated bounds
3072   for(S32 i=0; i<mSelected->size(); ++i)
3073   {
3074      if(i == baseObj)
3075         continue;
3076         
3077      SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] );
3078      if( !object )
3079         continue;
3080
3081      const Box3F& bounds = object->getWorldBox();
3082      F32 delta;
3083      if(useMax)
3084         delta = pos - bounds.maxExtents[axis];
3085      else
3086         delta = pos - bounds.minExtents[axis];
3087
3088      Point3F objPos = object->getPosition();
3089      objPos[axis] += delta;
3090      object->setPosition(objPos);
3091   }
3092
3093   return true;
3094}
3095
3096bool WorldEditor::alignByAxis( S32 axis )
3097{
3098   if(axis < 0 || axis > 2)
3099      return false;
3100
3101   if(mSelected->size() < 2)
3102      return true;
3103      
3104   SceneObject* primaryObj = dynamic_cast< SceneObject* >( ( *mSelected )[ 0 ] );
3105   if( !primaryObj)
3106      return false;
3107
3108   submitUndo( mSelected, "Align By Axis" );
3109
3110   // All objects will be repositioned to line up with the
3111   // first selected object
3112   Point3F pos = primaryObj->getPosition();
3113
3114   for(S32 i=0; i<mSelected->size(); ++i)
3115   {
3116      SceneObject* additionalObj = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] );
3117      if( !additionalObj)
3118         continue;
3119         
3120      Point3F objPos = additionalObj->getPosition();
3121      objPos[axis] = pos[axis];
3122     additionalObj->setPosition(objPos);
3123   }
3124
3125   return true;
3126}
3127
3128//------------------------------------------------------------------------------
3129
3130void WorldEditor::transformSelection(bool position, Point3F& p, bool relativePos, bool rotate, EulerF& r, bool relativeRot, bool rotLocal, S32 scaleType, Point3F& s, bool sRelative, bool sLocal)
3131{
3132   if(mSelected->size() == 0)
3133      return;
3134
3135   submitUndo( mSelected, "Transform Selection" );
3136
3137   if( position )
3138   {
3139      if( relativePos )
3140      {
3141         mSelected->offset(p, (!mUseGroupCenter && mGridSnap) ? mGridPlaneSize : 0.f);
3142      }
3143      else
3144      {
3145         mSelected->setCentroidPosition(mObjectsUseBoxCenter, p);
3146      }
3147   }
3148
3149   if( rotate )
3150   {
3151      Point3F centroid;
3152      if( mSelected->containsGlobalBounds() )
3153      {
3154         centroid = mSelected->getCentroid();
3155      }
3156      else
3157      {
3158         centroid = mObjectsUseBoxCenter ? mSelected->getBoxCentroid() : mSelected->getCentroid();
3159      }
3160
3161      if( relativeRot )
3162      {
3163         if( rotLocal )
3164         {
3165            mSelected->rotate(r);
3166         }
3167         else
3168         {
3169            mSelected->rotate(r, centroid);
3170         }
3171      }
3172      else if( rotLocal )
3173      {
3174         // Can only do absolute rotation for multiple objects about
3175         // object center
3176         mSelected->setRotate(r);
3177      }
3178   }
3179
3180   if( scaleType == 1 )
3181   {
3182      // Scale
3183
3184      Point3F centroid;
3185      if( mSelected->containsGlobalBounds() )
3186      {
3187         centroid = mSelected->getCentroid();
3188      }
3189      else
3190      {
3191         centroid = mObjectsUseBoxCenter ? mSelected->getBoxCentroid() : mSelected->getCentroid();
3192      }
3193
3194      if( sRelative )
3195      {
3196         if( sLocal )
3197         {
3198            mSelected->scale(s);
3199         }
3200         else
3201         {
3202            mSelected->scale(s, centroid);
3203         }
3204      }
3205      else
3206      {
3207         if( sLocal )
3208         {
3209            mSelected->setScale(s);
3210         }
3211         else
3212         {
3213            mSelected->setScale(s, centroid);
3214         }
3215      }
3216   }
3217   else if( scaleType == 2 )
3218   {
3219      // Size
3220
3221      if( mSelected->containsGlobalBounds() )
3222         return;
3223
3224      if( sRelative )
3225      {
3226         // Size is always local/object based
3227         mSelected->addSize(s);
3228      }
3229      else
3230      {
3231         // Size is always local/object based
3232         mSelected->setSize(s);
3233      }
3234   }
3235
3236   updateClientTransforms(mSelected);
3237
3238   if(mSelected->hasCentroidChanged())
3239   {
3240      Con::executef( this, "onSelectionCentroidChanged");
3241   }
3242
3243   if ( isMethod("onEndDrag") )
3244   {
3245      SimObject * obj = 0;
3246      if ( mRedirectID )
3247         obj = Sim::findObject( mRedirectID );
3248      Con::executef( obj ? obj : this, "onEndDrag", ( *mSelected )[ 0 ]->getIdString() );
3249   }
3250}
3251
3252//------------------------------------------------------------------------------
3253
3254void WorldEditor::resetSelectedRotation()
3255{
3256   for(S32 i=0; i<mSelected->size(); ++i)
3257   {
3258      SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] );
3259      if( !object )
3260         continue;
3261         
3262      MatrixF mat(true);
3263      mat.setPosition(object->getPosition());
3264      object->setTransform(mat);
3265   }
3266}
3267
3268void WorldEditor::resetSelectedScale()
3269{
3270   for(S32 i=0; i<mSelected->size(); ++i)
3271   {
3272      SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] );
3273      if( object )
3274         object->setScale(Point3F(1,1,1));
3275   }
3276}
3277
3278//------------------------------------------------------------------------------
3279
3280void WorldEditor::setEditorTool(EditorTool* newTool)
3281{
3282   if (mActiveEditorTool)
3283      mActiveEditorTool->onDeactivated();
3284
3285   mActiveEditorTool = newTool;
3286
3287   if (mActiveEditorTool)
3288      mActiveEditorTool->onActivated(this);
3289}
3290
3291//------------------------------------------------------------------------------
3292
3293DefineEngineStringlyVariadicMethod( WorldEditor, ignoreObjClass, void, 3, 0, "(string class_name, ...)")
3294{
3295   object->ignoreObjClass(argc, argv);
3296}
3297
3298DefineEngineMethod( WorldEditor, clearIgnoreList, void, (),,
3299   "Clear the ignore class list.\n")
3300{
3301   object->clearIgnoreList();
3302}
3303
3304DefineEngineMethod( WorldEditor, clearSelection, void, (),,
3305   "Clear the selection.\n")
3306{
3307   object->clearSelection();
3308}
3309
3310DefineEngineMethod( WorldEditor, getActiveSelection, S32, (),,
3311   "Return the currently active WorldEditorSelection object.\n"
3312   "@return currently active WorldEditorSelection object or 0 if no selection set is available.")
3313{
3314   if( !object->getActiveSelectionSet() )
3315      return 0;
3316      
3317   return object->getActiveSelectionSet()->getId();
3318}
3319
3320DefineEngineMethod( WorldEditor, setActiveSelection, void, ( WorldEditorSelection* selection), ,
3321   "Set the currently active WorldEditorSelection object.\n"
3322   "@param  selection A WorldEditorSelectionSet object to use for the selection container.")
3323{
3324   if (selection)
3325   object->makeActiveSelectionSet( selection );
3326}
3327
3328DefineEngineMethod( WorldEditor, selectObject, void, (SimObject* obj),,
3329   "Selects a single object."
3330   "@param obj Object to select.")
3331{
3332   object->selectObject(obj);
3333}
3334
3335DefineEngineMethod( WorldEditor, unselectObject, void, (SimObject* obj),,
3336   "Unselects a single object."
3337   "@param obj Object to unselect.")
3338{
3339   object->unselectObject(obj);
3340}
3341
3342DefineEngineMethod( WorldEditor, invalidateSelectionCentroid, void, (),,
3343   "Invalidate the selection sets centroid.")
3344{
3345   WorldEditor::Selection* sel = object->getActiveSelectionSet();
3346   if(sel)
3347      sel->invalidateCentroid();
3348}
3349
3350DefineEngineMethod( WorldEditor, getSelectionSize, S32, (),,
3351   "Return the number of objects currently selected in the editor."
3352   "@return number of objects currently selected in the editor.")
3353{
3354   return object->getSelectionSize();
3355}
3356
3357DefineEngineMethod( WorldEditor, getSelectedObject, S32, (S32 index),,
3358   "Return the selected object and the given index."
3359   "@param index Index of selected object to get."
3360   "@return selected object at given index or -1 if index is incorrect.")
3361{
3362   if(index < 0 || index >= object->getSelectionSize())
3363   {
3364      Con::errorf(ConsoleLogEntry::General, "WorldEditor::getSelectedObject: invalid object index");
3365      return(-1);
3366   }
3367
3368   return(object->getSelectObject(index));
3369}
3370
3371DefineEngineMethod( WorldEditor, getSelectionRadius, F32, (),,
3372   "Get the radius of the current selection."
3373   "@return radius of the current selection.")
3374{
3375   return object->getSelectionRadius();
3376}
3377
3378DefineEngineMethod( WorldEditor, getSelectionCentroid, Point3F, (),,
3379   "Get centroid of the selection."
3380   "@return centroid of the selection.")
3381{
3382   return object->getSelectionCentroid();
3383}
3384
3385DefineEngineMethod( WorldEditor, getSelectionExtent, Point3F, (),,
3386   "Get extent of the selection."
3387   "@return extent of the selection.")
3388{
3389   return object->getSelectionExtent();
3390}
3391
3392DefineEngineMethod( WorldEditor, dropSelection, void, (bool skipUndo), (false),
3393   "Drop the current selection."
3394   "@param skipUndo True to skip creating undo's for this action, false to create an undo.")
3395{
3396
3397   object->dropCurrentSelection( skipUndo );
3398}
3399
3400void WorldEditor::cutCurrentSelection()
3401{
3402   cutSelection(mSelected);   
3403}
3404
3405void WorldEditor::copyCurrentSelection()
3406{
3407   copySelection(mSelected);  
3408}
3409
3410DefineEngineMethod( WorldEditor, cutSelection, void, (), ,
3411   "Cut the current selection to be pasted later.")
3412{
3413   object->cutCurrentSelection();
3414}
3415
3416DefineEngineMethod( WorldEditor, copySelection, void, (), ,
3417   "Copy the current selection to be pasted later.")
3418{
3419   object->copyCurrentSelection();
3420}
3421
3422DefineEngineMethod( WorldEditor, pasteSelection, void, (), ,
3423   "Paste the current selection.")
3424{
3425   object->pasteSelection();
3426}
3427
3428bool WorldEditor::canPasteSelection()
3429{
3430   return mCopyBuffer.empty() != true;
3431}
3432
3433DefineEngineMethod( WorldEditor, canPasteSelection, bool, (), ,
3434   "Check if we can paste the current selection."
3435   "@return True if we can paste the current selection, false if not.")
3436{
3437   return object->canPasteSelection();
3438}
3439
3440DefineEngineMethod( WorldEditor, hideObject, void, (SceneObject* obj, bool hide), ,
3441   "Hide/show the given object."
3442   "@param obj Object to hide/show."
3443   "@param hide True to hide the object, false to show it.")
3444{
3445
3446   if (obj)
3447   object->hideObject(obj, hide);
3448}
3449
3450DefineEngineMethod( WorldEditor, hideSelection, void, (bool hide), ,
3451   "Hide/show the selection."
3452   "@param hide True to hide the selection, false to show it.")
3453{
3454   object->hideSelection(hide);
3455}
3456
3457DefineEngineMethod( WorldEditor, lockSelection, void, (bool lock), ,
3458   "Lock/unlock the selection."
3459   "@param lock True to lock the selection, false to unlock it.")
3460{
3461   object->lockSelection(lock);
3462}
3463
3464//TODO: Put in the param possible options and what they mean
3465DefineEngineMethod( WorldEditor, alignByBounds, void, (S32 boundsAxis), ,
3466   "Align all selected objects against the given bounds axis."
3467   "@param boundsAxis Bounds axis to align all selected objects against.")
3468{
3469   if(!object->alignByBounds(boundsAxis))
3470      Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByBounds: invalid bounds axis '%s'", boundsAxis));
3471}
3472
3473//TODO: Put in the param possible options and what they mean (assuming x,y,z)
3474DefineEngineMethod( WorldEditor, alignByAxis, void, (S32 axis), ,
3475   "Align all selected objects along the given axis."
3476   "@param axis Axis to align all selected objects along.")
3477{
3478   if(!object->alignByAxis(axis))
3479      Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByAxis: invalid axis '%s'", axis));
3480}
3481
3482DefineEngineMethod( WorldEditor, resetSelectedRotation, void, (), ,
3483   "Reset the rotation of the selection.")
3484{
3485   object->resetSelectedRotation();
3486}
3487
3488DefineEngineMethod( WorldEditor, resetSelectedScale, void, (), ,
3489   "Reset the scale of the selection.")
3490{
3491   object->resetSelectedScale();
3492}
3493
3494//TODO: Better documentation on exactly what this does.
3495DefineEngineMethod( WorldEditor, redirectConsole, void, (S32 objID), ,
3496   "Redirect console."
3497   "@param objID Object id.")
3498{
3499   object->redirectConsole(objID);
3500}
3501
3502DefineEngineMethod( WorldEditor, addUndoState, void, (), ,
3503   "Adds/Submits an undo state to the undo manager.")
3504{
3505   object->addUndoState();
3506}
3507
3508//-----------------------------------------------------------------------------
3509
3510DefineEngineMethod( WorldEditor, getSoftSnap, bool, (), ,
3511   "Is soft snapping always on?"
3512   "@return True if soft snap is on, false if not.")
3513{
3514   return object->mSoftSnap;
3515}
3516
3517DefineEngineMethod( WorldEditor, setSoftSnap, void, (bool softSnap), ,
3518   "Allow soft snapping all of the time."
3519   "@param softSnap True to turn soft snap on, false to turn it off.")
3520{
3521   object->mSoftSnap = softSnap;
3522}
3523
3524DefineEngineMethod( WorldEditor, getSoftSnapSize, F32, (), ,
3525   "Get the absolute size to trigger a soft snap."
3526   "@return absolute size to trigger a soft snap.")
3527{
3528   return object->mSoftSnapSize;
3529}
3530
3531DefineEngineMethod( WorldEditor, setSoftSnapSize, void, (F32 size), ,
3532   "Set the absolute size to trigger a soft snap."
3533   "@param size Absolute size to trigger a soft snap.")
3534{
3535   object->mSoftSnapSize = size;
3536}
3537
3538DefineEngineMethod( WorldEditor, getSoftSnapAlignment, WorldEditor::AlignmentType, (),,
3539   "Get the soft snap alignment."
3540   "@return soft snap alignment.")
3541{
3542   return object->mSoftSnapAlignment;
3543}
3544
3545DefineEngineMethod( WorldEditor, setSoftSnapAlignment, void, ( WorldEditor::AlignmentType type ),,
3546   "Set the soft snap alignment."
3547   "@param type Soft snap alignment type.")
3548{
3549   object->mSoftSnapAlignment = type;
3550}
3551
3552DefineEngineMethod( WorldEditor, softSnapSizeByBounds, void, (bool useBounds), ,
3553   "Use selection bounds size as soft snap bounds."
3554   "@param useBounds True to use selection bounds size as soft snap bounds, false to not.")
3555{
3556   object->mSoftSnapSizeByBounds = useBounds;
3557}
3558
3559DefineEngineMethod( WorldEditor, getSoftSnapBackfaceTolerance, F32, (),,
3560   "Get the fraction of the soft snap radius that backfaces may be included."
3561   "@return fraction of the soft snap radius that backfaces may be included.")
3562{
3563   return object->mSoftSnapBackfaceTolerance;
3564}
3565
3566DefineEngineMethod( WorldEditor, setSoftSnapBackfaceTolerance, void, (F32 tolerance),,
3567   "Set the fraction of the soft snap radius that backfaces may be included."
3568   "@param tolerance Fraction of the soft snap radius that backfaces may be included (range of 0..1).")
3569{
3570   object->mSoftSnapBackfaceTolerance = tolerance;
3571}
3572
3573DefineEngineMethod( WorldEditor, softSnapRender, void, (F32 render),,
3574   "Render the soft snapping bounds."
3575   "@param render True to render the soft snapping bounds, false to not.")
3576{
3577   object->mSoftSnapRender = render;
3578}
3579
3580DefineEngineMethod( WorldEditor, softSnapRenderTriangle, void, (F32 renderTriangle),,
3581   "Render the soft snapped triangle."
3582   "@param renderTriangle True to render the soft snapped triangle, false to not.")
3583{
3584   object->mSoftSnapRenderTriangle = renderTriangle;
3585}
3586
3587DefineEngineMethod( WorldEditor, softSnapDebugRender, void, (F32 debugRender),,
3588   "Toggle soft snapping debug rendering."
3589   "@param debugRender True to turn on soft snapping debug rendering, false to turn it off.")
3590{
3591   object->mSoftSnapDebugRender = debugRender;
3592}
3593
3594DefineEngineMethod( WorldEditor, getTerrainSnapAlignment, WorldEditor::AlignmentType, (),,
3595   "Get the terrain snap alignment."
3596   "@return terrain snap alignment type.")
3597{
3598   return object->mTerrainSnapAlignment;
3599}
3600
3601DefineEngineMethod( WorldEditor, setTerrainSnapAlignment, void, ( WorldEditor::AlignmentType alignment ),,
3602   "Set the terrain snap alignment."
3603   "@param alignment New terrain snap alignment type.")
3604{
3605   object->mTerrainSnapAlignment = alignment;
3606}
3607
3608DefineEngineMethod( WorldEditor, transformSelection, void, 
3609                   ( bool position,
3610                     Point3F point,
3611                     bool relativePos,
3612                     bool rotate,
3613                     Point3F rotation,
3614                     bool relativeRot,
3615                     bool rotLocal,
3616                     S32 scaleType,
3617                     Point3F scale,
3618                     bool sRelative,
3619                     bool sLocal ), ,
3620   "Transform selection by given parameters."
3621   "@param position True to transform the selection's position."
3622   "@param point Position to transform by."
3623   "@param relativePos True to use relative position."
3624   "@param rotate True to transform the selection's rotation."
3625   "@param rotation Rotation to transform by."
3626   "@param relativeRot True to use the relative rotation."
3627   "@param rotLocal True to use the local rotation."
3628   "@param scaleType Scale type to use."
3629   "@param scale Scale to transform by."
3630   "@param sRelative True to use a relative scale."
3631   "@param sLocal True to use a local scale.")
3632{
3633   object->transformSelection(position, point, relativePos, rotate, rotation, relativeRot, rotLocal, scaleType, scale, sRelative, sLocal);
3634}
3635
3636#include "core/strings/stringUnit.h"
3637#include "collision/optimizedPolyList.h"
3638#include "core/volume.h"
3639#ifdef TORQUE_COLLADA
3640   #include "ts/collada/colladaUtils.h"
3641#endif
3642
3643void WorldEditor::colladaExportSelection( const String &path )
3644{
3645#ifdef TORQUE_COLLADA
3646   
3647   Vector< SceneObject*> objectList;
3648
3649   for ( S32 i = 0; i < mSelected->size(); i++ )
3650   {
3651      SceneObject *pObj = dynamic_cast< SceneObject* >( ( *mSelected )[i] );
3652      if ( pObj )
3653         objectList.push_back( pObj );
3654   }
3655
3656   if ( objectList.empty() )
3657      return;
3658
3659   Point3F centroid;
3660   MatrixF orientation;
3661
3662   if ( objectList.size() == 1 )
3663   {
3664      orientation = objectList[0]->getTransform();
3665      centroid = objectList[0]->getPosition();
3666   }
3667   else
3668   {
3669      orientation.identity();
3670      centroid.zero();
3671
3672      S32 count = 0;
3673
3674      for ( S32 i = 0; i < objectList.size(); i++ )      
3675      {
3676         SceneObject *pObj = objectList[i];
3677         if ( pObj->isGlobalBounds() )
3678            continue;
3679
3680         centroid += pObj->getPosition();
3681         count++;
3682      }
3683            
3684      centroid /= count;
3685   }
3686
3687   orientation.setPosition( centroid );
3688   orientation.inverse();
3689
3690   OptimizedPolyList polyList;
3691   polyList.setBaseTransform( orientation );
3692
3693   for ( S32 i = 0; i < objectList.size(); i++ )
3694   {
3695      SceneObject *pObj = objectList[i];
3696      if ( !pObj->buildPolyList( PLC_Export, &polyList, pObj->getWorldBox(), pObj->getWorldSphere() ) )      
3697         Con::warnf( "colladaExportObjectList() - object %i returned no geometry.", pObj->getId() );               
3698   }
3699
3700   // Use a ColladaUtils function to do the actual export to a Collada file
3701   ColladaUtils::exportToCollada( path, polyList );
3702
3703#endif
3704}
3705
3706DefineEngineMethod( WorldEditor, colladaExportSelection, void, ( const char* path ),,
3707   "Export the combined geometry of all selected objects to the specified path in collada format."
3708   "@param path Path to export collada format to.")
3709{
3710   object->colladaExportSelection( path );
3711}
3712
3713void WorldEditor::makeSelectionPrefab( const char *filename, bool dontReplaceOriginals )
3714{
3715   if ( mSelected->size() == 0 )
3716   {
3717      Con::errorf( "WorldEditor::makeSelectionPrefab - Nothing selected." );
3718      return;
3719   }
3720
3721   Scene* scene = Scene::getRootScene();
3722   if ( !scene)
3723   {
3724      Con::errorf( "WorldEditor::makeSelectionPrefab - Could not find root Scene." );
3725      return;
3726   }
3727
3728   Vector< SimObject*> stack;
3729   Vector< SimObject*> found;
3730
3731   for ( S32 i = 0; i < mSelected->size(); i++ )
3732   {
3733      SimObject *obj = ( *mSelected )[i];
3734      stack.push_back( obj );      
3735   }
3736
3737   Vector< SimGroup*> cleanup;
3738
3739   while ( !stack.empty() )
3740   {
3741      SimObject *obj = stack.last();
3742      SimGroup *grp = dynamic_cast< SimGroup* >( obj );      
3743
3744      stack.pop_back();
3745
3746      if ( grp )
3747      {
3748         for ( S32 i = 0; i < grp->size(); i++ )
3749            stack.push_back( grp->at(i) );
3750         
3751         SceneObject* scn = dynamic_cast< SceneObject* >(grp);
3752         if (scn)
3753         {
3754            if (Prefab::isValidChild(obj, true))
3755               found.push_back(obj);
3756         }
3757         else
3758         {
3759            //Only push the cleanup of the group if it's ONLY a SimGroup.
3760         cleanup.push_back( grp );
3761         }
3762      }
3763      else
3764      {
3765         if ( Prefab::isValidChild( obj, true ) )
3766            found.push_back( obj );
3767      }
3768   }
3769
3770   if ( found.empty() )
3771   {
3772      Con::warnf( "WorldEditor::makeSelectionPrefab - No valid objects selected." );      
3773      return;
3774   }
3775   
3776   // SimGroup we collect prefab objects into.
3777   SimGroup *group = new SimGroup();
3778   group->registerObject();
3779
3780   // Transform from World to Prefab space.
3781   MatrixF fabMat(true);
3782   fabMat.setPosition( mSelected->getCentroid() );
3783
3784   MatrixF objMat;
3785   SimObject *obj = NULL;
3786   SceneObject *sObj = NULL;
3787
3788   F32 maxLen = 0;
3789   for (S32 i = 0; i < found.size(); i++)
3790   {
3791      obj = found[i];
3792      sObj = dynamic_cast<SceneObject*>(obj);
3793      if (sObj && !(sObj->isGlobalBounds()))
3794      {
3795         if (maxLen < sObj->getWorldBox().len_max())
3796         {
3797            maxLen = sObj->getWorldBox().len_max();
3798            fabMat.setPosition(sObj->getPosition());
3799         }
3800      }
3801   }
3802   fabMat.inverse();
3803
3804
3805   for ( S32 i = 0; i < found.size(); i++ )
3806   {      
3807      obj = found[i];
3808      sObj = dynamic_cast< SceneObject* >( obj );
3809
3810      obj->assignName( "" );
3811
3812      if ( sObj )
3813      {         
3814         objMat.mul( fabMat, sObj->getTransform() );
3815         sObj->setTransform( objMat );
3816      }
3817
3818      group->addObject( obj );         
3819   }
3820   
3821   // Save out .prefab file.
3822   group->save( filename, false, "$ThisPrefab = " );
3823
3824   if (!dontReplaceOriginals)
3825   {
3826      // Allocate Prefab object and add to level.
3827      Prefab* fab = new Prefab();
3828      fab->setFile(filename);
3829      fabMat.inverse();
3830      fab->setTransform(fabMat);
3831      fab->registerObject();
3832      scene->addObject(fab);
3833
3834      // Select it, mark level as dirty.
3835      clearSelection();
3836      selectObject(fab);
3837      setDirty();
3838
3839      // Delete original objects and temporary SimGroup.
3840      group->deleteObject();
3841      for (S32 i = 0; i < cleanup.size(); i++)
3842         cleanup[i]->deleteObject();
3843   }
3844}
3845
3846void WorldEditor::explodeSelectedPrefab()
3847{
3848   Vector<Prefab*> prefabList;
3849
3850   for ( S32 i = 0; i < mSelected->size(); i++ )
3851   {
3852      Prefab *obj = dynamic_cast<Prefab*>( ( *mSelected )[i] );
3853      if ( obj )
3854         prefabList.push_back(obj);
3855   }
3856
3857   if ( prefabList.empty() )
3858      return;
3859
3860   UndoManager *undoMan = NULL;
3861   if ( !Sim::findObject( "EUndoManager", undoMan ) )
3862   {
3863      Con::errorf( "WorldEditor::createUndo() - EUndoManager not found!" );
3864      return;           
3865   }
3866
3867   CompoundUndoAction *action = new CompoundUndoAction("Explode Prefab");
3868
3869   clearSelection();
3870
3871   for ( S32 i = 0; i < prefabList.size(); i++ )   
3872   {
3873      Prefab *prefab = prefabList[i];      
3874
3875      ExplodePrefabUndoAction *explodeAction = new ExplodePrefabUndoAction(prefab);
3876      action->addAction( explodeAction );      
3877
3878      selectObject( explodeAction->mGroup );
3879
3880      MEDeleteUndoAction *delAction = new MEDeleteUndoAction();
3881      delAction->deleteObject( prefab );
3882
3883      action->addAction( delAction );
3884   }
3885
3886   undoMan->addAction( action );   
3887
3888   setDirty();
3889}
3890
3891bool WorldEditor::makeSelectionAMesh(const char *filename)
3892{
3893   if (mSelected->size() == 0)
3894   {
3895      Con::errorf("WorldEditor::makeSelectionAMesh - Nothing selected.");
3896      return false;
3897   }
3898
3899   Scene* scene = Scene::getRootScene();
3900   if (!scene)
3901   {
3902      Con::errorf("WorldEditor::makeSelectionAMesh - Could not find root Scene.");
3903      return false;
3904   }
3905
3906   Vector< SimObject*> stack;
3907   Vector< SimObject*> found;
3908
3909   for (S32 i = 0; i < mSelected->size(); i++)
3910   {
3911      SimObject *obj = (*mSelected)[i];
3912      stack.push_back(obj);
3913   }
3914
3915   Vector< SimGroup*> cleanup;
3916
3917   while (!stack.empty())
3918   {
3919      SimObject *obj = stack.last();
3920      SimGroup *grp = dynamic_cast< SimGroup* >(obj);
3921
3922      stack.pop_back();
3923
3924      if (grp)
3925      {
3926         for (S32 i = 0; i < grp->size(); i++)
3927            stack.push_back(grp->at(i));
3928
3929         SceneObject* scn = dynamic_cast< SceneObject* >(grp);
3930         if (scn)
3931         {
3932            if (Prefab::isValidChild(obj, true))
3933               found.push_back(obj);
3934         }
3935         else
3936         {
3937            //Only push the cleanup of the group if it's ONLY a SimGroup.
3938            cleanup.push_back(grp);
3939         }
3940      }
3941      else
3942      {
3943         if (Prefab::isValidChild(obj, true))
3944            found.push_back(obj);
3945      }
3946   }
3947
3948   if (found.empty())
3949   {
3950      Con::warnf("WorldEditor::makeSelectionPrefab - No valid objects selected.");
3951      return false;
3952   }
3953
3954   // SimGroup we collect prefab objects into.
3955   SimGroup *group = new SimGroup();
3956   group->registerObject();
3957
3958   // Transform from World to Prefab space.
3959   MatrixF fabMat(true);
3960   fabMat.setPosition(mSelected->getCentroid());
3961   fabMat.inverse();
3962
3963   MatrixF objMat;
3964
3965   Vector< SceneObject*> objectList;
3966
3967   for ( S32 i = 0; i < mSelected->size(); i++ )
3968   {
3969      SceneObject *pObj = dynamic_cast< SceneObject* >( ( *mSelected )[i] );
3970      if (pObj)
3971      {
3972         //Minor sanity check to avoid baking animated shapes
3973         TSStatic* staticShape = dynamic_cast<TSStatic*>(pObj);
3974         if (staticShape)
3975         {
3976            if (staticShape->isAnimated() && staticShape->hasAnim())
3977               continue;
3978         }
3979
3980         objectList.push_back(pObj);
3981      }
3982   }
3983
3984   if ( objectList.empty() )
3985      return false;
3986
3987   //
3988   Point3F centroid;
3989   MatrixF orientation;
3990
3991   if (objectList.size() == 1)
3992   {
3993      orientation = objectList[0]->getTransform();
3994      centroid = objectList[0]->getPosition();
3995   }
3996   else
3997   {
3998      SimObject* obj = NULL;
3999      SceneObject* sObj = NULL;
4000      F32 maxLen = 0;
4001      for (S32 i = 0; i < found.size(); i++)
4002      {
4003         obj = found[i];
4004         sObj = dynamic_cast<SceneObject*>(obj);
4005         if (sObj && !(sObj->isGlobalBounds()))
4006         {
4007            if (maxLen < sObj->getWorldBox().len_max())
4008            {
4009               maxLen = sObj->getWorldBox().len_max();
4010               orientation = sObj->getTransform();
4011               centroid = sObj->getPosition();
4012            }
4013         }
4014      }
4015   }
4016
4017   orientation.setPosition(centroid);
4018   orientation.inverse();
4019
4020   OptimizedPolyList polyList;
4021   polyList.setBaseTransform(orientation);
4022
4023   ColladaUtils::ExportData exportData;
4024
4025   for (S32 i = 0; i < objectList.size(); i++)
4026   {
4027      SceneObject *pObj = objectList[i];
4028
4029      if (!pObj->buildExportPolyList(&exportData, pObj->getWorldBox(), pObj->getWorldSphere()))
4030         Con::warnf("colladaExportObjectList() - object %i returned no geometry.", pObj->getId());
4031   }
4032
4033   //Now that we have all of our mesh data, process it so we can correctly collapse everything.
4034   exportData.processData();
4035
4036   //recenter generated visual mesh results
4037   for (U32 dl = 0; dl < exportData.colMeshes.size(); dl++)
4038   {
4039      for (U32 pnt = 0; pnt < exportData.colMeshes[dl].mesh.mPoints.size(); pnt++)
4040      {
4041         exportData.colMeshes[dl].mesh.mPoints[pnt] -= centroid;
4042      }
4043   }
4044
4045   //recenter generated collision mesh results
4046   for (U32 dl = 0; dl < exportData.detailLevels.size(); dl++)
4047   {
4048      for (U32 pnt = 0; pnt < exportData.detailLevels[dl].mesh.mPoints.size(); pnt++)
4049      {
4050         exportData.detailLevels[dl].mesh.mPoints[pnt] -= centroid;
4051      }
4052   }
4053
4054   // Use a ColladaUtils function to do the actual export to a Collada file
4055   ColladaUtils::exportToCollada(filename, exportData);
4056   //
4057
4058   if (Platform::isFile(filename))
4059      return true;
4060   else
4061      return false;
4062}
4063
4064DefineEngineMethod( WorldEditor, makeSelectionPrefab, void, ( const char* filename, bool dontDeleteOriginals ), (false),
4065   "Save selected objects to a .prefab file and replace them in the level with a Prefab object."
4066   "@param filename Prefab file to save the selected objects to.")
4067{
4068   object->makeSelectionPrefab( filename, dontDeleteOriginals);
4069}
4070
4071DefineEngineMethod( WorldEditor, explodeSelectedPrefab, void, (),,
4072   "Replace selected Prefab objects with a SimGroup containing all children objects defined in the .prefab.")
4073{
4074   object->explodeSelectedPrefab();
4075}
4076
4077DefineEngineMethod(WorldEditor, makeSelectionAMesh, bool, (const char* filename), ,
4078   "Save selected objects to a .dae collada file and replace them in the level with a TSStatic object."
4079   "@param filename collada file to save the selected objects to.")
4080{
4081   return object->makeSelectionAMesh(filename);
4082}
4083
4084DefineEngineMethod( WorldEditor, mountRelative, void, ( SceneObject *objA, SceneObject *objB ),,
4085   "Mount object B relatively to object A."
4086   "@param objA Object to mount to."
4087   "@param objB Object to mount.")
4088{
4089   if (!objA || !objB)
4090      return;
4091
4092   MatrixF xfm = objB->getTransform();   
4093   MatrixF mat = objA->getWorldTransform();
4094   xfm.mul( mat );
4095   
4096   Point3F pos = objB->getPosition();
4097   MatrixF temp = objA->getTransform();
4098   temp.scale( objA->getScale() );
4099   temp.inverse();
4100   temp.mulP( pos );
4101   
4102   xfm.setPosition( pos );
4103   
4104
4105   objA->mountObject( objB, -1, xfm );
4106}
4107
4108//-----------------------------------------------------------------------------
4109
4110DefineEngineMethod( WorldEditor, createPolyhedralObject, SceneObject*, ( const char* className, SceneObject* geometryProvider ),,
4111   "Grab the geometry from @a geometryProvider, create a @a className object, and assign it the extracted geometry." )
4112{
4113   if( !geometryProvider )
4114   {
4115      Con::errorf( "WorldEditor::createPolyhedralObject - Invalid geometry provider!" );
4116      return NULL;
4117   }
4118
4119   if( !className || !className[ 0 ] )
4120   {
4121      Con::errorf( "WorldEditor::createPolyhedralObject - Invalid class name" );
4122      return NULL;
4123   }
4124
4125   AbstractClassRep* classRep = AbstractClassRep::findClassRep( className );
4126   if( !classRep )
4127   {
4128      Con::errorf( "WorldEditor::createPolyhedralObject - No such class: %s", className );
4129      return NULL;
4130   }
4131
4132   // We don't want the extracted poly list to be affected by the object's
4133   // current transform and scale so temporarily reset them.
4134
4135   MatrixF savedTransform = geometryProvider->getTransform();
4136   Point3F savedScale = geometryProvider->getScale();
4137
4138   geometryProvider->setTransform( MatrixF::Identity );
4139   geometryProvider->setScale( Point3F( 1.f, 1.f, 1.f ) );
4140
4141   // Extract the geometry.  Use the object-space bounding volumes
4142   // as we have moved the object to the origin for the moment.
4143
4144   OptimizedPolyList polyList;
4145   if( !geometryProvider->buildPolyList( PLC_Export, &polyList, geometryProvider->getObjBox(), geometryProvider->getObjBox().getBoundingSphere() ) )
4146   {
4147      Con::errorf( "WorldEditor::createPolyhedralObject - Failed to extract geometry!" );
4148      return NULL;
4149   }
4150
4151   // Restore the object's original transform.
4152
4153   geometryProvider->setTransform( savedTransform );
4154   geometryProvider->setScale( savedScale );
4155
4156   // Create the object.
4157
4158   SceneObject* polyObj = dynamic_cast< SceneObject* >( classRep->create() );
4159   if( !polyObj)
4160   {
4161      Con::errorf( "WorldEditor::createPolyhedralObject - Could not create SceneObject with class '%s'", className );
4162      return NULL;
4163   }
4164
4165   // Convert the polylist to a polyhedron.
4166
4167   Polyhedron polyhedron = polyList.toPolyhedron();
4168
4169   // Add the vertex data.
4170
4171   const U32 numPoints = polyhedron.getNumPoints();
4172   const Point3F* points = polyhedron.getPoints();
4173
4174   for( U32 i = 0; i < numPoints; ++ i )
4175   {
4176      static StringTableEntry sPoint = StringTable->insert( "point" );
4177     polyObj->setDataField( sPoint, NULL, EngineMarshallData( points[ i ] ) );
4178   }
4179
4180   // Add the plane data.
4181
4182   const U32 numPlanes = polyhedron.getNumPlanes();
4183   const PlaneF* planes = polyhedron.getPlanes();
4184
4185   for( U32 i = 0; i < numPlanes; ++ i )
4186   {
4187      static StringTableEntry sPlane = StringTable->insert( "plane" );
4188      const PlaneF& plane = planes[ i ];
4189
4190      char buffer[ 1024 ];
4191      dSprintf( buffer, sizeof( buffer ), "%g %g %g %g", plane.x, plane.y, plane.z, plane.d );
4192
4193     polyObj->setDataField( sPlane, NULL, buffer );
4194   }
4195
4196   // Add the edge data.
4197
4198   const U32 numEdges = polyhedron.getNumEdges();
4199   const Polyhedron::Edge* edges = polyhedron.getEdges();
4200
4201   for( U32 i = 0; i < numEdges; ++ i )
4202   {
4203      static StringTableEntry sEdge = StringTable->insert( "edge" );
4204      const Polyhedron::Edge& edge = edges[ i ];
4205
4206      char buffer[ 1024 ];
4207      dSprintf( buffer, sizeof( buffer ), "%i %i %i %i ",
4208         edge.face[ 0 ], edge.face[ 1 ],
4209         edge.vertex[ 0 ], edge.vertex[ 1 ]
4210      );
4211
4212     polyObj->setDataField( sEdge, NULL, buffer );
4213   }
4214
4215   // Set the transform.
4216
4217   polyObj->setTransform( savedTransform );
4218   polyObj->setScale( savedScale );
4219
4220   // Register and return the object.
4221
4222   if( !polyObj->registerObject() )
4223   {
4224      Con::errorf( "WorldEditor::createPolyhedralObject - Failed to register object!" );
4225      delete polyObj;
4226      return NULL;
4227   }
4228
4229   return polyObj;
4230}
4231
4232//-----------------------------------------------------------------------------
4233
4234DefineEngineMethod( WorldEditor, createConvexShapeFrom, ConvexShape*, ( SceneObject* polyObject ),,
4235   "Create a ConvexShape from the given polyhedral object." )
4236{
4237   if( !polyObject )
4238   {
4239      Con::errorf( "WorldEditor::createConvexShapeFrom - Invalid object" );
4240      return NULL;
4241   }
4242
4243   IScenePolyhedralObject* iPoly = dynamic_cast< IScenePolyhedralObject* >( polyObject );
4244   if( !iPoly )
4245   {
4246      Con::errorf( "WorldEditor::createConvexShapeFrom - Not a polyhedral object!" );
4247      return NULL;
4248   }
4249
4250   // Get polyhedron.
4251
4252   AnyPolyhedron polyhedron = iPoly->ToAnyPolyhedron();
4253   const U32 numPlanes = polyhedron.getNumPlanes();
4254   if( !numPlanes )
4255   {
4256      Con::errorf( "WorldEditor::createConvexShapeFrom - Object returned no valid polyhedron" );
4257      return NULL;
4258   }
4259
4260   // Create a ConvexShape.
4261
4262   ConvexShape* shape = new ConvexShape();
4263
4264   // Add all planes.
4265
4266   for( U32 i = 0; i < numPlanes; ++ i )
4267   {
4268      const PlaneF& plane = polyhedron.getPlanes()[ i ];
4269
4270      // Polyhedron planes are facing inwards so we need to
4271      // invert the normal here.
4272
4273      Point3F normal = plane.getNormal();
4274      normal.neg();
4275
4276      // Turn the orientation of the plane into a quaternion.
4277      // The normal is our up vector (that's what's expected
4278      // by ConvexShape for the surface orientation).
4279
4280      MatrixF orientation( true );
4281      MathUtils::getMatrixFromUpVector( normal, &orientation );
4282      const QuatF quat( orientation );
4283
4284      // Get the plane position.
4285
4286      const Point3F position = plane.getPosition();
4287
4288      // Turn everything into a "surface" property for the ConvexShape.
4289
4290      char buffer[ 1024 ];
4291      dSprintf( buffer, sizeof( buffer ), "%g %g %g %g %g %g %g",
4292         quat.x, quat.y, quat.z, quat.w,
4293         position.x, position.y, position.z
4294      );
4295
4296      // Add the surface.
4297
4298      static StringTableEntry sSurface = StringTable->insert( "surface" );
4299      shape->setDataField( sSurface, NULL, buffer );
4300   }
4301
4302   // Copy the transform.
4303
4304   shape->setTransform( polyObject->getTransform() );
4305   shape->setScale( polyObject->getScale() );
4306
4307   // Register the shape.
4308
4309   if( !shape->registerObject() )
4310   {
4311      Con::errorf( "WorldEditor::createConvexShapeFrom - Could not register ConvexShape!" );
4312      delete shape;
4313      return NULL;
4314   }
4315
4316   return shape;
4317}
4318
4319DefineEngineMethod(WorldEditor, setEditorTool, void, (EditorTool* newEditorTool), (nullAsType<EditorTool*>()),
4320   "Sets the active Editor Tool for the world editor.")
4321{
4322   object->setEditorTool(newEditorTool);
4323}
4324
4325DefineEngineMethod(WorldEditor, getActiveEditorTool, EditorTool*, (),,
4326   "Gets the active Editor Tool for the world editor.")
4327{
4328   return object->getActiveEditorTool();
4329}
4330