123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "environment/editors/guiMeshRoadEditorCtrl.h"
- #include "console/consoleTypes.h"
- #include "console/engineAPI.h"
- #include "environment/meshRoad.h"
- #include "renderInstance/renderPassManager.h"
- #include "collision/collision.h"
- #include "math/util/frustum.h"
- #include "math/mathUtils.h"
- #include "gfx/gfxPrimitiveBuffer.h"
- #include "gfx/gfxTextureHandle.h"
- #include "gfx/gfxTransformSaver.h"
- #include "gfx/primBuilder.h"
- #include "gfx/gfxDrawUtil.h"
- #include "scene/sceneRenderState.h"
- #include "scene/sceneManager.h"
- #include "gui/core/guiCanvas.h"
- #include "gui/buttons/guiButtonCtrl.h"
- #include "gui/worldEditor/undoActions.h"
- #include "T3D/gameBase/gameConnection.h"
- #include "gfx/sim/debugDraw.h"
- #include "materials/materialDefinition.h"
- #include "T3D/prefab.h"
- #include "T3D/Scene.h"
- IMPLEMENT_CONOBJECT(GuiMeshRoadEditorCtrl);
- ConsoleDocClass( GuiMeshRoadEditorCtrl,
- "@brief GUI tool that makes up the Mesh Road Editor\n\n"
- "Editor use only.\n\n"
- "@internal"
- );
- S32 _NodeIndexCmp( U32 const *a, U32 const *b )
- {
- S32 a2 = (*a);
- S32 b2 = (*b);
- S32 diff = a2 - b2;
- return diff < 0 ? 1 : diff > 0 ? -1 : 0;
- }
- GuiMeshRoadEditorCtrl::GuiMeshRoadEditorCtrl()
- :
- // Each of the mode names directly correlates with the Mesh Road Editor's
- // tool palette
- mSelectMeshRoadMode("MeshRoadEditorSelectMode"),
- mAddMeshRoadMode("MeshRoadEditorAddRoadMode"),
- mAddNodeMode("MeshRoadEditorAddNodeMode"),
- mInsertPointMode("MeshRoadEditorInsertPointMode"),
- mRemovePointMode("MeshRoadEditorRemovePointMode"),
- mMovePointMode("MeshRoadEditorMoveMode"),
- mScalePointMode("MeshRoadEditorScaleMode"),
- mRotatePointMode("MeshRoadEditorRotateMode"),
- mSavedDrag(false),
- mIsDirty( false ),
- mSavedProfileDrag( false ),
- mDeselectProfileNode( false ),
- mProfileNode( -1 ),
- mProfileColor( 255,255,0 ),
- mRoadSet( NULL ),
- mSelNode( -1 ),
- mHoverNode( -1 ),
- mAddNodeIdx( 0 ),
- mSelRoad( NULL ),
- mHoverRoad( NULL ),
- mMode(mSelectMeshRoadMode),
- mDefaultWidth( 10.0f ),
- mDefaultDepth( 5.0f ),
- mDefaultNormal( 0,0,1 ),
- mNodeHalfSize( 4,4 ),
- mHoverSplineColor( 255,0,0,255 ),
- mSelectedSplineColor( 0,255,0,255 ),
- mHoverNodeColor( 255,255,255,255 ),
- mHasCopied( false )
- {
- INIT_ASSET(TopMaterial);
- INIT_ASSET(BottomMaterial);
- INIT_ASSET(SideMaterial);
- mTopMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultTopMaterialAsset");
- mBottomMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultBottomMaterialAsset");
- mSideMaterialAssetId = Con::getVariable("$MeshRoadEditor::defaultSideMaterialAsset");
- }
- GuiMeshRoadEditorCtrl::~GuiMeshRoadEditorCtrl()
- {
- // nothing to do
- }
- void GuiMeshRoadEditorUndoAction::undo()
- {
- MeshRoad *object = NULL;
- if ( !Sim::findObject( mObjId, object ) )
- return;
- // Temporarily save the Roads current data.
- //F32 metersPerSeg = object->mMetersPerSegment;
- Vector<MeshRoadNode> nodes;
- nodes.merge( object->mNodes );
- // Restore the River properties saved in the UndoAction
- //object->mMetersPerSegment = mMetersPerSegment;
- // Restore the Nodes saved in the UndoAction
- object->mNodes.clear();
- for ( U32 i = 0; i < mNodes.size(); i++ )
- {
- object->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal );
- }
- // Temporarily save the Roads current profile data.
- Vector<MeshRoadProfileNode> profNodes;
- Vector<U8> profMtrls;
- profNodes.merge( object->mSideProfile.mNodes );
- profMtrls.merge( object->mSideProfile.mSegMtrls );
- // Restore the Profile Nodes saved in the UndoAction
- Point3F pos;
- object->mSideProfile.mNodes.clear();
- object->mSideProfile.mSegMtrls.clear();
- for ( U32 i = 0; i < mProfileNodes.size(); i++ )
- {
- MeshRoadProfileNode newNode;
- pos = mProfileNodes[i].getPosition();
- newNode.setSmoothing( mProfileNodes[i].isSmooth() );
- object->mSideProfile.mNodes.push_back( newNode );
- object->mSideProfile.mNodes.last().setPosition( pos.x, pos.y );
- if(i)
- object->mSideProfile.mSegMtrls.push_back(mProfileMtrls[i-1]);
- }
- // Set the first node position to trigger packet update to client
- pos.set(0.0f, 0.0f, 0.0f);
- object->mSideProfile.setNodePosition(0,pos);
- // Regenerate the Road
- object->mSideProfile.generateNormals();
- // If applicable set the selected Road and node
- mEditor->mProfileNode = -1;
- // Now save the previous Road data in this UndoAction
- // since an undo action must become a redo action and vice-versa
- //mMetersPerSegment = metersPerSeg;
- mProfileNodes.clear();
- mProfileNodes.merge( profNodes );
- mProfileMtrls.clear();
- mProfileMtrls.merge( profMtrls );
- // Regenerate the Road
- object->regenerate();
- // If applicable set the selected Road and node
- mEditor->mSelRoad = object;
- mEditor->mSelNode = -1;
- // Now save the previous Road data in this UndoAction
- // since an undo action must become a redo action and vice-versa
- //mMetersPerSegment = metersPerSeg;
- mNodes.clear();
- mNodes.merge( nodes );
- }
- bool GuiMeshRoadEditorCtrl::onAdd()
- {
- if( !Parent::onAdd() )
- return false;
- mRoadSet = MeshRoad::getServerSet();
- GFXStateBlockDesc desc;
- desc.fillMode = GFXFillSolid;
- desc.blendDefined = true;
- desc.blendEnable = false;
- desc.zDefined = true;
- desc.zEnable = false;
- desc.cullDefined = true;
- desc.cullMode = GFXCullNone;
- mZDisableSB = GFX->createStateBlock(desc);
- desc.zEnable = true;
- mZEnableSB = GFX->createStateBlock(desc);
- return true;
- }
- void GuiMeshRoadEditorCtrl::initPersistFields()
- {
- addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiMeshRoadEditorCtrl ) );
- addField( "DefaultDepth", TypeF32, Offset( mDefaultDepth, GuiMeshRoadEditorCtrl ) );
- addField( "DefaultNormal", TypePoint3F,Offset( mDefaultNormal, GuiMeshRoadEditorCtrl ) );
- addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiMeshRoadEditorCtrl ) );
- addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiMeshRoadEditorCtrl ) );
- addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiMeshRoadEditorCtrl ) );
- addField( "isDirty", TypeBool, Offset( mIsDirty, GuiMeshRoadEditorCtrl ) );
- INITPERSISTFIELD_MATERIALASSET(TopMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on upper surface road creation.");
- INITPERSISTFIELD_MATERIALASSET(BottomMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on bottom surface road creation.");
- INITPERSISTFIELD_MATERIALASSET(SideMaterial, GuiMeshRoadEditorCtrl, "Default Material used by the Mesh Road Editor on side surface road creation.");
- //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiMeshRoadEditorCtrl) );
- //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiMeshRoadEditorCtrl) );
- //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiMeshRoadEditorCtrl) );
- //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiMeshRoadEditorCtrl) );
- Parent::initPersistFields();
- }
- void GuiMeshRoadEditorCtrl::onSleep()
- {
- Parent::onSleep();
- mMode = mSelectMeshRoadMode;
- mHoverNode = -1;
- mHoverRoad = NULL;
- setSelectedNode(-1);
- }
- void GuiMeshRoadEditorCtrl::get3DCursor( GuiCursor *&cursor,
- bool &visible,
- const Gui3DMouseEvent &event_ )
- {
- //cursor = mAddNodeCursor;
- //visible = false;
-
- cursor = NULL;
- visible = false;
- GuiCanvas *root = getRoot();
- if ( !root )
- return;
- S32 currCursor = PlatformCursorController::curArrow;
- if ( root->mCursorChanged == currCursor )
- return;
- PlatformWindow *window = root->getPlatformWindow();
- PlatformCursorController *controller = window->getCursorController();
-
- // We've already changed the cursor,
- // so set it back before we change it again.
- if( root->mCursorChanged != -1)
- controller->popCursor();
- // Now change the cursor shape
- controller->pushCursor(currCursor);
- root->mCursorChanged = currCursor;
- }
- void GuiMeshRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
- {
- mHasCopied = false;
- mGizmo->on3DMouseDown( event );
- if ( !isFirstResponder() )
- setFirstResponder();
-
- if( MeshRoad::smShowRoadProfile && mSelRoad )
- {
- // Ctrl-Click = Add Node
- if(event.modifier & SI_CTRL)
- {
- S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
- if(clickedNode != -1)
- {
- // If clicked node is already in list, remove it, else add it to list
- if(!mSelProfNodeList.remove(clickedNode) && clickedNode > 0)
- mSelProfNodeList.push_back(clickedNode);
- return;
- }
- Point3F pos;
- PlaneF xy( mSelRoad->mSlices[0].p2, -mSelRoad->mSlices[0].fvec );
- xy.intersect(event.pos, event.vec, &pos);
- mSelRoad->mSideProfile.worldToObj(pos);
- U32 node = mSelRoad->mSideProfile.clickOnLine(pos);
- if(node != -1)
- {
- submitUndo( "Add Profile Node" );
- mSelRoad->mSideProfile.addPoint(node, pos);
- mProfileNode = node;
- mSelProfNodeList.clear();
- mSelProfNodeList.push_back(node);
- mIsDirty = true;
- }
- return;
- }
- // Alt-Click = Delete Node
- if(event.modifier & SI_ALT)
- {
- S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
- if(mSelProfNodeList.find_next(clickedNode) != -1)
- {
- submitUndo( "Delete Profile Node" );
- mSelProfNodeList.sort( _NodeIndexCmp );
- for(U32 i=0; i < mSelProfNodeList.size(); i++)
- mSelRoad->mSideProfile.removePoint( mSelProfNodeList[i] );
- mProfileNode = -1;
- mSelProfNodeList.clear();
- mIsDirty = true;
- }
- else if(clickedNode > 0 && clickedNode < mSelRoad->mSideProfile.mNodes.size()-1)
- {
- submitUndo( "Delete Profile Node" );
- mSelRoad->mSideProfile.removePoint( clickedNode );
- mProfileNode = -1;
- mSelProfNodeList.clear();
- mIsDirty = true;
- }
- return;
- }
- // Shift-Click = Toggle Node Smoothing
- if(event.modifier & SI_SHIFT)
- {
- S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
- if(clickedNode != -1)
- {
- submitUndo( "Smooth Profile Node" );
- if(mSelProfNodeList.find_next(clickedNode) != -1)
- {
- for(U32 i=0; i < mSelProfNodeList.size(); i++)
- mSelRoad->mSideProfile.toggleSmoothing(mSelProfNodeList[i]);
- }
- else
- {
- mSelRoad->mSideProfile.toggleSmoothing(clickedNode);
- if(clickedNode != 0)
- {
- mProfileNode = clickedNode;
- mSelProfNodeList.clear();
- mSelProfNodeList.push_back(clickedNode);
- }
- }
- mIsDirty = true;
- return;
- }
- Point3F pos;
- PlaneF xy( mSelRoad->mSlices[0].p2, -mSelRoad->mSlices[0].fvec );
- xy.intersect(event.pos, event.vec, &pos);
- mSelRoad->mSideProfile.worldToObj(pos);
- U32 node = mSelRoad->mSideProfile.clickOnLine(pos);
- if(node > 0)
- {
- submitUndo( "Profile Material" );
- mSelRoad->mSideProfile.toggleSegMtrl(node-1);
- mIsDirty = true;
- }
- return;
- }
- // Click to select/deselect nodes
- S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
- if(clickedNode != -1)
- {
- if(mSelProfNodeList.find_next(clickedNode) != -1)
- {
- mProfileNode = clickedNode;
- mDeselectProfileNode = true;
- }
- else if(clickedNode != 0)
- {
- mProfileNode = clickedNode;
- mSelProfNodeList.clear();
- mSelProfNodeList.push_back(clickedNode);
- }
- else
- {
- mProfileNode = -1;
- mSelProfNodeList.clear();
- // Reset profile if Node 0 is double-clicked
- if( event.mouseClickCount > 1 )
- {
- submitUndo( "Reset Profile" );
- mSelRoad->mSideProfile.resetProfile(mSelRoad->mSlices[0].depth);
- mSelRoad->regenerate();
- }
- }
- return;
- }
- mProfileNode = -1;
- mSelProfNodeList.clear();
- }
- // Get the raycast collision position
- Point3F tPos;
- if ( !getStaticPos( event, tPos ) )
- return;
- mouseLock();
- // Construct a LineSegment from the camera position to 1000 meters away in
- // the direction clicked.
- // If that segment hits the terrain, truncate the ray to only be that length.
- // We will use a LineSegment/Sphere intersection test to determine if a MeshRoadNode
- // was clicked.
- MeshRoad *pRoad = NULL;
- MeshRoad *pClickedRoad = NULL;
- U32 insertNodeIdx = -1;
- Point3F collisionPnt;
- Point3F startPnt = event.pos;
- Point3F endPnt = event.pos + event.vec * 2000.0f;
- RayInfo ri;
- if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
- endPnt = ri.point;
- DebugDrawer *ddraw = DebugDrawer::get();
- if ( false && ddraw )
- {
- ddraw->drawLine( startPnt, endPnt, ColorI(255,0,0,255) );
- ddraw->setLastTTL(DebugDrawer::DD_INFINITE);
- }
- // Check for collision with nodes of the currently selected or highlighted MeshRoad
- // Did we click on a MeshRoad? check currently selected road first
- if ( mSelRoad != NULL && mSelRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
- {
- pClickedRoad = mSelRoad;
- }
- else
- {
- for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
- {
- pRoad = static_cast<MeshRoad*>( *iter );
- // Do not select or edit a MeshRoad within a Prefab.
- if ( Prefab::getPrefabByChild(pRoad) )
- continue;
- if ( pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
- {
- pClickedRoad = pRoad;
- break;
- }
- }
- }
- /*
- else if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
- {
- MeshRoad *pRoad = NULL;
- pRoad = dynamic_cast<MeshRoad*>(ri.object);
- if ( pRoad && pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
- pClickedRoad = pRoad;
- }
- */
- // Did we click on a node?
- bool nodeClicked = false;
- S32 clickedNodeIdx = -1;
- // If we clicked on the currently selected road, only scan its nodes
- if ( mSelRoad != NULL && pClickedRoad == mSelRoad )
- {
- clickedNodeIdx = _getNodeAtScreenPos( mSelRoad, event.mousePoint );
- nodeClicked = clickedNodeIdx != -1;
- }
- else
- {
- for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
- {
- pRoad = static_cast<MeshRoad*>( *iter );
- // Do not select or edit a MeshRoad within a Prefab.
- if ( Prefab::getPrefabByChild(pRoad) )
- continue;
-
- clickedNodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint );
- if ( clickedNodeIdx != -1 )
- {
- nodeClicked = true;
- pClickedRoad = pRoad;
- break;
- }
- }
- }
-
- // shortcuts
- bool dblClick = ( event.mouseClickCount > 1 );
- if( dblClick )
- {
- if( mMode == mSelectMeshRoadMode )
- {
- setMode( mAddMeshRoadMode, true );
- return;
- }
- if( mMode == mAddNodeMode )
- {
- // Delete the node attached to the cursor.
- deleteSelectedNode();
- mMode = mAddMeshRoadMode;
- return;
- }
- }
-
- //this check is here in order to bounce back from deleting a whole road with ctrl+z
- //this check places the editor back into addrivermode
- if ( mMode == mAddNodeMode )
- {
- if ( !mSelRoad )
- mMode = mAddMeshRoadMode;
- }
- if ( mMode == mSelectMeshRoadMode )
- {
- // Did not click on a MeshRoad or a node.
- if ( !pClickedRoad )
- {
- setSelectedRoad( NULL );
- setSelectedNode( -1 );
-
- return;
- }
- // Clicked on a MeshRoad that wasn't the currently selected River.
- if ( pClickedRoad != mSelRoad )
- {
- setSelectedRoad( pClickedRoad );
- setSelectedNode( clickedNodeIdx );
- return;
- }
- // Clicked on a node in the currently selected River that wasn't
- // the currently selected node.
- if ( nodeClicked )
- {
- setSelectedNode( clickedNodeIdx );
- return;
- }
- }
- else if ( mMode == mAddMeshRoadMode )
- {
- if ( nodeClicked )
- {
- // A double-click on a node in Normal mode means set AddNode mode.
- if ( clickedNodeIdx == 0 )
- {
- setSelectedRoad( pClickedRoad );
- setSelectedNode( clickedNodeIdx );
- mAddNodeIdx = clickedNodeIdx;
- mMode = mAddNodeMode;
- mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
- mIsDirty = true;
- return;
- }
- else if ( clickedNodeIdx == pClickedRoad->mNodes.size() - 1 )
- {
- setSelectedRoad( pClickedRoad );
- setSelectedNode( clickedNodeIdx );
- mAddNodeIdx = U32_MAX;
- mMode = mAddNodeMode;
- mSelNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
- mIsDirty = true;
- setSelectedNode( mSelNode );
- return;
- }
- }
- MeshRoad *newRoad = new MeshRoad;
- if(mTopMaterialAsset.notNull())
- newRoad->_setTopMaterial(mTopMaterialAssetId);
- if (mBottomMaterialAsset.notNull())
- newRoad->_setBottomMaterial(mBottomMaterialAssetId);
- if (mSideMaterialAsset.notNull())
- newRoad->_setSideMaterial(mSideMaterialAssetId);
-
- newRoad->registerObject();
- // Add to scene
- Scene *scene;
- scene = Scene::getRootScene();
- if ( !scene)
- Con::errorf( "GuiMeshRoadEditorCtrl - could not find Scene to add new MeshRoad" );
- else
- scene->addObject( newRoad );
- Point3F pos( endPnt );
- pos.z += mDefaultDepth * 0.5f;
- newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 );
- U32 newNode = newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 );
- // Always add to the end of the road, the first node is the start.
- mAddNodeIdx = U32_MAX;
- setSelectedRoad( newRoad );
- setSelectedNode( newNode );
- mMode = mAddNodeMode;
- // Disable the hover node while in addNodeMode, we
- // don't want some random node enlarged.
- mHoverNode = -1;
- // Grab the mission editor undo manager.
- UndoManager *undoMan = NULL;
- if ( !Sim::findObject( "EUndoManager", undoMan ) )
- {
- Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
- return;
- }
- // Create the UndoAction.
- MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad");
- action->addObject( newRoad );
-
- // Submit it.
- undoMan->addAction( action );
- //send a callback to script after were done here if one exists
- if (isMethod("onRoadCreation"))
- Con::executef(this, "onRoadCreation");
- return;
- }
- else if ( mMode == mAddNodeMode )
- {
- // Oops the road got deleted, maybe from an undo action?
- // Back to NormalMode.
- if ( mSelRoad )
- {
- // A double-click on a node in Normal mode means set AddNode mode.
- if ( clickedNodeIdx == 0 )
- {
- submitUndo( "Add Node" );
- mAddNodeIdx = clickedNodeIdx;
- mMode = mAddNodeMode;
- mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
- mIsDirty = true;
- setSelectedNode( mSelNode );
- return;
- }
- else
- {
- if( pClickedRoad && clickedNodeIdx == pClickedRoad->mNodes.size() - 1 )
- {
- submitUndo( "Add Node" );
- mAddNodeIdx = U32_MAX;
- mMode = mAddNodeMode;
- U32 newNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
- mIsDirty = true;
- setSelectedNode( newNode );
- return;
- }
- else
- {
- submitUndo( "Insert Node" );
- // A single-click on empty space while in
- // AddNode mode means insert / add a node.
- //submitUndo( "Add Node" );
- U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx);
- mIsDirty = true;
- setSelectedNode( newNode );
- return;
- }
- }
- }
- }
- else if ( mMode == mInsertPointMode && mSelRoad != NULL )
- {
- if ( pClickedRoad == mSelRoad && insertNodeIdx != -1 )
- {
- // NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node...
- // in order to handle inserting nodes in the same way as for fxRoad
- U32 prevNodeIdx = insertNodeIdx;
- U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRoad->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1;
- const MeshRoadNode &prevNode = mSelRoad->mNodes[prevNodeIdx];
- const MeshRoadNode &nextNode = mSelRoad->mNodes[nextNodeIdx];
- F32 width = ( prevNode.width + nextNode.width ) * 0.5f;
- F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f;
- Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f;
- normal.normalize();
- submitUndo( "Insert Node" );
- U32 newNode = mSelRoad->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 );
- mIsDirty = true;
- setSelectedNode( newNode );
- return;
- }
- }
- else if ( mMode == mRemovePointMode && mSelRoad != NULL )
- {
- if ( nodeClicked && pClickedRoad == mSelRoad )
- {
- setSelectedNode( clickedNodeIdx );
- deleteSelectedNode();
- return;
- }
- }
- else if ( mMode == mMovePointMode )
- {
- if ( nodeClicked && pClickedRoad == mSelRoad )
- {
- setSelectedNode( clickedNodeIdx );
- return;
- }
- }
- else if ( mMode == mScalePointMode )
- {
- if ( nodeClicked && pClickedRoad == mSelRoad )
- {
- setSelectedNode( clickedNodeIdx );
- return;
- }
- }
- else if ( mMode == mRotatePointMode )
- {
- if ( nodeClicked && pClickedRoad == mSelRoad )
- {
- setSelectedNode( clickedNodeIdx );
- return;
- }
- }
- }
- void GuiMeshRoadEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event)
- {
- //mIsPanning = true;
- }
- void GuiMeshRoadEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event)
- {
- //mIsPanning = false;
- }
- void GuiMeshRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
- {
- mGizmo->on3DMouseUp(event);
- mSavedDrag = false;
- mSavedProfileDrag = false;
- if( MeshRoad::smShowRoadProfile && mSelRoad )
- {
- // If we need to deselect node... this means we clicked on a selected node without dragging
- if( mDeselectProfileNode )
- {
- S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
- if(clickedNode == mProfileNode)
- {
- mProfileNode = -1;
- mSelProfNodeList.clear();
- }
- mDeselectProfileNode = false;
- }
- // Else if we dragged a node, update the road
- else
- {
- S32 clickedNode = _getProfileNodeAtScreenPos( &mSelRoad->mSideProfile, event.mousePoint );
- if(clickedNode == mProfileNode)
- mSelRoad->regenerate(); // This regens the road for collision purposes on the server
- }
- }
- mouseUnlock();
- }
- void GuiMeshRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
- {
- if ( mSelRoad != NULL && mMode == mAddNodeMode )
- {
- Point3F pos;
- mSelRoad->disableCollision();
- if ( getStaticPos( event, pos ) )
- {
- pos.z += mSelRoad->getNodeDepth( mSelNode ) * 0.5f;
- mSelRoad->setNodePosition( mSelNode, pos );
- mIsDirty = true;
- }
- mSelRoad->enableCollision();
- return;
- }
-
- if ( mSelRoad != NULL && mSelNode != -1 )
- {
- mGizmo->on3DMouseMove( event );
- //mGizmo.collideAxisGizmo( event );
- //Con::printf( "SelectedAxis: %i", mGizmo.getSelectedAxis() );
- }
- // Is cursor hovering over a river?
- if ( mMode == mSelectMeshRoadMode )
- {
- mHoverRoad = NULL;
- Point3F startPnt = event.pos;
- Point3F endPnt = event.pos + event.vec * 1000.0f;
- RayInfo ri;
- if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
- {
- MeshRoad *pRoad = NULL;
- pRoad = dynamic_cast<MeshRoad*>(ri.object);
- // Do not select or edit a MeshRoad within a Prefab.
- if ( pRoad && !Prefab::getPrefabByChild(pRoad) )
- mHoverRoad = pRoad;
- }
- }
- // Is cursor over a node?
- if ( mHoverRoad )
- {
- MeshRoad *pRoad = NULL;
- S32 nodeIdx = -1;
- for ( SimSetIterator iter(mRoadSet); *iter; ++iter )
- {
- pRoad = static_cast<MeshRoad*>( *iter );
- nodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint );
- if ( nodeIdx != -1 )
- {
- mHoverRoad = pRoad;
- break;
- }
- }
- mHoverNode = nodeIdx;
- }
- }
- void GuiMeshRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
- {
- if( MeshRoad::smShowRoadProfile && mProfileNode > 0 && mSelRoad)
- {
- // If we haven't already saved,
- // save an undo action to get back to this state,
- // before we make any modifications to the selected node.
- if ( !mSavedProfileDrag )
- {
- submitUndo( "Modify Profile Node" );
- mSavedProfileDrag = true;
- mIsDirty = true;
- }
- U32 idx;
- Point3F pos, diff;
- PlaneF xy( mSelRoad->mSlices[0].p2, -mSelRoad->mSlices[0].fvec );
- xy.intersect(event.pos, event.vec, &pos);
- mSelRoad->mSideProfile.worldToObj(pos);
- diff = pos - mSelRoad->mSideProfile.mNodes[mProfileNode].getPosition();
- for(U32 i=0; i < mSelProfNodeList.size(); i++)
- {
- idx = mSelProfNodeList[i];
- pos = mSelRoad->mSideProfile.mNodes[idx].getPosition();
- pos += diff;
- if(pos.x < -mSelRoad->mSlices[0].width/2.0f)
- pos.x = -mSelRoad->mSlices[0].width/2.0f + 1e-6;
- mSelRoad->mSideProfile.setNodePosition( idx, pos );
- }
- mDeselectProfileNode = false;
- return;
- }
- // Drags are only used to transform nodes
- if ( !mSelRoad || mSelNode == -1 ||
- ( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) )
- return;
- // If we haven't already saved,
- // save an undo action to get back to this state,
- // before we make any modifications to the selected node.
- if ( !mSavedDrag )
- {
- submitUndo( "Modify Node" );
- mSavedDrag = true;
- }
- // If shift is held and we haven't already copied the node,
- // make a copy of the selected node and select it.
- if ( event.modifier & SI_SHIFT && !mHasCopied && mSelRoad->isEndNode( mSelNode ) )
- {
- const MeshRoadNode &data = mSelRoad->getNode( mSelNode );
-
- U32 insertIdx = ( mSelNode == 0 ) ? 0 : U32_MAX;
- U32 newNodeIdx = mSelRoad->insertNode( data.point, data.width, data.depth, data.normal, insertIdx );
- mIsDirty = true;
-
- mSelNode = -1;
- setSelectedNode( newNodeIdx );
- mHasCopied = true;
- }
- // Let the Gizmo handle the drag, eg, modify its transforms
- mGizmo->on3DMouseDragged( event );
- if ( mGizmo->isDirty() )
- {
- Point3F pos = mGizmo->getPosition();
- Point3F scale = mGizmo->getScale();
- const MatrixF &mat = mGizmo->getTransform();
- VectorF normal;
- mat.getColumn( 2, &normal );
- mSelRoad->setNode( pos, scale.x, scale.z, normal, mSelNode );
- mIsDirty = true;
- mGizmo->markClean();
- }
- Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) );
- }
- void GuiMeshRoadEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event)
- {
- // nothing to do
- }
- void GuiMeshRoadEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
- {
- // nothing to do
- }
- bool GuiMeshRoadEditorCtrl::onKeyDown(const GuiEvent& event)
- {
- if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode )
- {
- // Delete the node attached to the cursor.
- deleteSelectedNode();
- mMode = mAddMeshRoadMode;
- return true;
- }
- return false;
- }
- void GuiMeshRoadEditorCtrl::updateGuiInfo()
- {
- // nothing to do
- }
-
- void GuiMeshRoadEditorCtrl::renderScene(const RectI & updateRect)
- {
- //GFXDrawUtil *drawer = GFX->getDrawUtil();
- GFX->setStateBlock( mZDisableSB );
- // get the projected size...
- GameConnection* connection = GameConnection::getConnectionToServer();
- if(!connection)
- return;
- // Grab the camera's transform
- MatrixF mat;
- connection->getControlCameraTransform(0, &mat);
- // Get the camera position
- Point3F camPos;
- mat.getColumn(3,&camPos);
- // Set up transform
- if( mSelRoad )
- {
- MatrixF profileMat(true);
- profileMat.setRow(0, mSelRoad->mSlices[0].rvec);
- profileMat.setRow(1, mSelRoad->mSlices[0].uvec);
- profileMat.setRow(2, -mSelRoad->mSlices[0].fvec);
- mSelRoad->mSideProfile.setTransform(profileMat, mSelRoad->mSlices[0].p2);
- }
- if ( mHoverRoad && mHoverRoad != mSelRoad )
- {
- _drawSpline( mHoverRoad, mHoverSplineColor );
- }
- if ( mSelRoad )
- {
- _drawSpline( mSelRoad, mSelectedSplineColor );
- // Render Gizmo for selected node if were in either of the three transform modes
- if ( mSelNode != -1 && ( mMode == mMovePointMode || mMode == mScalePointMode || mMode == mRotatePointMode ) )
- {
- if( mMode == mMovePointMode )
- {
- mGizmo->getProfile()->mode = MoveMode;
- }
- else if( mMode == mScalePointMode )
- {
- mGizmo->getProfile()->mode = ScaleMode;
- }
- else if( mMode == mRotatePointMode )
- {
- mGizmo->getProfile()->mode = RotateMode;
- }
- const MeshRoadNode &node = mSelRoad->mNodes[mSelNode];
- MatrixF objMat = mSelRoad->getNodeTransform(mSelNode);
- Point3F objScale( node.width, 1.0f, node.depth );
- Point3F worldPos = node.point;
- mGizmo->set( objMat, worldPos, objScale );
- mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov );
-
- // Render Gizmo text
- mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection );
- }
- }
- DebugDrawer::get()->render();
- // Now draw all the 2d stuff!
- GFX->setClipRect(updateRect);
-
- // Draw Control nodes for selecting and highlighted rivers
- if ( mHoverRoad )
- _drawControlNodes( mHoverRoad, mHoverSplineColor );
- if ( mSelRoad )
- _drawControlNodes( mSelRoad, mSelectedSplineColor );
- if(MeshRoad::smShowRoadProfile)
- {
- char buf[64];
- Point2I posi;
- posi.x = 10;
- posi.y = updateRect.len_y() - 80;
- GFX->getDrawUtil()->setBitmapModulation(ColorI(128, 128, 128));
- dStrcpy(buf, "Reset Profile: Double-click Start Node", 64);
- GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
- posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
- dStrcpy(buf, "Move Node: Click and Drag Node", 64);
- GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
- posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
- dStrcpy(buf, "Select Multiple Nodes: Ctrl-click Nodes", 64);
- GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
- posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
- dStrcpy(buf, "Toggle Material: Shift-click Spline Segment", 64);
- GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
- posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
- dStrcpy(buf, "Toggle Smoothing: Shift-click Node", 64);
- GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
- posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
- dStrcpy(buf, "Delete Node: Alt-click Node", 64);
- GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
- posi.y -= mProfile->mFont->getCharHeight((U8)buf[0]) + 4;
- dStrcpy(buf, "Add Node: Ctrl-click Spline", 64);
- GFX->getDrawUtil()->drawTextN(mProfile->mFont, posi, buf, dStrlen(buf));
- }
- }
- S32 GuiMeshRoadEditorCtrl::_getNodeAtScreenPos( const MeshRoad *pRoad, const Point2I &posi )
- {
- for ( U32 i = 0; i < pRoad->mNodes.size(); i++ )
- {
- const Point3F &nodePos = pRoad->mNodes[i].point;
- Point3F screenPos;
- project( nodePos, &screenPos );
- if ( screenPos.z < 0.0f )
- continue;
- Point2I screenPosI( (S32)screenPos.x, (S32)screenPos.y );
- RectI nodeScreenRect( screenPosI - mNodeHalfSize, mNodeHalfSize * 2 );
- if ( nodeScreenRect.pointInRect(posi) )
- {
- // we found a hit!
- return i;
- }
- }
- return -1;
- }
- S32 GuiMeshRoadEditorCtrl::_getProfileNodeAtScreenPos( MeshRoadProfile *pProfile, const Point2I &posi)
- {
- for ( U32 i = 0; i < pProfile->mNodes.size(); i++ )
- {
- Point3F nodePos;
- pProfile->getNodeWorldPos(i, nodePos);
- Point3F screenPos;
- project( nodePos, &screenPos );
- if ( screenPos.z < 0.0f )
- continue;
- Point2I screenPosI( (S32)screenPos.x, (S32)screenPos.y );
- RectI nodeScreenRect( screenPosI - mNodeHalfSize, mNodeHalfSize * 2 );
- if ( nodeScreenRect.pointInRect(posi) )
- {
- // we found a hit!
- return i;
- }
- }
- return -1;
- }
- void GuiMeshRoadEditorCtrl::_drawSpline( MeshRoad *river, const ColorI &color )
- {
- if ( river->mSlices.size() <= 1 )
- return;
- if ( MeshRoad::smShowSpline )
- {
- // Render the River center-line
- if( MeshRoad::smShowRoadProfile )
- PrimBuild::color( ColorI(100,100,100) );
- else
- PrimBuild::color( color );
- PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
- for ( U32 i = 0; i < river->mSlices.size(); i++ )
- {
- PrimBuild::vertex3fv( river->mSlices[i].p1 );
- }
- PrimBuild::end();
- }
-
- if ( MeshRoad::smWireframe )
- {
- // Left-side line
- PrimBuild::color3i( 100, 100, 100 );
- PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
- for ( U32 i = 0; i < river->mSlices.size(); i++ )
- {
- PrimBuild::vertex3fv( river->mSlices[i].p0 );
- }
- PrimBuild::end();
- // Right-side line
- PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
- for ( U32 i = 0; i < river->mSlices.size(); i++ )
- {
- PrimBuild::vertex3fv( river->mSlices[i].p2 );
- }
- PrimBuild::end();
- // Cross-sections
- PrimBuild::begin( GFXLineList, river->mSlices.size() * 2 );
- for ( U32 i = 0; i < river->mSlices.size(); i++ )
- {
- PrimBuild::vertex3fv( river->mSlices[i].p0 );
- PrimBuild::vertex3fv( river->mSlices[i].p2 );
- }
- PrimBuild::end();
- }
- // If we are in Profile Edit Mode, draw the profile spline and node normals
- if ( MeshRoad::smShowRoadProfile )
- {
- Point3F nodePos;
- Point3F normEndPos;
- U32 numSide, numTop, numBottom;
- numSide = numTop = numBottom = 0;
- for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
- {
- switch(river->mSideProfile.mSegMtrls[i])
- {
- case MeshRoad::Side: numSide++; break;
- case MeshRoad::Top: numTop++; break;
- case MeshRoad::Bottom: numBottom++; break;
- }
- }
- // Render the profile spline
- // Side
- if(numSide)
- {
- PrimBuild::color( mProfileColor );
- PrimBuild::begin( GFXLineList, 2*numSide );
- for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
- {
- if(river->mSideProfile.mSegMtrls[i] == MeshRoad::Side)
- {
- river->mSideProfile.getNodeWorldPos(i, nodePos);
- PrimBuild::vertex3fv( nodePos );
- river->mSideProfile.getNodeWorldPos(i+1, nodePos);
- PrimBuild::vertex3fv( nodePos );
- }
- }
- PrimBuild::end();
- }
- // Top
- if(numTop)
- {
- PrimBuild::color( ColorI(0,255,0) );
- PrimBuild::begin( GFXLineList, 2*numTop );
- for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
- {
- if(river->mSideProfile.mSegMtrls[i] == MeshRoad::Top)
- {
- river->mSideProfile.getNodeWorldPos(i, nodePos);
- PrimBuild::vertex3fv( nodePos );
- river->mSideProfile.getNodeWorldPos(i+1, nodePos);
- PrimBuild::vertex3fv( nodePos );
- }
- }
- PrimBuild::end();
- }
- // Bottom
- if(numBottom)
- {
- PrimBuild::color( ColorI(255,0,255) );
- PrimBuild::begin( GFXLineList, 2*numBottom );
- for ( U32 i = 0; i < river->mSideProfile.mSegMtrls.size(); i++ )
- {
- if(river->mSideProfile.mSegMtrls[i] == MeshRoad::Bottom)
- {
- river->mSideProfile.getNodeWorldPos(i, nodePos);
- PrimBuild::vertex3fv( nodePos );
- river->mSideProfile.getNodeWorldPos(i+1, nodePos);
- PrimBuild::vertex3fv( nodePos );
- }
- }
- PrimBuild::end();
- }
- // Render node normals
- PrimBuild::color( ColorI(255,0,0) );
- PrimBuild::begin( GFXLineList, 4*river->mSideProfile.mNodes.size() - 4 );
- for ( U32 i = 0; i < river->mSideProfile.mNodes.size()-1; i++ )
- {
- for( U32 j = 0; j < 2; j++)
- {
- river->mSideProfile.getNodeWorldPos(i+j, nodePos);
- PrimBuild::vertex3fv( nodePos );
- river->mSideProfile.getNormWorldPos(2*i+j, normEndPos);
- PrimBuild::vertex3fv( normEndPos );
- }
- }
- PrimBuild::end();
- }
- // Segment
- }
- void GuiMeshRoadEditorCtrl::_drawControlNodes( MeshRoad *river, const ColorI &color )
- {
- if ( !MeshRoad::smShowSpline )
- return;
- RectI bounds = getBounds();
- GFXDrawUtil *drawer = GFX->getDrawUtil();
- bool isSelected = ( river == mSelRoad );
- bool isHighlighted = ( river == mHoverRoad );
-
- for ( U32 i = 0; i < river->mNodes.size(); i++ )
- {
- if ( false && isSelected && mSelNode == i )
- continue;
- const Point3F &wpos = river->mNodes[i].point;
- Point3F spos;
- project( wpos, &spos );
- if ( spos.z > 1.0f )
- continue;
- Point2I posi;
- posi.x = spos.x;
- posi.y = spos.y;
- if ( !bounds.pointInRect( posi ) )
- continue;
- ColorI theColor = color;
- Point2I nodeHalfSize = mNodeHalfSize;
-
- if ( isHighlighted && mHoverNode == i )
- {
- //theColor = mHoverNodeColor;
- nodeHalfSize += Point2I(2,2);
- }
- if ( isSelected )
- {
- if ( mSelNode == i )
- {
- theColor.set(0,0,255);
- }
- else if ( i == 0 )
- {
- theColor.set(0,255,0);
- }
- else if ( i == river->mNodes.size() - 1 )
- {
- theColor.set(255,0,0);
- }
- }
- if( MeshRoad::smShowRoadProfile && isSelected )
- theColor.set(100,100,100);
- drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor );
- }
- // Draw profile control nodes
- if( MeshRoad::smShowRoadProfile && isSelected )
- {
- Point3F wpos;
- Point3F spos;
- Point2I posi;
- ColorI theColor;
- for( U32 i = 0; i < river->mSideProfile.mNodes.size(); i++)
- {
- river->mSideProfile.getNodeWorldPos(i, wpos);
- project( wpos, &spos );
- if ( spos.z > 1.0f )
- continue;
- posi.x = spos.x;
- posi.y = spos.y;
- if ( !bounds.pointInRect( posi ) )
- continue;
- if(i == 0)
- theColor.set(mProfileColor.red/3, mProfileColor.green/3, mProfileColor.blue/3,255);
- else
- theColor.set(mProfileColor,255);
- if( mSelProfNodeList.find_next(i) != -1 )
- theColor.set(0,0,255);
- drawer->drawRectFill( posi - mNodeHalfSize, posi + mNodeHalfSize, theColor );
- }
- }
- }
- bool GuiMeshRoadEditorCtrl::getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos )
- {
- // Find clicked point on the terrain
- Point3F startPnt = event.pos;
- Point3F endPnt = event.pos + event.vec * 1000.0f;
- RayInfo ri;
- bool hit;
-
- hit = gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri);
- tpos = ri.point;
-
- return hit;
- }
- void GuiMeshRoadEditorCtrl::deleteSelectedNode()
- {
- if ( !mSelRoad || mSelNode == -1 )
- return;
- // If the Road has only two nodes remaining,
- // delete the whole Road.
- if ( mSelRoad->mNodes.size() <= 2 )
- {
- deleteSelectedRoad( mMode != mAddNodeMode );
- }
- else
- {
- if ( mMode != mAddNodeMode )
- submitUndo( "Delete Node" );
- // Delete the SelectedNode of the SelectedRoad
- mSelRoad->deleteNode( mSelNode );
- // We deleted the Node but not the Road (it has nodes left)
- // so decrement the currently selected node.
- if ( mSelRoad->mNodes.size() <= mSelNode )
- setSelectedNode( mSelNode - 1 );
- else
- {
- // force gizmo to update to the selected nodes position
- // the index didn't change but the node it refers to did.
- U32 i = mSelNode;
- mSelNode = -1;
- setSelectedNode( i );
- }
- }
- // If you were in addNodeMode,
- // deleting a node should ends it.
- //mMode = smNormalMode;
- }
- void GuiMeshRoadEditorCtrl::deleteSelectedRoad( bool undoAble )
- {
- AssertFatal( mSelRoad != NULL, "GuiMeshRoadEditorCtrl::deleteSelectedRoad() - No Road is selected" );
- // Not undoAble? Just delete it.
- if ( !undoAble )
- {
- mSelRoad->deleteObject();
- mIsDirty = true;
- Con::executef( this, "onRoadSelected" );
- mSelNode = -1;
- return;
- }
- // Grab the mission editor undo manager.
- UndoManager *undoMan = NULL;
- if ( !Sim::findObject( "EUndoManager", undoMan ) )
- {
- // Couldn't find it? Well just delete the Road.
- Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
- return;
- }
- else
- {
- // Create the UndoAction.
- MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road");
- action->deleteObject( mSelRoad );
- mIsDirty = true;
- // Submit it.
- undoMan->addAction( action );
- }
- // ScriptCallback with 'NULL' parameter for no Road currently selected.
- Con::executef( this, "onRoadSelected" );
- // Clear the SelectedNode (it has been deleted along with the River).
- setSelectedNode( -1 );
- mSelNode = -1;
- }
- void GuiMeshRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false )
- {
- mMode = mode;
- if( sourceShortcut )
- Con::executef( this, "paletteSync", mode );
- }
- void GuiMeshRoadEditorCtrl::setSelectedRoad( MeshRoad *road )
- {
- mSelRoad = road;
- if ( mSelRoad != NULL )
- Con::executef( this, "onRoadSelected", road->getIdString() );
- else
- Con::executef( this, "onRoadSelected" );
- if ( mSelRoad != road )
- setSelectedNode(-1);
- }
- void GuiMeshRoadEditorCtrl::setNodeWidth( F32 width )
- {
- if ( mSelRoad && mSelNode != -1 )
- {
- mSelRoad->setNodeWidth( mSelNode, width );
- mIsDirty = true;
- }
- }
- F32 GuiMeshRoadEditorCtrl::getNodeWidth()
- {
- if ( mSelRoad && mSelNode != -1 )
- return mSelRoad->getNodeWidth( mSelNode );
- return 0.0f;
- }
- void GuiMeshRoadEditorCtrl::setNodeDepth(F32 depth)
- {
- if ( mSelRoad && mSelNode != -1 )
- {
- mSelRoad->setNodeDepth( mSelNode, depth );
- mIsDirty = true;
- }
- }
- F32 GuiMeshRoadEditorCtrl::getNodeDepth()
- {
- if ( mSelRoad && mSelNode != -1 )
- return mSelRoad->getNodeDepth( mSelNode );
- return 0.0f;
- }
- void GuiMeshRoadEditorCtrl::setNodePosition(const Point3F& pos)
- {
- if ( mSelRoad && mSelNode != -1 )
- {
- mSelRoad->setNodePosition( mSelNode, pos );
- mIsDirty = true;
- }
- }
- Point3F GuiMeshRoadEditorCtrl::getNodePosition()
- {
- if ( mSelRoad && mSelNode != -1 )
- return mSelRoad->getNodePosition( mSelNode );
- return Point3F( 0, 0, 0 );
- }
- void GuiMeshRoadEditorCtrl::setNodeNormal( const VectorF &normal )
- {
- if ( mSelRoad && mSelNode != -1 )
- {
- mSelRoad->setNodeNormal( mSelNode, normal );
- mIsDirty = true;
- }
- }
- VectorF GuiMeshRoadEditorCtrl::getNodeNormal()
- {
- if ( mSelRoad && mSelNode != -1 )
- return mSelRoad->getNodeNormal( mSelNode );
- return VectorF::Zero;
- }
- void GuiMeshRoadEditorCtrl::setSelectedNode( S32 node )
- {
- if ( mSelNode == node )
- return;
- mSelNode = node;
- if ( mSelNode != -1 )
- {
- const MeshRoadNode &curNode = mSelRoad->mNodes[mSelNode];
- MatrixF objMat = mSelRoad->getNodeTransform(mSelNode);
- Point3F objScale(curNode.width, 1.0f, curNode.depth );
- Point3F worldPos = curNode.point;
-
- mGizmo->set( objMat, worldPos, objScale );
- }
-
- if ( mSelNode != -1 )
- Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode) );
- else
- Con::executef( this, "onNodeSelected", Con::getIntArg(-1) );
- }
- void GuiMeshRoadEditorCtrl::submitUndo( const UTF8 *name )
- {
- // Grab the mission editor undo manager.
- UndoManager *undoMan = NULL;
- if ( !Sim::findObject( "EUndoManager", undoMan ) )
- {
- Con::errorf( "GuiMeshRoadEditorCtrl::submitUndo() - EUndoManager not found!" );
- return;
- }
- // Setup the action.
- GuiMeshRoadEditorUndoAction *action = new GuiMeshRoadEditorUndoAction( name );
- action->mObjId = mSelRoad->getId();
- //action->mMetersPerSegment = mSelRoad->mMetersPerSegment;
- action->mEditor = this;
- for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ )
- {
- action->mNodes.push_back( mSelRoad->mNodes[i] );
- }
-
- // Save profile nodes and materials
- for( U32 i = 0; i < mSelRoad->mSideProfile.mNodes.size(); i++)
- {
- action->mProfileNodes.push_back( mSelRoad->mSideProfile.mNodes[i] );
- if(i)
- action->mProfileMtrls.push_back( mSelRoad->mSideProfile.mSegMtrls[i-1] );
- }
- undoMan->addAction( action );
- }
- void GuiMeshRoadEditorCtrl::matchTerrainToRoad()
- {
- if ( !mSelRoad )
- return;
- // Not implemented, but a potentially useful idea.
- // Move manipulate the terrain so that the MeshRoad appears to be sitting
- // on top of it.
- // The opposite could also be useful, manipulate the MeshRoad to line up
- // with the terrain underneath it.
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, deleteNode, void, (), , "deleteNode()" )
- {
- object->deleteSelectedNode();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, getMode, const char*, (), , "" )
- {
- return object->getMode();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, setMode, void, (const char * mode), , "setMode( String mode )" )
- {
- String newMode = ( mode );
- object->setMode( newMode );
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodeWidth, F32, (), , "" )
- {
- return object->getNodeWidth();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodeWidth, void, ( F32 width ), , "" )
- {
- object->setNodeWidth( width );
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodeDepth, F32, (), , "" )
- {
- return object->getNodeDepth();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodeDepth, void, ( F32 depth ), , "" )
- {
- object->setNodeDepth( depth );
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodePosition, Point3F, (), , "" )
- {
- return object->getNodePosition();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodePosition, void, (Point3F pos), , "" )
- {
- object->setNodePosition( pos );
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, getNodeNormal, Point3F, (), , "" )
- {
- return object->getNodeNormal();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, setNodeNormal, void, (Point3F normal), , "" )
- {
- object->setNodeNormal( normal );
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, setSelectedRoad, void, (const char * objName), (""), "" )
- {
- if ( String::isEmpty(objName) )
- object->setSelectedRoad(NULL);
- else
- {
- MeshRoad *road = NULL;
- if ( Sim::findObject( objName, road ) )
- object->setSelectedRoad(road);
- }
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, getSelectedRoad, S32, (), , "" )
- {
- MeshRoad *road = object->getSelectedRoad();
- if ( !road )
- return 0;
- return road->getId();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, regenerate, void, (), , "" )
- {
- MeshRoad *road = object->getSelectedRoad();
- if ( road )
- road->regenerate();
- }
- DefineEngineMethod( GuiMeshRoadEditorCtrl, matchTerrainToRoad, void, (), , "" )
- {
- object->matchTerrainToRoad();
- }
|