|
@@ -55,6 +55,14 @@ ConsoleDocClass( GuiMeshRoadEditorCtrl,
|
|
"@internal"
|
|
"@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()
|
|
GuiMeshRoadEditorCtrl::GuiMeshRoadEditorCtrl()
|
|
:
|
|
:
|
|
// Each of the mode names directly correlates with the Mesh Road Editor's
|
|
// Each of the mode names directly correlates with the Mesh Road Editor's
|
|
@@ -69,6 +77,10 @@ GuiMeshRoadEditorCtrl::GuiMeshRoadEditorCtrl()
|
|
mRotatePointMode("MeshRoadEditorRotateMode"),
|
|
mRotatePointMode("MeshRoadEditorRotateMode"),
|
|
mSavedDrag(false),
|
|
mSavedDrag(false),
|
|
mIsDirty( false ),
|
|
mIsDirty( false ),
|
|
|
|
+ mSavedProfileDrag( false ),
|
|
|
|
+ mDeselectProfileNode( false ),
|
|
|
|
+ mProfileNode( -1 ),
|
|
|
|
+ mProfileColor( 255,255,0 ),
|
|
mRoadSet( NULL ),
|
|
mRoadSet( NULL ),
|
|
mSelNode( -1 ),
|
|
mSelNode( -1 ),
|
|
mHoverNode( -1 ),
|
|
mHoverNode( -1 ),
|
|
@@ -116,6 +128,48 @@ void GuiMeshRoadEditorUndoAction::undo()
|
|
object->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal );
|
|
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
|
|
// Regenerate the Road
|
|
object->regenerate();
|
|
object->regenerate();
|
|
|
|
|
|
@@ -228,6 +282,158 @@ void GuiMeshRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
|
|
if ( !isFirstResponder() )
|
|
if ( !isFirstResponder() )
|
|
setFirstResponder();
|
|
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
|
|
// Get the raycast collision position
|
|
Point3F tPos;
|
|
Point3F tPos;
|
|
if ( !getStaticPos( event, tPos ) )
|
|
if ( !getStaticPos( event, tPos ) )
|
|
@@ -588,6 +794,33 @@ void GuiMeshRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
|
|
|
|
|
|
mSavedDrag = false;
|
|
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();
|
|
mouseUnlock();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -661,6 +894,43 @@ void GuiMeshRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
|
|
|
|
|
|
void GuiMeshRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
|
|
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
|
|
// Drags are only used to transform nodes
|
|
if ( !mSelRoad || mSelNode == -1 ||
|
|
if ( !mSelRoad || mSelNode == -1 ||
|
|
( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) )
|
|
( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) )
|
|
@@ -756,6 +1026,18 @@ void GuiMeshRoadEditorCtrl::renderScene(const RectI & updateRect)
|
|
Point3F camPos;
|
|
Point3F camPos;
|
|
mat.getColumn(3,&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 )
|
|
if ( mHoverRoad && mHoverRoad != mSelRoad )
|
|
{
|
|
{
|
|
_drawSpline( mHoverRoad, mHoverSplineColor );
|
|
_drawSpline( mHoverRoad, mHoverSplineColor );
|
|
@@ -806,6 +1088,37 @@ void GuiMeshRoadEditorCtrl::renderScene(const RectI & updateRect)
|
|
_drawControlNodes( mHoverRoad, mHoverSplineColor );
|
|
_drawControlNodes( mHoverRoad, mHoverSplineColor );
|
|
if ( mSelRoad )
|
|
if ( mSelRoad )
|
|
_drawControlNodes( mSelRoad, mSelectedSplineColor );
|
|
_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 )
|
|
S32 GuiMeshRoadEditorCtrl::_getNodeAtScreenPos( const MeshRoad *pRoad, const Point2I &posi )
|
|
@@ -834,6 +1147,33 @@ S32 GuiMeshRoadEditorCtrl::_getNodeAtScreenPos( const MeshRoad *pRoad, const Poi
|
|
return -1;
|
|
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 )
|
|
void GuiMeshRoadEditorCtrl::_drawSpline( MeshRoad *river, const ColorI &color )
|
|
{
|
|
{
|
|
if ( river->mSlices.size() <= 1 )
|
|
if ( river->mSlices.size() <= 1 )
|
|
@@ -842,8 +1182,12 @@ void GuiMeshRoadEditorCtrl::_drawSpline( MeshRoad *river, const ColorI &color )
|
|
if ( MeshRoad::smShowSpline )
|
|
if ( MeshRoad::smShowSpline )
|
|
{
|
|
{
|
|
// Render the River center-line
|
|
// Render the River center-line
|
|
- PrimBuild::color( color );
|
|
|
|
- PrimBuild::begin( GFXLineStrip, river->mSlices.size() );
|
|
|
|
|
|
+ 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++ )
|
|
for ( U32 i = 0; i < river->mSlices.size(); i++ )
|
|
{
|
|
{
|
|
PrimBuild::vertex3fv( river->mSlices[i].p1 );
|
|
PrimBuild::vertex3fv( river->mSlices[i].p1 );
|
|
@@ -880,6 +1224,100 @@ void GuiMeshRoadEditorCtrl::_drawSpline( MeshRoad *river, const ColorI &color )
|
|
PrimBuild::end();
|
|
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
|
|
// Segment
|
|
}
|
|
}
|
|
|
|
|
|
@@ -940,8 +1378,46 @@ void GuiMeshRoadEditorCtrl::_drawControlNodes( MeshRoad *river, const ColorI &co
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if( MeshRoad::smShowRoadProfile && isSelected )
|
|
|
|
+ theColor.set(100,100,100);
|
|
|
|
+
|
|
drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor );
|
|
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 )
|
|
bool GuiMeshRoadEditorCtrl::getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos )
|
|
@@ -1174,6 +1650,15 @@ void GuiMeshRoadEditorCtrl::submitUndo( const UTF8 *name )
|
|
action->mNodes.push_back( mSelRoad->mNodes[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 );
|
|
undoMan->addAction( action );
|
|
}
|
|
}
|
|
|
|
|