forestSelectionTool.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platform/platform.h"
  23. #include "forest/editor/forestSelectionTool.h"
  24. #include "forest/forest.h"
  25. #include "forest/editor/forestUndo.h"
  26. #include "forest/editor/forestEditorCtrl.h"
  27. #include "gui/worldEditor/editTSCtrl.h"
  28. #include "gui/worldEditor/gizmo.h"
  29. #include "console/consoleTypes.h"
  30. #include "console/engineAPI.h"
  31. #include "core/util/tVector.h"
  32. #include "core/util/safeDelete.h"
  33. #include "gfx/gfxDrawUtil.h"
  34. #include "gui/worldEditor/worldEditor.h"
  35. #include "math/mMatrix.h"
  36. template <>
  37. MatrixF Selection<ForestItem>::getOrientation()
  38. {
  39. if ( size() == 1 )
  40. return first().getTransform();
  41. return MatrixF::Identity;
  42. }
  43. template <>
  44. Point3F Selection<ForestItem>::getOrigin()
  45. {
  46. Point3F centroid( Point3F::Zero );
  47. if ( empty() )
  48. return centroid;
  49. Selection<ForestItem>::iterator itr = begin();
  50. for (; itr != end(); ++itr)
  51. {
  52. const MatrixF &mat = itr->getTransform();
  53. Point3F wPos;
  54. mat.getColumn( 3, &wPos );
  55. centroid += wPos;
  56. }
  57. centroid /= (F32)size();
  58. return centroid;
  59. }
  60. template <>
  61. Point3F Selection<ForestItem>::getScale()
  62. {
  63. if ( size() == 1 )
  64. return Point3F( first().getScale() );
  65. return Point3F::One;
  66. }
  67. void ForestItemSelection::offsetObject( ForestItem &object, const Point3F &delta )
  68. {
  69. if ( !mData )
  70. return;
  71. MatrixF newMat( object.getTransform() );
  72. newMat.displace( delta );
  73. object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), newMat, object.getScale() );
  74. }
  75. void ForestItemSelection::rotateObject( ForestItem &object, const EulerF &delta, const Point3F &origin )
  76. {
  77. if ( !mData )
  78. return;
  79. MatrixF mat = object.getTransform();
  80. Point3F pos;
  81. mat.getColumn( 3, &pos );
  82. MatrixF wMat( mat );
  83. wMat.inverse();
  84. // get offset in obj space
  85. Point3F offset = pos - origin;
  86. if ( size() == 1 )
  87. {
  88. wMat.mulV(offset);
  89. MatrixF transform(EulerF::Zero, -offset);
  90. transform.mul(MatrixF(delta));
  91. transform.mul(MatrixF(EulerF::Zero, offset));
  92. mat.mul(transform);
  93. }
  94. else
  95. {
  96. MatrixF transform( delta );
  97. Point3F wOffset;
  98. transform.mulV( offset, &wOffset );
  99. wMat.mulV( offset );
  100. transform.set( EulerF::Zero, -offset );
  101. mat.setColumn( 3, Point3F::Zero );
  102. wMat.setColumn( 3, Point3F::Zero );
  103. transform.mul( wMat );
  104. transform.mul( MatrixF(delta) );
  105. transform.mul( mat );
  106. mat.mul( transform );
  107. mat.normalize();
  108. mat.setColumn( 3, wOffset + origin );
  109. }
  110. object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), mat, object.getScale() );
  111. }
  112. void ForestItemSelection::scaleObject( ForestItem &object, const Point3F &delta )
  113. {
  114. if ( !mData )
  115. return;
  116. object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), object.getTransform(), delta.z );
  117. }
  118. IMPLEMENT_CONOBJECT( ForestSelectionTool );
  119. ConsoleDocClass( ForestSelectionTool,
  120. "@brief Specialized selection tool for picking individual trees in a forest.\n\n"
  121. "Editor use only.\n\n"
  122. "@internal"
  123. );
  124. ForestSelectionTool::ForestSelectionTool()
  125. : Parent(),
  126. mGizmo( NULL ),
  127. mCurrAction( NULL ),
  128. mGizmoProfile( NULL ),
  129. mMouseDragged(false),
  130. mUsingGizmo(false)
  131. {
  132. mBounds = Box3F::Invalid;
  133. mDragRectColor.set(255,255,0);
  134. mMouseDown = false;
  135. mDragSelect = false;
  136. }
  137. ForestSelectionTool::~ForestSelectionTool()
  138. {
  139. SAFE_DELETE( mCurrAction );
  140. }
  141. void ForestSelectionTool::setParentEditor( ForestEditorCtrl *editor )
  142. {
  143. mEditor = editor;
  144. mGizmo = editor->getGizmo();
  145. mGizmoProfile = mGizmo->getProfile();
  146. }
  147. void ForestSelectionTool::_selectItem( const ForestItem &item )
  148. {
  149. // Make sure its not already selected.
  150. for ( U32 i=0; i < mSelection.size(); i++ )
  151. {
  152. if ( mSelection[i].getKey() == item.getKey() )
  153. return;
  154. }
  155. mSelection.push_back( item );
  156. mBounds.intersect( item.getWorldBox() );
  157. }
  158. void ForestSelectionTool::deleteSelection()
  159. {
  160. if (!mEditor)
  161. return;
  162. ForestDeleteUndoAction *action = new ForestDeleteUndoAction( mForest->getData(), mEditor );
  163. for ( U32 i=0; i < mSelection.size(); i++ )
  164. {
  165. const ForestItem &item = mSelection[i];
  166. action->removeItem( item );
  167. }
  168. clearSelection();
  169. _submitUndo( action );
  170. }
  171. void ForestSelectionTool::clearSelection()
  172. {
  173. mSelection.clear();
  174. mBounds = Box3F::Invalid;
  175. }
  176. void ForestSelectionTool::cutSelection()
  177. {
  178. }
  179. void ForestSelectionTool::copySelection()
  180. {
  181. }
  182. void ForestSelectionTool::pasteSelection()
  183. {
  184. }
  185. void ForestSelectionTool::setActiveForest( Forest *forest )
  186. {
  187. mForest = forest;
  188. if ( forest )
  189. mSelection.setForestData( forest->getData() );
  190. else
  191. mSelection.setForestData( NULL );
  192. }
  193. void ForestSelectionTool::on3DMouseDown( const Gui3DMouseEvent &evt )
  194. {
  195. mMouseDown = true;
  196. mMouseDragged = false;
  197. mUsingGizmo = !mSelection.empty() && mGizmo->getSelection() != Gizmo::None;
  198. if ( mUsingGizmo )
  199. {
  200. mGizmo->on3DMouseDown( evt );
  201. return;
  202. }
  203. mDragSelection.clear();
  204. mDragRect.set( Point2I(evt.mousePoint), Point2I(0,0) );
  205. mDragStart = evt.mousePoint;
  206. const bool multiSel = evt.modifier & SI_CTRL;
  207. if ( !multiSel )
  208. clearSelection();
  209. if ( mHoverItem.isValid() )
  210. _selectItem( mHoverItem );
  211. // This should never happen... it should have been
  212. // submitted and nulled in on3DMouseUp()!
  213. //
  214. // Yeah, unless you had a breakpoint there and on3DMouseUp never fired,
  215. // in which case this is really annoying.
  216. //
  217. //AssertFatal( !mCurrAction, "ForestSelectionTool::on3DMouseDown() - Dangling undo action!" );
  218. }
  219. void ForestSelectionTool::on3DMouseMove( const Gui3DMouseEvent &evt )
  220. {
  221. // Invalidate the hover item first... we're gonna find a new one.
  222. mHoverItem.makeInvalid();
  223. if ( !mForest )
  224. return;
  225. if ( !mSelection.empty() )
  226. mGizmo->on3DMouseMove( evt );
  227. RayInfo ri;
  228. ri.userData = new ForestItem;
  229. Point3F startPnt = evt.pos;
  230. Point3F endPnt = evt.pos + evt.vec * 1000.0f;
  231. if ( mForest->castRayRendered( startPnt, endPnt, &ri ) )
  232. mHoverItem = (*(ForestItem*)ri.userData);
  233. delete static_cast<ForestItem*>(ri.userData);
  234. }
  235. void ForestSelectionTool::on3DMouseDragged( const Gui3DMouseEvent &evt )
  236. {
  237. mHoverItem.makeInvalid();
  238. if ( mUsingGizmo )
  239. {
  240. mGizmo->on3DMouseDragged( evt );
  241. const Point3F &deltaRot = mGizmo->getDeltaRot();
  242. const Point3F &deltaPos = mGizmo->getOffset();
  243. const Point3F &deltaScale = mGizmo->getDeltaScale();
  244. if ( deltaRot.isZero() && deltaPos.isZero() && deltaScale.isZero() )
  245. return;
  246. // Store the current item states!
  247. if ( !mCurrAction )
  248. {
  249. mCurrAction = new ForestUpdateAction( mForest->getData(), mEditor );
  250. for ( U32 i=0; i < mSelection.size(); i++ )
  251. {
  252. const ForestItem &item = mSelection[i];
  253. mCurrAction->saveItem( item );
  254. }
  255. }
  256. switch ( mGizmo->getMode() )
  257. {
  258. case MoveMode:
  259. mSelection.offset( deltaPos ); break;
  260. case RotateMode:
  261. mSelection.rotate( deltaRot ); break;
  262. case ScaleMode:
  263. mSelection.scale( mGizmo->getScale() ); break;
  264. default: ;
  265. }
  266. return;
  267. }
  268. mDragSelect = true;
  269. mHoverItem.makeInvalid();
  270. // Doing a drag selection.
  271. if ( mDragSelect )
  272. {
  273. // build the drag selection on the renderScene method - make sure no neg extent!
  274. mDragRect.point.x = (evt.mousePoint.x < mDragStart.x) ? evt.mousePoint.x : mDragStart.x;
  275. mDragRect.extent.x = (evt.mousePoint.x > mDragStart.x) ? evt.mousePoint.x - mDragStart.x : mDragStart.x - evt.mousePoint.x;
  276. mDragRect.point.y = (evt.mousePoint.y < mDragStart.y) ? evt.mousePoint.y : mDragStart.y;
  277. mDragRect.extent.y = (evt.mousePoint.y > mDragStart.y) ? evt.mousePoint.y - mDragStart.y : mDragStart.y - evt.mousePoint.y;
  278. return;
  279. }
  280. }
  281. void ForestSelectionTool::on3DMouseUp( const Gui3DMouseEvent &evt )
  282. {
  283. mGizmo->on3DMouseUp( evt );
  284. mMouseDown = false;
  285. // If we have an undo action then we must have
  286. // moved, rotated, or scaled something.
  287. if ( mCurrAction )
  288. {
  289. _submitUndo( mCurrAction );
  290. mCurrAction = NULL;
  291. return;
  292. }
  293. // If we were performing a drag select, finalize it now.
  294. if ( mDragSelect )
  295. {
  296. mDragSelect = false;
  297. clearSelection();
  298. for ( S32 i = 0; i < mDragSelection.size(); i++ )
  299. _selectItem( mDragSelection[i] );
  300. mDragSelection.clear();
  301. return;
  302. }
  303. }
  304. void ForestSelectionTool::onRender3D()
  305. {
  306. GFXDrawUtil *drawUtil = GFX->getDrawUtil();
  307. ColorI color( 255, 255, 255, 255 );
  308. MatrixF treeMat;
  309. GFXStateBlockDesc desc;
  310. desc.setBlend( true );
  311. desc.setZReadWrite( true, false );
  312. if ( mHoverItem.isValid() )
  313. {
  314. treeMat = mHoverItem.getTransform();
  315. drawUtil->drawObjectBox( desc, mHoverItem.getSize(), mHoverItem.getWorldBox().getCenter(), treeMat, color );
  316. }
  317. if ( !mSelection.empty() )
  318. {
  319. for ( U32 i = 0; i < mSelection.size(); i++ )
  320. {
  321. const ForestItem &item = mSelection[i];
  322. treeMat = item.getTransform();
  323. drawUtil->drawObjectBox( desc, item.getSize(), item.getWorldBox().getCenter(), treeMat, color );
  324. }
  325. mGizmo->set( mSelection.getOrientation(), mSelection.getOrigin(), mSelection.getScale() );
  326. mGizmo->renderGizmo( mEditor->getLastCameraQuery().cameraMatrix, mEditor->getLastCameraQuery().fov );
  327. }
  328. }
  329. static Frustum gDragFrustum;
  330. void ForestSelectionTool::onRender2D()
  331. {
  332. // Draw drag selection rect.
  333. if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 )
  334. GFX->getDrawUtil()->drawRect( mDragRect, mDragRectColor );
  335. // update what is in the selection
  336. if ( mDragSelect )
  337. mDragSelection.clear();
  338. // Determine selected objects based on the drag box touching
  339. // a mesh if a drag operation has begun.
  340. if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 )
  341. {
  342. // Build the drag frustum based on the rect
  343. F32 wwidth;
  344. F32 wheight;
  345. F32 aspectRatio = F32(mEditor->getWidth()) / F32(mEditor->getHeight());
  346. const CameraQuery &lastCameraQuery = mEditor->getLastCameraQuery();
  347. if(!lastCameraQuery.ortho)
  348. {
  349. wheight = lastCameraQuery.nearPlane * mTan(lastCameraQuery.fov / 2);
  350. wwidth = aspectRatio * wheight;
  351. }
  352. else
  353. {
  354. wheight = lastCameraQuery.fov;
  355. wwidth = aspectRatio * wheight;
  356. }
  357. F32 hscale = wwidth * 2 / F32(mEditor->getWidth());
  358. F32 vscale = wheight * 2 / F32(mEditor->getHeight());
  359. Point2I editorPosition = mEditor->getPosition();
  360. F32 left = (mDragRect.point.x - editorPosition.x) * hscale - wwidth;
  361. F32 right = (mDragRect.point.x - editorPosition.x + mDragRect.extent.x) * hscale - wwidth;
  362. F32 top = wheight - vscale * (mDragRect.point.y - editorPosition.y);
  363. F32 bottom = wheight - vscale * (mDragRect.point.y - editorPosition.y + mDragRect.extent.y);
  364. gDragFrustum.set(lastCameraQuery.ortho, left, right, top, bottom, lastCameraQuery.nearPlane, lastCameraQuery.farPlane, lastCameraQuery.cameraMatrix );
  365. mForest->getData()->getItems( gDragFrustum, &mDragSelection );
  366. }
  367. }
  368. bool ForestSelectionTool::updateGuiInfo()
  369. {
  370. SimObject *statusbar;
  371. if ( !Sim::findObject( "EditorGuiStatusBar", statusbar ) )
  372. return false;
  373. String text( "Forest Editor." );
  374. GizmoMode mode = mGizmoProfile->mode;
  375. if ( mMouseDown && mGizmo->getSelection() != Gizmo::None )
  376. {
  377. Point3F delta;
  378. String qualifier;
  379. if ( mode == RotateMode )
  380. delta = mGizmo->getDeltaTotalRot();
  381. else if ( mode == MoveMode )
  382. delta = mGizmo->getTotalOffset();
  383. else if ( mode == ScaleMode )
  384. delta = mGizmo->getDeltaTotalScale();
  385. if ( mGizmo->getAlignment() == Object && mode != ScaleMode )
  386. {
  387. mSelection.getOrientation().mulV( delta );
  388. }
  389. if ( mIsZero( delta.x, 0.0001f ) )
  390. delta.x = 0.0f;
  391. if ( mIsZero( delta.y, 0.0001f ) )
  392. delta.y = 0.0f;
  393. if ( mIsZero( delta.z, 0.0001f ) )
  394. delta.z = 0.0f;
  395. if ( mode == RotateMode )
  396. {
  397. delta.x = mRadToDeg( delta.x );
  398. delta.y = mRadToDeg( delta.y );
  399. delta.z = mRadToDeg( delta.z );
  400. text = String::ToString( "Delta angle ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z );
  401. }
  402. else if ( mode == MoveMode )
  403. text = String::ToString( "Delta position ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z );
  404. else if ( mode == ScaleMode )
  405. text = String::ToString( "Delta scale ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z );
  406. }
  407. else
  408. {
  409. if ( mode == MoveMode )
  410. text = "Move selection. SHIFT while dragging duplicates objects.";
  411. else if ( mode == RotateMode )
  412. text = "Rotate selection.";
  413. else if ( mode == ScaleMode )
  414. text = "Scale selection.";
  415. }
  416. Con::executef( statusbar, "setInfo", text.c_str() );
  417. Con::executef( statusbar, "setSelectionObjectsByCount", mSelection.size() );
  418. return true;
  419. }
  420. void ForestSelectionTool::updateGizmo()
  421. {
  422. mGizmoProfile->restoreDefaultState();
  423. const GizmoMode &mode = mGizmoProfile->mode;
  424. if ( mode == ScaleMode )
  425. {
  426. mGizmoProfile->flags &= ~GizmoProfile::PlanarHandlesOn;
  427. mGizmoProfile->allAxesScaleUniform = true;
  428. }
  429. }
  430. void ForestSelectionTool::onUndoAction()
  431. {
  432. ForestData *data = mForest->getData();
  433. // Remove items from our selection that no longer exist.
  434. for ( S32 i = 0; i < mSelection.size(); i++ )
  435. {
  436. const ForestItem &item = data->findItem( mSelection[i].getKey(), mSelection[i].getPosition() );
  437. if ( item == ForestItem::Invalid )
  438. {
  439. mSelection.erase_fast( i );
  440. i--;
  441. }
  442. else
  443. mSelection[i] = item;
  444. }
  445. // Recalculate our selection bounds.
  446. mBounds = Box3F::Invalid;
  447. for ( S32 i = 0; i < mSelection.size(); i++ )
  448. mBounds.intersect( mSelection[i].getWorldBox() );
  449. }
  450. DefineEngineMethod( ForestSelectionTool, getSelectionCount, S32, (), , "" )
  451. {
  452. return object->getSelectionCount();
  453. }
  454. DefineEngineMethod( ForestSelectionTool, deleteSelection, void, (), , "" )
  455. {
  456. object->deleteSelection();
  457. }
  458. DefineEngineMethod( ForestSelectionTool, clearSelection, void, (), , "" )
  459. {
  460. object->clearSelection();
  461. }
  462. DefineEngineMethod( ForestSelectionTool, cutSelection, void, (), , "" )
  463. {
  464. object->cutSelection();
  465. }
  466. DefineEngineMethod( ForestSelectionTool, copySelection, void, (), , "" )
  467. {
  468. object->copySelection();
  469. }
  470. DefineEngineMethod( ForestSelectionTool, pasteSelection, void, (), , "" )
  471. {
  472. object->pasteSelection();
  473. }