Torque3D/Engine/source/environment/editors/guiRiverEditorCtrl.cpp

1511 lines
41 KiB
C++
Raw Normal View History

2012-09-19 15:15:01 +00:00
//-----------------------------------------------------------------------------
// 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/guiRiverEditorCtrl.h"
#include "console/consoleTypes.h"
#include "environment/river.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 "T3D/prefab.h"
IMPLEMENT_CONOBJECT(GuiRiverEditorCtrl);
ConsoleDocClass( GuiRiverEditorCtrl,
"@brief GUI tool that makes up the River Editor\n\n"
"Editor use only.\n\n"
"@internal"
);
GuiRiverEditorCtrl::GuiRiverEditorCtrl()
: mDefaultNormal( 0, 0, 1 ),
mDefaultWidth( 10.0f ),
mDefaultDepth( 5.0f )
{
// Each of the mode names directly correlates with the River Editor's
// tool palette
mSelectRiverMode = "RiverEditorSelectMode";
mAddRiverMode = "RiverEditorAddRiverMode";
mMovePointMode = "RiverEditorMoveMode";
mRotatePointMode = "RiverEditorRotateMode";
mScalePointMode = "RiverEditorScaleMode";
mAddNodeMode = "RiverEditorAddNodeMode";
mInsertPointMode = "RiverEditorInsertPointMode";
mRemovePointMode = "RiverEditorRemovePointMode";
mMode = mSelectRiverMode;
mRiverSet = NULL;
mSelNode = -1;
mSelRiver = NULL;
mHoverRiver = NULL;
mAddNodeIdx = 0;
mHoverNode = -1;
mInsertIdx = -1;
mStartWidth = -1.0f;
mStartHeight = -1.0f;
mStartX = 0;
mIsDirty = false;
mNodeHalfSize.set(4,4);
mNodeSphereRadius = 15.0f;
mNodeSphereFillColor.set( 15,15,100,145 );
mNodeSphereLineColor.set( 25,25,25,0 );
mHoverSplineColor.set( 255,0,0,255 );
mSelectedSplineColor.set( 0,255,0,255 );
mHoverNodeColor.set( 255,255,255,255 );
mStartDragMousePoint = InvalidMousePoint;
//mMoveNodeCursor = NULL;
//mAddNodeCursor = NULL;
//mInsertNodeCursor = NULL;
//mResizeNodeCursor = NULL;
}
GuiRiverEditorCtrl::~GuiRiverEditorCtrl()
{
// nothing to do
}
void GuiRiverEditorUndoAction::undo()
{
River *river = NULL;
if ( !Sim::findObject( mObjId, river ) )
return;
// Temporarily save the Rivers current data.
F32 metersPerSeg = river->mMetersPerSegment;
Vector<RiverNode> nodes;
nodes.merge( river->mNodes );
// Restore the River properties saved in the UndoAction
river->mMetersPerSegment = mMetersPerSegment;
// Restore the Nodes saved in the UndoAction
river->mNodes.clear();
for ( U32 i = 0; i < mNodes.size(); i++ )
{
river->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal );
}
// Regenerate the River
river->regenerate();
// If applicable set the selected River and node
mRiverEditor->mSelRiver = river;
mRiverEditor->mSelNode = -1;
// Now save the previous River data in this UndoAction
// since an undo action must become a redo action and vice-versa
mMetersPerSegment = metersPerSeg;
mNodes.clear();
mNodes.merge( nodes );
}
bool GuiRiverEditorCtrl::onAdd()
{
if( !Parent::onAdd() )
return false;
mRiverSet = River::getServerSet();
GFXStateBlockDesc desc;
desc.fillMode = GFXFillSolid;
desc.setBlend( false );
desc.setZReadWrite( false, false );
desc.setCullMode( GFXCullNone );
mZDisableSB = GFX->createStateBlock(desc);
desc.setZReadWrite( true, true );
mZEnableSB = GFX->createStateBlock(desc);
SceneManager::getPreRenderSignal().notify( this, &GuiRiverEditorCtrl::_prepRenderImage );
return true;
}
void GuiRiverEditorCtrl::initPersistFields()
{
addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiRiverEditorCtrl ) );
addField( "DefaultDepth", TypeF32, Offset( mDefaultDepth, GuiRiverEditorCtrl ) );
addField( "DefaultNormal", TypePoint3F,Offset( mDefaultNormal, GuiRiverEditorCtrl ) );
addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiRiverEditorCtrl ) );
addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiRiverEditorCtrl ) );
addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiRiverEditorCtrl ) );
addField( "isDirty", TypeBool, Offset( mIsDirty, GuiRiverEditorCtrl ) );
//addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiRiverEditorCtrl) );
//addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiRiverEditorCtrl) );
//addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiRiverEditorCtrl) );
//addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiRiverEditorCtrl) );
Parent::initPersistFields();
}
void GuiRiverEditorCtrl::onSleep()
{
Parent::onSleep();
mMode = mSelectRiverMode;
mHoverNode = -1;
mHoverRiver = NULL;
setSelectedNode(-1);
//mSelRiver = NULL;
//mSelNode = -1;
}
void GuiRiverEditorCtrl::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 GuiRiverEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event)
{
_process3DMouseDown( event );
2012-09-19 15:15:01 +00:00
mGizmo->on3DMouseDown( event );
if ( !isFirstResponder() )
setFirstResponder();
}
void GuiRiverEditorCtrl::_process3DMouseDown( const Gui3DMouseEvent& event )
{
2012-09-19 15:15:01 +00:00
// 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 RiverNode
// was clicked.
Point3F startPnt = event.pos;
Point3F endPnt = event.pos + event.vec * 1000.0f;
RayInfo ri;
if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
endPnt = ri.point;
River *riverPtr = NULL;
River *clickedRiverPtr = NULL;
// Did we click on a river? check current selection first
U32 insertNodeIdx = -1;
Point3F collisionPnt;
if ( mSelRiver != NULL && mSelRiver->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
{
clickedRiverPtr = mSelRiver;
}
else
{
for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
{
riverPtr = static_cast<River*>( *iter );
// Do not select or edit a River within a Prefab.
if ( Prefab::getPrefabByChild(riverPtr) )
continue;
if ( riverPtr->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) )
{
clickedRiverPtr = riverPtr;
break;
}
}
}
// Did we click on a riverNode?
bool nodeClicked = false;
S32 clickedNodeIdx = -1;
F32 clickedNodeDist = mNodeSphereRadius;
// If we clicked on the currently selected river, only scan its nodes
if ( mSelRiver != NULL && clickedRiverPtr == mSelRiver )
{
for ( U32 i = 0; i < mSelRiver->mNodes.size(); i++ )
{
const Point3F &nodePos = mSelRiver->mNodes[i].point;
Point3F screenPos;
project( nodePos, &screenPos );
F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
if ( dist < clickedNodeDist )
{
clickedNodeDist = dist;
clickedNodeIdx = i;
insertNodeIdx = i;
nodeClicked = true;
}
}
}
else
{
for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
{
riverPtr = static_cast<River*>( *iter );
// Do not select or edit a River within a Prefab.
if ( Prefab::getPrefabByChild(riverPtr) )
continue;
for ( U32 i = 0; i < riverPtr->mNodes.size(); i++ )
{
const Point3F &nodePos = riverPtr->mNodes[i].point;
Point3F screenPos;
project( nodePos, &screenPos );
F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
if ( dist < clickedNodeDist )
{
// we found a hit!
clickedNodeDist = dist;
clickedNodeIdx = i;
insertNodeIdx = i;
nodeClicked = true;
clickedRiverPtr = riverPtr;
}
}
}
}
// shortcuts
bool dblClick = ( event.mouseClickCount > 1 );
if( dblClick )
{
if( mMode == mSelectRiverMode )
{
setMode( mAddRiverMode, true );
return;
}
if( mMode == mAddNodeMode )
{
// Delete the node attached to the cursor.
deleteSelectedNode();
mMode = mAddRiverMode;
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 ( !mSelRiver )
mMode = mAddRiverMode;
}
if ( mMode == mSelectRiverMode )
{
// Did not click on a River or a node.
if ( !clickedRiverPtr )
{
setSelectedRiver( NULL );
setSelectedNode( -1 );
return;
}
// Clicked on a River that wasn't the currently selected River.
if ( clickedRiverPtr != mSelRiver )
{
setSelectedRiver( clickedRiverPtr );
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 == mAddRiverMode )
{
if ( nodeClicked )
{
// A double-click on a node in Normal mode means set AddNode mode.
if ( clickedNodeIdx == 0 )
{
setSelectedRiver( clickedRiverPtr );
setSelectedNode( clickedNodeIdx );
mAddNodeIdx = clickedNodeIdx;
mMode = mAddNodeMode;
mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
mIsDirty = true;
return;
}
else if ( clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 )
{
setSelectedRiver( clickedRiverPtr );
setSelectedNode( clickedNodeIdx );
mAddNodeIdx = U32_MAX;
mMode = mAddNodeMode;
mSelNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal);
mIsDirty = true;
setSelectedNode( mSelNode );
return;
}
}
if ( !isMethod( "createRiver" ) )
{
Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method does not exist." );
return;
}
const char *res = Con::executef( this, "createRiver" );
River *newRiver;
if ( !Sim::findObject( res, newRiver ) )
{
Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method did not return a river object." );
return;
}
// Add to MissionGroup
SimGroup *missionGroup;
if ( !Sim::findObject( "MissionGroup", missionGroup ) )
Con::errorf( "GuiRiverEditorCtrl - could not find MissionGroup to add new River" );
else
missionGroup->addObject( newRiver );
Point3F pos( endPnt );
pos.z += mDefaultDepth * 0.5f;
newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 );
U32 newNode = newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 );
// Always add to the end of the road, the first node is the start.
mAddNodeIdx = U32_MAX;
setSelectedRiver( newRiver );
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( newRiver );
// Submit it.
undoMan->addAction( action );
return;
}
else if ( mMode == mAddNodeMode )
{
// Oops the road got deleted, maybe from an undo action?
// Back to NormalMode.
if ( mSelRiver )
{
// A double-click on a node in Normal mode means set AddNode mode.
if ( clickedNodeIdx == 0 )
{
submitUndo( "Add Node" );
mAddNodeIdx = clickedNodeIdx;
mMode = mAddNodeMode;
mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx );
mIsDirty = true;
setSelectedNode( mSelNode );
return;
}
else
{
if( clickedRiverPtr && clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 )
{
submitUndo( "Add Node" );
mAddNodeIdx = U32_MAX;
mMode = mAddNodeMode;
U32 newNode = mSelRiver->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" );
//F32 width = mSelRiver->getNodeWidth( mSelNode );
U32 newNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx);
mIsDirty = true;
setSelectedNode( newNode );
return;
}
}
}
}
else if ( mMode == mInsertPointMode && mSelRiver != NULL )
{
if ( clickedRiverPtr == mSelRiver )
{
// 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 DecalRoad
U32 prevNodeIdx = insertNodeIdx;
U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRiver->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1;
const RiverNode &prevNode = mSelRiver->mNodes[prevNodeIdx];
const RiverNode &nextNode = mSelRiver->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 = mSelRiver->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 );
mIsDirty = true;
setSelectedNode( newNode );
return;
}
}
else if ( mMode == mRemovePointMode && mSelRiver != NULL )
{
if ( nodeClicked && clickedRiverPtr == mSelRiver )
{
setSelectedNode( clickedNodeIdx );
deleteSelectedNode();
return;
}
}
else if ( mMode == mMovePointMode )
{
if ( nodeClicked && clickedRiverPtr == mSelRiver )
{
setSelectedNode( clickedNodeIdx );
return;
}
}
else if ( mMode == mScalePointMode )
{
if ( nodeClicked && clickedRiverPtr == mSelRiver )
{
setSelectedNode( clickedNodeIdx );
return;
}
}
else if ( mMode == mRotatePointMode )
{
if ( nodeClicked && clickedRiverPtr == mSelRiver )
{
setSelectedNode( clickedNodeIdx );
return;
}
}
}
void GuiRiverEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event)
{
//mIsPanning = true;
}
void GuiRiverEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event)
{
//mIsPanning = false;
}
void GuiRiverEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event)
{
// Keep the Gizmo up to date.
mGizmo->on3DMouseUp( event );
mStartWidth = -1.0f;
mStartHeight = -1.0f;
mSavedDrag = false;
mouseUnlock();
}
void GuiRiverEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event)
{
if ( mSelRiver != NULL && mMode == mAddNodeMode )
{
Point3F pos;
if ( getStaticPos( event, pos ) )
{
pos.z += mSelRiver->getNodeDepth(mSelNode) * 0.5f;
mSelRiver->setNodePosition( mSelNode, pos );
mIsDirty = true;
}
return;
}
if ( mSelRiver != NULL && mSelNode != -1 )
mGizmo->on3DMouseMove( event );
// Is cursor hovering over a river?
if ( mMode == mSelectRiverMode )
{
mHoverRiver = NULL;
Point3F startPnt = event.pos;
Point3F endPnt = event.pos + event.vec * 1000.0f;
RayInfo ri;
if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) )
endPnt = ri.point;
River *pRiver = NULL;
for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
{
pRiver = static_cast<River*>( *iter );
// Do not select or edit a River within a Prefab.
if ( Prefab::getPrefabByChild(pRiver) )
continue;
if ( pRiver->collideRay( event.pos, event.vec ) )
{
mHoverRiver = pRiver;
break;
}
}
}
// Is cursor hovering over a RiverNode?
if ( mHoverRiver )
{
River *pRiver = mHoverRiver;
S32 hoverNodeIdx = -1;
F32 hoverNodeDist = mNodeSphereRadius;
//for ( SimSetIterator iter(mRiverSet); *iter; ++iter )
//{
// River *pRiver = static_cast<River*>( *iter );
for ( U32 i = 0; i < pRiver->mNodes.size(); i++ )
{
const Point3F &nodePos = pRiver->mNodes[i].point;
Point3F screenPos;
project( nodePos, &screenPos );
F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len();
if ( dist < hoverNodeDist )
{
// we found a hit!
hoverNodeDist = dist;
hoverNodeIdx = i;
}
}
//}
mHoverNode = hoverNodeIdx;
}
}
void GuiRiverEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event)
{
// Drags are only used to transform nodes
if ( !mSelRiver || 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;
}
// 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 );
mSelRiver->setNode( pos, scale.x, scale.z, normal, mSelNode );
mIsDirty = true;
}
Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) );
/*
// If we are just starting a new drag,
// we need to save the starting screen position of the mouse,
// and the starting position of the selected node.
if ( mStartDragMousePoint == InvalidMousePoint )
{
mStartDragMousePoint = event.mousePoint;
mStartDragNodePos = mSelRiver->getNodePosition( mSelNode );
}
MathUtils::Line clickLine;
clickLine.p = event.pos;
clickLine.d = event.vec;
MathUtils::Line axisLine;
axisLine.p = mStartDragNodePos;
axisLine.d = mGizmo.selectionToAxisVector( mGizmoSelection );
MathUtils::LineSegment segment;
MathUtils::mShortestSegmentBetweenLines( clickLine, axisLine, segment );
// Segment.p1 is the closest point on the axis line,
// We want to put the selected gizmo handle at that point,
// So calculate the offset from the handle to the centerPoint to
// determine the gizmo's position.
mSelRiver->setNodePosition( mSelNode, segment.p1 );
*/
/*
// Convert the delta (dragged mouse distance) from screen space
// into world space.
Point2I deltaScreen = event.mousePoint - mStartDragMousePoint;
F32 worldDist = ( event.pos - mStartDragNodePos ).len();
Point2F deltaWorld;
deltaWorld.x = GFX->unprojectRadius( worldDist, deltaScreen.x );
deltaWorld.y = GFX->unprojectRadius( worldDist, deltaScreen.y );
// Now modify the selected node depending on the kind of operation we are doing.
if ( mGizmoSelection == Gizmo::Axis_X )
{
Point3F newPos = mStartDragNodePos;
newPos.x += deltaWorld.x;
mSelRiver->setNodePosition( mSelNode, newPos );
}
else if ( mGizmoSelection == Gizmo::Axis_Y )
{
Point3F newPos = mStartDragNodePos;
newPos.y += deltaWorld.x;
mSelRiver->setNodePosition( mSelNode, newPos );
}
else if ( mGizmoSelection == Gizmo::Axis_Z )
{
Point3F newPos = mStartDragNodePos;
newPos.z += deltaWorld.y;
mSelRiver->setNodePosition( mSelNode, newPos );
}
*/
/*
F32 height = mStartHeight + deltaWorldX;
Con::printf( "height = %g", height );
mSelRiver->setNodeHeight( mSelNode, height );
Con::executef( this, "onNodeHeightModified", Con::getFloatArg(height) );
if ( event.modifier & SI_PRIMARY_CTRL )
{
//Point3F tPos;
//if ( !getStaticPos( event, tPos ) )
// return;
if ( mStartHeight == -1.0f )
{
mStartHeight = mSelRiver->mNodes[mSelNode].point.z;
mStartX = event.mousePoint.x;
mStartWorld = mSelRiver->mNodes[mSelNode].point;
}
S32 deltaScreenX = event.mousePoint.x - mStartX;
F32 worldDist = ( event.pos - mStartWorld ).len();
F32 deltaWorldX = GFX->unprojectRadius( worldDist, deltaScreenX );
F32 height = mStartHeight + deltaWorldX;
Con::printf( "height = %g", height );
mSelRiver->setNodeHeight( mSelNode, height );
Con::executef( this, "onNodeHeightModified", Con::getFloatArg(height) );
}
else if ( event.modifier & SI_SHIFT )
{
Point3F tPos;
if ( !getStaticPos( event, tPos ) )
return;
if ( mStartWidth == -1.0f )
{
mStartWidth = mSelRiver->mNodes[mSelNode].width;
mStartX = event.mousePoint.x;
mStartWorld = tPos;
}
S32 deltaScreenX = event.mousePoint.x - mStartX;
F32 worldDist = ( event.pos - mStartWorld ).len();
F32 deltaWorldX = GFX->unprojectRadius( worldDist, deltaScreenX );
F32 width = mStartWidth + deltaWorldX;
mSelRiver->setNodeWidth( mSelNode, width );
Con::executef( this, "onNodeWidthModified", Con::getFloatArg(width) );
}
else
{
Point3F tPos;
if ( !getStaticPos( event, tPos ) )
return;
else if ( mGizmoSelection == Gizmo::Axis_Y )
{
Point3F newPos = mStartDragNodePos;
newPos.y += deltaWorld.x;
mSelRiver->setNodePosition( mSelNode, newPos );
}
mSelRiver->setNodePosition( mSelNode, tPos );
}
*/
}
void GuiRiverEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event)
{
// nothing to do
}
void GuiRiverEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event)
{
// nothing to do
}
bool GuiRiverEditorCtrl::onKeyDown(const GuiEvent& event)
{
if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode )
{
// Delete the node attached to the cursor.
deleteSelectedNode();
mMode = mAddRiverMode;
return true;
}
return false;
}
void GuiRiverEditorCtrl::updateGuiInfo()
{
// nothing to do
}
void GuiRiverEditorCtrl::onRender( Point2I offset, const RectI &updateRect )
{
PROFILE_SCOPE( GuiRiverEditorCtrl_OnRender );
Parent::onRender( offset, updateRect );
return;
}
void GuiRiverEditorCtrl::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);
if ( mHoverRiver && mHoverRiver != mSelRiver )
{
_drawRiverSpline( mHoverRiver, mHoverSplineColor );
}
if ( mSelRiver )
{
_drawRiverSpline( mSelRiver, 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 RiverNode &node = mSelRiver->mNodes[mSelNode];
MatrixF objMat = mSelRiver->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 );
}
}
// Now draw all the 2d stuff!
GFX->setClipRect(updateRect);
// Draw Control nodes for selecting and highlighted rivers
if ( mHoverRiver )
_drawRiverControlNodes( mHoverRiver, mHoverSplineColor );
if ( mSelRiver )
_drawRiverControlNodes( mSelRiver, mSelectedSplineColor );
}
void GuiRiverEditorCtrl::_drawRiverSpline( River *river, const ColorI &color )
{
if ( river->mSlices.size() <= 1 )
return;
if ( River::smShowSpline )
{
// Render the River center-line
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 ( River::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();
}
// Segment
}
void GuiRiverEditorCtrl::_drawRiverControlNodes( River *river, const ColorI &color )
{
if ( !River::smShowSpline )
return;
RectI bounds = getBounds();
GFXDrawUtil *drawer = GFX->getDrawUtil();
bool isSelected = ( river == mSelRiver );
bool isHighlighted = ( river == mHoverRiver );
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);
}
}
drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor );
}
}
bool GuiRiverEditorCtrl::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 GuiRiverEditorCtrl::deleteSelectedNode()
{
if ( !mSelRiver || mSelNode == -1 )
return;
// If the River has only two nodes remaining,
// delete the whole River.
if ( mSelRiver->mNodes.size() <= 2 )
{
deleteSelectedRiver( mMode != mAddNodeMode );
}
else
{
if ( mMode != mAddNodeMode )
submitUndo( "Delete Node" );
// Delete the SelectedNode of the SelectedRiver
mSelRiver->deleteNode(mSelNode);
mIsDirty = true;
// We deleted the Node but not the River (it has nodes left)
// so decrement the currently selected node.
if ( mSelRiver->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 GuiRiverEditorCtrl::deleteSelectedRiver( bool undoAble )
{
AssertFatal( mSelRiver != NULL, "GuiRiverEditorCtrl::deleteSelectedRiver() - No River IS selected" );
// Not undoAble? Just delete it.
if ( !undoAble )
{
mSelRiver->deleteObject();
mIsDirty = true;
Con::executef( this, "onRiverSelected" );
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 River.
Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown() - EUndoManager not found!" );
return;
}
else
{
// Create the UndoAction.
MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted River");
action->deleteObject( mSelRiver );
mIsDirty = true;
// Submit it.
undoMan->addAction( action );
}
// ScriptCallback with 'NULL' parameter for no River currently selected.
Con::executef( this, "onRiverSelected" );
// Clear the SelectedNode (it has been deleted along with the River).
setSelectedNode( -1 );
mSelNode = -1;
// SelectedRiver is a SimObjectPtr and will be NULL automatically.
}
void GuiRiverEditorCtrl::setMode( String mode, bool sourceShortcut = false )
{
mMode = mode;
if( sourceShortcut )
Con::executef( this, "paletteSync", mode );
}
void GuiRiverEditorCtrl::setSelectedRiver( River *river )
{
mSelRiver = river;
if ( mSelRiver != NULL )
Con::executef( this, "onRiverSelected", river->getIdString() );
else
Con::executef( this, "onRiverSelected" );
if ( mSelRiver != river )
setSelectedNode(-1);
}
void GuiRiverEditorCtrl::setNodeWidth( F32 width )
{
if ( mSelRiver && mSelNode != -1 )
{
mSelRiver->setNodeWidth( mSelNode, width );
mIsDirty = true;
}
}
F32 GuiRiverEditorCtrl::getNodeWidth()
{
if ( mSelRiver && mSelNode != -1 )
return mSelRiver->getNodeWidth( mSelNode );
return 0.0f;
}
void GuiRiverEditorCtrl::setNodeDepth(F32 depth)
{
if ( mSelRiver && mSelNode != -1 )
{
mSelRiver->setNodeDepth( mSelNode, depth );
mIsDirty = true;
}
}
F32 GuiRiverEditorCtrl::getNodeDepth()
{
if ( mSelRiver && mSelNode != -1 )
return mSelRiver->getNodeDepth( mSelNode );
return 0.0f;
}
void GuiRiverEditorCtrl::setNodePosition( Point3F pos )
{
if ( mSelRiver && mSelNode != -1 )
{
mSelRiver->setNodePosition( mSelNode, pos );
mIsDirty = true;
}
}
Point3F GuiRiverEditorCtrl::getNodePosition()
{
if ( mSelRiver && mSelNode != -1 )
return mSelRiver->getNodePosition( mSelNode );
return Point3F( 0, 0, 0 );
}
void GuiRiverEditorCtrl::setNodeNormal( const VectorF &normal )
{
if ( mSelRiver && mSelNode != -1 )
{
mSelRiver->setNodeNormal( mSelNode, normal );
mIsDirty = true;
}
}
VectorF GuiRiverEditorCtrl::getNodeNormal()
{
if ( mSelRiver && mSelNode != -1 )
return mSelRiver->getNodeNormal( mSelNode );
return VectorF::Zero;
}
void GuiRiverEditorCtrl::setSelectedNode( S32 node )
{
//if ( mSelNode == node )
// return;
mSelNode = node;
if ( mSelNode != -1 )
{
const RiverNode &node = mSelRiver->mNodes[mSelNode];
MatrixF objMat = mSelRiver->getNodeTransform(mSelNode);
Point3F objScale( node.width, 1.0f, node.depth );
Point3F worldPos = node.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 GuiRiverEditorCtrl::submitUndo( const UTF8 *name )
{
// Grab the mission editor undo manager.
UndoManager *undoMan = NULL;
if ( !Sim::findObject( "EUndoManager", undoMan ) )
{
Con::errorf( "GuiRiverEditorCtrl::submitUndo() - EUndoManager not found!" );
return;
}
// Setup the action.
GuiRiverEditorUndoAction *action = new GuiRiverEditorUndoAction( name );
action->mObjId = mSelRiver->getId();
action->mMetersPerSegment = mSelRiver->mMetersPerSegment;
action->mSegmentsPerBatch = mSelRiver->mSegmentsPerBatch;
action->mRiverEditor = this;
for( U32 i = 0; i < mSelRiver->mNodes.size(); i++ )
{
action->mNodes.push_back( mSelRiver->mNodes[i] );
}
undoMan->addAction( action );
}
void GuiRiverEditorCtrl::_prepRenderImage( SceneManager* sceneGraph, const SceneRenderState* state )
{
if ( isAwake() && River::smEditorOpen && mSelRiver )
{
ObjectRenderInst *ri = state->getRenderPass()->allocInst<ObjectRenderInst>();
ri->type = RenderPassManager::RIT_Editor;
ri->renderDelegate.bind( this, &GuiRiverEditorCtrl::_renderSelectedRiver );
ri->defaultKey = 100;
state->getRenderPass()->addInst( ri );
}
}
void GuiRiverEditorCtrl::_renderSelectedRiver( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst )
{
if ( !mSelRiver || !River::smEditorOpen)
return;
GFXTransformSaver saver;
GFX->setStateBlock( mZEnableSB );
if ( River::smShowWalls && mSelRiver->mSlices.size() > 1 )
{
Point3F offset(0,0,1);
// Render the River volume
PrimBuild::begin( GFXTriangleList, 18 * mSelRiver->mSlices.size() - 1 );
for ( U32 i = 0; i < mSelRiver->mSlices.size() - 1; i++ )
{
const RiverSlice &slice = mSelRiver->mSlices[i];
const RiverSlice &nextSlice = mSelRiver->mSlices[i+1];
// Top face
//drawer->drawQuad( slice.p0, nextSlice.p0, nextSlice.p2, slice.p2, colorRed, true );
//PrimBuild::color3i( 0, 0, 255 );
//PrimBuild::vertex3fv( slice.p0 );
//PrimBuild::vertex3fv( nextSlice.p0 );
//PrimBuild::vertex3fv( nextSlice.p2 );
//PrimBuild::vertex3fv( slice.p0 );
//PrimBuild::vertex3fv( nextSlice.p2 );
//PrimBuild::vertex3fv( slice.p2 );
// Bottom face
PrimBuild::color3i( 0, 255, 0 );
PrimBuild::vertex3fv( slice.pb0 );
PrimBuild::vertex3fv( nextSlice.pb0 );
PrimBuild::vertex3fv( nextSlice.pb2 );
PrimBuild::vertex3fv( slice.pb0 );
PrimBuild::vertex3fv( nextSlice.pb2 );
PrimBuild::vertex3fv( slice.pb2 );
// Left face
PrimBuild::color3i( 255, 0, 0 );
PrimBuild::vertex3fv( slice.pb0 );
PrimBuild::vertex3fv( nextSlice.pb0 );
PrimBuild::vertex3fv( nextSlice.p0 );
PrimBuild::vertex3fv( slice.pb0 );
PrimBuild::vertex3fv( nextSlice.p0 );
PrimBuild::vertex3fv( slice.p0 );
// Right face
PrimBuild::color3i( 255, 0, 0 );
PrimBuild::vertex3fv( slice.p2 );
PrimBuild::vertex3fv( nextSlice.p2 );
PrimBuild::vertex3fv( nextSlice.pb2 );
PrimBuild::vertex3fv( slice.p2 );
PrimBuild::vertex3fv( nextSlice.pb2 );
PrimBuild::vertex3fv( slice.pb2 );
}
PrimBuild::end();
}
}
ConsoleMethod( GuiRiverEditorCtrl, deleteNode, void, 2, 2, "deleteNode()" )
{
object->deleteSelectedNode();
}
ConsoleMethod( GuiRiverEditorCtrl, getMode, const char*, 2, 2, "" )
{
return object->getMode();
}
ConsoleMethod( GuiRiverEditorCtrl, setMode, void, 3, 3, "setMode( String mode )" )
{
String newMode = ( argv[2] );
object->setMode( newMode );
}
ConsoleMethod( GuiRiverEditorCtrl, getNodeWidth, F32, 2, 2, "" )
{
return object->getNodeWidth();
}
ConsoleMethod( GuiRiverEditorCtrl, setNodeWidth, void, 3, 3, "" )
{
object->setNodeWidth( dAtof(argv[2]) );
}
ConsoleMethod( GuiRiverEditorCtrl, getNodeDepth, F32, 2, 2, "" )
{
return object->getNodeDepth();
}
ConsoleMethod( GuiRiverEditorCtrl, setNodeDepth, void, 3, 3, "" )
{
object->setNodeDepth( dAtof(argv[2]) );
}
ConsoleMethod( GuiRiverEditorCtrl, getNodePosition, const char*, 2, 2, "" )
{
static const U32 bufSize = 256;
char* returnBuffer = Con::getReturnBuffer(bufSize);
2012-09-19 15:15:01 +00:00
dSprintf(returnBuffer, bufSize, "%f %f %f",
2012-09-19 15:15:01 +00:00
object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z);
return returnBuffer;
}
ConsoleMethod( GuiRiverEditorCtrl, setNodePosition, void, 3, 3, "" )
{
Point3F pos;
S32 count = dSscanf( argv[2], "%f %f %f",
&pos.x, &pos.y, &pos.z);
if ( (count != 3) )
{
Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]);
return;
}
object->setNodePosition( pos );
}
ConsoleMethod( GuiRiverEditorCtrl, getNodeNormal, const char*, 2, 2, "" )
{
static const U32 bufSize = 256;
char* returnBuffer = Con::getReturnBuffer(bufSize);
2012-09-19 15:15:01 +00:00
dSprintf(returnBuffer, bufSize, "%f %f %f",
2012-09-19 15:15:01 +00:00
object->getNodeNormal().x, object->getNodeNormal().y, object->getNodeNormal().z);
return returnBuffer;
}
ConsoleMethod( GuiRiverEditorCtrl, setNodeNormal, void, 3, 3, "" )
{
VectorF normal;
S32 count = dSscanf( argv[2], "%f %f %f",
&normal.x, &normal.y, &normal.z);
if ( (count != 3) )
{
Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]);
return;
}
object->setNodeNormal( normal );
}
ConsoleMethod( GuiRiverEditorCtrl, setSelectedRiver, void, 2, 3, "" )
{
if ( argc == 2 )
object->setSelectedRiver(NULL);
else
{
River *river = NULL;
if ( Sim::findObject( argv[2], river ) )
object->setSelectedRiver(river);
}
}
ConsoleMethod( GuiRiverEditorCtrl, getSelectedRiver, const char*, 2, 2, "" )
{
River *river = object->getSelectedRiver();
if ( !river )
return NULL;
return river->getIdString();
}
ConsoleMethod( GuiRiverEditorCtrl, regenerate, void, 2, 2, "" )
{
River *river = object->getSelectedRiver();
if ( river )
river->regenerate();
}