guiMeshRoadEditorCtrl.cpp 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780
  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 "console/engineAPI.h"
  26. #include "environment/meshRoad.h"
  27. #include "renderInstance/renderPassManager.h"
  28. #include "collision/collision.h"
  29. #include "math/util/frustum.h"
  30. #include "math/mathUtils.h"
  31. #include "gfx/gfxPrimitiveBuffer.h"
  32. #include "gfx/gfxTextureHandle.h"
  33. #include "gfx/gfxTransformSaver.h"
  34. #include "gfx/primBuilder.h"
  35. #include "gfx/gfxDrawUtil.h"
  36. #include "scene/sceneRenderState.h"
  37. #include "scene/sceneManager.h"
  38. #include "gui/core/guiCanvas.h"
  39. #include "gui/buttons/guiButtonCtrl.h"
  40. #include "gui/worldEditor/undoActions.h"
  41. #include "T3D/gameBase/gameConnection.h"
  42. #include "gfx/sim/debugDraw.h"
  43. #include "materials/materialDefinition.h"
  44. #include "T3D/prefab.h"
  45. #include "T3D/Scene.h"
  46. IMPLEMENT_CONOBJECT(GuiMeshRoadEditorCtrl);
  47. ConsoleDocClass( GuiMeshRoadEditorCtrl,
  48. "@brief GUI tool that makes up the Mesh Road Editor\n\n"
  49. "Editor use only.\n\n"
  50. "@internal"
  51. );
  52. S32 _NodeIndexCmp( U32 const *a, U32 const *b )
  53. {
  54. S32 a2 = (*a);
  55. S32 b2 = (*b);
  56. S32 diff = a2 - b2;
  57. return diff < 0 ? 1 : diff > 0 ? -1 : 0;
  58. }
  59. GuiMeshRoadEditorCtrl::GuiMeshRoadEditorCtrl()
  60. :
  61. // Each of the mode names directly correlates with the Mesh Road Editor's
  62. // tool palette
  63. mSelectMeshRoadMode("MeshRoadEditorSelectMode"),
  64. mAddMeshRoadMode("MeshRoadEditorAddRoadMode"),
  65. mAddNodeMode("MeshRoadEditorAddNodeMode"),
  66. mInsertPointMode("MeshRoadEditorInsertPointMode"),
  67. mRemovePointMode("MeshRoadEditorRemovePointMode"),
  68. mMovePointMode("MeshRoadEditorMoveMode"),
  69. mScalePointMode("MeshRoadEditorScaleMode"),
  70. mRotatePointMode("MeshRoadEditorRotateMode"),
  71. mSavedDrag(false),
  72. mIsDirty( false ),
  73. mSavedProfileDrag( false ),
  74. mDeselectProfileNode( false ),
  75. mProfileNode( -1 ),
  76. mProfileColor( 255,255,0 ),
  77. mRoadSet( NULL ),
  78. mSelNode( -1 ),
  79. mHoverNode( -1 ),
  80. mAddNodeIdx( 0 ),
  81. mSelRoad( NULL ),
  82. mHoverRoad( NULL ),
  83. mMode(mSelectMeshRoadMode),
  84. mDefaultWidth( 10.0f ),
  85. mDefaultDepth( 5.0f ),
  86. mDefaultNormal( 0,0,1 ),
  87. mNodeHalfSize( 4,4 ),
  88. mHoverSplineColor( 255,0,0,255 ),
  89. mSelectedSplineColor( 0,255,0,255 ),
  90. mHoverNodeColor( 255,255,255,255 ),
  91. mHasCopied( false )
  92. {
  93. INIT_ASSET(TopMaterial);
  94. INIT_ASSET(BottomMaterial);
  95. INIT_ASSET(SideMaterial);
  96. mTopMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultTopMaterialAsset");
  97. mBottomMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultBottomMaterialAsset");
  98. mSideMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultSideMaterialAsset");
  99. }
  100. GuiMeshRoadEditorCtrl::~GuiMeshRoadEditorCtrl()
  101. {
  102. // nothing to do
  103. }
  104. void GuiMeshRoadEditorUndoAction::undo()
  105. {
  106. MeshRoad *object = NULL;
  107. if ( !Sim::findObject( mObjId, object ) )
  108. return;
  109. // Temporarily save the Roads current data.
  110. //F32 metersPerSeg = object->mMetersPerSegment;
  111. Vector<MeshRoadNode> nodes;
  112. nodes.merge( object->mNodes );
  113. // Restore the River properties saved in the UndoAction
  114. //object->mMetersPerSegment = mMetersPerSegment;
  115. // Restore the Nodes saved in the UndoAction
  116. object->mNodes.clear();
  117. for ( U32 i = 0; i < mNodes.size(); i++ )
  118. {
  119. object->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal );
  120. }
  121. // Temporarily save the Roads current profile data.
  122. Vector<MeshRoadProfileNode> profNodes;
  123. Vector<U8> profMtrls;
  124. profNodes.merge( object->mSideProfile.mNodes );
  125. profMtrls.merge( object->mSideProfile.mSegMtrls );
  126. // Restore the Profile Nodes saved in the UndoAction
  127. Point3F pos;
  128. object->mSideProfile.mNodes.clear();
  129. object->mSideProfile.mSegMtrls.clear();
  130. for ( U32 i = 0; i < mProfileNodes.size(); i++ )
  131. {
  132. MeshRoadProfileNode newNode;
  133. pos = mProfileNodes[i].getPosition();
  134. newNode.setSmoothing( mProfileNodes[i].isSmooth() );
  135. object->mSideProfile.mNodes.push_back( newNode );
  136. object->mSideProfile.mNodes.last().setPosition( pos.x, pos.y );
  137. if(i)
  138. object->mSideProfile.mSegMtrls.push_back(mProfileMtrls[i-1]);
  139. }
  140. // Set the first node position to trigger packet update to client
  141. pos.set(0.0f, 0.0f, 0.0f);
  142. object->mSideProfile.setNodePosition(0,pos);
  143. // Regenerate the Road
  144. object->mSideProfile.generateNormals();
  145. // If applicable set the selected Road and node
  146. mEditor->mProfileNode = -1;
  147. // Now save the previous Road data in this UndoAction
  148. // since an undo action must become a redo action and vice-versa
  149. //mMetersPerSegment = metersPerSeg;
  150. mProfileNodes.clear();
  151. mProfileNodes.merge( profNodes );
  152. mProfileMtrls.clear();
  153. mProfileMtrls.merge( profMtrls );
  154. // Regenerate the Road
  155. object->regenerate();
  156. // If applicable set the selected Road and node
  157. mEditor->mSelRoad = object;
  158. mEditor->mSelNode = -1;
  159. // Now save the previous Road data in this UndoAction
  160. // since an undo action must become a redo action and vice-versa
  161. //mMetersPerSegment = metersPerSeg;
  162. mNodes.clear();
  163. mNodes.merge( nodes );
  164. }
  165. bool GuiMeshRoadEditorCtrl::onAdd()
  166. {
  167. if( !Parent::onAdd() )
  168. return false;
  169. mRoadSet = MeshRoad::getServerSet();
  170. GFXStateBlockDesc desc;
  171. desc.fillMode = GFXFillSolid;
  172. desc.blendDefined = true;
  173. desc.blendEnable = false;
  174. desc.zDefined = true;
  175. desc.zEnable = false;
  176. desc.cullDefined = true;
  177. desc.cullMode = GFXCullNone;
  178. mZDisableSB = GFX->createStateBlock(desc);
  179. desc.zEnable = true;
  180. mZEnableSB = GFX->createStateBlock(desc);
  181. return true;
  182. }
  183. void GuiMeshRoadEditorCtrl::initPersistFields()
  184. {
  185. docsURL;
  186. addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiMeshRoadEditorCtrl ) );
  187. addField( "DefaultDepth", TypeF32, Offset( mDefaultDepth, GuiMeshRoadEditorCtrl ) );
  188. addField( "DefaultNormal", TypePoint3F,Offset( mDefaultNormal, GuiMeshRoadEditorCtrl ) );
  189. addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiMeshRoadEditorCtrl ) );
  190. addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiMeshRoadEditorCtrl ) );
  191. addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiMeshRoadEditorCtrl ) );
  192. addField( "isDirty", TypeBool, Offset( mIsDirty, GuiMeshRoadEditorCtrl ) );
  193. INITPERSISTFIELD_MATERIALASSET(TopMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on upper surface road creation.");
  194. INITPERSISTFIELD_MATERIALASSET(BottomMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on bottom surface road creation.");
  195. INITPERSISTFIELD_MATERIALASSET(SideMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on side surface road creation.");
  196. //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiMeshRoadEditorCtrl) );
  197. //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiMeshRoadEditorCtrl) );
  198. //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiMeshRoadEditorCtrl) );
  199. //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiMeshRoadEditorCtrl) );
  200. Parent::initPersistFields();
  201. }
  202. void GuiMeshRoadEditorCtrl::onSleep()
  203. {
  204. Parent::onSleep();
  205. mMode = mSelectMeshRoadMode;
  206. mHoverNode = -1;
  207. mHoverRoad = NULL;
  208. setSelectedNode(-1);
  209. }
  210. void GuiMeshRoadEditorCtrl::get3DCursor( GuiCursor *&cursor,
  211. bool &visible,
  212. const Gui3DMouseEvent &event_ )
  213. {
  214. //cursor = mAddNodeCursor;
  215. //visible = false;
  216. cursor = NULL;
  217. visible = false;
  218. GuiCanvas *root = getRoot();
  219. if ( !root )
  220. return;
  221. S32 currCursor = PlatformCursorController::curArrow;
  222. if ( root->mCursorChanged == currCursor )
  223. return;
  224. PlatformWindow *window = root->getPlatformWindow();
  225. PlatformCursorController *controller = window->getCursorController();
  226. // We've already changed the cursor,
  227. // so set it back before we change it again.
  228. if( root->mCursorChanged != -1)
  229. controller->popCursor();
  230. // Now change the cursor shape
  231. controller->pushCursor(currCursor);
  232. root->mCursorChanged = currCursor;
  233. }
  234. void GuiMeshRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
  235. {
  236. mHasCopied = false;
  237. mGizmo->on3DMouseDown( event );
  238. if ( !isFirstResponder() )
  239. setFirstResponder();
  240. if( MeshRoad::smShowRoadProfile && mSelRoad )
  241. {
  242. // Ctrl-Click = Add Node
  243. if(event.modifier & SI_CTRL)
  244. {
  245. S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
  246. if(clickedNode != -1)
  247. {
  248. // If clicked node is already in list, remove it, else add it to list
  249. if(!mSelProfNodeList.remove(clickedNode) && clickedNode > 0)
  250. mSelProfNodeList.push_back(clickedNode);
  251. return;
  252. }
  253. Point3F pos;
  254. PlaneF xy( mSelRoad->mSlices[0].p2, -mSelRoad->mSlices[0].fvec );
  255. xy.intersect(event.pos, event.vec, &pos);
  256. mSelRoad->mSideProfile.worldToObj(pos);
  257. U32 node = mSelRoad->mSideProfile.clickOnLine(pos);
  258. if(node != -1)
  259. {
  260. submitUndo( "Add Profile Node" );
  261. mSelRoad->mSideProfile.addPoint(node, pos);
  262. mProfileNode = node;
  263. mSelProfNodeList.clear();
  264. mSelProfNodeList.push_back(node);
  265. mIsDirty = true;
  266. }
  267. return;
  268. }
  269. // Alt-Click = Delete Node
  270. if(event.modifier & SI_ALT)
  271. {
  272. S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
  273. if(mSelProfNodeList.find_next(clickedNode) != -1)
  274. {
  275. submitUndo( "Delete Profile Node" );
  276. mSelProfNodeList.sort( _NodeIndexCmp );
  277. for(U32 i=0; i < mSelProfNodeList.size(); i++)
  278. mSelRoad->mSideProfile.removePoint( mSelProfNodeList[i] );
  279. mProfileNode = -1;
  280. mSelProfNodeList.clear();
  281. mIsDirty = true;
  282. }
  283. else if(clickedNode > 0 && clickedNode < mSelRoad->mSideProfile.mNodes.size()-1)
  284. {
  285. submitUndo( "Delete Profile Node" );
  286. mSelRoad->mSideProfile.removePoint( clickedNode );
  287. mProfileNode = -1;
  288. mSelProfNodeList.clear();
  289. mIsDirty = true;
  290. }
  291. return;
  292. }
  293. // Shift-Click = Toggle Node Smoothing
  294. if(event.modifier & SI_SHIFT)
  295. {
  296. S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
  297. if(clickedNode != -1)
  298. {
  299. submitUndo( "Smooth Profile Node" );
  300. if(mSelProfNodeList.find_next(clickedNode) != -1)
  301. {
  302. for(U32 i=0; i < mSelProfNodeList.size(); i++)
  303. mSelRoad->mSideProfile.toggleSmoothing(mSelProfNodeList[i]);
  304. }
  305. else
  306. {
  307. mSelRoad->mSideProfile.toggleSmoothing(clickedNode);
  308. if(clickedNode != 0)
  309. {
  310. mProfileNode = clickedNode;
  311. mSelProfNodeList.clear();
  312. mSelProfNodeList.push_back(clickedNode);
  313. }
  314. }
  315. mIsDirty = true;
  316. return;
  317. }
  318. Point3F pos;
  319. PlaneF xy( mSelRoad->mSlices[0].p2, -mSelRoad->mSlices[0].fvec );
  320. xy.intersect(event.pos, event.vec, &pos);
  321. mSelRoad->mSideProfile.worldToObj(pos);
  322. U32 node = mSelRoad->mSideProfile.clickOnLine(pos);
  323. if(node > 0)
  324. {
  325. submitUndo( "Profile Material" );
  326. mSelRoad->mSideProfile.toggleSegMtrl(node-1);
  327. mIsDirty = true;
  328. }
  329. return;
  330. }
  331. // Click to select/deselect nodes
  332. S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
  333. if(clickedNode != -1)
  334. {
  335. if(mSelProfNodeList.find_next(clickedNode) != -1)
  336. {
  337. mProfileNode = clickedNode;
  338. mDeselectProfileNode = true;
  339. }
  340. else if(clickedNode != 0)
  341. {
  342. mProfileNode = clickedNode;
  343. mSelProfNodeList.clear();
  344. mSelProfNodeList.push_back(clickedNode);
  345. }
  346. else
  347. {
  348. mProfileNode = -1;
  349. mSelProfNodeList.clear();
  350. // Reset profile if Node 0 is double-clicked
  351. if( event.mouseClickCount > 1 )
  352. {
  353. submitUndo( "Reset Profile" );
  354. mSelRoad->mSideProfile.resetProfile(mSelRoad->mSlices[0].depth);
  355. mSelRoad->regenerate();
  356. }
  357. }
  358. return;
  359. }
  360. mProfileNode = -1;
  361. mSelProfNodeList.clear();
  362. }
  363. // Get the raycast collision position
  364. Point3F tPos;
  365. if ( !getStaticPos( event, tPos ) )
  366. return;
  367. mouseLock();
  368. // Construct a LineSegment from the camera position to 1000 meters away in
  369. // the direction clicked.
  370. // If that segment hits the terrain, truncate the ray to only be that length.
  371. // We will use a LineSegment/Sphere intersection test to determine if a MeshRoadNode
  372. // was clicked.
  373. MeshRoad *pRoad = NULL;
  374. MeshRoad *pClickedRoad = NULL;
  375. U32 insertNodeIdx = -1;
  376. Point3F collisionPnt;
  377. Point3F startPnt = event.pos;
  378. Point3F endPnt = event.pos + event.vec * 2000.0f;
  379. RayInfo ri;
  380. if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
  381. endPnt = ri.point;
  382. DebugDrawer *ddraw = DebugDrawer::get();
  383. if ( false && ddraw )
  384. {
  385. ddraw->drawLine( startPnt, endPnt, ColorI(255,0,0,255) );
  386. ddraw->setLastTTL(DebugDrawer::DD_INFINITE);
  387. }
  388. // Check for collision with nodes of the currently selected or highlighted MeshRoad
  389. // Did we click on a MeshRoad? check currently selected road first
  390. if ( mSelRoad != NULL && mSelRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
  391. {
  392. pClickedRoad = mSelRoad;
  393. }
  394. else
  395. {
  396. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  397. {
  398. pRoad = static_cast<MeshRoad*>( *iter );
  399. // Do not select or edit a MeshRoad within a Prefab.
  400. if ( Prefab::getPrefabByChild(pRoad) )
  401. continue;
  402. if ( pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
  403. {
  404. pClickedRoad = pRoad;
  405. break;
  406. }
  407. }
  408. }
  409. /*
  410. else if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
  411. {
  412. MeshRoad *pRoad = NULL;
  413. pRoad = dynamic_cast<MeshRoad*>(ri.object);
  414. if ( pRoad && pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
  415. pClickedRoad = pRoad;
  416. }
  417. */
  418. // Did we click on a node?
  419. bool nodeClicked = false;
  420. S32 clickedNodeIdx = -1;
  421. // If we clicked on the currently selected road, only scan its nodes
  422. if ( mSelRoad != NULL && pClickedRoad == mSelRoad )
  423. {
  424. clickedNodeIdx = _getNodeAtScreenPos( mSelRoad, event.mousePoint );
  425. nodeClicked = clickedNodeIdx != -1;
  426. }
  427. else
  428. {
  429. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  430. {
  431. pRoad = static_cast<MeshRoad*>( *iter );
  432. // Do not select or edit a MeshRoad within a Prefab.
  433. if ( Prefab::getPrefabByChild(pRoad) )
  434. continue;
  435. clickedNodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint );
  436. if ( clickedNodeIdx != -1 )
  437. {
  438. nodeClicked = true;
  439. pClickedRoad = pRoad;
  440. break;
  441. }
  442. }
  443. }
  444. // shortcuts
  445. bool dblClick = ( event.mouseClickCount > 1 );
  446. if( dblClick )
  447. {
  448. if( mMode == mSelectMeshRoadMode )
  449. {
  450. setMode( mAddMeshRoadMode, true );
  451. return;
  452. }
  453. if( mMode == mAddNodeMode )
  454. {
  455. // Delete the node attached to the cursor.
  456. deleteSelectedNode();
  457. mMode = mAddMeshRoadMode;
  458. return;
  459. }
  460. }
  461. //this check is here in order to bounce back from deleting a whole road with ctrl+z
  462. //this check places the editor back into addrivermode
  463. if ( mMode == mAddNodeMode )
  464. {
  465. if ( !mSelRoad )
  466. mMode = mAddMeshRoadMode;
  467. }
  468. if ( mMode == mSelectMeshRoadMode )
  469. {
  470. // Did not click on a MeshRoad or a node.
  471. if ( !pClickedRoad )
  472. {
  473. setSelectedRoad( NULL );
  474. setSelectedNode( -1 );
  475. return;
  476. }
  477. // Clicked on a MeshRoad that wasn't the currently selected River.
  478. if ( pClickedRoad != mSelRoad )
  479. {
  480. setSelectedRoad( pClickedRoad );
  481. setSelectedNode( clickedNodeIdx );
  482. return;
  483. }
  484. // Clicked on a node in the currently selected River that wasn't
  485. // the currently selected node.
  486. if ( nodeClicked )
  487. {
  488. setSelectedNode( clickedNodeIdx );
  489. return;
  490. }
  491. }
  492. else if ( mMode == mAddMeshRoadMode )
  493. {
  494. if ( nodeClicked )
  495. {
  496. // A double-click on a node in Normal mode means set AddNode mode.
  497. if ( clickedNodeIdx == 0 )
  498. {
  499. setSelectedRoad( pClickedRoad );
  500. setSelectedNode( clickedNodeIdx );
  501. mAddNodeIdx = clickedNodeIdx;
  502. mMode = mAddNodeMode;
  503. mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
  504. mIsDirty = true;
  505. return;
  506. }
  507. else if ( clickedNodeIdx == pClickedRoad->mNodes.size() - 1 )
  508. {
  509. setSelectedRoad( pClickedRoad );
  510. setSelectedNode( clickedNodeIdx );
  511. mAddNodeIdx = U32_MAX;
  512. mMode = mAddNodeMode;
  513. mSelNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
  514. mIsDirty = true;
  515. setSelectedNode( mSelNode );
  516. return;
  517. }
  518. }
  519. MeshRoad *newRoad = new MeshRoad;
  520. if(mTopMaterialAsset.notNull())
  521. newRoad->_setTopMaterial(mTopMaterialAssetId);
  522. if (mBottomMaterialAsset.notNull())
  523. newRoad->_setBottomMaterial(mBottomMaterialAssetId);
  524. if (mSideMaterialAsset.notNull())
  525. newRoad->_setSideMaterial(mSideMaterialAssetId);
  526. newRoad->registerObject();
  527. // Add to scene
  528. Scene *scene;
  529. scene = Scene::getRootScene();
  530. if ( !scene)
  531. Con::errorf( "GuiMeshRoadEditorCtrl - could not find Scene to add new MeshRoad" );
  532. else
  533. scene->addObject( newRoad );
  534. Point3F pos( endPnt );
  535. pos.z += mDefaultDepth * 0.5f;
  536. newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 );
  537. U32 newNode = newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 );
  538. // Always add to the end of the road, the first node is the start.
  539. mAddNodeIdx = U32_MAX;
  540. setSelectedRoad( newRoad );
  541. setSelectedNode( newNode );
  542. mMode = mAddNodeMode;
  543. // Disable the hover node while in addNodeMode, we
  544. // don't want some random node enlarged.
  545. mHoverNode = -1;
  546. // Grab the mission editor undo manager.
  547. UndoManager *undoMan = NULL;
  548. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  549. {
  550. Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
  551. return;
  552. }
  553. // Create the UndoAction.
  554. MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad");
  555. action->addObject( newRoad );
  556. // Submit it.
  557. undoMan->addAction( action );
  558. //send a callback to script after were done here if one exists
  559. if (isMethod("onRoadCreation"))
  560. Con::executef(this, "onRoadCreation");
  561. return;
  562. }
  563. else if ( mMode == mAddNodeMode )
  564. {
  565. // Oops the road got deleted, maybe from an undo action?
  566. // Back to NormalMode.
  567. if ( mSelRoad )
  568. {
  569. // A double-click on a node in Normal mode means set AddNode mode.
  570. if ( clickedNodeIdx == 0 )
  571. {
  572. submitUndo( "Add Node" );
  573. mAddNodeIdx = clickedNodeIdx;
  574. mMode = mAddNodeMode;
  575. mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
  576. mIsDirty = true;
  577. setSelectedNode( mSelNode );
  578. return;
  579. }
  580. else
  581. {
  582. if( pClickedRoad && clickedNodeIdx == pClickedRoad->mNodes.size() - 1 )
  583. {
  584. submitUndo( "Add Node" );
  585. mAddNodeIdx = U32_MAX;
  586. mMode = mAddNodeMode;
  587. U32 newNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
  588. mIsDirty = true;
  589. setSelectedNode( newNode );
  590. return;
  591. }
  592. else
  593. {
  594. submitUndo( "Insert Node" );
  595. // A single-click on empty space while in
  596. // AddNode mode means insert / add a node.
  597. //submitUndo( "Add Node" );
  598. U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx);
  599. mIsDirty = true;
  600. setSelectedNode( newNode );
  601. return;
  602. }
  603. }
  604. }
  605. }
  606. else if ( mMode == mInsertPointMode && mSelRoad != NULL )
  607. {
  608. if ( pClickedRoad == mSelRoad && insertNodeIdx != -1 )
  609. {
  610. // NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node...
  611. // in order to handle inserting nodes in the same way as for fxRoad
  612. U32 prevNodeIdx = insertNodeIdx;
  613. U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRoad->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1;
  614. const MeshRoadNode &prevNode = mSelRoad->mNodes[prevNodeIdx];
  615. const MeshRoadNode &nextNode = mSelRoad->mNodes[nextNodeIdx];
  616. F32 width = ( prevNode.width + nextNode.width ) * 0.5f;
  617. F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f;
  618. Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f;
  619. normal.normalize();
  620. submitUndo( "Insert Node" );
  621. U32 newNode = mSelRoad->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 );
  622. mIsDirty = true;
  623. setSelectedNode( newNode );
  624. return;
  625. }
  626. }
  627. else if ( mMode == mRemovePointMode && mSelRoad != NULL )
  628. {
  629. if ( nodeClicked && pClickedRoad == mSelRoad )
  630. {
  631. setSelectedNode( clickedNodeIdx );
  632. deleteSelectedNode();
  633. return;
  634. }
  635. }
  636. else if ( mMode == mMovePointMode )
  637. {
  638. if ( nodeClicked && pClickedRoad == mSelRoad )
  639. {
  640. setSelectedNode( clickedNodeIdx );
  641. return;
  642. }
  643. }
  644. else if ( mMode == mScalePointMode )
  645. {
  646. if ( nodeClicked && pClickedRoad == mSelRoad )
  647. {
  648. setSelectedNode( clickedNodeIdx );
  649. return;
  650. }
  651. }
  652. else if ( mMode == mRotatePointMode )
  653. {
  654. if ( nodeClicked && pClickedRoad == mSelRoad )
  655. {
  656. setSelectedNode( clickedNodeIdx );
  657. return;
  658. }
  659. }
  660. }
  661. void GuiMeshRoadEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event)
  662. {
  663. //mIsPanning = true;
  664. }
  665. void GuiMeshRoadEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event)
  666. {
  667. //mIsPanning = false;
  668. }
  669. void GuiMeshRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
  670. {
  671. mGizmo->on3DMouseUp(event);
  672. mSavedDrag = false;
  673. mSavedProfileDrag = false;
  674. if( MeshRoad::smShowRoadProfile && mSelRoad )
  675. {
  676. // If we need to deselect node... this means we clicked on a selected node without dragging
  677. if( mDeselectProfileNode )
  678. {
  679. S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
  680. if(clickedNode == mProfileNode)
  681. {
  682. mProfileNode = -1;
  683. mSelProfNodeList.clear();
  684. }
  685. mDeselectProfileNode = false;
  686. }
  687. // Else if we dragged a node, update the road
  688. else
  689. {
  690. S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
  691. if(clickedNode == mProfileNode)
  692. mSelRoad->regenerate(); // This regens the road for collision purposes on the server
  693. }
  694. }
  695. mouseUnlock();
  696. }
  697. void GuiMeshRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
  698. {
  699. if ( mSelRoad != NULL && mMode == mAddNodeMode )
  700. {
  701. Point3F pos;
  702. mSelRoad->disableCollision();
  703. if ( getStaticPos( event, pos ) )
  704. {
  705. pos.z += mSelRoad->getNodeDepth( mSelNode ) * 0.5f;
  706. mSelRoad->setNodePosition( mSelNode, pos );
  707. mIsDirty = true;
  708. }
  709. mSelRoad->enableCollision();
  710. return;
  711. }
  712. if ( mSelRoad != NULL && mSelNode != -1 )
  713. {
  714. mGizmo->on3DMouseMove( event );
  715. //mGizmo.collideAxisGizmo( event );
  716. //Con::printf( "SelectedAxis: %i", mGizmo.getSelectedAxis() );
  717. }
  718. // Is cursor hovering over a river?
  719. if ( mMode == mSelectMeshRoadMode )
  720. {
  721. mHoverRoad = NULL;
  722. Point3F startPnt = event.pos;
  723. Point3F endPnt = event.pos + event.vec * 1000.0f;
  724. RayInfo ri;
  725. if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
  726. {
  727. MeshRoad *pRoad = NULL;
  728. pRoad = dynamic_cast<MeshRoad*>(ri.object);
  729. // Do not select or edit a MeshRoad within a Prefab.
  730. if ( pRoad && !Prefab::getPrefabByChild(pRoad) )
  731. mHoverRoad = pRoad;
  732. }
  733. }
  734. // Is cursor over a node?
  735. if ( mHoverRoad )
  736. {
  737. MeshRoad *pRoad = NULL;
  738. S32 nodeIdx = -1;
  739. for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
  740. {
  741. pRoad = static_cast<MeshRoad*>( *iter );
  742. nodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint );
  743. if ( nodeIdx != -1 )
  744. {
  745. mHoverRoad = pRoad;
  746. break;
  747. }
  748. }
  749. mHoverNode = nodeIdx;
  750. }
  751. }
  752. void GuiMeshRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
  753. {
  754. if( MeshRoad::smShowRoadProfile && mProfileNode > 0 && mSelRoad)
  755. {
  756. // If we haven't already saved,
  757. // save an undo action to get back to this state,
  758. // before we make any modifications to the selected node.
  759. if ( !mSavedProfileDrag )
  760. {
  761. submitUndo( "Modify Profile Node" );
  762. mSavedProfileDrag = true;
  763. mIsDirty = true;
  764. }
  765. U32 idx;
  766. Point3F pos, diff;
  767. PlaneF xy( mSelRoad->mSlices[0].p2, -mSelRoad->mSlices[0].fvec );
  768. xy.intersect(event.pos, event.vec, &pos);
  769. mSelRoad->mSideProfile.worldToObj(pos);
  770. diff = pos - mSelRoad->mSideProfile.mNodes[mProfileNode].getPosition();
  771. for(U32 i=0; i < mSelProfNodeList.size(); i++)
  772. {
  773. idx = mSelProfNodeList[i];
  774. pos = mSelRoad->mSideProfile.mNodes[idx].getPosition();
  775. pos += diff;
  776. if(pos.x < -mSelRoad->mSlices[0].width/2.0f)
  777. pos.x = -mSelRoad->mSlices[0].width/2.0f + 1e-6;
  778. mSelRoad->mSideProfile.setNodePosition( idx, pos );
  779. }
  780. mDeselectProfileNode = false;
  781. return;
  782. }
  783. // Drags are only used to transform nodes
  784. if ( !mSelRoad || mSelNode == -1 ||
  785. ( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) )
  786. return;
  787. // If we haven't already saved,
  788. // save an undo action to get back to this state,
  789. // before we make any modifications to the selected node.
  790. if ( !mSavedDrag )
  791. {
  792. submitUndo( "Modify Node" );
  793. mSavedDrag = true;
  794. }
  795. // If shift is held and we haven't already copied the node,
  796. // make a copy of the selected node and select it.
  797. if ( event.modifier & SI_SHIFT && !mHasCopied && mSelRoad->isEndNode( mSelNode ) )
  798. {
  799. const MeshRoadNode &data = mSelRoad->getNode( mSelNode );
  800. U32 insertIdx = ( mSelNode == 0 ) ? 0 : U32_MAX;
  801. U32 newNodeIdx = mSelRoad->insertNode( data.point, data.width, data.depth, data.normal, insertIdx );
  802. mIsDirty = true;
  803. mSelNode = -1;
  804. setSelectedNode( newNodeIdx );
  805. mHasCopied = true;
  806. }
  807. // Let the Gizmo handle the drag, eg, modify its transforms
  808. mGizmo->on3DMouseDragged( event );
  809. if ( mGizmo->isDirty() )
  810. {
  811. Point3F pos = mGizmo->getPosition();
  812. Point3F scale = mGizmo->getScale();
  813. const MatrixF &mat = mGizmo->getTransform();
  814. VectorF normal;
  815. mat.getColumn( 2, &normal );
  816. mSelRoad->setNode( pos, scale.x, scale.z, normal, mSelNode );
  817. mIsDirty = true;
  818. mGizmo->markClean();
  819. }
  820. Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) );
  821. }
  822. void GuiMeshRoadEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event)
  823. {
  824. // nothing to do
  825. }
  826. void GuiMeshRoadEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
  827. {
  828. // nothing to do
  829. }
  830. bool GuiMeshRoadEditorCtrl::onKeyDown(const GuiEvent& event)
  831. {
  832. if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode )
  833. {
  834. // Delete the node attached to the cursor.
  835. deleteSelectedNode();
  836. mMode = mAddMeshRoadMode;
  837. return true;
  838. }
  839. return false;
  840. }
  841. void GuiMeshRoadEditorCtrl::updateGuiInfo()
  842. {
  843. // nothing to do
  844. }
  845. void GuiMeshRoadEditorCtrl::renderScene(const RectI & updateRect)
  846. {
  847. //GFXDrawUtil *drawer = GFX->getDrawUtil();
  848. GFX->setStateBlock( mZDisableSB );
  849. // get the projected size...
  850. GameConnection* connection = GameConnection::getConnectionToServer();
  851. if(!connection)
  852. return;
  853. // Grab the camera's transform
  854. MatrixF mat;
  855. connection->getControlCameraTransform(0, &mat);
  856. // Get the camera position
  857. Point3F camPos;
  858. mat.getColumn(3,&camPos);
  859. // Set up transform
  860. if( mSelRoad )
  861. {
  862. MatrixF profileMat(true);
  863. profileMat.setRow(0, mSelRoad->mSlices[0].rvec);
  864. profileMat.setRow(1, mSelRoad->mSlices[0].uvec);
  865. profileMat.setRow(2, -mSelRoad->mSlices[0].fvec);
  866. mSelRoad->mSideProfile.setTransform(profileMat, mSelRoad->mSlices[0].p2);
  867. }
  868. if ( mHoverRoad && mHoverRoad != mSelRoad )
  869. {
  870. _drawSpline( mHoverRoad, mHoverSplineColor );
  871. }
  872. if ( mSelRoad )
  873. {
  874. _drawSpline( mSelRoad, mSelectedSplineColor );
  875. // Render Gizmo for selected node if were in either of the three transform modes
  876. if ( mSelNode != -1 && ( mMode == mMovePointMode || mMode == mScalePointMode || mMode == mRotatePointMode ) )
  877. {
  878. if( mMode == mMovePointMode )
  879. {
  880. mGizmo->getProfile()->mode = MoveMode;
  881. }
  882. else if( mMode == mScalePointMode )
  883. {
  884. mGizmo->getProfile()->mode = ScaleMode;
  885. }
  886. else if( mMode == mRotatePointMode )
  887. {
  888. mGizmo->getProfile()->mode = RotateMode;
  889. }
  890. const MeshRoadNode &node = mSelRoad->mNodes[mSelNode];
  891. MatrixF objMat = mSelRoad->getNodeTransform(mSelNode);
  892. Point3F objScale( node.width, 1.0f, node.depth );
  893. Point3F worldPos = node.point;
  894. mGizmo->set( objMat, worldPos, objScale );
  895. mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov );
  896. // Render Gizmo text
  897. mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection );
  898. }
  899. }
  900. DebugDrawer::get()->render();
  901. // Now draw all the 2d stuff!
  902. GFX->setClipRect(updateRect);
  903. // Draw Control nodes for selecting and highlighted rivers
  904. if ( mHoverRoad )
  905. _drawControlNodes( mHoverRoad, mHoverSplineColor );
  906. if ( mSelRoad )
  907. _drawControlNodes( mSelRoad, mSelectedSplineColor );
  908. if(MeshRoad::smShowRoadProfile)
  909. {
  910. char buf[64];
  911. Point2I posi;
  912. posi.x = 10;
  913. posi.y = updateRect.len_y() - 80;
  914. GFX->getDrawUtil()->setBitmapModulation(ColorI(128, 128, 128));
  915. dStrcpy(buf, "Reset Profile: Double-click Start Node", 64);
  916. GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
  917. posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
  918. dStrcpy(buf, "Move Node: Click and Drag Node", 64);
  919. GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
  920. posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
  921. dStrcpy(buf, "Select Multiple Nodes: Ctrl-click Nodes", 64);
  922. GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
  923. posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
  924. dStrcpy(buf, "Toggle Material: Shift-click Spline Segment", 64);
  925. GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
  926. posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
  927. dStrcpy(buf, "Toggle Smoothing: Shift-click Node", 64);
  928. GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
  929. posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
  930. dStrcpy(buf, "Delete Node: Alt-click Node", 64);
  931. GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
  932. posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
  933. dStrcpy(buf, "Add Node: Ctrl-click Spline", 64);
  934. GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
  935. }
  936. }
  937. S32 GuiMeshRoadEditorCtrl::_getNodeAtScreenPos( const MeshRoad *pRoad, const Point2I &posi )
  938. {
  939. for ( U32 i = 0; i < pRoad->mNodes.size(); i++ )
  940. {
  941. const Point3F &nodePos = pRoad->mNodes[i].point;
  942. Point3F screenPos;
  943. project( nodePos, &screenPos );
  944. if ( screenPos.z < 0.0f )
  945. continue;
  946. Point2I screenPosI( (S32)screenPos.x, (S32)screenPos.y );
  947. RectI nodeScreenRect( screenPosI - mNodeHalfSize, mNodeHalfSize * 2 );
  948. if ( nodeScreenRect.pointInRect(posi) )
  949. {
  950. // we found a hit!
  951. return i;
  952. }
  953. }
  954. return -1;
  955. }
  956. S32 GuiMeshRoadEditorCtrl::_getProfileNodeAtScreenPos( MeshRoadProfile *pProfile, const Point2I &posi)
  957. {
  958. for ( U32 i = 0; i < pProfile->mNodes.size(); i++ )
  959. {
  960. Point3F nodePos;
  961. pProfile->getNodeWorldPos(i, nodePos);
  962. Point3F screenPos;
  963. project( nodePos, &screenPos );
  964. if ( screenPos.z < 0.0f )
  965. continue;
  966. Point2I screenPosI( (S32)screenPos.x, (S32)screenPos.y );
  967. RectI nodeScreenRect( screenPosI - mNodeHalfSize, mNodeHalfSize * 2 );
  968. if ( nodeScreenRect.pointInRect(posi) )
  969. {
  970. // we found a hit!
  971. return i;
  972. }
  973. }
  974. return -1;
  975. }
  976. void GuiMeshRoadEditorCtrl::_drawSpline( MeshRoad *river, const ColorI &color )
  977. {
  978. if ( river->mSlices.size() <= 1 )
  979. return;
  980. if ( MeshRoad::smShowSpline )
  981. {
  982. // Render the River center-line
  983. if( MeshRoad::smShowRoadProfile )
  984. PrimBuild::color( ColorI(100,100,100) );
  985. else
  986. PrimBuild::color( color );
  987. PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
  988. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  989. {
  990. PrimBuild::vertex3fv( river->mSlices[i].p1 );
  991. }
  992. PrimBuild::end();
  993. }
  994. if ( MeshRoad::smWireframe )
  995. {
  996. // Left-side line
  997. PrimBuild::color3i( 100, 100, 100 );
  998. PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
  999. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  1000. {
  1001. PrimBuild::vertex3fv( river->mSlices[i].p0 );
  1002. }
  1003. PrimBuild::end();
  1004. // Right-side line
  1005. PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
  1006. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  1007. {
  1008. PrimBuild::vertex3fv( river->mSlices[i].p2 );
  1009. }
  1010. PrimBuild::end();
  1011. // Cross-sections
  1012. PrimBuild::begin( GFXLineList, river->mSlices.size() * 2 );
  1013. for ( U32 i = 0; i < river->mSlices.size(); i++ )
  1014. {
  1015. PrimBuild::vertex3fv( river->mSlices[i].p0 );
  1016. PrimBuild::vertex3fv( river->mSlices[i].p2 );
  1017. }
  1018. PrimBuild::end();
  1019. }
  1020. // If we are in Profile Edit Mode, draw the profile spline and node normals
  1021. if ( MeshRoad::smShowRoadProfile )
  1022. {
  1023. Point3F nodePos;
  1024. Point3F normEndPos;
  1025. U32 numSide, numTop, numBottom;
  1026. numSide = numTop = numBottom = 0;
  1027. for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
  1028. {
  1029. switch(river->mSideProfile.mSegMtrls[i])
  1030. {
  1031. case MeshRoad::Side: numSide++; break;
  1032. case MeshRoad::Top: numTop++; break;
  1033. case MeshRoad::Bottom: numBottom++; break;
  1034. }
  1035. }
  1036. // Render the profile spline
  1037. // Side
  1038. if(numSide)
  1039. {
  1040. PrimBuild::color( mProfileColor );
  1041. PrimBuild::begin( GFXLineList, 2*numSide );
  1042. for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
  1043. {
  1044. if(river->mSideProfile.mSegMtrls[i] == MeshRoad::Side)
  1045. {
  1046. river->mSideProfile.getNodeWorldPos(i, nodePos);
  1047. PrimBuild::vertex3fv( nodePos );
  1048. river->mSideProfile.getNodeWorldPos(i+1, nodePos);
  1049. PrimBuild::vertex3fv( nodePos );
  1050. }
  1051. }
  1052. PrimBuild::end();
  1053. }
  1054. // Top
  1055. if(numTop)
  1056. {
  1057. PrimBuild::color( ColorI(0,255,0) );
  1058. PrimBuild::begin( GFXLineList, 2*numTop );
  1059. for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
  1060. {
  1061. if(river->mSideProfile.mSegMtrls[i] == MeshRoad::Top)
  1062. {
  1063. river->mSideProfile.getNodeWorldPos(i, nodePos);
  1064. PrimBuild::vertex3fv( nodePos );
  1065. river->mSideProfile.getNodeWorldPos(i+1, nodePos);
  1066. PrimBuild::vertex3fv( nodePos );
  1067. }
  1068. }
  1069. PrimBuild::end();
  1070. }
  1071. // Bottom
  1072. if(numBottom)
  1073. {
  1074. PrimBuild::color( ColorI(255,0,255) );
  1075. PrimBuild::begin( GFXLineList, 2*numBottom );
  1076. for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
  1077. {
  1078. if(river->mSideProfile.mSegMtrls[i] == MeshRoad::Bottom)
  1079. {
  1080. river->mSideProfile.getNodeWorldPos(i, nodePos);
  1081. PrimBuild::vertex3fv( nodePos );
  1082. river->mSideProfile.getNodeWorldPos(i+1, nodePos);
  1083. PrimBuild::vertex3fv( nodePos );
  1084. }
  1085. }
  1086. PrimBuild::end();
  1087. }
  1088. // Render node normals
  1089. PrimBuild::color( ColorI(255,0,0) );
  1090. PrimBuild::begin( GFXLineList, 4*river->mSideProfile.mNodes.size() - 4 );
  1091. for ( U32 i = 0; i < river->mSideProfile.mNodes.size()-1; i++ )
  1092. {
  1093. for( U32 j = 0; j < 2; j++)
  1094. {
  1095. river->mSideProfile.getNodeWorldPos(i+j, nodePos);
  1096. PrimBuild::vertex3fv( nodePos );
  1097. river->mSideProfile.getNormWorldPos(2*i+j, normEndPos);
  1098. PrimBuild::vertex3fv( normEndPos );
  1099. }
  1100. }
  1101. PrimBuild::end();
  1102. }
  1103. // Segment
  1104. }
  1105. void GuiMeshRoadEditorCtrl::_drawControlNodes( MeshRoad *river, const ColorI &color )
  1106. {
  1107. if ( !MeshRoad::smShowSpline )
  1108. return;
  1109. RectI bounds = getBounds();
  1110. GFXDrawUtil *drawer = GFX->getDrawUtil();
  1111. bool isSelected = ( river == mSelRoad );
  1112. bool isHighlighted = ( river == mHoverRoad );
  1113. for ( U32 i = 0; i < river->mNodes.size(); i++ )
  1114. {
  1115. if ( false && isSelected && mSelNode == i )
  1116. continue;
  1117. const Point3F &wpos = river->mNodes[i].point;
  1118. Point3F spos;
  1119. project( wpos, &spos );
  1120. if ( spos.z > 1.0f )
  1121. continue;
  1122. Point2I posi;
  1123. posi.x = spos.x;
  1124. posi.y = spos.y;
  1125. if ( !bounds.pointInRect( posi ) )
  1126. continue;
  1127. ColorI theColor = color;
  1128. Point2I nodeHalfSize = mNodeHalfSize;
  1129. if ( isHighlighted && mHoverNode == i )
  1130. {
  1131. //theColor = mHoverNodeColor;
  1132. nodeHalfSize += Point2I(2,2);
  1133. }
  1134. if ( isSelected )
  1135. {
  1136. if ( mSelNode == i )
  1137. {
  1138. theColor.set(0,0,255);
  1139. }
  1140. else if ( i == 0 )
  1141. {
  1142. theColor.set(0,255,0);
  1143. }
  1144. else if ( i == river->mNodes.size() - 1 )
  1145. {
  1146. theColor.set(255,0,0);
  1147. }
  1148. }
  1149. if( MeshRoad::smShowRoadProfile && isSelected )
  1150. theColor.set(100,100,100);
  1151. drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor );
  1152. }
  1153. // Draw profile control nodes
  1154. if( MeshRoad::smShowRoadProfile && isSelected )
  1155. {
  1156. Point3F wpos;
  1157. Point3F spos;
  1158. Point2I posi;
  1159. ColorI theColor;
  1160. for( U32 i = 0; i < river->mSideProfile.mNodes.size(); i++)
  1161. {
  1162. river->mSideProfile.getNodeWorldPos(i, wpos);
  1163. project( wpos, &spos );
  1164. if ( spos.z > 1.0f )
  1165. continue;
  1166. posi.x = spos.x;
  1167. posi.y = spos.y;
  1168. if ( !bounds.pointInRect( posi ) )
  1169. continue;
  1170. if(i == 0)
  1171. theColor.set(mProfileColor.red/3, mProfileColor.green/3, mProfileColor.blue/3,255);
  1172. else
  1173. theColor.set(mProfileColor,255);
  1174. if( mSelProfNodeList.find_next(i) != -1 )
  1175. theColor.set(0,0,255);
  1176. drawer->drawRectFill( posi - mNodeHalfSize, posi + mNodeHalfSize, theColor );
  1177. }
  1178. }
  1179. }
  1180. bool GuiMeshRoadEditorCtrl::getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos )
  1181. {
  1182. // Find clicked point on the terrain
  1183. Point3F startPnt = event.pos;
  1184. Point3F endPnt = event.pos + event.vec * 1000.0f;
  1185. RayInfo ri;
  1186. bool hit;
  1187. hit = gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri);
  1188. tpos = ri.point;
  1189. return hit;
  1190. }
  1191. void GuiMeshRoadEditorCtrl::deleteSelectedNode()
  1192. {
  1193. if ( !mSelRoad || mSelNode == -1 )
  1194. return;
  1195. // If the Road has only two nodes remaining,
  1196. // delete the whole Road.
  1197. if ( mSelRoad->mNodes.size() <= 2 )
  1198. {
  1199. deleteSelectedRoad( mMode != mAddNodeMode );
  1200. }
  1201. else
  1202. {
  1203. if ( mMode != mAddNodeMode )
  1204. submitUndo( "Delete Node" );
  1205. // Delete the SelectedNode of the SelectedRoad
  1206. mSelRoad->deleteNode( mSelNode );
  1207. // We deleted the Node but not the Road (it has nodes left)
  1208. // so decrement the currently selected node.
  1209. if ( mSelRoad->mNodes.size() <= mSelNode )
  1210. setSelectedNode( mSelNode - 1 );
  1211. else
  1212. {
  1213. // force gizmo to update to the selected nodes position
  1214. // the index didn't change but the node it refers to did.
  1215. U32 i = mSelNode;
  1216. mSelNode = -1;
  1217. setSelectedNode( i );
  1218. }
  1219. }
  1220. // If you were in addNodeMode,
  1221. // deleting a node should ends it.
  1222. //mMode = smNormalMode;
  1223. }
  1224. void GuiMeshRoadEditorCtrl::deleteSelectedRoad( bool undoAble )
  1225. {
  1226. AssertFatal( mSelRoad != NULL, "GuiMeshRoadEditorCtrl::deleteSelectedRoad() - No Road is selected" );
  1227. // Not undoAble? Just delete it.
  1228. if ( !undoAble )
  1229. {
  1230. mSelRoad->deleteObject();
  1231. mIsDirty = true;
  1232. Con::executef( this, "onRoadSelected" );
  1233. mSelNode = -1;
  1234. return;
  1235. }
  1236. // Grab the mission editor undo manager.
  1237. UndoManager *undoMan = NULL;
  1238. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  1239. {
  1240. // Couldn't find it? Well just delete the Road.
  1241. Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
  1242. return;
  1243. }
  1244. else
  1245. {
  1246. // Create the UndoAction.
  1247. MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road");
  1248. action->deleteObject( mSelRoad );
  1249. mIsDirty = true;
  1250. // Submit it.
  1251. undoMan->addAction( action );
  1252. }
  1253. // ScriptCallback with 'NULL' parameter for no Road currently selected.
  1254. Con::executef( this, "onRoadSelected" );
  1255. // Clear the SelectedNode (it has been deleted along with the River).
  1256. setSelectedNode( -1 );
  1257. mSelNode = -1;
  1258. }
  1259. void GuiMeshRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false )
  1260. {
  1261. mMode = mode;
  1262. if( sourceShortcut )
  1263. Con::executef( this, "paletteSync", mode );
  1264. }
  1265. void GuiMeshRoadEditorCtrl::setSelectedRoad( MeshRoad *road )
  1266. {
  1267. mSelRoad = road;
  1268. if ( mSelRoad != NULL )
  1269. Con::executef( this, "onRoadSelected", road->getIdString() );
  1270. else
  1271. Con::executef( this, "onRoadSelected" );
  1272. if ( mSelRoad != road )
  1273. setSelectedNode(-1);
  1274. }
  1275. void GuiMeshRoadEditorCtrl::setNodeWidth( F32 width )
  1276. {
  1277. if ( mSelRoad && mSelNode != -1 )
  1278. {
  1279. mSelRoad->setNodeWidth( mSelNode, width );
  1280. mIsDirty = true;
  1281. }
  1282. }
  1283. F32 GuiMeshRoadEditorCtrl::getNodeWidth()
  1284. {
  1285. if ( mSelRoad && mSelNode != -1 )
  1286. return mSelRoad->getNodeWidth( mSelNode );
  1287. return 0.0f;
  1288. }
  1289. void GuiMeshRoadEditorCtrl::setNodeDepth(F32 depth)
  1290. {
  1291. if ( mSelRoad && mSelNode != -1 )
  1292. {
  1293. mSelRoad->setNodeDepth( mSelNode, depth );
  1294. mIsDirty = true;
  1295. }
  1296. }
  1297. F32 GuiMeshRoadEditorCtrl::getNodeDepth()
  1298. {
  1299. if ( mSelRoad && mSelNode != -1 )
  1300. return mSelRoad->getNodeDepth( mSelNode );
  1301. return 0.0f;
  1302. }
  1303. void GuiMeshRoadEditorCtrl::setNodePosition(const Point3F& pos)
  1304. {
  1305. if ( mSelRoad && mSelNode != -1 )
  1306. {
  1307. mSelRoad->setNodePosition( mSelNode, pos );
  1308. mIsDirty = true;
  1309. }
  1310. }
  1311. Point3F GuiMeshRoadEditorCtrl::getNodePosition()
  1312. {
  1313. if ( mSelRoad && mSelNode != -1 )
  1314. return mSelRoad->getNodePosition( mSelNode );
  1315. return Point3F( 0, 0, 0 );
  1316. }
  1317. void GuiMeshRoadEditorCtrl::setNodeNormal( const VectorF &normal )
  1318. {
  1319. if ( mSelRoad && mSelNode != -1 )
  1320. {
  1321. mSelRoad->setNodeNormal( mSelNode, normal );
  1322. mIsDirty = true;
  1323. }
  1324. }
  1325. VectorF GuiMeshRoadEditorCtrl::getNodeNormal()
  1326. {
  1327. if ( mSelRoad && mSelNode != -1 )
  1328. return mSelRoad->getNodeNormal( mSelNode );
  1329. return VectorF::Zero;
  1330. }
  1331. void GuiMeshRoadEditorCtrl::setSelectedNode( S32 node )
  1332. {
  1333. if ( mSelNode == node )
  1334. return;
  1335. mSelNode = node;
  1336. if ( mSelNode != -1 )
  1337. {
  1338. const MeshRoadNode &curNode = mSelRoad->mNodes[mSelNode];
  1339. MatrixF objMat = mSelRoad->getNodeTransform(mSelNode);
  1340. Point3F objScale(curNode.width, 1.0f, curNode.depth );
  1341. Point3F worldPos = curNode.point;
  1342. mGizmo->set( objMat, worldPos, objScale );
  1343. }
  1344. if ( mSelNode != -1 )
  1345. Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode) );
  1346. else
  1347. Con::executef( this, "onNodeSelected", Con::getIntArg(-1) );
  1348. }
  1349. void GuiMeshRoadEditorCtrl::submitUndo( const UTF8 *name )
  1350. {
  1351. // Grab the mission editor undo manager.
  1352. UndoManager *undoMan = NULL;
  1353. if ( !Sim::findObject( "EUndoManager", undoMan ) )
  1354. {
  1355. Con::errorf( "GuiMeshRoadEditorCtrl::submitUndo() - EUndoManager not found!" );
  1356. return;
  1357. }
  1358. // Setup the action.
  1359. GuiMeshRoadEditorUndoAction *action = new GuiMeshRoadEditorUndoAction( name );
  1360. action->mObjId = mSelRoad->getId();
  1361. //action->mMetersPerSegment = mSelRoad->mMetersPerSegment;
  1362. action->mEditor = this;
  1363. for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ )
  1364. {
  1365. action->mNodes.push_back( mSelRoad->mNodes[i] );
  1366. }
  1367. // Save profile nodes and materials
  1368. for( U32 i = 0; i < mSelRoad->mSideProfile.mNodes.size(); i++)
  1369. {
  1370. action->mProfileNodes.push_back( mSelRoad->mSideProfile.mNodes[i] );
  1371. if(i)
  1372. action->mProfileMtrls.push_back( mSelRoad->mSideProfile.mSegMtrls[i-1] );
  1373. }
  1374. undoMan->addAction( action );
  1375. }
  1376. void GuiMeshRoadEditorCtrl::matchTerrainToRoad()
  1377. {
  1378. if ( !mSelRoad )
  1379. return;
  1380. // Not implemented, but a potentially useful idea.
  1381. // Move manipulate the terrain so that the MeshRoad appears to be sitting
  1382. // on top of it.
  1383. // The opposite could also be useful, manipulate the MeshRoad to line up
  1384. // with the terrain underneath it.
  1385. }
  1386. DefineEngineMethod( GuiMeshRoadEditorCtrl, deleteNode, void, (), , "deleteNode()" )
  1387. {
  1388. object->deleteSelectedNode();
  1389. }
  1390. DefineEngineMethod( GuiMeshRoadEditorCtrl, getMode, const char*, (), , "" )
  1391. {
  1392. return object->getMode();
  1393. }
  1394. DefineEngineMethod( GuiMeshRoadEditorCtrl, setMode, void, (const char * mode), , "setMode( String mode )" )
  1395. {
  1396. String newMode = ( mode );
  1397. object->setMode( newMode );
  1398. }
  1399. DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodeWidth, F32, (), , "" )
  1400. {
  1401. return object->getNodeWidth();
  1402. }
  1403. DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodeWidth, void, ( F32 width ), , "" )
  1404. {
  1405. object->setNodeWidth( width );
  1406. }
  1407. DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodeDepth, F32, (), , "" )
  1408. {
  1409. return object->getNodeDepth();
  1410. }
  1411. DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodeDepth, void, ( F32 depth ), , "" )
  1412. {
  1413. object->setNodeDepth( depth );
  1414. }
  1415. DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodePosition, Point3F, (), , "" )
  1416. {
  1417. return object->getNodePosition();
  1418. }
  1419. DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodePosition, void, (Point3F pos), , "" )
  1420. {
  1421. object->setNodePosition( pos );
  1422. }
  1423. DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodeNormal, Point3F, (), , "" )
  1424. {
  1425. return object->getNodeNormal();
  1426. }
  1427. DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodeNormal, void, (Point3F normal), , "" )
  1428. {
  1429. object->setNodeNormal( normal );
  1430. }
  1431. DefineEngineMethod( GuiMeshRoadEditorCtrl, setSelectedRoad, void, (const char * objName), (""), "" )
  1432. {
  1433. if ( String::isEmpty(objName) )
  1434. object->setSelectedRoad(NULL);
  1435. else
  1436. {
  1437. MeshRoad *road = NULL;
  1438. if ( Sim::findObject( objName, road ) )
  1439. object->setSelectedRoad(road);
  1440. }
  1441. }
  1442. DefineEngineMethod( GuiMeshRoadEditorCtrl, getSelectedRoad, S32, (), , "" )
  1443. {
  1444. MeshRoad *road = object->getSelectedRoad();
  1445. if ( !road )
  1446. return 0;
  1447. return road->getId();
  1448. }
  1449. DefineEngineMethod( GuiMeshRoadEditorCtrl, regenerate, void, (), , "" )
  1450. {
  1451. MeshRoad *road = object->getSelectedRoad();
  1452. if ( road )
  1453. road->regenerate();
  1454. }
  1455. DefineEngineMethod( GuiMeshRoadEditorCtrl, matchTerrainToRoad, void, (), , "" )
  1456. {
  1457. object->matchTerrainToRoad();
  1458. }