guiRoadEditorCtrl.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  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 "environment/editors/guiRoadEditorCtrl.h"
  24. #include "console/consoleTypes.h"
  25. #include "console/engineAPI.h"
  26. #include "scene/sceneManager.h"
  27. #include "collision/collision.h"
  28. #include "math/util/frustum.h"
  29. #include "gfx/gfxPrimitiveBuffer.h"
  30. #include "gfx/gfxTextureHandle.h"
  31. #include "gfx/gfxTransformSaver.h"
  32. #include "gfx/gfxDrawUtil.h"
  33. #include "gfx/primBuilder.h"
  34. #include "T3D/gameBase/gameConnection.h"
  35. #include "gui/core/guiCanvas.h"
  36. #include "gui/buttons/guiButtonCtrl.h"
  37. #include "gui/worldEditor/undoActions.h"
  38. #include "materials/materialDefinition.h"
  39. IMPLEMENT_CONOBJECT(GuiRoadEditorCtrl);
  40. ConsoleDocClass( GuiRoadEditorCtrl,
  41. "@brief GUI tool that makes up the Decal Road Editor\n\n"
  42. "Editor use only.\n\n"
  43. "@internal"
  44. );
  45. GuiRoadEditorCtrl::GuiRoadEditorCtrl()
  46. {
  47. // Each of the mode names directly correlates with the River Editor's
  48. // tool palette
  49. mSelectRoadMode = "RoadEditorSelectMode";
  50. mAddRoadMode = "RoadEditorAddRoadMode";
  51. mMovePointMode = "RoadEditorMoveMode";
  52. mScalePointMode = "RoadEditorScaleMode";
  53. mAddNodeMode = "RoadEditorAddNodeMode";
  54. mInsertPointMode = "RoadEditorInsertPointMode";
  55. mRemovePointMode = "RoadEditorRemovePointMode";
  56. mMode = mSelectRoadMode;
  57. mRoadSet = NULL;
  58. mSelNode = -1;
  59. mHoverNode = -1;
  60. mSelRoad = NULL;
  61. mHoverRoad = NULL;
  62. mAddNodeIdx = 0;
  63. mDefaultWidth = 10.0f;
  64. mInsertIdx = -1;
  65. mStartWidth = -1.0f;
  66. mStartX = 0;
  67. mNodeHalfSize.set(4,4);
  68. mHoverSplineColor.set( 255,0,0,255 );
  69. mSelectedSplineColor.set( 0,255,0,255 );
  70. mHoverNodeColor.set( 255,255,255,255 );
  71. mIsDirty = false;
  72. mMaterialName = StringTable->insert("DefaultDecalRoadMaterial");
  73. }
  74. GuiRoadEditorCtrl::~GuiRoadEditorCtrl()
  75. {
  76. // nothing to do
  77. }
  78. void GuiRoadEditorUndoAction::undo()
  79. {
  80. DecalRoad *road = NULL;
  81. if ( !Sim::findObject( mObjId, road ) )
  82. return;
  83. // Temporarily save the roads current data.
  84. String materialName = road->mMaterialName;
  85. F32 textureLength = road->mTextureLength;
  86. F32 breakAngle = road->mBreakAngle;
  87. F32 segmentsPerBatch = road->mSegmentsPerBatch;
  88. Vector<RoadNode> nodes;
  89. nodes.merge( road->mNodes );
  90. // Restore the Road properties saved in the UndoAction
  91. road->mMaterialName = materialName;
  92. road->mBreakAngle = breakAngle;
  93. road->mSegmentsPerBatch = segmentsPerBatch;
  94. road->mTextureLength = textureLength;
  95. road->inspectPostApply();
  96. // Restore the Nodes saved in the UndoAction
  97. road->mNodes.clear();
  98. for ( U32 i = 0; i < mNodes.size(); i++ )
  99. {
  100. road->_addNode( mNodes[i].point, mNodes[i].width );
  101. }
  102. // Regenerate the road
  103. road->regenerate();
  104. // If applicable set the selected road and node
  105. mRoadEditor->mSelRoad = road;
  106. mRoadEditor->mSelNode = -1;
  107. // Now save the previous Road data in this UndoAction
  108. // since an undo action must become a redo action and vice-versa
  109. mMaterialName = materialName;
  110. mBreakAngle = breakAngle;
  111. mSegmentsPerBatch = segmentsPerBatch;
  112. mTextureLength = textureLength;
  113. mNodes.clear();
  114. mNodes.merge( nodes );
  115. }
  116. bool GuiRoadEditorCtrl::onAdd()
  117. {
  118. if( !Parent::onAdd() )
  119. return false;
  120. mRoadSet = DecalRoad::getServerSet();
  121. GFXStateBlockDesc desc;
  122. desc.setCullMode( GFXCullNone );
  123. desc.setBlend(false);
  124. desc.setZReadWrite( false, false );
  125. mZDisableSB = GFX->createStateBlock(desc);
  126. return true;
  127. }
  128. void GuiRoadEditorCtrl::initPersistFields()
  129. {
  130. addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiRoadEditorCtrl ) );
  131. addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiRoadEditorCtrl ) );
  132. addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiRoadEditorCtrl ) );
  133. addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiRoadEditorCtrl ) );
  134. addField( "isDirty", TypeBool, Offset( mIsDirty, GuiRoadEditorCtrl ) );
  135. addField( "materialName", TypeString, Offset( mMaterialName, GuiRoadEditorCtrl ),
  136. "Default Material used by the Road Editor on road creation." );
  137. //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiRoadEditorCtrl) );
  138. //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiRoadEditorCtrl) );
  139. //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiRoadEditorCtrl) );
  140. //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiRoadEditorCtrl) );
  141. Parent::initPersistFields();
  142. }
  143. void GuiRoadEditorCtrl::onSleep()
  144. {
  145. Parent::onSleep();
  146. mMode = mSelectRoadMode;
  147. mHoverNode = -1;
  148. mHoverRoad = NULL;
  149. setSelectedNode(-1);
  150. }
  151. void GuiRoadEditorCtrl::get3DCursor( GuiCursor *&cursor,
  152. bool &visible,
  153. const Gui3DMouseEvent &event_ )
  154. {
  155. //cursor = mAddNodeCursor;
  156. //visible = false;
  157. cursor = NULL;
  158. visible = false;
  159. GuiCanvas *root = getRoot();
  160. if ( !root )
  161. return;
  162. S32 currCursor = PlatformCursorController::curArrow;
  163. if ( root->mCursorChanged == currCursor )
  164. return;
  165. PlatformWindow *window = root->getPlatformWindow();
  166. PlatformCursorController *controller = window->getCursorController();
  167. // We've already changed the cursor,
  168. // so set it back before we change it again.
  169. if( root->mCursorChanged != -1)
  170. controller->popCursor();
  171. // Now change the cursor shape
  172. controller->pushCursor(currCursor);
  173. root->mCursorChanged = currCursor;
  174. }
  175. void GuiRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
  176. {
  177. if ( !isFirstResponder() )
  178. setFirstResponder();
  179. // Get the clicked terrain position.
  180. Point3F tPos;
  181. if ( !getTerrainPos( event, tPos ) )
  182. return;
  183. mouseLock();
  184. // Find any road / node at the clicked position.
  185. // TODO: handle overlapping roads/nodes somehow, cycle through them.
  186. DecalRoad *roadPtr = NULL;
  187. S32 closestNodeIdx = -1;
  188. F32 closestDist = F32_MAX;
  189. DecalRoad *closestNodeRoad = NULL;
  190. // First, find the closest node in any road to the clicked position.
  191. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  192. {
  193. roadPtr = static_cast<DecalRoad*>( *iter );
  194. U32 idx;
  195. if ( roadPtr->getClosestNode( tPos, idx ) )
  196. {
  197. Point3F nodePos = roadPtr->getNodePosition(idx);
  198. F32 dist = ( nodePos - tPos ).len();
  199. if ( dist < closestDist )
  200. {
  201. closestNodeIdx = idx;
  202. closestDist = dist;
  203. closestNodeRoad = roadPtr;
  204. }
  205. }
  206. }
  207. //
  208. // Second, determine if the screen-space node rectangle
  209. // contains the clicked position.
  210. bool nodeClicked = false;
  211. S32 clickedNodeIdx = -1;
  212. if ( closestNodeIdx != -1 )
  213. {
  214. Point3F nodePos = closestNodeRoad->getNodePosition( closestNodeIdx );
  215. Point3F temp;
  216. project( nodePos, &temp );
  217. Point2I screenPos( temp.x, temp.y );
  218. RectI nodeRect( screenPos - mNodeHalfSize, mNodeHalfSize * 2 );
  219. nodeClicked = nodeRect.pointInRect( event.mousePoint );
  220. if ( nodeClicked )
  221. clickedNodeIdx = closestNodeIdx;
  222. }
  223. //
  224. // Determine the clickedRoad
  225. //
  226. DecalRoad *clickedRoadPtr = NULL;
  227. U32 insertNodeIdx = 0;
  228. if ( nodeClicked && (mSelRoad == NULL || closestNodeRoad == mSelRoad) )
  229. {
  230. // If a node was clicked, the owning road is always
  231. // considered the clicked road.
  232. clickedRoadPtr = closestNodeRoad;
  233. }
  234. else
  235. {
  236. // check the selected road first
  237. if ( mSelRoad != NULL && mSelRoad->containsPoint( tPos, &insertNodeIdx ) )
  238. {
  239. clickedRoadPtr = mSelRoad;
  240. nodeClicked = false;
  241. clickedNodeIdx = -1;
  242. }
  243. else
  244. {
  245. // Otherwise, we must ask each road if it contains
  246. // the clicked pos.
  247. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  248. {
  249. roadPtr = static_cast<DecalRoad*>( *iter );
  250. if ( roadPtr->containsPoint( tPos, &insertNodeIdx ) )
  251. {
  252. clickedRoadPtr = roadPtr;
  253. break;
  254. }
  255. }
  256. }
  257. }
  258. // shortcuts
  259. bool dblClick = ( event.mouseClickCount > 1 );
  260. if( dblClick )
  261. {
  262. if( mMode == mSelectRoadMode )
  263. {
  264. setMode( mAddRoadMode, true );
  265. return;
  266. }
  267. if( mMode == mAddNodeMode )
  268. {
  269. // Delete the node attached to the cursor.
  270. deleteSelectedNode();
  271. mMode = mAddRoadMode;
  272. return;
  273. }
  274. }
  275. //this check is here in order to bounce back from deleting a whole road with ctrl+z
  276. //this check places the editor back into addroadmode
  277. if ( mMode == mAddNodeMode )
  278. {
  279. if ( !mSelRoad )
  280. mMode = mAddRoadMode;
  281. }
  282. if ( mMode == mSelectRoadMode )
  283. {
  284. // Did not click on a road or a node.
  285. if ( !clickedRoadPtr )
  286. {
  287. setSelectedRoad( NULL );
  288. setSelectedNode( -1 );
  289. return;
  290. }
  291. // Clicked on a road that wasn't the currently selected road.
  292. if ( clickedRoadPtr != mSelRoad )
  293. {
  294. setSelectedRoad( clickedRoadPtr );
  295. setSelectedNode( -1 );
  296. return;
  297. }
  298. // Clicked on a node in the currently selected road that wasn't
  299. // the currently selected node.
  300. if ( nodeClicked )
  301. {
  302. setSelectedNode( clickedNodeIdx );
  303. return;
  304. }
  305. // Clicked a position on the currently selected road
  306. // that did not contain a node.
  307. //U32 newNode = clickedRoadPtr->insertNode( tPos, mDefaultWidth, insertNodeIdx );
  308. //setSelectedNode( newNode );
  309. }
  310. else if ( mMode == mAddRoadMode )
  311. {
  312. if ( nodeClicked && clickedRoadPtr )
  313. {
  314. // A double-click on a node in Normal mode means set AddNode mode.
  315. if ( clickedNodeIdx == 0 )
  316. {
  317. setSelectedRoad( clickedRoadPtr );
  318. setSelectedNode( clickedNodeIdx );
  319. mAddNodeIdx = clickedNodeIdx;
  320. mMode = mAddNodeMode;
  321. mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx );
  322. mIsDirty = true;
  323. return;
  324. }
  325. else if ( clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 )
  326. {
  327. setSelectedRoad( clickedRoadPtr );
  328. setSelectedNode( clickedNodeIdx );
  329. mAddNodeIdx = U32_MAX;
  330. mMode = mAddNodeMode;
  331. mSelNode = mSelRoad->addNode( tPos, mDefaultWidth );
  332. mIsDirty = true;
  333. setSelectedNode( mSelNode );
  334. return;
  335. }
  336. }
  337. DecalRoad *newRoad = new DecalRoad;
  338. newRoad->mMaterialName = mMaterialName;
  339. newRoad->registerObject();
  340. // Add to MissionGroup
  341. SimGroup *missionGroup;
  342. if ( !Sim::findObject( "MissionGroup", missionGroup ) )
  343. Con::errorf( "GuiDecalRoadEditorCtrl - could not find MissionGroup to add new DecalRoad" );
  344. else
  345. missionGroup->addObject( newRoad );
  346. newRoad->insertNode( tPos, mDefaultWidth, 0 );
  347. U32 newNode = newRoad->insertNode( tPos, mDefaultWidth, 1 );
  348. // Always add to the end of the road, the first node is the start.
  349. mAddNodeIdx = U32_MAX;
  350. setSelectedRoad( newRoad );
  351. setSelectedNode( newNode );
  352. mMode = mAddNodeMode;
  353. // Disable the hover node while in addNodeMode, we
  354. // don't want some random node enlarged.
  355. mHoverNode = -1;
  356. // Grab the mission editor undo manager.
  357. UndoManager *undoMan = NULL;
  358. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  359. {
  360. Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
  361. return;
  362. }
  363. // Create the UndoAction.
  364. MECreateUndoAction *action = new MECreateUndoAction("Create Road");
  365. action->addObject( newRoad );
  366. // Submit it.
  367. undoMan->addAction( action );
  368. //send a callback to script after were done here if one exists
  369. if ( isMethod( "onRoadCreation" ) )
  370. Con::executef( this, "onRoadCreation" );
  371. return;
  372. }
  373. else if ( mMode == mAddNodeMode )
  374. {
  375. // Oops the road got deleted, maybe from an undo action?
  376. // Back to NormalMode.
  377. if ( mSelRoad )
  378. {
  379. // A double-click on a node in Normal mode means set AddNode mode.
  380. if ( clickedNodeIdx == 0 )
  381. {
  382. submitUndo( "Add Node" );
  383. mAddNodeIdx = clickedNodeIdx;
  384. mMode = mAddNodeMode;
  385. mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx );
  386. mIsDirty = true;
  387. setSelectedNode( mSelNode );
  388. return;
  389. }
  390. else
  391. {
  392. if( clickedRoadPtr && clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 )
  393. {
  394. submitUndo( "Add Node" );
  395. mAddNodeIdx = U32_MAX;
  396. mMode = mAddNodeMode;
  397. mSelNode = mSelRoad->addNode( tPos, mDefaultWidth );
  398. mIsDirty = true;
  399. setSelectedNode( mSelNode );
  400. return;
  401. }
  402. else
  403. {
  404. submitUndo( "Insert Node" );
  405. // A single-click on empty space while in
  406. // AddNode mode means insert / add a node.
  407. //submitUndo( "Add Node" );
  408. //F32 width = mSelRoad->getNodeWidth( mSelNode );
  409. U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx);
  410. mIsDirty = true;
  411. setSelectedNode( newNode );
  412. return;
  413. }
  414. }
  415. }
  416. }
  417. else if ( mMode == mInsertPointMode && mSelRoad != NULL)
  418. {
  419. if ( clickedRoadPtr == mSelRoad )
  420. {
  421. F32 w0 = mSelRoad->getNodeWidth( insertNodeIdx );
  422. F32 w1 = mSelRoad->getNodeWidth( insertNodeIdx + 1 );
  423. F32 width = ( w0 + w1 ) * 0.5f;
  424. submitUndo( "Insert Node" );
  425. U32 newNode = mSelRoad->insertNode( tPos, width, insertNodeIdx + 1);
  426. mIsDirty = true;
  427. setSelectedNode( newNode );
  428. return;
  429. }
  430. }
  431. else if ( mMode == mRemovePointMode && mSelRoad != NULL)
  432. {
  433. if ( nodeClicked && clickedRoadPtr == mSelRoad )
  434. {
  435. setSelectedNode( clickedNodeIdx );
  436. deleteSelectedNode();
  437. return;
  438. }
  439. }
  440. else if ( mMode == mMovePointMode )
  441. {
  442. if ( nodeClicked && clickedRoadPtr == mSelRoad )
  443. {
  444. setSelectedNode( clickedNodeIdx );
  445. return;
  446. }
  447. }
  448. else if ( mMode == mScalePointMode )
  449. {
  450. if ( nodeClicked && clickedRoadPtr == mSelRoad )
  451. {
  452. setSelectedNode( clickedNodeIdx );
  453. return;
  454. }
  455. }
  456. }
  457. void GuiRoadEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event)
  458. {
  459. //mIsPanning = true;
  460. }
  461. void GuiRoadEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event)
  462. {
  463. //mIsPanning = false;
  464. }
  465. void GuiRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
  466. {
  467. mStartWidth = -1.0f;
  468. mSavedDrag = false;
  469. mouseUnlock();
  470. }
  471. void GuiRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
  472. {
  473. if ( mSelRoad != NULL && mMode == mAddNodeMode )
  474. {
  475. Point3F startPnt = event.pos;
  476. Point3F endPnt = event.pos + event.vec * 1000.0f;
  477. RayInfo ri;
  478. if ( gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri) )
  479. {
  480. mSelRoad->setNodePosition( mSelNode, ri.point );
  481. mIsDirty = true;
  482. }
  483. return;
  484. }
  485. // Is cursor hovering over a road?
  486. if ( mMode == mSelectRoadMode )
  487. {
  488. mHoverRoad = NULL;
  489. Point3F startPnt = event.pos;
  490. Point3F endPnt = event.pos + event.vec * 1000.0f;
  491. RayInfo ri;
  492. if ( gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri) )
  493. {
  494. DecalRoad *pRoad = NULL;
  495. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  496. {
  497. pRoad = static_cast<DecalRoad*>( *iter );
  498. if ( pRoad->containsPoint( ri.point ) )
  499. {
  500. mHoverRoad = pRoad;
  501. break;
  502. }
  503. }
  504. }
  505. }
  506. // Is cursor hovering over a RoadNode?
  507. if ( mHoverRoad )
  508. {
  509. DecalRoad *pRoad = mHoverRoad;
  510. S32 hoverNodeIdx = -1;
  511. F32 hoverNodeDist = F32_MAX;
  512. for ( U32 i = 0; i < pRoad->mNodes.size(); i++ )
  513. {
  514. const Point3F &nodePos = pRoad->mNodes[i].point;
  515. Point3F screenPos;
  516. project( nodePos, &screenPos );
  517. RectI rect( Point2I((S32)screenPos.x,(S32)screenPos.y) - mNodeHalfSize, mNodeHalfSize * 2 );
  518. if ( rect.pointInRect( event.mousePoint ) && screenPos.z < hoverNodeDist )
  519. {
  520. hoverNodeDist = screenPos.z;
  521. hoverNodeIdx = i;
  522. }
  523. }
  524. mHoverNode = hoverNodeIdx;
  525. }
  526. }
  527. void GuiRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
  528. {
  529. // Drags are only used to transform nodes
  530. if ( !mSelRoad || mSelNode == -1 ||
  531. ( mMode != mMovePointMode && mMode != mScalePointMode ) )
  532. return;
  533. if ( !mSavedDrag )
  534. {
  535. submitUndo( "Modify Node" );
  536. mSavedDrag = true;
  537. }
  538. if ( mMode == mScalePointMode )
  539. {
  540. Point3F tPos;
  541. if ( !getTerrainPos( event, tPos ) )
  542. return;
  543. if ( mStartWidth == -1.0f )
  544. {
  545. mStartWidth = mSelRoad->mNodes[mSelNode].width;
  546. mStartX = event.mousePoint.x;
  547. mStartWorld = tPos;
  548. }
  549. S32 deltaScreenX = event.mousePoint.x - mStartX;
  550. F32 worldDist = ( event.pos - mStartWorld ).len();
  551. F32 deltaWorldX = ( deltaScreenX * worldDist ) / getWorldToScreenScale().y;
  552. F32 width = mStartWidth + deltaWorldX;
  553. mSelRoad->setNodeWidth( mSelNode, width );
  554. mIsDirty = true;
  555. }
  556. else if( mMode == mMovePointMode )
  557. {
  558. Point3F tPos;
  559. if ( !getTerrainPos( event, tPos ) )
  560. return;
  561. mSelRoad->setNodePosition( mSelNode, tPos );
  562. mIsDirty = true;
  563. }
  564. Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) );
  565. }
  566. void GuiRoadEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event)
  567. {
  568. // nothing to do
  569. }
  570. void GuiRoadEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
  571. {
  572. // nothing to do
  573. }
  574. bool GuiRoadEditorCtrl::onKeyDown(const GuiEvent& event)
  575. {
  576. if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode )
  577. {
  578. // Delete the node attached to the cursor.
  579. deleteSelectedNode();
  580. mMode = mAddRoadMode;
  581. return true;
  582. }
  583. return false;
  584. }
  585. void GuiRoadEditorCtrl::updateGuiInfo()
  586. {
  587. // nothing to do
  588. }
  589. void GuiRoadEditorCtrl::onRender( Point2I offset, const RectI &updateRect )
  590. {
  591. PROFILE_SCOPE( GuiRoadEditorCtrl_OnRender );
  592. Parent::onRender( offset, updateRect );
  593. return;
  594. }
  595. void GuiRoadEditorCtrl::renderScene(const RectI & updateRect)
  596. {
  597. GFX->setStateBlock( mZDisableSB );
  598. // Draw the spline based from the client-side road
  599. // because the serverside spline is not actually reliable...
  600. // Can be incorrect if the DecalRoad is before the TerrainBlock
  601. // in the MissionGroup.
  602. if ( mHoverRoad && mHoverRoad != mSelRoad )
  603. {
  604. DecalRoad *pRoad = (DecalRoad*)mHoverRoad->getClientObject();
  605. if ( pRoad )
  606. _drawRoadSpline( pRoad, mHoverSplineColor );
  607. }
  608. if ( mSelRoad )
  609. {
  610. DecalRoad *pRoad = (DecalRoad*)mSelRoad->getClientObject();
  611. if ( pRoad )
  612. _drawRoadSpline( pRoad, mSelectedSplineColor );
  613. }
  614. }
  615. void GuiRoadEditorCtrl::renderGui( Point2I offset, const RectI &updateRect )
  616. {
  617. // Draw Control nodes for selected and highlighted roads
  618. if ( mHoverRoad )
  619. _drawRoadControlNodes( mHoverRoad, mHoverSplineColor );
  620. if ( mSelRoad )
  621. _drawRoadControlNodes( mSelRoad, mSelectedSplineColor );
  622. Parent::renderGui(offset, updateRect);
  623. }
  624. void GuiRoadEditorCtrl::_drawRoadSpline( DecalRoad *road, const ColorI &color )
  625. {
  626. if ( road->mEdges.size() <= 1 )
  627. return;
  628. GFXTransformSaver saver;
  629. if ( DecalRoad::smShowSpline )
  630. {
  631. // Render the center-line
  632. PrimBuild::color( color );
  633. PrimBuild::begin( GFXLineStrip, road->mEdges.size() );
  634. for ( U32 i = 0; i < road->mEdges.size(); i++ )
  635. {
  636. PrimBuild::vertex3fv( road->mEdges[i].p1 );
  637. }
  638. PrimBuild::end();
  639. }
  640. if ( DecalRoad::smWireframe )
  641. {
  642. // Left-side line
  643. PrimBuild::color3i( 100, 100, 100 );
  644. PrimBuild::begin( GFXLineStrip, road->mEdges.size() );
  645. for ( U32 i = 0; i < road->mEdges.size(); i++ )
  646. {
  647. PrimBuild::vertex3fv( road->mEdges[i].p0 );
  648. }
  649. PrimBuild::end();
  650. // Right-side line
  651. PrimBuild::begin( GFXLineStrip, road->mEdges.size() );
  652. for ( U32 i = 0; i < road->mEdges.size(); i++ )
  653. {
  654. PrimBuild::vertex3fv( road->mEdges[i].p2 );
  655. }
  656. PrimBuild::end();
  657. // Cross-sections
  658. PrimBuild::begin( GFXLineList, road->mEdges.size() * 2 );
  659. for ( U32 i = 0; i < road->mEdges.size(); i++ )
  660. {
  661. PrimBuild::vertex3fv( road->mEdges[i].p0 );
  662. PrimBuild::vertex3fv( road->mEdges[i].p2 );
  663. }
  664. PrimBuild::end();
  665. }
  666. }
  667. void GuiRoadEditorCtrl::_drawRoadControlNodes( DecalRoad *road, const ColorI &color )
  668. {
  669. if ( !DecalRoad::smShowSpline )
  670. return;
  671. RectI bounds = getBounds();
  672. GFXDrawUtil *drawer = GFX->getDrawUtil();
  673. bool isSelected = ( road == mSelRoad );
  674. bool isHighlighted = ( road == mHoverRoad );
  675. for ( U32 i = 0; i < road->mNodes.size(); i++ )
  676. {
  677. if ( false && isSelected && mSelNode == i )
  678. continue;
  679. const Point3F &wpos = road->mNodes[i].point;
  680. Point3F spos;
  681. project( wpos, &spos );
  682. if ( spos.z > 1.0f )
  683. continue;
  684. Point2I posi;
  685. posi.x = spos.x;
  686. posi.y = spos.y;
  687. if ( !bounds.pointInRect( posi ) )
  688. continue;
  689. ColorI theColor = color;
  690. Point2I nodeHalfSize = mNodeHalfSize;
  691. if ( isHighlighted && mHoverNode == i )
  692. nodeHalfSize += Point2I(2,2);
  693. if ( isSelected )
  694. {
  695. if ( mSelNode == i )
  696. {
  697. theColor.set(0,0,255);
  698. }
  699. else if ( i == 0 )
  700. {
  701. theColor.set(0,255,0);
  702. }
  703. else if ( i == road->mNodes.size() - 1 )
  704. {
  705. theColor.set(255,0,0);
  706. }
  707. }
  708. drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor );
  709. }
  710. }
  711. bool GuiRoadEditorCtrl::getTerrainPos( const Gui3DMouseEvent & event, Point3F &tpos )
  712. {
  713. // Find clicked point on the terrain
  714. Point3F startPnt = event.pos;
  715. Point3F endPnt = event.pos + event.vec * 10000.0f;
  716. RayInfo ri;
  717. bool hit;
  718. hit = gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri);
  719. tpos = ri.point;
  720. return hit;
  721. }
  722. void GuiRoadEditorCtrl::deleteSelectedNode()
  723. {
  724. if ( !mSelRoad || mSelNode == -1 )
  725. return;
  726. // If the road has only two nodes remaining,
  727. // delete the whole road.
  728. if ( mSelRoad->mNodes.size() <= 2 )
  729. {
  730. deleteSelectedRoad();
  731. }
  732. else
  733. {
  734. // Only submit undo if we weren't in AddMode
  735. if ( mMode != mAddNodeMode )
  736. submitUndo( "Delete Node" );
  737. // Delete the SelectedNode of the SelectedRoad
  738. mSelRoad->deleteNode(mSelNode);
  739. mIsDirty = true;
  740. // We deleted the Node but not the Road (it has nodes left)
  741. // so decrement the currently selected node.
  742. if ( mSelRoad->mNodes.size() <= mSelNode )
  743. mSelNode--;
  744. }
  745. }
  746. void GuiRoadEditorCtrl::deleteSelectedRoad( bool undoAble )
  747. {
  748. AssertFatal( mSelRoad != NULL, "GuiRoadEditorCtrl::deleteSelectedRoad() - No road IS selected" );
  749. // Not undo-able? Just delete it.
  750. if ( !undoAble )
  751. {
  752. DecalRoad *lastRoad = mSelRoad;
  753. setSelectedRoad(NULL);
  754. lastRoad->deleteObject();
  755. mIsDirty = true;
  756. return;
  757. }
  758. // Grab the mission editor undo manager.
  759. UndoManager *undoMan = NULL;
  760. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  761. {
  762. // Couldn't find it? Well just delete the road.
  763. Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
  764. return;
  765. }
  766. else
  767. {
  768. DecalRoad *lastRoad = mSelRoad;
  769. setSelectedRoad(NULL);
  770. // Create the UndoAction.
  771. MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road");
  772. action->deleteObject( lastRoad );
  773. mIsDirty = true;
  774. // Submit it.
  775. undoMan->addAction( action );
  776. }
  777. }
  778. void GuiRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false )
  779. {
  780. mMode = mode;
  781. if( sourceShortcut )
  782. Con::executef( this, "paletteSync", mode );
  783. }
  784. void GuiRoadEditorCtrl::setSelectedRoad( DecalRoad *road )
  785. {
  786. mSelRoad = road;
  787. if ( road != NULL )
  788. Con::executef( this, "onRoadSelected", road->getIdString() );
  789. else
  790. Con::executef( this, "onRoadSelected" );
  791. if ( mSelRoad != road )
  792. setSelectedNode(-1);
  793. }
  794. void GuiRoadEditorCtrl::setNodeWidth( F32 width )
  795. {
  796. if ( mSelRoad && mSelNode != -1 )
  797. {
  798. mSelRoad->setNodeWidth( mSelNode, width );
  799. mIsDirty = true;
  800. }
  801. }
  802. F32 GuiRoadEditorCtrl::getNodeWidth()
  803. {
  804. if ( mSelRoad && mSelNode != -1 )
  805. return mSelRoad->getNodeWidth( mSelNode );
  806. return 0.0f;
  807. }
  808. void GuiRoadEditorCtrl::setNodePosition(const Point3F& pos)
  809. {
  810. if ( mSelRoad && mSelNode != -1 )
  811. {
  812. mSelRoad->setNodePosition( mSelNode, pos );
  813. mIsDirty = true;
  814. }
  815. }
  816. Point3F GuiRoadEditorCtrl::getNodePosition()
  817. {
  818. if ( mSelRoad && mSelNode != -1 )
  819. return mSelRoad->getNodePosition( mSelNode );
  820. return Point3F( 0, 0, 0 );
  821. }
  822. void GuiRoadEditorCtrl::setSelectedNode( S32 node )
  823. {
  824. //if ( mSelNode == node )
  825. // return;
  826. mSelNode = node;
  827. if ( mSelNode != -1 && mSelRoad != NULL )
  828. Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode), Con::getFloatArg(mSelRoad->mNodes[mSelNode].width) );
  829. else
  830. Con::executef( this, "onNodeSelected", Con::getIntArg(-1) );
  831. }
  832. void GuiRoadEditorCtrl::submitUndo( const UTF8 *name )
  833. {
  834. // Grab the mission editor undo manager.
  835. UndoManager *undoMan = NULL;
  836. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  837. {
  838. Con::errorf( "GuiRoadEditorCtrl::submitUndo() - EUndoManager not found!" );
  839. return;
  840. }
  841. // Setup the action.
  842. GuiRoadEditorUndoAction *action = new GuiRoadEditorUndoAction( name );
  843. action->mObjId = mSelRoad->getId();
  844. action->mBreakAngle = mSelRoad->mBreakAngle;
  845. action->mMaterialName = mSelRoad->mMaterialName;
  846. action->mSegmentsPerBatch = mSelRoad->mSegmentsPerBatch;
  847. action->mTextureLength = mSelRoad->mTextureLength;
  848. action->mRoadEditor = this;
  849. for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ )
  850. {
  851. action->mNodes.push_back( mSelRoad->mNodes[i] );
  852. }
  853. undoMan->addAction( action );
  854. }
  855. DefineConsoleMethod( GuiRoadEditorCtrl, deleteNode, void, (), , "deleteNode()" )
  856. {
  857. object->deleteSelectedNode();
  858. }
  859. DefineConsoleMethod( GuiRoadEditorCtrl, getMode, const char*, (), , "" )
  860. {
  861. return object->getMode();
  862. }
  863. DefineConsoleMethod( GuiRoadEditorCtrl, setMode, void, ( const char * mode ), , "setMode( String mode )" )
  864. {
  865. String newMode = ( mode );
  866. object->setMode( newMode );
  867. }
  868. DefineConsoleMethod( GuiRoadEditorCtrl, getNodeWidth, F32, (), , "" )
  869. {
  870. return object->getNodeWidth();
  871. }
  872. DefineConsoleMethod( GuiRoadEditorCtrl, setNodeWidth, void, ( F32 width ), , "" )
  873. {
  874. object->setNodeWidth( width );
  875. }
  876. DefineConsoleMethod( GuiRoadEditorCtrl, getNodePosition, Point3F, (), , "" )
  877. {
  878. return object->getNodePosition();
  879. }
  880. DefineConsoleMethod( GuiRoadEditorCtrl, setNodePosition, void, ( Point3F pos ), , "" )
  881. {
  882. object->setNodePosition( pos );
  883. }
  884. DefineConsoleMethod( GuiRoadEditorCtrl, setSelectedRoad, void, ( const char * pathRoad ), (""), "" )
  885. {
  886. if (dStrcmp( pathRoad,"")==0 )
  887. object->setSelectedRoad(NULL);
  888. else
  889. {
  890. DecalRoad *road = NULL;
  891. if ( Sim::findObject( pathRoad, road ) )
  892. object->setSelectedRoad(road);
  893. }
  894. }
  895. DefineConsoleMethod( GuiRoadEditorCtrl, getSelectedRoad, S32, (), , "" )
  896. {
  897. DecalRoad *road = object->getSelectedRoad();
  898. if ( road )
  899. return road->getId();
  900. return NULL;
  901. }
  902. DefineConsoleMethod( GuiRoadEditorCtrl, getSelectedNode, S32, (), , "" )
  903. {
  904. return object->getSelectedNode();
  905. }
  906. DefineConsoleMethod( GuiRoadEditorCtrl, deleteRoad, void, (), , "" )
  907. {
  908. object->deleteSelectedRoad();
  909. }