guiMeshRoadEditorCtrl.cpp 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307
  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/guiMeshRoadEditorCtrl.h"
  24. #include "console/consoleTypes.h"
  25. #include "environment/meshRoad.h"
  26. #include "renderInstance/renderPassManager.h"
  27. #include "collision/collision.h"
  28. #include "math/util/frustum.h"
  29. #include "math/mathUtils.h"
  30. #include "gfx/gfxPrimitiveBuffer.h"
  31. #include "gfx/gfxTextureHandle.h"
  32. #include "gfx/gfxTransformSaver.h"
  33. #include "gfx/primBuilder.h"
  34. #include "gfx/gfxDrawUtil.h"
  35. #include "scene/sceneRenderState.h"
  36. #include "scene/sceneManager.h"
  37. #include "gui/core/guiCanvas.h"
  38. #include "gui/buttons/guiButtonCtrl.h"
  39. #include "gui/worldEditor/undoActions.h"
  40. #include "T3D/gameBase/gameConnection.h"
  41. #include "gfx/sim/debugDraw.h"
  42. #include "materials/materialDefinition.h"
  43. #include "T3D/prefab.h"
  44. IMPLEMENT_CONOBJECT(GuiMeshRoadEditorCtrl);
  45. ConsoleDocClass( GuiMeshRoadEditorCtrl,
  46. "@brief GUI tool that makes up the Mesh Road Editor\n\n"
  47. "Editor use only.\n\n"
  48. "@internal"
  49. );
  50. GuiMeshRoadEditorCtrl::GuiMeshRoadEditorCtrl()
  51. :
  52. // Each of the mode names directly correlates with the Mesh Road Editor's
  53. // tool palette
  54. mSelectMeshRoadMode("MeshRoadEditorSelectMode"),
  55. mAddMeshRoadMode("MeshRoadEditorAddRoadMode"),
  56. mMovePointMode("MeshRoadEditorMoveMode"),
  57. mRotatePointMode("MeshRoadEditorRotateMode"),
  58. mScalePointMode("MeshRoadEditorScaleMode"),
  59. mAddNodeMode("MeshRoadEditorAddNodeMode"),
  60. mInsertPointMode("MeshRoadEditorInsertPointMode"),
  61. mRemovePointMode("MeshRoadEditorRemovePointMode"),
  62. mMode(mSelectMeshRoadMode),
  63. mHasCopied( false ),
  64. mIsDirty( false ),
  65. mRoadSet( NULL ),
  66. mSelNode( -1 ),
  67. mSelRoad( NULL ),
  68. mHoverRoad( NULL ),
  69. mHoverNode( -1 ),
  70. mDefaultWidth( 10.0f ),
  71. mDefaultDepth( 5.0f ),
  72. mDefaultNormal( 0,0,1 ),
  73. mAddNodeIdx( 0 ),
  74. mNodeHalfSize( 4,4 ),
  75. mHoverSplineColor( 255,0,0,255 ),
  76. mSelectedSplineColor( 0,255,0,255 ),
  77. mHoverNodeColor( 255,255,255,255 )
  78. {
  79. mMaterialName[Top] = StringTable->insert("DefaultRoadMaterialTop");
  80. mMaterialName[Bottom] = StringTable->insert("DefaultRoadMaterialOther");
  81. mMaterialName[Side] = StringTable->insert("DefaultRoadMaterialOther");
  82. }
  83. GuiMeshRoadEditorCtrl::~GuiMeshRoadEditorCtrl()
  84. {
  85. // nothing to do
  86. }
  87. void GuiMeshRoadEditorUndoAction::undo()
  88. {
  89. MeshRoad *object = NULL;
  90. if ( !Sim::findObject( mObjId, object ) )
  91. return;
  92. // Temporarily save the Roads current data.
  93. //F32 metersPerSeg = object->mMetersPerSegment;
  94. Vector<MeshRoadNode> nodes;
  95. nodes.merge( object->mNodes );
  96. // Restore the River properties saved in the UndoAction
  97. //object->mMetersPerSegment = mMetersPerSegment;
  98. // Restore the Nodes saved in the UndoAction
  99. object->mNodes.clear();
  100. for ( U32 i = 0; i < mNodes.size(); i++ )
  101. {
  102. object->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal );
  103. }
  104. // Regenerate the Road
  105. object->regenerate();
  106. // If applicable set the selected Road and node
  107. mEditor->mSelRoad = object;
  108. mEditor->mSelNode = -1;
  109. // Now save the previous Road data in this UndoAction
  110. // since an undo action must become a redo action and vice-versa
  111. //mMetersPerSegment = metersPerSeg;
  112. mNodes.clear();
  113. mNodes.merge( nodes );
  114. }
  115. bool GuiMeshRoadEditorCtrl::onAdd()
  116. {
  117. if( !Parent::onAdd() )
  118. return false;
  119. mRoadSet = MeshRoad::getServerSet();
  120. GFXStateBlockDesc desc;
  121. desc.fillMode = GFXFillSolid;
  122. desc.blendDefined = true;
  123. desc.blendEnable = false;
  124. desc.zDefined = true;
  125. desc.zEnable = false;
  126. desc.cullDefined = true;
  127. desc.cullMode = GFXCullNone;
  128. mZDisableSB = GFX->createStateBlock(desc);
  129. desc.zEnable = true;
  130. mZEnableSB = GFX->createStateBlock(desc);
  131. return true;
  132. }
  133. void GuiMeshRoadEditorCtrl::initPersistFields()
  134. {
  135. addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiMeshRoadEditorCtrl ) );
  136. addField( "DefaultDepth", TypeF32, Offset( mDefaultDepth, GuiMeshRoadEditorCtrl ) );
  137. addField( "DefaultNormal", TypePoint3F,Offset( mDefaultNormal, GuiMeshRoadEditorCtrl ) );
  138. addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiMeshRoadEditorCtrl ) );
  139. addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiMeshRoadEditorCtrl ) );
  140. addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiMeshRoadEditorCtrl ) );
  141. addField( "isDirty", TypeBool, Offset( mIsDirty, GuiMeshRoadEditorCtrl ) );
  142. addField( "topMaterialName", TypeString, Offset( mMaterialName[Top], GuiMeshRoadEditorCtrl ),
  143. "Default Material used by the Mesh Road Editor on upper surface road creation." );
  144. addField( "bottomMaterialName", TypeString, Offset( mMaterialName[Bottom], GuiMeshRoadEditorCtrl ),
  145. "Default Material used by the Mesh Road Editor on bottom surface road creation." );
  146. addField( "sideMaterialName", TypeString, Offset( mMaterialName[Side], GuiMeshRoadEditorCtrl ),
  147. "Default Material used by the Mesh Road Editor on side surface road creation." );
  148. //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiMeshRoadEditorCtrl) );
  149. //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiMeshRoadEditorCtrl) );
  150. //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiMeshRoadEditorCtrl) );
  151. //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiMeshRoadEditorCtrl) );
  152. Parent::initPersistFields();
  153. }
  154. void GuiMeshRoadEditorCtrl::onSleep()
  155. {
  156. Parent::onSleep();
  157. mMode = mSelectMeshRoadMode;
  158. mHoverNode = -1;
  159. mHoverRoad = NULL;
  160. setSelectedNode(-1);
  161. }
  162. void GuiMeshRoadEditorCtrl::get3DCursor( GuiCursor *&cursor,
  163. bool &visible,
  164. const Gui3DMouseEvent &event_ )
  165. {
  166. //cursor = mAddNodeCursor;
  167. //visible = false;
  168. cursor = NULL;
  169. visible = false;
  170. GuiCanvas *root = getRoot();
  171. if ( !root )
  172. return;
  173. S32 currCursor = PlatformCursorController::curArrow;
  174. if ( root->mCursorChanged == currCursor )
  175. return;
  176. PlatformWindow *window = root->getPlatformWindow();
  177. PlatformCursorController *controller = window->getCursorController();
  178. // We've already changed the cursor,
  179. // so set it back before we change it again.
  180. if( root->mCursorChanged != -1)
  181. controller->popCursor();
  182. // Now change the cursor shape
  183. controller->pushCursor(currCursor);
  184. root->mCursorChanged = currCursor;
  185. }
  186. void GuiMeshRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
  187. {
  188. mHasCopied = false;
  189. mGizmo->on3DMouseDown( event );
  190. if ( !isFirstResponder() )
  191. setFirstResponder();
  192. // Get the raycast collision position
  193. Point3F tPos;
  194. if ( !getStaticPos( event, tPos ) )
  195. return;
  196. mouseLock();
  197. // Construct a LineSegment from the camera position to 1000 meters away in
  198. // the direction clicked.
  199. // If that segment hits the terrain, truncate the ray to only be that length.
  200. // We will use a LineSegment/Sphere intersection test to determine if a MeshRoadNode
  201. // was clicked.
  202. MeshRoad *pRoad = NULL;
  203. MeshRoad *pClickedRoad = NULL;
  204. U32 insertNodeIdx = -1;
  205. Point3F collisionPnt;
  206. Point3F startPnt = event.pos;
  207. Point3F endPnt = event.pos + event.vec * 2000.0f;
  208. RayInfo ri;
  209. if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
  210. endPnt = ri.point;
  211. DebugDrawer *ddraw = DebugDrawer::get();
  212. if ( false && ddraw )
  213. {
  214. ddraw->drawLine( startPnt, endPnt, ColorI(255,0,0,255) );
  215. ddraw->setLastTTL(DebugDrawer::DD_INFINITE);
  216. }
  217. // Check for collision with nodes of the currently selected or highlighted MeshRoad
  218. // Did we click on a MeshRoad? check currently selected road first
  219. if ( mSelRoad != NULL && mSelRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
  220. {
  221. pClickedRoad = mSelRoad;
  222. }
  223. else
  224. {
  225. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  226. {
  227. pRoad = static_cast<MeshRoad*>( *iter );
  228. // Do not select or edit a MeshRoad within a Prefab.
  229. if ( Prefab::getPrefabByChild(pRoad) )
  230. continue;
  231. if ( pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
  232. {
  233. pClickedRoad = pRoad;
  234. break;
  235. }
  236. }
  237. }
  238. /*
  239. else if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
  240. {
  241. MeshRoad *pRoad = NULL;
  242. pRoad = dynamic_cast<MeshRoad*>(ri.object);
  243. if ( pRoad && pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
  244. pClickedRoad = pRoad;
  245. }
  246. */
  247. // Did we click on a node?
  248. bool nodeClicked = false;
  249. S32 clickedNodeIdx = -1;
  250. // If we clicked on the currently selected road, only scan its nodes
  251. if ( mSelRoad != NULL && pClickedRoad == mSelRoad )
  252. {
  253. clickedNodeIdx = _getNodeAtScreenPos( mSelRoad, event.mousePoint );
  254. nodeClicked = clickedNodeIdx != -1;
  255. }
  256. else
  257. {
  258. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  259. {
  260. pRoad = static_cast<MeshRoad*>( *iter );
  261. // Do not select or edit a MeshRoad within a Prefab.
  262. if ( Prefab::getPrefabByChild(pRoad) )
  263. continue;
  264. clickedNodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint );
  265. if ( clickedNodeIdx != -1 )
  266. {
  267. nodeClicked = true;
  268. pClickedRoad = pRoad;
  269. break;
  270. }
  271. }
  272. }
  273. // shortcuts
  274. bool dblClick = ( event.mouseClickCount > 1 );
  275. if( dblClick )
  276. {
  277. if( mMode == mSelectMeshRoadMode )
  278. {
  279. setMode( mAddMeshRoadMode, true );
  280. return;
  281. }
  282. if( mMode == mAddNodeMode )
  283. {
  284. // Delete the node attached to the cursor.
  285. deleteSelectedNode();
  286. mMode = mAddMeshRoadMode;
  287. return;
  288. }
  289. }
  290. //this check is here in order to bounce back from deleting a whole road with ctrl+z
  291. //this check places the editor back into addrivermode
  292. if ( mMode == mAddNodeMode )
  293. {
  294. if ( !mSelRoad )
  295. mMode = mAddMeshRoadMode;
  296. }
  297. if ( mMode == mSelectMeshRoadMode )
  298. {
  299. // Did not click on a MeshRoad or a node.
  300. if ( !pClickedRoad )
  301. {
  302. setSelectedRoad( NULL );
  303. setSelectedNode( -1 );
  304. return;
  305. }
  306. // Clicked on a MeshRoad that wasn't the currently selected River.
  307. if ( pClickedRoad != mSelRoad )
  308. {
  309. setSelectedRoad( pClickedRoad );
  310. setSelectedNode( clickedNodeIdx );
  311. return;
  312. }
  313. // Clicked on a node in the currently selected River that wasn't
  314. // the currently selected node.
  315. if ( nodeClicked )
  316. {
  317. setSelectedNode( clickedNodeIdx );
  318. return;
  319. }
  320. }
  321. else if ( mMode == mAddMeshRoadMode )
  322. {
  323. if ( nodeClicked )
  324. {
  325. // A double-click on a node in Normal mode means set AddNode mode.
  326. if ( clickedNodeIdx == 0 )
  327. {
  328. setSelectedRoad( pClickedRoad );
  329. setSelectedNode( clickedNodeIdx );
  330. mAddNodeIdx = clickedNodeIdx;
  331. mMode = mAddNodeMode;
  332. mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
  333. mIsDirty = true;
  334. return;
  335. }
  336. else if ( clickedNodeIdx == pClickedRoad->mNodes.size() - 1 )
  337. {
  338. setSelectedRoad( pClickedRoad );
  339. setSelectedNode( clickedNodeIdx );
  340. mAddNodeIdx = U32_MAX;
  341. mMode = mAddNodeMode;
  342. mSelNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
  343. mIsDirty = true;
  344. setSelectedNode( mSelNode );
  345. return;
  346. }
  347. }
  348. MeshRoad *newRoad = new MeshRoad;
  349. newRoad->mMaterialName[Top] = mMaterialName[Top];
  350. newRoad->mMaterialName[Bottom] = mMaterialName[Bottom];
  351. newRoad->mMaterialName[Side] = mMaterialName[Side];
  352. newRoad->registerObject();
  353. // Add to MissionGroup
  354. SimGroup *missionGroup;
  355. if ( !Sim::findObject( "MissionGroup", missionGroup ) )
  356. Con::errorf( "GuiMeshRoadEditorCtrl - could not find MissionGroup to add new MeshRoad" );
  357. else
  358. missionGroup->addObject( newRoad );
  359. Point3F pos( endPnt );
  360. pos.z += mDefaultDepth * 0.5f;
  361. newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 );
  362. U32 newNode = newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 );
  363. // Always add to the end of the road, the first node is the start.
  364. mAddNodeIdx = U32_MAX;
  365. setSelectedRoad( newRoad );
  366. setSelectedNode( newNode );
  367. mMode = mAddNodeMode;
  368. // Disable the hover node while in addNodeMode, we
  369. // don't want some random node enlarged.
  370. mHoverNode = -1;
  371. // Grab the mission editor undo manager.
  372. UndoManager *undoMan = NULL;
  373. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  374. {
  375. Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
  376. return;
  377. }
  378. // Create the UndoAction.
  379. MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad");
  380. action->addObject( newRoad );
  381. // Submit it.
  382. undoMan->addAction( action );
  383. return;
  384. }
  385. else if ( mMode == mAddNodeMode )
  386. {
  387. // Oops the road got deleted, maybe from an undo action?
  388. // Back to NormalMode.
  389. if ( mSelRoad )
  390. {
  391. // A double-click on a node in Normal mode means set AddNode mode.
  392. if ( clickedNodeIdx == 0 )
  393. {
  394. submitUndo( "Add Node" );
  395. mAddNodeIdx = clickedNodeIdx;
  396. mMode = mAddNodeMode;
  397. mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
  398. mIsDirty = true;
  399. setSelectedNode( mSelNode );
  400. return;
  401. }
  402. else
  403. {
  404. if( pClickedRoad && clickedNodeIdx == pClickedRoad->mNodes.size() - 1 )
  405. {
  406. submitUndo( "Add Node" );
  407. mAddNodeIdx = U32_MAX;
  408. mMode = mAddNodeMode;
  409. U32 newNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
  410. mIsDirty = true;
  411. setSelectedNode( newNode );
  412. return;
  413. }
  414. else
  415. {
  416. submitUndo( "Insert Node" );
  417. // A single-click on empty space while in
  418. // AddNode mode means insert / add a node.
  419. //submitUndo( "Add Node" );
  420. U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx);
  421. mIsDirty = true;
  422. setSelectedNode( newNode );
  423. return;
  424. }
  425. }
  426. }
  427. }
  428. else if ( mMode == mInsertPointMode && mSelRoad != NULL )
  429. {
  430. if ( pClickedRoad == mSelRoad && insertNodeIdx != -1 )
  431. {
  432. // NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node...
  433. // in order to handle inserting nodes in the same way as for fxRoad
  434. U32 prevNodeIdx = insertNodeIdx;
  435. U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRoad->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1;
  436. const MeshRoadNode &prevNode = mSelRoad->mNodes[prevNodeIdx];
  437. const MeshRoadNode &nextNode = mSelRoad->mNodes[nextNodeIdx];
  438. F32 width = ( prevNode.width + nextNode.width ) * 0.5f;
  439. F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f;
  440. Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f;
  441. normal.normalize();
  442. submitUndo( "Insert Node" );
  443. U32 newNode = mSelRoad->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 );
  444. mIsDirty = true;
  445. setSelectedNode( newNode );
  446. return;
  447. }
  448. }
  449. else if ( mMode == mRemovePointMode && mSelRoad != NULL )
  450. {
  451. if ( nodeClicked && pClickedRoad == mSelRoad )
  452. {
  453. setSelectedNode( clickedNodeIdx );
  454. deleteSelectedNode();
  455. return;
  456. }
  457. }
  458. else if ( mMode == mMovePointMode )
  459. {
  460. if ( nodeClicked && pClickedRoad == mSelRoad )
  461. {
  462. setSelectedNode( clickedNodeIdx );
  463. return;
  464. }
  465. }
  466. else if ( mMode == mScalePointMode )
  467. {
  468. if ( nodeClicked && pClickedRoad == mSelRoad )
  469. {
  470. setSelectedNode( clickedNodeIdx );
  471. return;
  472. }
  473. }
  474. else if ( mMode == mRotatePointMode )
  475. {
  476. if ( nodeClicked && pClickedRoad == mSelRoad )
  477. {
  478. setSelectedNode( clickedNodeIdx );
  479. return;
  480. }
  481. }
  482. }
  483. void GuiMeshRoadEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event)
  484. {
  485. //mIsPanning = true;
  486. }
  487. void GuiMeshRoadEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event)
  488. {
  489. //mIsPanning = false;
  490. }
  491. void GuiMeshRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
  492. {
  493. mGizmo->on3DMouseUp(event);
  494. mSavedDrag = false;
  495. mouseUnlock();
  496. }
  497. void GuiMeshRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
  498. {
  499. if ( mSelRoad != NULL && mMode == mAddNodeMode )
  500. {
  501. Point3F pos;
  502. mSelRoad->disableCollision();
  503. if ( getStaticPos( event, pos ) )
  504. {
  505. pos.z += mSelRoad->getNodeDepth( mSelNode ) * 0.5f;
  506. mSelRoad->setNodePosition( mSelNode, pos );
  507. mIsDirty = true;
  508. }
  509. mSelRoad->enableCollision();
  510. return;
  511. }
  512. if ( mSelRoad != NULL && mSelNode != -1 )
  513. {
  514. mGizmo->on3DMouseMove( event );
  515. //mGizmo.collideAxisGizmo( event );
  516. //Con::printf( "SelectedAxis: %i", mGizmo.getSelectedAxis() );
  517. }
  518. // Is cursor hovering over a river?
  519. if ( mMode == mSelectMeshRoadMode )
  520. {
  521. mHoverRoad = NULL;
  522. Point3F startPnt = event.pos;
  523. Point3F endPnt = event.pos + event.vec * 1000.0f;
  524. RayInfo ri;
  525. if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
  526. {
  527. MeshRoad *pRoad = NULL;
  528. pRoad = dynamic_cast<MeshRoad*>(ri.object);
  529. // Do not select or edit a MeshRoad within a Prefab.
  530. if ( pRoad && !Prefab::getPrefabByChild(pRoad) )
  531. mHoverRoad = pRoad;
  532. }
  533. }
  534. // Is cursor over a node?
  535. if ( mHoverRoad )
  536. {
  537. MeshRoad *pRoad = NULL;
  538. S32 nodeIdx = -1;
  539. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  540. {
  541. pRoad = static_cast<MeshRoad*>( *iter );
  542. nodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint );
  543. if ( nodeIdx != -1 )
  544. {
  545. mHoverRoad = pRoad;
  546. break;
  547. }
  548. }
  549. mHoverNode = nodeIdx;
  550. }
  551. }
  552. void GuiMeshRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
  553. {
  554. // Drags are only used to transform nodes
  555. if ( !mSelRoad || mSelNode == -1 ||
  556. ( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) )
  557. return;
  558. // If we haven't already saved,
  559. // save an undo action to get back to this state,
  560. // before we make any modifications to the selected node.
  561. if ( !mSavedDrag )
  562. {
  563. submitUndo( "Modify Node" );
  564. mSavedDrag = true;
  565. }
  566. // If shift is held and we haven't already copied the node,
  567. // make a copy of the selected node and select it.
  568. if ( event.modifier & SI_SHIFT && !mHasCopied && mSelRoad->isEndNode( mSelNode ) )
  569. {
  570. const MeshRoadNode &data = mSelRoad->getNode( mSelNode );
  571. U32 insertIdx = ( mSelNode == 0 ) ? 0 : U32_MAX;
  572. U32 newNodeIdx = mSelRoad->insertNode( data.point, data.width, data.depth, data.normal, insertIdx );
  573. mIsDirty = true;
  574. mSelNode = -1;
  575. setSelectedNode( newNodeIdx );
  576. mHasCopied = true;
  577. }
  578. // Let the Gizmo handle the drag, eg, modify its transforms
  579. mGizmo->on3DMouseDragged( event );
  580. if ( mGizmo->isDirty() )
  581. {
  582. Point3F pos = mGizmo->getPosition();
  583. Point3F scale = mGizmo->getScale();
  584. const MatrixF &mat = mGizmo->getTransform();
  585. VectorF normal;
  586. mat.getColumn( 2, &normal );
  587. mSelRoad->setNode( pos, scale.x, scale.z, normal, mSelNode );
  588. mIsDirty = true;
  589. mGizmo->markClean();
  590. }
  591. Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) );
  592. }
  593. void GuiMeshRoadEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event)
  594. {
  595. // nothing to do
  596. }
  597. void GuiMeshRoadEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
  598. {
  599. // nothing to do
  600. }
  601. bool GuiMeshRoadEditorCtrl::onKeyDown(const GuiEvent& event)
  602. {
  603. if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode )
  604. {
  605. // Delete the node attached to the cursor.
  606. deleteSelectedNode();
  607. mMode = mAddMeshRoadMode;
  608. return true;
  609. }
  610. return false;
  611. }
  612. void GuiMeshRoadEditorCtrl::updateGuiInfo()
  613. {
  614. // nothing to do
  615. }
  616. void GuiMeshRoadEditorCtrl::renderScene(const RectI & updateRect)
  617. {
  618. //GFXDrawUtil *drawer = GFX->getDrawUtil();
  619. GFX->setStateBlock( mZDisableSB );
  620. // get the projected size...
  621. GameConnection* connection = GameConnection::getConnectionToServer();
  622. if(!connection)
  623. return;
  624. // Grab the camera's transform
  625. MatrixF mat;
  626. connection->getControlCameraTransform(0, &mat);
  627. // Get the camera position
  628. Point3F camPos;
  629. mat.getColumn(3,&camPos);
  630. if ( mHoverRoad && mHoverRoad != mSelRoad )
  631. {
  632. _drawSpline( mHoverRoad, mHoverSplineColor );
  633. }
  634. if ( mSelRoad )
  635. {
  636. _drawSpline( mSelRoad, mSelectedSplineColor );
  637. // Render Gizmo for selected node if were in either of the three transform modes
  638. if ( mSelNode != -1 && ( mMode == mMovePointMode || mMode == mScalePointMode || mMode == mRotatePointMode ) )
  639. {
  640. if( mMode == mMovePointMode )
  641. {
  642. mGizmo->getProfile()->mode = MoveMode;
  643. }
  644. else if( mMode == mScalePointMode )
  645. {
  646. mGizmo->getProfile()->mode = ScaleMode;
  647. }
  648. else if( mMode == mRotatePointMode )
  649. {
  650. mGizmo->getProfile()->mode = RotateMode;
  651. }
  652. const MeshRoadNode &node = mSelRoad->mNodes[mSelNode];
  653. MatrixF objMat = mSelRoad->getNodeTransform(mSelNode);
  654. Point3F objScale( node.width, 1.0f, node.depth );
  655. Point3F worldPos = node.point;
  656. mGizmo->set( objMat, worldPos, objScale );
  657. mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov );
  658. // Render Gizmo text
  659. mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection );
  660. }
  661. }
  662. DebugDrawer::get()->render();
  663. // Now draw all the 2d stuff!
  664. GFX->setClipRect(updateRect);
  665. // Draw Control nodes for selecting and highlighted rivers
  666. if ( mHoverRoad )
  667. _drawControlNodes( mHoverRoad, mHoverSplineColor );
  668. if ( mSelRoad )
  669. _drawControlNodes( mSelRoad, mSelectedSplineColor );
  670. }
  671. S32 GuiMeshRoadEditorCtrl::_getNodeAtScreenPos( const MeshRoad *pRoad, const Point2I &posi )
  672. {
  673. for ( U32 i = 0; i < pRoad->mNodes.size(); i++ )
  674. {
  675. const Point3F &nodePos = pRoad->mNodes[i].point;
  676. Point3F screenPos;
  677. project( nodePos, &screenPos );
  678. if ( screenPos.z < 0.0f )
  679. continue;
  680. Point2I screenPosI( (S32)screenPos.x, (S32)screenPos.y );
  681. RectI nodeScreenRect( screenPosI - mNodeHalfSize, mNodeHalfSize * 2 );
  682. if ( nodeScreenRect.pointInRect(posi) )
  683. {
  684. // we found a hit!
  685. return i;
  686. }
  687. }
  688. return -1;
  689. }
  690. void GuiMeshRoadEditorCtrl::_drawSpline( MeshRoad *river, const ColorI &color )
  691. {
  692. if ( river->mSlices.size() <= 1 )
  693. return;
  694. if ( MeshRoad::smShowSpline )
  695. {
  696. // Render the River center-line
  697. PrimBuild::color( color );
  698. PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
  699. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  700. {
  701. PrimBuild::vertex3fv( river->mSlices[i].p1 );
  702. }
  703. PrimBuild::end();
  704. }
  705. if ( MeshRoad::smWireframe )
  706. {
  707. // Left-side line
  708. PrimBuild::color3i( 100, 100, 100 );
  709. PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
  710. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  711. {
  712. PrimBuild::vertex3fv( river->mSlices[i].p0 );
  713. }
  714. PrimBuild::end();
  715. // Right-side line
  716. PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
  717. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  718. {
  719. PrimBuild::vertex3fv( river->mSlices[i].p2 );
  720. }
  721. PrimBuild::end();
  722. // Cross-sections
  723. PrimBuild::begin( GFXLineList, river->mSlices.size() * 2 );
  724. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  725. {
  726. PrimBuild::vertex3fv( river->mSlices[i].p0 );
  727. PrimBuild::vertex3fv( river->mSlices[i].p2 );
  728. }
  729. PrimBuild::end();
  730. }
  731. // Segment
  732. }
  733. void GuiMeshRoadEditorCtrl::_drawControlNodes( MeshRoad *river, const ColorI &color )
  734. {
  735. if ( !MeshRoad::smShowSpline )
  736. return;
  737. RectI bounds = getBounds();
  738. GFXDrawUtil *drawer = GFX->getDrawUtil();
  739. bool isSelected = ( river == mSelRoad );
  740. bool isHighlighted = ( river == mHoverRoad );
  741. for ( U32 i = 0; i < river->mNodes.size(); i++ )
  742. {
  743. if ( false && isSelected && mSelNode == i )
  744. continue;
  745. const Point3F &wpos = river->mNodes[i].point;
  746. Point3F spos;
  747. project( wpos, &spos );
  748. if ( spos.z > 1.0f )
  749. continue;
  750. Point2I posi;
  751. posi.x = spos.x;
  752. posi.y = spos.y;
  753. if ( !bounds.pointInRect( posi ) )
  754. continue;
  755. ColorI theColor = color;
  756. Point2I nodeHalfSize = mNodeHalfSize;
  757. if ( isHighlighted && mHoverNode == i )
  758. {
  759. //theColor = mHoverNodeColor;
  760. nodeHalfSize += Point2I(2,2);
  761. }
  762. if ( isSelected )
  763. {
  764. if ( mSelNode == i )
  765. {
  766. theColor.set(0,0,255);
  767. }
  768. else if ( i == 0 )
  769. {
  770. theColor.set(0,255,0);
  771. }
  772. else if ( i == river->mNodes.size() - 1 )
  773. {
  774. theColor.set(255,0,0);
  775. }
  776. }
  777. drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor );
  778. }
  779. }
  780. bool GuiMeshRoadEditorCtrl::getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos )
  781. {
  782. // Find clicked point on the terrain
  783. Point3F startPnt = event.pos;
  784. Point3F endPnt = event.pos + event.vec * 1000.0f;
  785. RayInfo ri;
  786. bool hit;
  787. hit = gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri);
  788. tpos = ri.point;
  789. return hit;
  790. }
  791. void GuiMeshRoadEditorCtrl::deleteSelectedNode()
  792. {
  793. if ( !mSelRoad || mSelNode == -1 )
  794. return;
  795. // If the Road has only two nodes remaining,
  796. // delete the whole Road.
  797. if ( mSelRoad->mNodes.size() <= 2 )
  798. {
  799. deleteSelectedRoad( mMode != mAddNodeMode );
  800. }
  801. else
  802. {
  803. if ( mMode != mAddNodeMode )
  804. submitUndo( "Delete Node" );
  805. // Delete the SelectedNode of the SelectedRoad
  806. mSelRoad->deleteNode( mSelNode );
  807. // We deleted the Node but not the Road (it has nodes left)
  808. // so decrement the currently selected node.
  809. if ( mSelRoad->mNodes.size() <= mSelNode )
  810. setSelectedNode( mSelNode - 1 );
  811. else
  812. {
  813. // force gizmo to update to the selected nodes position
  814. // the index didn't change but the node it refers to did.
  815. U32 i = mSelNode;
  816. mSelNode = -1;
  817. setSelectedNode( i );
  818. }
  819. }
  820. // If you were in addNodeMode,
  821. // deleting a node should ends it.
  822. //mMode = smNormalMode;
  823. }
  824. void GuiMeshRoadEditorCtrl::deleteSelectedRoad( bool undoAble )
  825. {
  826. AssertFatal( mSelRoad != NULL, "GuiMeshRoadEditorCtrl::deleteSelectedRoad() - No Road is selected" );
  827. // Not undoAble? Just delete it.
  828. if ( !undoAble )
  829. {
  830. mSelRoad->deleteObject();
  831. mIsDirty = true;
  832. Con::executef( this, "onRoadSelected" );
  833. mSelNode = -1;
  834. return;
  835. }
  836. // Grab the mission editor undo manager.
  837. UndoManager *undoMan = NULL;
  838. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  839. {
  840. // Couldn't find it? Well just delete the Road.
  841. Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
  842. return;
  843. }
  844. else
  845. {
  846. // Create the UndoAction.
  847. MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road");
  848. action->deleteObject( mSelRoad );
  849. mIsDirty = true;
  850. // Submit it.
  851. undoMan->addAction( action );
  852. }
  853. // ScriptCallback with 'NULL' parameter for no Road currently selected.
  854. Con::executef( this, "onRoadSelected" );
  855. // Clear the SelectedNode (it has been deleted along with the River).
  856. setSelectedNode( -1 );
  857. mSelNode = -1;
  858. }
  859. void GuiMeshRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false )
  860. {
  861. mMode = mode;
  862. if( sourceShortcut )
  863. Con::executef( this, "paletteSync", (const char*)mode );
  864. }
  865. void GuiMeshRoadEditorCtrl::setSelectedRoad( MeshRoad *road )
  866. {
  867. mSelRoad = road;
  868. if ( mSelRoad != NULL )
  869. Con::executef( this, "onRoadSelected", road->getIdString() );
  870. else
  871. Con::executef( this, "onRoadSelected" );
  872. if ( mSelRoad != road )
  873. setSelectedNode(-1);
  874. }
  875. void GuiMeshRoadEditorCtrl::setNodeWidth( F32 width )
  876. {
  877. if ( mSelRoad && mSelNode != -1 )
  878. {
  879. mSelRoad->setNodeWidth( mSelNode, width );
  880. mIsDirty = true;
  881. }
  882. }
  883. F32 GuiMeshRoadEditorCtrl::getNodeWidth()
  884. {
  885. if ( mSelRoad && mSelNode != -1 )
  886. return mSelRoad->getNodeWidth( mSelNode );
  887. return 0.0f;
  888. }
  889. void GuiMeshRoadEditorCtrl::setNodeDepth(F32 depth)
  890. {
  891. if ( mSelRoad && mSelNode != -1 )
  892. {
  893. mSelRoad->setNodeDepth( mSelNode, depth );
  894. mIsDirty = true;
  895. }
  896. }
  897. F32 GuiMeshRoadEditorCtrl::getNodeDepth()
  898. {
  899. if ( mSelRoad && mSelNode != -1 )
  900. return mSelRoad->getNodeDepth( mSelNode );
  901. return 0.0f;
  902. }
  903. void GuiMeshRoadEditorCtrl::setNodePosition( Point3F pos )
  904. {
  905. if ( mSelRoad && mSelNode != -1 )
  906. {
  907. mSelRoad->setNodePosition( mSelNode, pos );
  908. mIsDirty = true;
  909. }
  910. }
  911. Point3F GuiMeshRoadEditorCtrl::getNodePosition()
  912. {
  913. if ( mSelRoad && mSelNode != -1 )
  914. return mSelRoad->getNodePosition( mSelNode );
  915. return Point3F( 0, 0, 0 );
  916. }
  917. void GuiMeshRoadEditorCtrl::setNodeNormal( const VectorF &normal )
  918. {
  919. if ( mSelRoad && mSelNode != -1 )
  920. {
  921. mSelRoad->setNodeNormal( mSelNode, normal );
  922. mIsDirty = true;
  923. }
  924. }
  925. VectorF GuiMeshRoadEditorCtrl::getNodeNormal()
  926. {
  927. if ( mSelRoad && mSelNode != -1 )
  928. return mSelRoad->getNodeNormal( mSelNode );
  929. return VectorF::Zero;
  930. }
  931. void GuiMeshRoadEditorCtrl::setSelectedNode( S32 node )
  932. {
  933. if ( mSelNode == node )
  934. return;
  935. mSelNode = node;
  936. if ( mSelNode != -1 )
  937. {
  938. const MeshRoadNode &node = mSelRoad->mNodes[mSelNode];
  939. MatrixF objMat = mSelRoad->getNodeTransform(mSelNode);
  940. Point3F objScale( node.width, 1.0f, node.depth );
  941. Point3F worldPos = node.point;
  942. mGizmo->set( objMat, worldPos, objScale );
  943. }
  944. if ( mSelNode != -1 )
  945. Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode) );
  946. else
  947. Con::executef( this, "onNodeSelected", Con::getIntArg(-1) );
  948. }
  949. void GuiMeshRoadEditorCtrl::submitUndo( const UTF8 *name )
  950. {
  951. // Grab the mission editor undo manager.
  952. UndoManager *undoMan = NULL;
  953. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  954. {
  955. Con::errorf( "GuiMeshRoadEditorCtrl::submitUndo() - EUndoManager not found!" );
  956. return;
  957. }
  958. // Setup the action.
  959. GuiMeshRoadEditorUndoAction *action = new GuiMeshRoadEditorUndoAction( name );
  960. action->mObjId = mSelRoad->getId();
  961. //action->mMetersPerSegment = mSelRoad->mMetersPerSegment;
  962. action->mEditor = this;
  963. for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ )
  964. {
  965. action->mNodes.push_back( mSelRoad->mNodes[i] );
  966. }
  967. undoMan->addAction( action );
  968. }
  969. void GuiMeshRoadEditorCtrl::matchTerrainToRoad()
  970. {
  971. if ( !mSelRoad )
  972. return;
  973. // Not implemented, but a potentially useful idea.
  974. // Move manipulate the terrain so that the MeshRoad appears to be sitting
  975. // on top of it.
  976. // The opposite could also be useful, manipulate the MeshRoad to line up
  977. // with the terrain underneath it.
  978. }
  979. ConsoleMethod( GuiMeshRoadEditorCtrl, deleteNode, void, 2, 2, "deleteNode()" )
  980. {
  981. object->deleteSelectedNode();
  982. }
  983. ConsoleMethod( GuiMeshRoadEditorCtrl, getMode, const char*, 2, 2, "" )
  984. {
  985. return object->getMode();
  986. }
  987. ConsoleMethod( GuiMeshRoadEditorCtrl, setMode, void, 3, 3, "setMode( String mode )" )
  988. {
  989. String newMode = ( argv[2] );
  990. object->setMode( newMode );
  991. }
  992. ConsoleMethod( GuiMeshRoadEditorCtrl, getNodeWidth, F32, 2, 2, "" )
  993. {
  994. return object->getNodeWidth();
  995. }
  996. ConsoleMethod( GuiMeshRoadEditorCtrl, setNodeWidth, void, 3, 3, "" )
  997. {
  998. object->setNodeWidth( dAtof(argv[2]) );
  999. }
  1000. ConsoleMethod( GuiMeshRoadEditorCtrl, getNodeDepth, F32, 2, 2, "" )
  1001. {
  1002. return object->getNodeDepth();
  1003. }
  1004. ConsoleMethod( GuiMeshRoadEditorCtrl, setNodeDepth, void, 3, 3, "" )
  1005. {
  1006. object->setNodeDepth( dAtof(argv[2]) );
  1007. }
  1008. ConsoleMethod( GuiMeshRoadEditorCtrl, getNodePosition, const char*, 2, 2, "" )
  1009. {
  1010. char* returnBuffer = Con::getReturnBuffer(256);
  1011. dSprintf(returnBuffer, 256, "%f %f %f",
  1012. object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z);
  1013. return returnBuffer;
  1014. }
  1015. ConsoleMethod( GuiMeshRoadEditorCtrl, setNodePosition, void, 3, 3, "" )
  1016. {
  1017. Point3F pos;
  1018. S32 count = dSscanf( argv[2], "%f %f %f",
  1019. &pos.x, &pos.y, &pos.z);
  1020. if ( (count != 3) )
  1021. {
  1022. Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]);
  1023. return;
  1024. }
  1025. object->setNodePosition( pos );
  1026. }
  1027. ConsoleMethod( GuiMeshRoadEditorCtrl, getNodeNormal, const char*, 2, 2, "" )
  1028. {
  1029. char* returnBuffer = Con::getReturnBuffer(256);
  1030. dSprintf(returnBuffer, 256, "%f %f %f",
  1031. object->getNodeNormal().x, object->getNodeNormal().y, object->getNodeNormal().z);
  1032. return returnBuffer;
  1033. }
  1034. ConsoleMethod( GuiMeshRoadEditorCtrl, setNodeNormal, void, 3, 3, "" )
  1035. {
  1036. VectorF normal;
  1037. S32 count = dSscanf( argv[2], "%f %f %f",
  1038. &normal.x, &normal.y, &normal.z);
  1039. if ( (count != 3) )
  1040. {
  1041. Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]);
  1042. return;
  1043. }
  1044. object->setNodeNormal( normal );
  1045. }
  1046. ConsoleMethod( GuiMeshRoadEditorCtrl, setSelectedRoad, void, 2, 3, "" )
  1047. {
  1048. if ( argc == 2 )
  1049. object->setSelectedRoad(NULL);
  1050. else
  1051. {
  1052. MeshRoad *road = NULL;
  1053. if ( Sim::findObject( argv[2], road ) )
  1054. object->setSelectedRoad(road);
  1055. }
  1056. }
  1057. ConsoleMethod( GuiMeshRoadEditorCtrl, getSelectedRoad, const char*, 2, 2, "" )
  1058. {
  1059. MeshRoad *road = object->getSelectedRoad();
  1060. if ( !road )
  1061. return NULL;
  1062. return road->getIdString();
  1063. }
  1064. ConsoleMethod( GuiMeshRoadEditorCtrl, regenerate, void, 2, 2, "" )
  1065. {
  1066. MeshRoad *road = object->getSelectedRoad();
  1067. if ( road )
  1068. road->regenerate();
  1069. }
  1070. ConsoleMethod( GuiMeshRoadEditorCtrl, matchTerrainToRoad, void, 2, 2, "" )
  1071. {
  1072. object->matchTerrainToRoad();
  1073. }