worldEditor.cpp
Engine/source/gui/worldEditor/worldEditor.cpp
Classes:
class
Public Variables
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)
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