mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-07-12 07:04:36 +00:00
Engine directory for ticket #1
This commit is contained in:
parent
352279af7a
commit
7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions
200
Engine/source/forest/editor/forestBrushElement.cpp
Normal file
200
Engine/source/forest/editor/forestBrushElement.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "forest/editor/forestBrushElement.h"
|
||||
|
||||
#include "console/consoleTypes.h"
|
||||
#include "forest/forestItem.h"
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ForestBrushElement
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
IMPLEMENT_CONOBJECT( ForestBrushElement );
|
||||
|
||||
ConsoleDocClass( ForestBrushElement,
|
||||
"@brief Represents a type of ForestItem and parameters for how it is placed"
|
||||
" when painting with a ForestBrush that contains it.\n\n"
|
||||
"@ingroup Forest"
|
||||
);
|
||||
|
||||
ForestBrushElement::ForestBrushElement()
|
||||
: mData( NULL ),
|
||||
mProbability( 1 ),
|
||||
mRotationRange( 360 ),
|
||||
mScaleMin( 1 ),
|
||||
mScaleMax( 1 ),
|
||||
mScaleExponent( 1 ),
|
||||
mSinkMin( 0.0f ),
|
||||
mSinkMax( 0.0f ),
|
||||
mSinkRadius( 1 ),
|
||||
mSlopeMax( 90.0f ),
|
||||
mSlopeMin( 0.0f ),
|
||||
mElevationMin( -10000.0f ),
|
||||
mElevationMax( 10000.0f )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ForestBrushElement::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup( "ForestBrushElement" );
|
||||
|
||||
addField( "forestItemData", TYPEID< ForestItemData >(), Offset( mData, ForestBrushElement ),
|
||||
"The type of ForestItem this element holds placement parameters for." );
|
||||
|
||||
addField( "probability", TypeF32, Offset( mProbability, ForestBrushElement ),
|
||||
"The probability that this element will be created during an editor brush stroke "
|
||||
"is the sum of all element probabilities in the brush divided by the probability "
|
||||
"of this element." );
|
||||
|
||||
addField( "rotationRange", TypeF32, Offset( mRotationRange, ForestBrushElement ),
|
||||
"The max rotation in degrees that items will be placed." );
|
||||
|
||||
addField( "scaleMin", TypeF32, Offset( mScaleMin, ForestBrushElement ),
|
||||
"The minimum random size for each item." );
|
||||
|
||||
addField( "scaleMax", TypeF32, Offset( mScaleMax, ForestBrushElement ),
|
||||
"The maximum random size of each item." );
|
||||
|
||||
addField( "scaleExponent", TypeF32, Offset( mScaleExponent, ForestBrushElement ),
|
||||
"An exponent used to bias between the minimum and maximum random sizes." );
|
||||
|
||||
addField( "sinkMin", TypeF32, Offset( mSinkMin, ForestBrushElement ),
|
||||
"Min variation in the sink radius." );
|
||||
|
||||
addField( "sinkMax", TypeF32, Offset( mSinkMax, ForestBrushElement ),
|
||||
"Max variation in the sink radius." );
|
||||
|
||||
addField( "sinkRadius", TypeF32, Offset( mSinkRadius, ForestBrushElement ),
|
||||
"This is the radius used to calculate how much to sink the trunk at "
|
||||
"its base and is used to sink the tree into the ground when its on a slope." );
|
||||
|
||||
addField( "slopeMin", TypeF32, Offset( mSlopeMin, ForestBrushElement ),
|
||||
"The min surface slope in degrees this item will be placed on." );
|
||||
|
||||
addField( "slopeMax", TypeF32, Offset( mSlopeMax, ForestBrushElement ),
|
||||
"The max surface slope in degrees this item will be placed on." );
|
||||
|
||||
addField( "elevationMin", TypeF32, Offset( mElevationMin, ForestBrushElement ),
|
||||
"The min world space elevation this item will be placed." );
|
||||
|
||||
addField( "elevationMax", TypeF32, Offset( mElevationMax, ForestBrushElement ),
|
||||
"The max world space elevation this item will be placed." );
|
||||
|
||||
endGroup( "ForestBrushElement" );
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ForestBrushElementSet
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
SimObjectPtr<SimGroup> ForestBrush::smGroup = NULL;
|
||||
|
||||
IMPLEMENT_CONOBJECT( ForestBrush );
|
||||
|
||||
ConsoleDocClass( ForestBrush,
|
||||
"@brief Container class for ForestBrushElements\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
ForestBrush::ForestBrush()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ForestBrush::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
getGroup()->addObject( this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForestBrush::addObject( SimObject *inObj )
|
||||
{
|
||||
ForestBrushElement *ele = dynamic_cast<ForestBrushElement*>( inObj );
|
||||
if ( !ele )
|
||||
return;
|
||||
|
||||
//if ( containsItemData( ele->mData ) )
|
||||
// return;
|
||||
|
||||
Parent::addObject( inObj );
|
||||
}
|
||||
|
||||
SimGroup* ForestBrush::getGroup()
|
||||
{
|
||||
if ( !smGroup )
|
||||
{
|
||||
SimGroup *dummy;
|
||||
if ( Sim::findObject( "ForestBrushGroup", dummy ) )
|
||||
{
|
||||
smGroup = dummy;
|
||||
return smGroup;
|
||||
}
|
||||
|
||||
smGroup = new SimGroup;
|
||||
smGroup->assignName( "ForestBrushGroup" );
|
||||
smGroup->registerObject();
|
||||
Sim::getRootGroup()->addObject( smGroup );
|
||||
}
|
||||
|
||||
return smGroup;
|
||||
}
|
||||
|
||||
bool ForestBrush::containsItemData( const ForestItemData *inData )
|
||||
{
|
||||
SimObjectList::iterator iter = objectList.begin();
|
||||
for ( ; iter != objectList.end(); iter++ )
|
||||
{
|
||||
ForestBrushElement *pElement = dynamic_cast<ForestBrushElement*>(*iter);
|
||||
|
||||
if ( !pElement )
|
||||
continue;
|
||||
|
||||
if ( pElement->mData == inData )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestBrush, containsItemData, bool, 3, 3, "( ForestItemData obj )" )
|
||||
{
|
||||
ForestItemData *data = NULL;
|
||||
if ( !Sim::findObject( argv[2], data ) )
|
||||
{
|
||||
Con::warnf( "ForestBrush::containsItemData - invalid object passed" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return object->containsItemData( data );
|
||||
}
|
||||
125
Engine/source/forest/editor/forestBrushElement.h
Normal file
125
Engine/source/forest/editor/forestBrushElement.h
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOREST_EDITOR_BRUSHELEMENT_H_
|
||||
#define _FOREST_EDITOR_BRUSHELEMENT_H_
|
||||
|
||||
//#ifndef _SIMOBJECT_H_
|
||||
//#include "console/simObject.h"
|
||||
//#endif
|
||||
#ifndef _SIMSET_H_
|
||||
#include "console/simSet.h"
|
||||
#endif
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ForestBrushElement
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class ForestItemData;
|
||||
|
||||
class ForestBrushElement : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
|
||||
public:
|
||||
|
||||
ForestBrushElement();
|
||||
|
||||
DECLARE_CONOBJECT( ForestBrushElement );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
public:
|
||||
|
||||
/// The type of ForestItem this element holds placement parameters for.
|
||||
ForestItemData *mData;
|
||||
|
||||
/// The probability that this element will be
|
||||
/// created during an editor brush stroke.
|
||||
F32 mProbability;
|
||||
|
||||
/// The max rotation in degrees that items will be placed.
|
||||
F32 mRotationRange;
|
||||
|
||||
/// The minimum random size for each item.
|
||||
F32 mScaleMin;
|
||||
|
||||
/// The maximum random size of each item.
|
||||
F32 mScaleMax;
|
||||
|
||||
/// An exponent used to bias between the minimum
|
||||
/// and maximum random sizes.
|
||||
F32 mScaleExponent;
|
||||
|
||||
/// Min variation in the sink radius.
|
||||
F32 mSinkMin;
|
||||
|
||||
/// Max variation in the sink radius.
|
||||
F32 mSinkMax;
|
||||
|
||||
/// This is the radius used to calculate how much to
|
||||
/// sink the trunk at its base and is used to sink
|
||||
/// the tree into the ground when its on a slope.
|
||||
F32 mSinkRadius;
|
||||
|
||||
/// The min surface slope in degrees this item will be placed on.
|
||||
F32 mSlopeMin;
|
||||
|
||||
/// The max surface slope in degrees this item will be placed on.
|
||||
F32 mSlopeMax;
|
||||
|
||||
/// The min world space elevation this item will be placed.
|
||||
F32 mElevationMin;
|
||||
|
||||
/// The max world space elevation this item will be placed.
|
||||
F32 mElevationMax;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// ForestBrush
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
class ForestBrush : public SimGroup
|
||||
{
|
||||
typedef SimGroup Parent;
|
||||
|
||||
public:
|
||||
|
||||
ForestBrush();
|
||||
|
||||
DECLARE_CONOBJECT( ForestBrush );
|
||||
|
||||
virtual bool onAdd();
|
||||
|
||||
virtual void addObject(SimObject*);
|
||||
|
||||
static SimGroup* getGroup();
|
||||
|
||||
bool containsItemData( const ForestItemData *inData );
|
||||
protected:
|
||||
|
||||
static SimObjectPtr<SimGroup> smGroup;
|
||||
};
|
||||
|
||||
|
||||
#endif // _FOREST_EDITOR_BRUSHELEMENT_H_
|
||||
687
Engine/source/forest/editor/forestBrushTool.cpp
Normal file
687
Engine/source/forest/editor/forestBrushTool.cpp
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "forest/editor/forestBrushTool.h"
|
||||
|
||||
#include "forest/forest.h"
|
||||
#include "forest/editor/forestUndo.h"
|
||||
#include "forest/editor/forestBrushElement.h"
|
||||
#include "forest/editor/forestEditorCtrl.h"
|
||||
|
||||
#include "gui/worldEditor/editTSCtrl.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "gfx/primBuilder.h"
|
||||
#include "gui/controls/guiTreeViewCtrl.h"
|
||||
#include "core/strings/stringUnit.h"
|
||||
#include "math/mRandomDeck.h"
|
||||
#include "math/mRandomSet.h"
|
||||
|
||||
|
||||
bool ForestBrushTool::protectedSetSize( void *object, const char *index, const char *data )
|
||||
{
|
||||
ForestBrushTool *tool = static_cast<ForestBrushTool*>( object );
|
||||
F32 val = dAtof(data);
|
||||
tool->setSize( val );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ForestBrushTool::protectedSetPressure( void *object, const char *index, const char *data )
|
||||
{
|
||||
ForestBrushTool *tool = static_cast<ForestBrushTool*>( object );
|
||||
F32 val = dAtof(data);
|
||||
tool->setPressure( val );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ForestBrushTool::protectedSetHardness( void *object, const char *index, const char *data )
|
||||
{
|
||||
ForestBrushTool *tool = static_cast<ForestBrushTool*>( object );
|
||||
F32 val = dAtof(data);
|
||||
tool->setHardness( val );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ImplementEnumType( ForestBrushMode,
|
||||
"Active brush mode type.\n"
|
||||
"@internal\n\n")
|
||||
{ ForestBrushTool::Paint, "Paint", "Creates Items based on the Elements you have selected.\n" },
|
||||
{ ForestBrushTool::Erase, "Erase", "Erases Items of any Mesh type.\n" },
|
||||
{ ForestBrushTool::EraseSelected, "EraseSelected", "Erases items of a specific type.\n" },
|
||||
EndImplementEnumType;
|
||||
|
||||
|
||||
ForestBrushTool::ForestBrushTool()
|
||||
: mSize( 5.0f ),
|
||||
mPressure( 0.1f ),
|
||||
mHardness( 1.0f ),
|
||||
mDrawBrush( false ),
|
||||
mCurrAction( NULL ),
|
||||
mStrokeEvent( 0 ),
|
||||
mBrushDown( false ),
|
||||
mColor( ColorI::WHITE ),
|
||||
mMode( Paint ),
|
||||
mRandom( Platform::getRealMilliseconds() + 1 )
|
||||
{
|
||||
}
|
||||
|
||||
ForestBrushTool::~ForestBrushTool()
|
||||
{
|
||||
}
|
||||
|
||||
IMPLEMENT_CONOBJECT( ForestBrushTool );
|
||||
|
||||
ConsoleDocClass( ForestBrushTool,
|
||||
"@brief Defines the brush properties when painting trees in Forest Editor\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
void ForestBrushTool::initPersistFields()
|
||||
{
|
||||
addGroup( "ForestBrushTool" );
|
||||
|
||||
addField( "mode", TYPEID< BrushMode >(), Offset( mMode, ForestBrushTool) );
|
||||
|
||||
addProtectedField( "size", TypeF32, Offset( mSize, ForestBrushTool ),
|
||||
&protectedSetSize, &defaultProtectedGetFn, "Brush Size" );
|
||||
|
||||
addProtectedField( "pressure", TypeF32, Offset( mPressure, ForestBrushTool ),
|
||||
&protectedSetPressure, &defaultProtectedGetFn, "Brush Pressure" );
|
||||
|
||||
addProtectedField( "hardness", TypeF32, Offset( mHardness, ForestBrushTool ),
|
||||
&protectedSetHardness, &defaultProtectedGetFn, "Brush Hardness" );
|
||||
|
||||
endGroup( "ForestBrushTool" );
|
||||
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool ForestBrushTool::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForestBrushTool::onRemove()
|
||||
{
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
void ForestBrushTool::on3DMouseDown( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
Con::executef( this, "onMouseDown" );
|
||||
|
||||
if ( !_updateBrushPoint( evt ) || !mForest )
|
||||
return;
|
||||
|
||||
mBrushDown = true;
|
||||
|
||||
mEditor->getRoot()->showCursor( false );
|
||||
|
||||
_collectElements();
|
||||
_onStroke();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ForestBrushTool::on3DMouseUp( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
_updateBrushPoint( evt );
|
||||
Sim::cancelEvent( mStrokeEvent );
|
||||
mBrushDown = false;
|
||||
|
||||
mEditor->getRoot()->showCursor( true );
|
||||
|
||||
if ( mCurrAction )
|
||||
{
|
||||
_submitUndo( mCurrAction );
|
||||
mCurrAction = NULL;
|
||||
}
|
||||
|
||||
//mElements.clear();
|
||||
}
|
||||
|
||||
void ForestBrushTool::on3DMouseMove( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
_updateBrushPoint( evt );
|
||||
}
|
||||
|
||||
void ForestBrushTool::on3DMouseDragged( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
_updateBrushPoint( evt );
|
||||
|
||||
if ( mBrushDown && !Sim::isEventPending( mStrokeEvent ) )
|
||||
mStrokeEvent = Sim::postEvent( this, new ForestBrushToolEvent(), Sim::getCurrentTime() + 250 );
|
||||
}
|
||||
|
||||
bool ForestBrushTool::onMouseWheel( const GuiEvent &evt )
|
||||
{
|
||||
if ( evt.modifier & SI_PRIMARY_CTRL )
|
||||
setPressure( mPressure + evt.fval * ( 0.05f / 120.0f ) );
|
||||
else if ( evt.modifier & SI_SHIFT )
|
||||
setHardness( mHardness + evt.fval * ( 0.05f / 120.0f ) );
|
||||
else
|
||||
setSize( mSize + evt.fval * ( 1.0f / 120.0f ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForestBrushTool::onRender3D( )
|
||||
{
|
||||
}
|
||||
|
||||
void ForestBrushTool::onRender2D()
|
||||
{
|
||||
if ( !mDrawBrush )
|
||||
return;
|
||||
|
||||
if ( !mEditor->getRoot()->isCursorON() && !mBrushDown )
|
||||
return;
|
||||
|
||||
RayInfo ri;
|
||||
Point3F start( 0, 0, mLastBrushPoint.z + mSize );
|
||||
Point3F end( 0, 0, mLastBrushPoint.z - mSize );
|
||||
|
||||
Vector<Point3F> pointList;
|
||||
|
||||
const U32 steps = 32;
|
||||
|
||||
if ( mForest )
|
||||
mForest->disableCollision();
|
||||
|
||||
for ( S32 i = 0; i < steps; i++ )
|
||||
{
|
||||
F32 radians = (F32)i / (F32)(steps-1) * M_2PI_F;
|
||||
VectorF vec(0,1,0);
|
||||
MathUtils::vectorRotateZAxis( vec, radians );
|
||||
|
||||
end.x = start.x = mLastBrushPoint.x + vec.x * mSize;
|
||||
end.y = start.y = mLastBrushPoint.y + vec.y * mSize;
|
||||
|
||||
bool hit = gServerContainer.castRay( start, end, TerrainObjectType | StaticShapeObjectType, &ri );
|
||||
|
||||
if ( hit )
|
||||
pointList.push_back( ri.point );
|
||||
}
|
||||
|
||||
if ( mForest )
|
||||
mForest->enableCollision();
|
||||
|
||||
if ( pointList.empty() )
|
||||
return;
|
||||
|
||||
ColorI brushColor( ColorI::WHITE );
|
||||
|
||||
if ( mMode == Paint )
|
||||
brushColor = ColorI::BLUE;
|
||||
else if ( mMode == Erase )
|
||||
brushColor = ColorI::RED;
|
||||
else if ( mMode == EraseSelected )
|
||||
brushColor.set( 150, 0, 0 );
|
||||
|
||||
if ( mMode == Paint || mMode == EraseSelected )
|
||||
{
|
||||
if ( mElements.empty() )
|
||||
{
|
||||
brushColor.set( 140, 140, 140 );
|
||||
}
|
||||
}
|
||||
|
||||
mEditor->drawLineList( pointList, brushColor, 1.5f );
|
||||
}
|
||||
|
||||
void ForestBrushTool::onActivated( const Gui3DMouseEvent &lastEvent )
|
||||
{
|
||||
_updateBrushPoint( lastEvent );
|
||||
|
||||
Con::executef( this, "onActivated" );
|
||||
}
|
||||
|
||||
void ForestBrushTool::onDeactivated()
|
||||
{
|
||||
Con::executef( this, "onDeactivated" );
|
||||
}
|
||||
|
||||
bool ForestBrushTool::updateGuiInfo()
|
||||
{
|
||||
GuiTextCtrl *statusbar;
|
||||
Sim::findObject( "EWorldEditorStatusBarInfo", statusbar );
|
||||
|
||||
GuiTextCtrl *selectionBar;
|
||||
Sim::findObject( "EWorldEditorStatusBarSelection", selectionBar );
|
||||
|
||||
String text;
|
||||
|
||||
if ( mMode == Paint )
|
||||
text = "Forest Editor ( Paint Tool ) - This brush creates Items based on the Elements you have selected.";
|
||||
else if ( mMode == Erase )
|
||||
text = "Forest Editor ( Erase Tool ) - This brush erases Items of any Mesh type.";
|
||||
else if ( mMode == EraseSelected )
|
||||
text = "Forest Editor ( Erase Selected ) - This brush erases Items based on the Elements you have selected.";
|
||||
|
||||
if ( statusbar )
|
||||
statusbar->setText( text );
|
||||
|
||||
if ( mMode == Paint || mMode == EraseSelected )
|
||||
text = String::ToString( "%i elements selected", mElements.size() );
|
||||
else
|
||||
text = "";
|
||||
|
||||
if ( selectionBar )
|
||||
selectionBar->setText( text );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForestBrushTool::setSize( F32 val )
|
||||
{
|
||||
mSize = mClampF( val, 0.0f, 150.0f );
|
||||
Con::executef( this, "syncBrushToolbar" );
|
||||
}
|
||||
|
||||
void ForestBrushTool::setPressure( F32 val )
|
||||
{
|
||||
mPressure = mClampF( val, 0.0f, 1.0f );
|
||||
Con::executef( this, "syncBrushToolbar" );
|
||||
}
|
||||
|
||||
void ForestBrushTool::setHardness( F32 val )
|
||||
{
|
||||
mHardness = mClampF( val, 0.0f, 1.0f );
|
||||
Con::executef( this, "syncBrushToolbar" );
|
||||
}
|
||||
|
||||
void ForestBrushTool::_onStroke()
|
||||
{
|
||||
if ( !mForest || !mBrushDown )
|
||||
return;
|
||||
|
||||
_action( mLastBrushPoint );
|
||||
}
|
||||
|
||||
void ForestBrushTool::_action( const Point3F &point )
|
||||
{
|
||||
if ( mMode == Paint )
|
||||
_paint( point );
|
||||
else if ( mMode == Erase || mMode == EraseSelected )
|
||||
_erase( point );
|
||||
}
|
||||
|
||||
inline F32 mCircleArea( F32 radius )
|
||||
{
|
||||
return radius * radius * M_PI_F;
|
||||
}
|
||||
|
||||
void ForestBrushTool::_paint( const Point3F &point )
|
||||
{
|
||||
AssertFatal( mForest, "ForestBrushTool::_paint() - Can't paint without a Forest!" );
|
||||
|
||||
if ( mElements.empty() )
|
||||
return;
|
||||
|
||||
// Iterators, pointers, and temporaries.
|
||||
ForestBrushElement *pElement;
|
||||
ForestItemData *pData;
|
||||
F32 radius, area;
|
||||
|
||||
// How much area do we have to fill with trees ( within the brush ).
|
||||
F32 fillArea = mCircleArea( mSize );
|
||||
|
||||
// Scale that down by pressure, this is how much area we want to fill.
|
||||
fillArea *= mPressure;
|
||||
|
||||
// Create an MRandomSet we can get items we are painting out of with
|
||||
// the desired distribution.
|
||||
// Also grab the smallest and largest radius elements while we are looping.
|
||||
|
||||
ForestBrushElement *smallestElement, *largestElement;
|
||||
smallestElement = largestElement = mElements[0];
|
||||
|
||||
MRandomSet<ForestBrushElement*> randElementSet(&mRandom);
|
||||
|
||||
for ( S32 i = 0; i < mElements.size(); i++ )
|
||||
{
|
||||
pElement = mElements[i];
|
||||
|
||||
if ( pElement->mData->mRadius > largestElement->mData->mRadius )
|
||||
largestElement = pElement;
|
||||
if ( pElement->mData->mRadius < smallestElement->mData->mRadius )
|
||||
smallestElement = pElement;
|
||||
|
||||
randElementSet.add( pElement, pElement->mProbability );
|
||||
}
|
||||
|
||||
// Pull elements from the random set until we would theoretically fill
|
||||
// the desired area.
|
||||
|
||||
F32 areaLeft = fillArea;
|
||||
F32 scaleFactor, sink, randRot, worldCoordZ, slope;
|
||||
Point2F worldCoords;
|
||||
Point3F normalVector, right;
|
||||
Point2F temp;
|
||||
|
||||
const S32 MaxTries = 5;
|
||||
|
||||
|
||||
while ( areaLeft > 0.0f )
|
||||
{
|
||||
pElement = randElementSet.get();
|
||||
pData = pElement->mData;
|
||||
|
||||
scaleFactor = mLerp( pElement->mScaleMin, pElement->mScaleMax, mClampF( mPow( mRandom.randF(), pElement->mScaleExponent ), 0.0f, 1.0f ) );
|
||||
radius = getMax( pData->mRadius * scaleFactor, 0.1f );
|
||||
area = mCircleArea( radius );
|
||||
|
||||
areaLeft -= area * 5.0f; // fudge value
|
||||
|
||||
// No room left we are done.
|
||||
//if ( areaLeft < 0.0f )
|
||||
// break;
|
||||
|
||||
// We have area left to fill...
|
||||
|
||||
const F32 rotRange = mDegToRad( pElement->mRotationRange );
|
||||
|
||||
// Get a random sink value.
|
||||
sink = mRandom.randF( pElement->mSinkMin, pElement->mSinkMax ) * scaleFactor;
|
||||
|
||||
// Get a random rotation.
|
||||
randRot = mRandom.randF( 0.0f, rotRange );
|
||||
|
||||
// Look for a place within the brush area to place this item.
|
||||
// We may have to try several times or give and go onto the next.
|
||||
|
||||
S32 i = 0;
|
||||
for ( ; i < MaxTries; i++ )
|
||||
{
|
||||
// Pick some randoms for placement.
|
||||
worldCoords = MathUtils::randomPointInCircle( mSize ) + point.asPoint2F();
|
||||
|
||||
// Look for the ground at this position.
|
||||
if ( !getGroundAt( Point3F( worldCoords.x, worldCoords.y , point.z ),
|
||||
&worldCoordZ,
|
||||
&normalVector ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Does this pass our slope and elevation limits.
|
||||
right.set( normalVector.x, normalVector.y, 0 );
|
||||
right.normalizeSafe();
|
||||
slope = mRadToDeg( mDot( right, normalVector ) );
|
||||
|
||||
if ( worldCoordZ < pElement->mElevationMin ||
|
||||
worldCoordZ > pElement->mElevationMax ||
|
||||
slope < pElement->mSlopeMin ||
|
||||
slope > pElement->mSlopeMax )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Are we up against another tree?
|
||||
if ( mForest->getData()->getItems( worldCoords, radius, NULL ) > 0 )
|
||||
continue;
|
||||
|
||||
// If the trunk radius is set then we need to sink
|
||||
// the tree into the ground a bit to hide the bottom.
|
||||
if ( pElement->mSinkRadius > 0.0f )
|
||||
{
|
||||
// Items that are not aligned to the ground surface
|
||||
// get sunken down to hide the bottom of their trunks.
|
||||
|
||||
// sunk down a bit to hide their bottoms on slopes.
|
||||
normalVector.z = 0;
|
||||
normalVector.normalizeSafe();
|
||||
normalVector *= sink;
|
||||
temp = worldCoords + normalVector.asPoint2F();
|
||||
getGroundAt( Point3F( temp.x, temp.y, point.z ),
|
||||
&worldCoordZ,
|
||||
NULL );
|
||||
}
|
||||
|
||||
worldCoordZ -= sink;
|
||||
|
||||
// Create a new undo action if this is a new stroke.
|
||||
ForestCreateUndoAction *action = dynamic_cast<ForestCreateUndoAction*>( mCurrAction );
|
||||
if ( !action )
|
||||
{
|
||||
action = new ForestCreateUndoAction( mForest->getData(), mEditor );
|
||||
mCurrAction = action;
|
||||
}
|
||||
|
||||
//Con::printf( "worldCoords = %g, %g, %g", worldCoords.x, worldCoords.y, worldCoordZ );
|
||||
|
||||
// Let the action manage adding it to the forest.
|
||||
action->addItem( pData,
|
||||
Point3F( worldCoords.x, worldCoords.y, worldCoordZ ),
|
||||
randRot,
|
||||
scaleFactor );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ForestBrushTool::_erase( const Point3F &point )
|
||||
{
|
||||
AssertFatal( mForest, "ForestBrushTool::_erase() - Can't erase without a Forest!" );
|
||||
|
||||
// First grab all the forest items around the point.
|
||||
ForestItemVector trees;
|
||||
if ( mForest->getData()->getItems( point.asPoint2F(), mSize, &trees ) == 0 )
|
||||
return;
|
||||
|
||||
if ( mMode == EraseSelected )
|
||||
{
|
||||
for ( U32 i = 0; i < trees.size(); i++ )
|
||||
{
|
||||
const ForestItem &tree = trees[i];
|
||||
|
||||
if ( !mDatablocks.contains( tree.getData() ) )
|
||||
{
|
||||
trees.erase_fast( i );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( trees.empty() )
|
||||
return;
|
||||
|
||||
// Number of trees to erase depending on pressure.
|
||||
S32 eraseCount = getMax( (S32)mCeil( (F32)trees.size() * mPressure ), 0 );
|
||||
|
||||
// Initialize an MRandomDeck with trees under the brush.
|
||||
MRandomDeck<ForestItem> deck(&mRandom);
|
||||
deck.addToPile( trees );
|
||||
deck.shuffle();
|
||||
|
||||
ForestItem currentTree;
|
||||
|
||||
// Draw eraseCount number of trees from MRandomDeck, adding them to our erase action.
|
||||
for ( U32 i = 0; i < eraseCount; i++ )
|
||||
{
|
||||
deck.draw(¤tTree);
|
||||
|
||||
// Create a new undo action if this is a new stroke.
|
||||
ForestDeleteUndoAction *action = dynamic_cast<ForestDeleteUndoAction*>( mCurrAction );
|
||||
if ( !action )
|
||||
{
|
||||
action = new ForestDeleteUndoAction( mForest->getData(), mEditor );
|
||||
mCurrAction = action;
|
||||
}
|
||||
|
||||
action->removeItem( currentTree );
|
||||
}
|
||||
}
|
||||
|
||||
bool ForestBrushTool::_updateBrushPoint( const Gui3DMouseEvent &event_ )
|
||||
{
|
||||
// Do a raycast for terrain... thats the placement center.
|
||||
const U32 mask = TerrainObjectType | StaticShapeObjectType; // TODO: Make an option!
|
||||
|
||||
Point3F start( event_.pos );
|
||||
Point3F end( event_.pos + ( event_.vec * 10000.0f ) );
|
||||
|
||||
if ( mForest )
|
||||
mForest->disableCollision();
|
||||
|
||||
RayInfo rinfo;
|
||||
mDrawBrush = gServerContainer.castRay( start, end, mask, &rinfo );
|
||||
|
||||
if ( mForest )
|
||||
mForest->enableCollision();
|
||||
|
||||
if ( mDrawBrush )
|
||||
{
|
||||
mLastBrushPoint = rinfo.point;
|
||||
mLastBrushNormal = rinfo.normal;
|
||||
}
|
||||
|
||||
return mDrawBrush;
|
||||
}
|
||||
|
||||
bool findSelectedElements( ForestBrushElement *obj )
|
||||
{
|
||||
if ( obj->isSelectedRecursive() )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ForestBrushTool::_collectElements()
|
||||
{
|
||||
mElements.clear();
|
||||
|
||||
// Get the selected objects from the tree view.
|
||||
// These can be a combination of ForestBrush(s) and ForestBrushElement(s).
|
||||
|
||||
GuiTreeViewCtrl *brushTree;
|
||||
if ( !Sim::findObject( "ForestEditBrushTree", brushTree ) )
|
||||
return;
|
||||
|
||||
const char* objectIdList = Con::executef( brushTree, "getSelectedObjectList" );
|
||||
|
||||
// Collect those objects in a vector and mark them as selected.
|
||||
|
||||
Vector<SimObject*> objectList;
|
||||
SimObject *simobj;
|
||||
|
||||
S32 wordCount = StringUnit::getUnitCount( objectIdList, " " );
|
||||
|
||||
for ( S32 i = 0; i < wordCount; i++ )
|
||||
{
|
||||
const char* word = StringUnit::getUnit( objectIdList, i, " " );
|
||||
|
||||
if ( Sim::findObject( word, simobj ) )
|
||||
{
|
||||
objectList.push_back( simobj );
|
||||
simobj->setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Find all ForestBrushElements that are directly or indirectly selected.
|
||||
|
||||
SimGroup *brushGroup = ForestBrush::getGroup();
|
||||
brushGroup->findObjectByCallback( findSelectedElements, mElements );
|
||||
|
||||
// We just needed to flag these objects as selected for the benefit of our
|
||||
// findSelectedElements callback, we can now mark them un-selected again.
|
||||
|
||||
for ( S32 i = 0; i < objectList.size(); i++ )
|
||||
objectList[i]->setSelected(false);
|
||||
|
||||
// If we are in Paint or EraseSelected mode we filter out elements with
|
||||
// a non-positive probability.
|
||||
|
||||
if ( mMode == Paint || mMode == EraseSelected )
|
||||
{
|
||||
for ( S32 i = 0; i < mElements.size(); i++ )
|
||||
{
|
||||
if ( mElements[i]->mProbability <= 0.0f )
|
||||
{
|
||||
mElements.erase_fast(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out elements with NULL datablocks and collect all unique datablocks
|
||||
// in a vector.
|
||||
|
||||
mDatablocks.clear();
|
||||
for ( S32 i = 0; i < mElements.size(); i++ )
|
||||
{
|
||||
if ( mElements[i]->mData == NULL )
|
||||
{
|
||||
mElements.erase_fast(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
mDatablocks.push_back_unique( mElements[i]->mData );
|
||||
}
|
||||
}
|
||||
|
||||
bool ForestBrushTool::getGroundAt( const Point3F &worldPt, float *zValueOut, VectorF *normalOut )
|
||||
{
|
||||
const U32 mask = TerrainObjectType | StaticShapeObjectType;
|
||||
|
||||
Point3F start( worldPt.x, worldPt.y, worldPt.z + mSize );
|
||||
Point3F end( worldPt.x, worldPt.y, worldPt.z - mSize );
|
||||
|
||||
if ( mForest )
|
||||
mForest->disableCollision();
|
||||
|
||||
// Do a cast ray at this point from the top to
|
||||
// the bottom of our brush radius.
|
||||
|
||||
RayInfo rinfo;
|
||||
bool hit = gServerContainer.castRay( start, end, mask, &rinfo );
|
||||
|
||||
if ( mForest )
|
||||
mForest->enableCollision();
|
||||
|
||||
if ( !hit )
|
||||
return false;
|
||||
|
||||
if (zValueOut)
|
||||
*zValueOut = rinfo.point.z;
|
||||
|
||||
if (normalOut)
|
||||
*normalOut = rinfo.normal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestBrushTool, collectElements, void, 2, 2, "" )
|
||||
{
|
||||
object->collectElements();
|
||||
}
|
||||
152
Engine/source/forest/editor/forestBrushTool.h
Normal file
152
Engine/source/forest/editor/forestBrushTool.h
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOREST_EDITOR_BRUSH_H_
|
||||
#define _FOREST_EDITOR_BRUSH_H_
|
||||
|
||||
#ifndef _FOREST_EDITOR_TOOL_H_
|
||||
#include "forest/editor/forestTool.h"
|
||||
#endif
|
||||
#ifndef _FORESTITEM_H_
|
||||
#include "forest/forestItem.h"
|
||||
#endif
|
||||
#ifndef _FOREST_EDITOR_BRUSHELEMENT_H_
|
||||
#include "forest/editor/forestBrushElement.h"
|
||||
#endif
|
||||
#ifndef _COLOR_H_
|
||||
#include "core/color.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
class Forest;
|
||||
class ForestUndoAction;
|
||||
|
||||
class ForestBrushTool : public ForestTool
|
||||
{
|
||||
typedef ForestTool Parent;
|
||||
|
||||
friend class ForestBrushToolEvent;
|
||||
|
||||
public:
|
||||
|
||||
enum BrushMode
|
||||
{
|
||||
Paint = 0,
|
||||
Erase,
|
||||
EraseSelected
|
||||
};
|
||||
|
||||
ForestBrushTool();
|
||||
virtual ~ForestBrushTool();
|
||||
|
||||
// SimObject
|
||||
DECLARE_CONOBJECT( ForestBrushTool );
|
||||
static void initPersistFields();
|
||||
virtual bool onAdd();
|
||||
virtual void onRemove();
|
||||
|
||||
// ForestTool
|
||||
virtual void on3DMouseDown( const Gui3DMouseEvent &evt );
|
||||
virtual void on3DMouseUp( const Gui3DMouseEvent &evt );
|
||||
virtual void on3DMouseMove( const Gui3DMouseEvent &evt );
|
||||
virtual void on3DMouseDragged( const Gui3DMouseEvent &evt );
|
||||
virtual bool onMouseWheel(const GuiEvent &evt );
|
||||
virtual void onRender3D();
|
||||
virtual void onRender2D();
|
||||
virtual void onActivated( const Gui3DMouseEvent &lastEvent );
|
||||
virtual void onDeactivated();
|
||||
virtual bool updateGuiInfo();
|
||||
|
||||
// ForestBrushTool
|
||||
void setSize( F32 val );
|
||||
void setPressure( F32 val );
|
||||
void setHardness( F32 val );
|
||||
void collectElements() { _collectElements(); }
|
||||
bool getGroundAt( const Point3F &worldPt, float *zValueOut, VectorF *normalOut );
|
||||
|
||||
protected:
|
||||
|
||||
void _onStroke();
|
||||
virtual void _action( const Point3F &point );
|
||||
virtual void _paint( const Point3F &point );
|
||||
virtual void _erase( const Point3F &point );
|
||||
|
||||
bool _updateBrushPoint( const Gui3DMouseEvent &event_ );
|
||||
virtual void _collectElements();
|
||||
|
||||
static bool protectedSetSize( void *object, const char *index, const char *data );
|
||||
static bool protectedSetPressure( void *object, const char *index, const char *data );
|
||||
static bool protectedSetHardness( void *object, const char *index, const char *data );
|
||||
|
||||
protected:
|
||||
|
||||
MRandom mRandom;
|
||||
|
||||
/// We treat this as a radius.
|
||||
F32 mSize;
|
||||
F32 mPressure;
|
||||
F32 mHardness;
|
||||
|
||||
U32 mMode;
|
||||
|
||||
ColorI mColor;
|
||||
|
||||
Vector<ForestBrushElement*> mElements;
|
||||
Vector<ForestItemData*> mDatablocks;
|
||||
|
||||
///
|
||||
bool mBrushDown;
|
||||
|
||||
///
|
||||
bool mDrawBrush;
|
||||
|
||||
///
|
||||
U32 mStrokeEvent;
|
||||
|
||||
///
|
||||
Point3F mLastBrushPoint;
|
||||
Point3F mLastBrushNormal;
|
||||
|
||||
/// The creation action we're actively filling.
|
||||
ForestUndoAction *mCurrAction;
|
||||
};
|
||||
|
||||
typedef ForestBrushTool::BrushMode ForestBrushMode;
|
||||
DefineEnumType( ForestBrushMode );
|
||||
|
||||
|
||||
class ForestBrushToolEvent : public SimEvent
|
||||
{
|
||||
public:
|
||||
|
||||
void process( SimObject *object )
|
||||
{
|
||||
((ForestBrushTool*)object)->_onStroke();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // _FOREST_EDITOR_BRUSH_H_
|
||||
|
||||
|
||||
|
||||
402
Engine/source/forest/editor/forestEditorCtrl.cpp
Normal file
402
Engine/source/forest/editor/forestEditorCtrl.cpp
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "forest/editor/forestEditorCtrl.h"
|
||||
|
||||
#include "forest/editor/forestBrushTool.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "gui/core/guiCanvas.h"
|
||||
#include "windowManager/platformCursorController.h"
|
||||
#include "forest/editor/forestUndo.h"
|
||||
#include "gui/worldEditor/undoActions.h"
|
||||
|
||||
|
||||
IMPLEMENT_CONOBJECT( ForestEditorCtrl );
|
||||
|
||||
ConsoleDocClass( ForestEditorCtrl,
|
||||
"@brief The actual Forest Editor control\n\n"
|
||||
"Editor use only, should not be modified.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
ForestEditorCtrl::ForestEditorCtrl()
|
||||
{
|
||||
dMemset( &mLastEvent, 0, sizeof(Gui3DMouseEvent) );
|
||||
}
|
||||
|
||||
ForestEditorCtrl::~ForestEditorCtrl()
|
||||
{
|
||||
}
|
||||
|
||||
bool ForestEditorCtrl::onAdd()
|
||||
{
|
||||
if ( !Parent::onAdd() )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
}
|
||||
|
||||
bool ForestEditorCtrl::onWake()
|
||||
{
|
||||
if ( !Parent::onWake() )
|
||||
return false;
|
||||
|
||||
// Push our default cursor on here once.
|
||||
GuiCanvas *root = getRoot();
|
||||
if ( root )
|
||||
{
|
||||
S32 currCursor = PlatformCursorController::curArrow;
|
||||
|
||||
PlatformWindow *window = root->getPlatformWindow();
|
||||
PlatformCursorController *controller = window->getCursorController();
|
||||
controller->pushCursor( currCursor );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::onSleep()
|
||||
{
|
||||
// Pop our default cursor off.
|
||||
GuiCanvas *root = getRoot();
|
||||
if ( root )
|
||||
{
|
||||
PlatformWindow *window = root->getPlatformWindow();
|
||||
PlatformCursorController *controller = window->getCursorController();
|
||||
controller->popCursor();
|
||||
}
|
||||
|
||||
Parent::onSleep();
|
||||
}
|
||||
|
||||
bool ForestEditorCtrl::updateActiveForest( bool createNew )
|
||||
{
|
||||
mForest = dynamic_cast<Forest*>( Sim::findObject( "theForest" ) );
|
||||
Con::executef( this, "onActiveForestUpdated", mForest ? mForest->getIdString() : "", createNew ? "1" : "0" );
|
||||
|
||||
if ( mTool )
|
||||
mTool->setActiveForest( mForest );
|
||||
|
||||
return mForest;
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::setActiveTool( ForestTool *tool )
|
||||
{
|
||||
if ( mTool )
|
||||
{
|
||||
mTool->onDeactivated();
|
||||
}
|
||||
|
||||
mTool = tool;
|
||||
|
||||
if ( mTool )
|
||||
{
|
||||
mTool->setActiveForest( mForest );
|
||||
mTool->setParentEditor( this );
|
||||
mTool->onActivated( mLastEvent );
|
||||
}
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::onMouseUp( const GuiEvent &event_ )
|
||||
{
|
||||
Parent::onMouseUp( event_ );
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::get3DCursor( GuiCursor *&cursor,
|
||||
bool &visible,
|
||||
const Gui3DMouseEvent &event_ )
|
||||
{
|
||||
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 ForestEditorCtrl::on3DMouseDown( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
if ( !mForest && !updateActiveForest( true ) )
|
||||
return;
|
||||
|
||||
if ( mTool )
|
||||
mTool->on3DMouseDown( evt );
|
||||
|
||||
mouseLock();
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::on3DMouseUp( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
if ( !isMouseLocked() )
|
||||
return;
|
||||
|
||||
if ( mTool )
|
||||
mTool->on3DMouseUp( evt );
|
||||
|
||||
mouseUnlock();
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::on3DMouseMove( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
if ( !mForest )
|
||||
updateActiveForest( false );
|
||||
|
||||
if ( mTool )
|
||||
mTool->on3DMouseMove( evt );
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::on3DMouseDragged( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
if ( mTool )
|
||||
mTool->on3DMouseDragged( evt );
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::on3DMouseEnter( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
if ( mTool )
|
||||
mTool->on3DMouseEnter( evt );
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::on3DMouseLeave( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
if ( mTool )
|
||||
mTool->on3DMouseLeave( evt );
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::on3DRightMouseDown( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::on3DRightMouseUp( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
}
|
||||
|
||||
bool ForestEditorCtrl::onMouseWheelUp(const GuiEvent &event_)
|
||||
{
|
||||
if ( mTool )
|
||||
return mTool->onMouseWheel( event_ );
|
||||
|
||||
return Parent::onMouseWheelUp( event_ );
|
||||
}
|
||||
|
||||
bool ForestEditorCtrl::onMouseWheelDown(const GuiEvent &event_)
|
||||
{
|
||||
if ( mTool )
|
||||
return mTool->onMouseWheel( event_ );
|
||||
|
||||
return Parent::onMouseWheelDown( event_ );
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::updateGuiInfo()
|
||||
{
|
||||
// Note: This is intended to be used for updating
|
||||
// GuiControls with info before they are rendered.
|
||||
|
||||
SimObject *statusbar;
|
||||
Sim::findObject( "EditorGuiStatusBar", statusbar );
|
||||
|
||||
SimObject *selectionBar;
|
||||
Sim::findObject( "EWorldEditorStatusBarSelection", selectionBar );
|
||||
|
||||
String text;
|
||||
|
||||
if ( !mForest )
|
||||
{
|
||||
if ( statusbar )
|
||||
Con::executef( statusbar, "setInfo", "Forest Editor. You have no Forest in your level; click anywhere in the scene to create one." );
|
||||
if ( selectionBar )
|
||||
Con::executef( selectionBar, "setInfo", "" );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mTool )
|
||||
{
|
||||
if ( mTool->updateGuiInfo() )
|
||||
return;
|
||||
}
|
||||
|
||||
// Tool did not handle the update so we will.
|
||||
|
||||
if ( statusbar )
|
||||
Con::executef( statusbar, "setInfo", "Forest Editor." );
|
||||
if ( selectionBar )
|
||||
Con::executef( selectionBar, "setInfo", "" );
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::renderScene( const RectI &updateRect )
|
||||
{
|
||||
if ( mTool )
|
||||
mTool->onRender3D();
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::updateGizmo()
|
||||
{
|
||||
if ( mTool )
|
||||
mTool->updateGizmo();
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::renderGui( Point2I offset, const RectI &updateRect )
|
||||
{
|
||||
if ( mTool )
|
||||
mTool->onRender2D();
|
||||
}
|
||||
|
||||
static ForestItemData* sKey = NULL;
|
||||
|
||||
bool findMeshReferences( SimObject *obj )
|
||||
{
|
||||
ForestBrushElement *element = dynamic_cast<ForestBrushElement*>(obj);
|
||||
|
||||
if ( element && element->mData == sKey )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::onUndoAction()
|
||||
{
|
||||
if ( mTool )
|
||||
mTool->onUndoAction();
|
||||
|
||||
updateCollision();
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::deleteMeshSafe( ForestItemData *mesh )
|
||||
{
|
||||
UndoManager *undoMan = NULL;
|
||||
if ( !Sim::findObject( "EUndoManager", undoMan ) )
|
||||
{
|
||||
Con::errorf( "ForestEditorCtrl::deleteMeshSafe() - EUndoManager not found." );
|
||||
return;
|
||||
}
|
||||
|
||||
// CompoundUndoAction which will delete the ForestItemData, ForestItem(s), and ForestBrushElement(s).
|
||||
CompoundUndoAction *compoundAction = new CompoundUndoAction( "Delete Forest Mesh" );
|
||||
|
||||
// Find ForestItem(s) referencing this datablock and add their deletion
|
||||
// to the undo action.
|
||||
if ( mForest )
|
||||
{
|
||||
Vector<ForestItem> foundItems;
|
||||
mForest->getData()->getItems( mesh, &foundItems );
|
||||
|
||||
ForestDeleteUndoAction *itemAction = new ForestDeleteUndoAction( mForest->getData(), this );
|
||||
itemAction->removeItem( foundItems );
|
||||
compoundAction->addAction( itemAction );
|
||||
}
|
||||
|
||||
// Find ForestBrushElement(s) referencing this datablock.
|
||||
SimGroup *brushGroup = ForestBrush::getGroup();
|
||||
sKey = mesh;
|
||||
Vector<SimObject*> foundElements;
|
||||
brushGroup->findObjectByCallback( &findMeshReferences, foundElements );
|
||||
|
||||
// Add UndoAction to delete the ForestBrushElement(s) and the ForestItemData.
|
||||
MEDeleteUndoAction *elementAction = new MEDeleteUndoAction();
|
||||
elementAction->deleteObject( foundElements );
|
||||
elementAction->deleteObject( mesh );
|
||||
|
||||
// Add compound action to the UndoManager. Done.
|
||||
undoMan->addAction( compoundAction );
|
||||
|
||||
updateCollision();
|
||||
}
|
||||
|
||||
void ForestEditorCtrl::updateCollision()
|
||||
{
|
||||
if ( mForest )
|
||||
{
|
||||
mForest->updateCollision();
|
||||
|
||||
if ( mForest->getClientObject() )
|
||||
((Forest*)(mForest->getClientObject()))->updateCollision();
|
||||
}
|
||||
}
|
||||
|
||||
void FindDirtyForests( SceneObject *obj, void *key )
|
||||
{
|
||||
Forest *forest = dynamic_cast<Forest*>(obj);
|
||||
if ( forest && forest->getData()->isDirty() )
|
||||
*((bool*)(key)) = true;
|
||||
}
|
||||
|
||||
bool ForestEditorCtrl::isDirty()
|
||||
{
|
||||
bool foundDirty = false;
|
||||
gServerContainer.findObjects( EnvironmentObjectType, FindDirtyForests, (void*)&foundDirty );
|
||||
|
||||
return foundDirty;
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestEditorCtrl, updateActiveForest, void, 2, 2, "()" )
|
||||
{
|
||||
object->updateActiveForest( true );
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestEditorCtrl, setActiveTool, void, 3, 3, "( ForestTool tool )" )
|
||||
{
|
||||
ForestTool *tool = dynamic_cast<ForestTool*>( Sim::findObject( argv[2] ) );
|
||||
object->setActiveTool( tool );
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestEditorCtrl, getActiveTool, S32, 2, 2, "()" )
|
||||
{
|
||||
ForestTool *tool = object->getActiveTool();
|
||||
return tool ? tool->getId() : 0;
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestEditorCtrl, deleteMeshSafe, void, 3, 3, "( ForestItemData obj )" )
|
||||
{
|
||||
ForestItemData *db;
|
||||
if ( !Sim::findObject( argv[2], db ) )
|
||||
return;
|
||||
|
||||
object->deleteMeshSafe( db );
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestEditorCtrl, isDirty, bool, 2, 2, "" )
|
||||
{
|
||||
return object->isDirty();
|
||||
}
|
||||
107
Engine/source/forest/editor/forestEditorCtrl.h
Normal file
107
Engine/source/forest/editor/forestEditorCtrl.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOREST_EDITOR_FORESTEDITORCTRL_H_
|
||||
#define _FOREST_EDITOR_FORESTEDITORCTRL_H_
|
||||
|
||||
#ifndef _EDITTSCTRL_H_
|
||||
#include "gui/worldEditor/editTSCtrl.h"
|
||||
#endif
|
||||
|
||||
#ifndef _H_FOREST_
|
||||
#include "forest/forest.h"
|
||||
#endif
|
||||
|
||||
#ifndef _FOREST_EDITOR_TOOL_H_
|
||||
#include "forest/editor/forestTool.h"
|
||||
#endif
|
||||
|
||||
|
||||
class ForestEditorCtrl : public EditTSCtrl
|
||||
{
|
||||
typedef EditTSCtrl Parent;
|
||||
|
||||
friend class ForestPaintEvent;
|
||||
|
||||
protected:
|
||||
|
||||
/// The server Forest we're editing.
|
||||
SimObjectPtr<Forest> mForest;
|
||||
|
||||
/// The active tool in used by the editor.
|
||||
SimObjectPtr<ForestTool> mTool;
|
||||
|
||||
public:
|
||||
|
||||
ForestEditorCtrl();
|
||||
virtual ~ForestEditorCtrl();
|
||||
|
||||
DECLARE_CONOBJECT( ForestEditorCtrl );
|
||||
|
||||
// SimObject
|
||||
bool onAdd();
|
||||
static void initPersistFields();
|
||||
|
||||
// GuiControl
|
||||
virtual bool onWake();
|
||||
virtual void onSleep();
|
||||
virtual void onMouseUp( const GuiEvent &event_ );
|
||||
|
||||
// EditTSCtrl
|
||||
void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ );
|
||||
void on3DMouseDown( const Gui3DMouseEvent &event_ );
|
||||
void on3DMouseUp( const Gui3DMouseEvent &event_ );
|
||||
void on3DMouseMove( const Gui3DMouseEvent &event_ );
|
||||
void on3DMouseDragged( const Gui3DMouseEvent &event_ );
|
||||
void on3DMouseEnter( const Gui3DMouseEvent &event_ );
|
||||
void on3DMouseLeave( const Gui3DMouseEvent &event_ );
|
||||
void on3DRightMouseDown( const Gui3DMouseEvent &event_ );
|
||||
void on3DRightMouseUp( const Gui3DMouseEvent &event_ );
|
||||
bool onMouseWheelUp(const GuiEvent &event_);
|
||||
bool onMouseWheelDown(const GuiEvent &event_);
|
||||
void updateGuiInfo();
|
||||
void updateGizmo();
|
||||
void renderScene( const RectI &updateRect );
|
||||
void renderGui( Point2I offset, const RectI &updateRect );
|
||||
|
||||
/// Causes the editor to reselect the active forest.
|
||||
bool updateActiveForest( bool createNew );
|
||||
|
||||
/// Returns the active Forest.
|
||||
Forest *getActiveForest() const { return mForest; }
|
||||
|
||||
void setActiveTool( ForestTool *tool );
|
||||
ForestTool* getActiveTool() { return mTool; }
|
||||
|
||||
void onUndoAction();
|
||||
|
||||
void deleteMeshSafe( ForestItemData *itemData );
|
||||
|
||||
void updateCollision();
|
||||
|
||||
bool isDirty();
|
||||
};
|
||||
|
||||
#endif // _FOREST_EDITOR_FORESTEDITORCTRL_H_
|
||||
|
||||
|
||||
|
||||
590
Engine/source/forest/editor/forestSelectionTool.cpp
Normal file
590
Engine/source/forest/editor/forestSelectionTool.cpp
Normal file
|
|
@ -0,0 +1,590 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "forest/editor/forestSelectionTool.h"
|
||||
|
||||
#include "forest/forest.h"
|
||||
#include "forest/editor/forestUndo.h"
|
||||
#include "forest/editor/forestEditorCtrl.h"
|
||||
|
||||
#include "gui/worldEditor/editTSCtrl.h"
|
||||
#include "gui/worldEditor/gizmo.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/util/tVector.h"
|
||||
#include "core/util/safeDelete.h"
|
||||
#include "gfx/gfxDrawUtil.h"
|
||||
#include "gui/worldEditor/worldEditor.h"
|
||||
#include "math/mMatrix.h"
|
||||
|
||||
template <>
|
||||
MatrixF Selection<ForestItem>::getOrientation()
|
||||
{
|
||||
if ( size() == 1 )
|
||||
return first().getTransform();
|
||||
|
||||
return MatrixF::Identity;
|
||||
}
|
||||
|
||||
template <>
|
||||
Point3F Selection<ForestItem>::getOrigin()
|
||||
{
|
||||
Point3F centroid( Point3F::Zero );
|
||||
|
||||
if ( empty() )
|
||||
return centroid;
|
||||
|
||||
Selection<ForestItem>::iterator itr = begin();
|
||||
|
||||
for ( ; itr != end(); itr++ )
|
||||
{
|
||||
const MatrixF &mat = itr->getTransform();
|
||||
Point3F wPos;
|
||||
mat.getColumn( 3, &wPos );
|
||||
|
||||
centroid += wPos;
|
||||
}
|
||||
|
||||
centroid /= (F32)size();
|
||||
|
||||
return centroid;
|
||||
}
|
||||
|
||||
template <>
|
||||
Point3F Selection<ForestItem>::getScale()
|
||||
{
|
||||
if ( size() == 1 )
|
||||
return Point3F( first().getScale() );
|
||||
|
||||
return Point3F::One;
|
||||
}
|
||||
|
||||
void ForestItemSelection::offsetObject( ForestItem &object, const Point3F &delta )
|
||||
{
|
||||
if ( !mData )
|
||||
return;
|
||||
|
||||
MatrixF newMat( object.getTransform() );
|
||||
newMat.displace( delta );
|
||||
|
||||
object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), newMat, object.getScale() );
|
||||
}
|
||||
|
||||
void ForestItemSelection::rotateObject( ForestItem &object, const EulerF &delta, const Point3F &origin )
|
||||
{
|
||||
if ( !mData )
|
||||
return;
|
||||
|
||||
MatrixF mat = object.getTransform();
|
||||
|
||||
Point3F pos;
|
||||
mat.getColumn( 3, &pos );
|
||||
|
||||
MatrixF wMat( mat );
|
||||
wMat.inverse();
|
||||
|
||||
// get offset in obj space
|
||||
Point3F offset = pos - origin;
|
||||
|
||||
if ( size() == 1 )
|
||||
{
|
||||
wMat.mulV(offset);
|
||||
|
||||
MatrixF transform(EulerF::Zero, -offset);
|
||||
transform.mul(MatrixF(delta));
|
||||
transform.mul(MatrixF(EulerF::Zero, offset));
|
||||
mat.mul(transform);
|
||||
}
|
||||
else
|
||||
{
|
||||
MatrixF transform( delta );
|
||||
Point3F wOffset;
|
||||
transform.mulV( offset, &wOffset );
|
||||
|
||||
wMat.mulV( offset );
|
||||
|
||||
transform.set( EulerF::Zero, -offset );
|
||||
|
||||
mat.setColumn( 3, Point3F::Zero );
|
||||
wMat.setColumn( 3, Point3F::Zero );
|
||||
|
||||
transform.mul( wMat );
|
||||
transform.mul( MatrixF(delta) );
|
||||
transform.mul( mat );
|
||||
mat.mul( transform );
|
||||
|
||||
mat.normalize();
|
||||
mat.setColumn( 3, wOffset + origin );
|
||||
}
|
||||
|
||||
object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), mat, object.getScale() );
|
||||
}
|
||||
|
||||
void ForestItemSelection::scaleObject( ForestItem &object, const Point3F &delta )
|
||||
{
|
||||
if ( !mData )
|
||||
return;
|
||||
|
||||
object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), object.getTransform(), delta.z );
|
||||
}
|
||||
|
||||
|
||||
IMPLEMENT_CONOBJECT( ForestSelectionTool );
|
||||
|
||||
ConsoleDocClass( ForestSelectionTool,
|
||||
"@brief Specialized selection tool for picking individual trees in a forest.\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
ForestSelectionTool::ForestSelectionTool()
|
||||
: Parent(),
|
||||
mCurrAction( NULL ),
|
||||
mGizmo( NULL ),
|
||||
mGizmoProfile( NULL )
|
||||
{
|
||||
mBounds = Box3F::Invalid;
|
||||
|
||||
mDragRectColor.set(255,255,0);
|
||||
mMouseDown = false;
|
||||
mDragSelect = false;
|
||||
}
|
||||
|
||||
ForestSelectionTool::~ForestSelectionTool()
|
||||
{
|
||||
SAFE_DELETE( mCurrAction );
|
||||
}
|
||||
|
||||
void ForestSelectionTool::setParentEditor( ForestEditorCtrl *editor )
|
||||
{
|
||||
mEditor = editor;
|
||||
mGizmo = editor->getGizmo();
|
||||
mGizmoProfile = mGizmo->getProfile();
|
||||
}
|
||||
|
||||
void ForestSelectionTool::_selectItem( const ForestItem &item )
|
||||
{
|
||||
// Make sure its not already selected.
|
||||
for ( U32 i=0; i < mSelection.size(); i++ )
|
||||
{
|
||||
if ( mSelection[i].getKey() == item.getKey() )
|
||||
return;
|
||||
}
|
||||
|
||||
mSelection.push_back( item );
|
||||
mBounds.intersect( item.getWorldBox() );
|
||||
}
|
||||
|
||||
void ForestSelectionTool::deleteSelection()
|
||||
{
|
||||
ForestDeleteUndoAction *action = new ForestDeleteUndoAction( mForest->getData(), mEditor );
|
||||
|
||||
for ( U32 i=0; i < mSelection.size(); i++ )
|
||||
{
|
||||
const ForestItem &item = mSelection[i];
|
||||
action->removeItem( item );
|
||||
}
|
||||
|
||||
clearSelection();
|
||||
_submitUndo( action );
|
||||
}
|
||||
|
||||
void ForestSelectionTool::clearSelection()
|
||||
{
|
||||
mSelection.clear();
|
||||
mBounds = Box3F::Invalid;
|
||||
}
|
||||
|
||||
void ForestSelectionTool::cutSelection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ForestSelectionTool::copySelection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ForestSelectionTool::pasteSelection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ForestSelectionTool::setActiveForest( Forest *forest )
|
||||
{
|
||||
mForest = forest;
|
||||
|
||||
if ( forest )
|
||||
mSelection.setForestData( forest->getData() );
|
||||
else
|
||||
mSelection.setForestData( NULL );
|
||||
}
|
||||
|
||||
void ForestSelectionTool::on3DMouseDown( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
mMouseDown = true;
|
||||
mMouseDragged = false;
|
||||
|
||||
mUsingGizmo = !mSelection.empty() && mGizmo->getSelection() != Gizmo::None;
|
||||
|
||||
if ( mUsingGizmo )
|
||||
{
|
||||
mGizmo->on3DMouseDown( evt );
|
||||
return;
|
||||
}
|
||||
|
||||
mDragSelection.clear();
|
||||
mDragRect.set( Point2I(evt.mousePoint), Point2I(0,0) );
|
||||
mDragStart = evt.mousePoint;
|
||||
|
||||
const bool multiSel = evt.modifier & SI_CTRL;
|
||||
|
||||
if ( !multiSel )
|
||||
clearSelection();
|
||||
|
||||
if ( mHoverItem.isValid() )
|
||||
_selectItem( mHoverItem );
|
||||
|
||||
// This should never happen... it should have been
|
||||
// submitted and nulled in on3DMouseUp()!
|
||||
//
|
||||
// Yeah, unless you had a breakpoint there and on3DMouseUp never fired,
|
||||
// in which case this is really annoying.
|
||||
//
|
||||
//AssertFatal( !mCurrAction, "ForestSelectionTool::on3DMouseDown() - Dangling undo action!" );
|
||||
}
|
||||
|
||||
void ForestSelectionTool::on3DMouseMove( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
// Invalidate the hover item first... we're gonna find a new one.
|
||||
mHoverItem.makeInvalid();
|
||||
|
||||
if ( !mForest )
|
||||
return;
|
||||
|
||||
if ( !mSelection.empty() )
|
||||
mGizmo->on3DMouseMove( evt );
|
||||
|
||||
RayInfo ri;
|
||||
ri.userData = new ForestItem;
|
||||
Point3F startPnt = evt.pos;
|
||||
Point3F endPnt = evt.pos + evt.vec * 1000.0f;
|
||||
|
||||
if ( mForest->castRayRendered( startPnt, endPnt, &ri ) )
|
||||
mHoverItem = (*(ForestItem*)ri.userData);
|
||||
|
||||
delete static_cast<ForestItem*>(ri.userData);
|
||||
}
|
||||
|
||||
void ForestSelectionTool::on3DMouseDragged( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
mHoverItem.makeInvalid();
|
||||
|
||||
if ( mUsingGizmo )
|
||||
{
|
||||
mGizmo->on3DMouseDragged( evt );
|
||||
|
||||
const Point3F &deltaRot = mGizmo->getDeltaRot();
|
||||
const Point3F &deltaPos = mGizmo->getOffset();
|
||||
const Point3F &deltaScale = mGizmo->getDeltaScale();
|
||||
|
||||
if ( deltaRot.isZero() && deltaPos.isZero() && deltaScale.isZero() )
|
||||
return;
|
||||
|
||||
// Store the current item states!
|
||||
if ( !mCurrAction )
|
||||
{
|
||||
mCurrAction = new ForestUpdateAction( mForest->getData(), mEditor );
|
||||
for ( U32 i=0; i < mSelection.size(); i++ )
|
||||
{
|
||||
const ForestItem &item = mSelection[i];
|
||||
mCurrAction->saveItem( item );
|
||||
}
|
||||
}
|
||||
|
||||
switch ( mGizmo->getMode() )
|
||||
{
|
||||
case MoveMode:
|
||||
mSelection.offset( deltaPos ); break;
|
||||
case RotateMode:
|
||||
mSelection.rotate( deltaRot ); break;
|
||||
case ScaleMode:
|
||||
mSelection.scale( mGizmo->getScale() ); break;
|
||||
default: ;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
mDragSelect = true;
|
||||
mHoverItem.makeInvalid();
|
||||
|
||||
// Doing a drag selection.
|
||||
|
||||
if ( mDragSelect )
|
||||
{
|
||||
// build the drag selection on the renderScene method - make sure no neg extent!
|
||||
mDragRect.point.x = (evt.mousePoint.x < mDragStart.x) ? evt.mousePoint.x : mDragStart.x;
|
||||
mDragRect.extent.x = (evt.mousePoint.x > mDragStart.x) ? evt.mousePoint.x - mDragStart.x : mDragStart.x - evt.mousePoint.x;
|
||||
mDragRect.point.y = (evt.mousePoint.y < mDragStart.y) ? evt.mousePoint.y : mDragStart.y;
|
||||
mDragRect.extent.y = (evt.mousePoint.y > mDragStart.y) ? evt.mousePoint.y - mDragStart.y : mDragStart.y - evt.mousePoint.y;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ForestSelectionTool::on3DMouseUp( const Gui3DMouseEvent &evt )
|
||||
{
|
||||
mGizmo->on3DMouseUp( evt );
|
||||
|
||||
mMouseDown = false;
|
||||
|
||||
// If we have an undo action then we must have
|
||||
// moved, rotated, or scaled something.
|
||||
if ( mCurrAction )
|
||||
{
|
||||
_submitUndo( mCurrAction );
|
||||
mCurrAction = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we were performing a drag select, finalize it now.
|
||||
if ( mDragSelect )
|
||||
{
|
||||
mDragSelect = false;
|
||||
|
||||
clearSelection();
|
||||
|
||||
for ( S32 i = 0; i < mDragSelection.size(); i++ )
|
||||
_selectItem( mDragSelection[i] );
|
||||
|
||||
mDragSelection.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ForestSelectionTool::onRender3D()
|
||||
{
|
||||
GFXDrawUtil *drawUtil = GFX->getDrawUtil();
|
||||
ColorI color( 255, 255, 255, 255 );
|
||||
MatrixF treeMat;
|
||||
|
||||
GFXStateBlockDesc desc;
|
||||
desc.setBlend( true );
|
||||
desc.setZReadWrite( true, false );
|
||||
|
||||
if ( mHoverItem.isValid() )
|
||||
{
|
||||
treeMat = mHoverItem.getTransform();
|
||||
drawUtil->drawObjectBox( desc, mHoverItem.getSize(), mHoverItem.getWorldBox().getCenter(), treeMat, color );
|
||||
}
|
||||
|
||||
if ( !mSelection.empty() )
|
||||
{
|
||||
for ( U32 i = 0; i < mSelection.size(); i++ )
|
||||
{
|
||||
const ForestItem &item = mSelection[i];
|
||||
treeMat = item.getTransform();
|
||||
drawUtil->drawObjectBox( desc, item.getSize(), item.getWorldBox().getCenter(), treeMat, color );
|
||||
}
|
||||
|
||||
mGizmo->set( mSelection.getOrientation(), mSelection.getOrigin(), mSelection.getScale() );
|
||||
|
||||
mGizmo->renderGizmo( mEditor->getLastCameraQuery().cameraMatrix, mEditor->getLastCameraQuery().fov );
|
||||
}
|
||||
}
|
||||
|
||||
static Frustum gDragFrustum;
|
||||
|
||||
void ForestSelectionTool::onRender2D()
|
||||
{
|
||||
// Draw drag selection rect.
|
||||
if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 )
|
||||
GFX->getDrawUtil()->drawRect( mDragRect, mDragRectColor );
|
||||
|
||||
// update what is in the selection
|
||||
if ( mDragSelect )
|
||||
mDragSelection.clear();
|
||||
|
||||
// Determine selected objects based on the drag box touching
|
||||
// a mesh if a drag operation has begun.
|
||||
if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 )
|
||||
{
|
||||
// Build the drag frustum based on the rect
|
||||
F32 wwidth;
|
||||
F32 wheight;
|
||||
F32 aspectRatio = F32(mEditor->getWidth()) / F32(mEditor->getHeight());
|
||||
|
||||
const CameraQuery &lastCameraQuery = mEditor->getLastCameraQuery();
|
||||
if(!lastCameraQuery.ortho)
|
||||
{
|
||||
wheight = lastCameraQuery.nearPlane * mTan(lastCameraQuery.fov / 2);
|
||||
wwidth = aspectRatio * wheight;
|
||||
}
|
||||
else
|
||||
{
|
||||
wheight = lastCameraQuery.fov;
|
||||
wwidth = aspectRatio * wheight;
|
||||
}
|
||||
|
||||
F32 hscale = wwidth * 2 / F32(mEditor->getWidth());
|
||||
F32 vscale = wheight * 2 / F32(mEditor->getHeight());
|
||||
|
||||
F32 left = (mDragRect.point.x - mEditor->getPosition().x) * hscale - wwidth;
|
||||
F32 right = (mDragRect.point.x - mEditor->getPosition().x + mDragRect.extent.x) * hscale - wwidth;
|
||||
F32 top = wheight - vscale * (mDragRect.point.y - mEditor->getPosition().y);
|
||||
F32 bottom = wheight - vscale * (mDragRect.point.y - mEditor->getPosition().y + mDragRect.extent.y);
|
||||
gDragFrustum.set(lastCameraQuery.ortho, left, right, top, bottom, lastCameraQuery.nearPlane, lastCameraQuery.farPlane, lastCameraQuery.cameraMatrix );
|
||||
|
||||
mForest->getData()->getItems( gDragFrustum, &mDragSelection );
|
||||
}
|
||||
}
|
||||
|
||||
bool ForestSelectionTool::updateGuiInfo()
|
||||
{
|
||||
SimObject *statusbar;
|
||||
if ( !Sim::findObject( "EditorGuiStatusBar", statusbar ) )
|
||||
return false;
|
||||
|
||||
String text( "Forest Editor." );
|
||||
GizmoMode mode = mGizmoProfile->mode;
|
||||
|
||||
if ( mMouseDown && mGizmo->getSelection() != Gizmo::None )
|
||||
{
|
||||
Point3F delta;
|
||||
String qualifier;
|
||||
|
||||
if ( mode == RotateMode )
|
||||
delta = mGizmo->getDeltaTotalRot();
|
||||
else if ( mode == MoveMode )
|
||||
delta = mGizmo->getTotalOffset();
|
||||
else if ( mode == ScaleMode )
|
||||
delta = mGizmo->getDeltaTotalScale();
|
||||
|
||||
if ( mGizmo->getAlignment() == Object && mode != ScaleMode )
|
||||
{
|
||||
mSelection.getOrientation().mulV( delta );
|
||||
}
|
||||
|
||||
if ( mIsZero( delta.x, 0.0001f ) )
|
||||
delta.x = 0.0f;
|
||||
if ( mIsZero( delta.y, 0.0001f ) )
|
||||
delta.y = 0.0f;
|
||||
if ( mIsZero( delta.z, 0.0001f ) )
|
||||
delta.z = 0.0f;
|
||||
|
||||
if ( mode == RotateMode )
|
||||
{
|
||||
delta.x = mRadToDeg( delta.x );
|
||||
delta.y = mRadToDeg( delta.y );
|
||||
delta.z = mRadToDeg( delta.z );
|
||||
text = String::ToString( "Delta angle ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z );
|
||||
}
|
||||
else if ( mode == MoveMode )
|
||||
text = String::ToString( "Delta position ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z );
|
||||
else if ( mode == ScaleMode )
|
||||
text = String::ToString( "Delta scale ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mode == MoveMode )
|
||||
text = "Move selection. SHIFT while dragging duplicates objects.";
|
||||
else if ( mode == RotateMode )
|
||||
text = "Rotate selection.";
|
||||
else if ( mode == ScaleMode )
|
||||
text = "Scale selection.";
|
||||
}
|
||||
|
||||
Con::executef( statusbar, "setInfo", text.c_str() );
|
||||
|
||||
Con::executef( statusbar, "setSelectionObjectsByCount", Con::getIntArg( mSelection.size() ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ForestSelectionTool::updateGizmo()
|
||||
{
|
||||
mGizmoProfile->restoreDefaultState();
|
||||
|
||||
const GizmoMode &mode = mGizmoProfile->mode;
|
||||
|
||||
if ( mode == ScaleMode )
|
||||
{
|
||||
mGizmoProfile->flags &= ~GizmoProfile::PlanarHandlesOn;
|
||||
mGizmoProfile->allAxesScaleUniform = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ForestSelectionTool::onUndoAction()
|
||||
{
|
||||
ForestData *data = mForest->getData();
|
||||
|
||||
// Remove items from our selection that no longer exist.
|
||||
for ( S32 i = 0; i < mSelection.size(); i++ )
|
||||
{
|
||||
const ForestItem &item = data->findItem( mSelection[i].getKey(), mSelection[i].getPosition() );
|
||||
|
||||
if ( item == ForestItem::Invalid )
|
||||
{
|
||||
mSelection.erase_fast( i );
|
||||
i--;
|
||||
}
|
||||
else
|
||||
mSelection[i] = item;
|
||||
}
|
||||
|
||||
// Recalculate our selection bounds.
|
||||
mBounds = Box3F::Invalid;
|
||||
for ( S32 i = 0; i < mSelection.size(); i++ )
|
||||
mBounds.intersect( mSelection[i].getWorldBox() );
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestSelectionTool, getSelectionCount, S32, 2, 2, "" )
|
||||
{
|
||||
return object->getSelectionCount();
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestSelectionTool, deleteSelection, void, 2, 2, "" )
|
||||
{
|
||||
object->deleteSelection();
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestSelectionTool, clearSelection, void, 2, 2, "" )
|
||||
{
|
||||
object->clearSelection();
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestSelectionTool, cutSelection, void, 2, 2, "" )
|
||||
{
|
||||
object->cutSelection();
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestSelectionTool, copySelection, void, 2, 2, "" )
|
||||
{
|
||||
object->copySelection();
|
||||
}
|
||||
|
||||
ConsoleMethod( ForestSelectionTool, pasteSelection, void, 2, 2, "" )
|
||||
{
|
||||
object->pasteSelection();
|
||||
}
|
||||
|
||||
121
Engine/source/forest/editor/forestSelectionTool.h
Normal file
121
Engine/source/forest/editor/forestSelectionTool.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOREST_EDITOR_SELECTIONTOOL_H_
|
||||
#define _FOREST_EDITOR_SELECTIONTOOL_H_
|
||||
|
||||
#ifndef _FOREST_EDITOR_TOOL_H_
|
||||
#include "forest/editor/forestTool.h"
|
||||
#endif
|
||||
#ifndef _FORESTITEM_H_
|
||||
#include "forest/forestItem.h"
|
||||
#endif
|
||||
#ifndef _TSELECTION_H_
|
||||
#include "gui/worldEditor/tSelection.h"
|
||||
#endif
|
||||
|
||||
|
||||
class Forest;
|
||||
class Gizmo;
|
||||
class GizmoProfile;
|
||||
class ForestUpdateAction;
|
||||
|
||||
class ForestItemSelection : public Selection<ForestItem>
|
||||
{
|
||||
public:
|
||||
|
||||
void setForestData( ForestData* data ) { mData = data; }
|
||||
|
||||
protected:
|
||||
|
||||
void offsetObject( ForestItem &object, const Point3F &delta );
|
||||
void rotateObject( ForestItem &object, const EulerF &delta, const Point3F &origin );
|
||||
void scaleObject( ForestItem &object, const Point3F &delta );
|
||||
|
||||
protected:
|
||||
|
||||
ForestData *mData;
|
||||
};
|
||||
|
||||
|
||||
class ForestSelectionTool : public ForestTool
|
||||
{
|
||||
typedef ForestTool Parent;
|
||||
|
||||
protected:
|
||||
|
||||
Gizmo *mGizmo;
|
||||
GizmoProfile *mGizmoProfile;
|
||||
|
||||
ForestItem mHoverItem;
|
||||
|
||||
ForestItemSelection mSelection;
|
||||
|
||||
ForestItemSelection mDragSelection;
|
||||
bool mDragSelect;
|
||||
RectI mDragRect;
|
||||
Point2I mDragStart;
|
||||
bool mMouseDown;
|
||||
bool mMouseDragged;
|
||||
ColorI mDragRectColor;
|
||||
bool mUsingGizmo;
|
||||
|
||||
Box3F mBounds;
|
||||
|
||||
ForestUpdateAction *mCurrAction;
|
||||
|
||||
void _selectItem( const ForestItem &item );
|
||||
|
||||
public:
|
||||
|
||||
ForestSelectionTool();
|
||||
virtual ~ForestSelectionTool();
|
||||
|
||||
DECLARE_CONOBJECT( ForestSelectionTool );
|
||||
|
||||
// ForestTool
|
||||
virtual void setParentEditor( ForestEditorCtrl *editor );
|
||||
virtual void setActiveForest( Forest *forest );
|
||||
virtual void on3DMouseDown( const Gui3DMouseEvent &evt );
|
||||
virtual void on3DMouseUp( const Gui3DMouseEvent &evt );
|
||||
virtual void on3DMouseMove( const Gui3DMouseEvent &evt );
|
||||
virtual void on3DMouseDragged( const Gui3DMouseEvent &evt );
|
||||
virtual void onRender3D();
|
||||
virtual void onRender2D();
|
||||
virtual bool updateGuiInfo();
|
||||
virtual void updateGizmo();
|
||||
virtual void onUndoAction();
|
||||
|
||||
S32 getSelectionCount() const { return mSelection.size(); }
|
||||
|
||||
void deleteSelection();
|
||||
void clearSelection();
|
||||
void cutSelection();
|
||||
void copySelection();
|
||||
void pasteSelection();
|
||||
};
|
||||
|
||||
|
||||
#endif // _FOREST_EDITOR_SELECTIONTOOL_H_
|
||||
|
||||
|
||||
|
||||
68
Engine/source/forest/editor/forestTool.cpp
Normal file
68
Engine/source/forest/editor/forestTool.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "forest/editor/forestTool.h"
|
||||
#include "forest/editor/forestEditorCtrl.h"
|
||||
|
||||
#include "forest/forestDataFile.h"
|
||||
#include "forest/forestCell.h"
|
||||
|
||||
#include "util/undo.h"
|
||||
#include "math/mMath.h"
|
||||
#include "math/mathUtils.h"
|
||||
|
||||
|
||||
IMPLEMENT_CONOBJECT( ForestTool );
|
||||
|
||||
ConsoleDocClass( ForestTool,
|
||||
"@brief Base class for Forest Editor specific tools\n\n"
|
||||
"Editor use only.\n\n"
|
||||
"@internal"
|
||||
);
|
||||
|
||||
ForestTool::ForestTool()
|
||||
: mForest( NULL ),
|
||||
mEditor( NULL )
|
||||
{
|
||||
}
|
||||
|
||||
ForestTool::~ForestTool()
|
||||
{
|
||||
}
|
||||
|
||||
void ForestTool::_submitUndo( UndoAction *action )
|
||||
{
|
||||
AssertFatal( action, "ForestTool::_submitUndo() - No undo action!" );
|
||||
|
||||
// Grab the mission editor undo manager.
|
||||
UndoManager *undoMan = NULL;
|
||||
if ( !Sim::findObject( "EUndoManager", undoMan ) )
|
||||
{
|
||||
Con::errorf( "ForestTool::_submitUndo() - EUndoManager not found!" );
|
||||
return;
|
||||
}
|
||||
|
||||
undoMan->addAction( action );
|
||||
|
||||
mEditor->updateCollision();
|
||||
}
|
||||
87
Engine/source/forest/editor/forestTool.h
Normal file
87
Engine/source/forest/editor/forestTool.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOREST_EDITOR_TOOL_H_
|
||||
#define _FOREST_EDITOR_TOOL_H_
|
||||
|
||||
#ifndef _FORESTITEM_H_
|
||||
#include "forest/forestItem.h"
|
||||
#endif
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
#ifndef _GUITYPES_H_
|
||||
#include "gui/core/guiTypes.h"
|
||||
#endif
|
||||
|
||||
class Forest;
|
||||
class ForestEditorCtrl;
|
||||
class ForestData;
|
||||
class UndoAction;
|
||||
class GuiTSCtrl;
|
||||
class Point3F;
|
||||
struct Gui3DMouseEvent;
|
||||
|
||||
|
||||
class ForestTool : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
|
||||
protected:
|
||||
|
||||
SimObjectPtr<Forest> mForest;
|
||||
ForestEditorCtrl *mEditor;
|
||||
|
||||
void _submitUndo( UndoAction *action );
|
||||
|
||||
public:
|
||||
|
||||
ForestTool();
|
||||
virtual ~ForestTool();
|
||||
|
||||
DECLARE_CONOBJECT( ForestTool );
|
||||
|
||||
virtual void setActiveForest( Forest *forest ) { mForest = forest; }
|
||||
virtual void setParentEditor( ForestEditorCtrl *editor ) { mEditor = editor; }
|
||||
|
||||
virtual void onActivated( const Gui3DMouseEvent &lastEvent ) {}
|
||||
virtual void onDeactivated() {}
|
||||
|
||||
virtual void on3DMouseDown( const Gui3DMouseEvent &evt ) {}
|
||||
virtual void on3DMouseUp( const Gui3DMouseEvent &evt ) {}
|
||||
virtual void on3DMouseMove( const Gui3DMouseEvent &evt ) {}
|
||||
virtual void on3DMouseDragged( const Gui3DMouseEvent &evt ) {}
|
||||
virtual void on3DMouseEnter( const Gui3DMouseEvent &evt ) {}
|
||||
virtual void on3DMouseLeave( const Gui3DMouseEvent &evt ) {}
|
||||
virtual bool onMouseWheel( const GuiEvent &evt ) { return false; }
|
||||
virtual void onRender3D() {}
|
||||
virtual void onRender2D() {}
|
||||
virtual void updateGizmo() {}
|
||||
virtual bool updateGuiInfo() { return false; }
|
||||
virtual void onUndoAction() {}
|
||||
};
|
||||
|
||||
|
||||
#endif // _FOREST_EDITOR_TOOL_H_
|
||||
|
||||
|
||||
|
||||
223
Engine/source/forest/editor/forestUndo.cpp
Normal file
223
Engine/source/forest/editor/forestUndo.cpp
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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 "forest/editor/forestUndo.h"
|
||||
|
||||
#include "forest/forestDataFile.h"
|
||||
#include "forest/editor/forestEditorCtrl.h"
|
||||
|
||||
|
||||
ForestUndoAction::ForestUndoAction( const Resource<ForestData> &data,
|
||||
ForestEditorCtrl *editor,
|
||||
const char *description )
|
||||
: UndoAction( description ),
|
||||
mData( data ),
|
||||
mEditor( editor )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
ForestCreateUndoAction::ForestCreateUndoAction( const Resource<ForestData> &data,
|
||||
ForestEditorCtrl *editor )
|
||||
: ForestUndoAction( data, editor, "Create Forest Items" )
|
||||
{
|
||||
}
|
||||
|
||||
void ForestCreateUndoAction::addItem( ForestItemData *data,
|
||||
const Point3F &position,
|
||||
F32 rotation,
|
||||
F32 scale )
|
||||
{
|
||||
const ForestItem &item = mData->addItem( data, position, rotation, scale );
|
||||
mItems.push_back( item );
|
||||
|
||||
// We store the datablock ID rather than the actual pointer
|
||||
// since the pointer could go bad.
|
||||
SimObjectId dataId = item.getData()->getId();
|
||||
mItems.last().setData( (ForestItemData*)dataId );
|
||||
}
|
||||
|
||||
void ForestCreateUndoAction::redo()
|
||||
{
|
||||
for ( U32 i = 0; i < mItems.size(); i++ )
|
||||
{
|
||||
const ForestItem &item = mItems[i];
|
||||
|
||||
// Not 64bit safe!
|
||||
// We store the datablock ID rather than the actual pointer
|
||||
// since the pointer could go bad.
|
||||
ForestItemData *data;
|
||||
if ( !Sim::findObject( (SimObjectId)(item.getData()), data ) )
|
||||
{
|
||||
Con::errorf( "ForestCreateUndoAction::redo() - ForestItemData for item to restore does not seem to exist. Undo stack may be hosed." );
|
||||
continue;
|
||||
}
|
||||
|
||||
mData->addItem( item.getKey(),
|
||||
data,
|
||||
item.getTransform(),
|
||||
item.getScale() );
|
||||
}
|
||||
|
||||
mEditor->onUndoAction();
|
||||
}
|
||||
|
||||
void ForestCreateUndoAction::undo()
|
||||
{
|
||||
for ( S32 i = mItems.size()-1; i >= 0; i-- )
|
||||
{
|
||||
const ForestItem &item = mItems[i];
|
||||
mData->removeItem( item.getKey(), item.getPosition() );
|
||||
}
|
||||
|
||||
mEditor->onUndoAction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ForestDeleteUndoAction::ForestDeleteUndoAction( const Resource<ForestData> &data,
|
||||
ForestEditorCtrl *editor )
|
||||
: ForestUndoAction( data, editor, "Delete Forest Items" )
|
||||
{
|
||||
}
|
||||
|
||||
void ForestDeleteUndoAction::removeItem( const ForestItem &item )
|
||||
{
|
||||
// Not 64bit safe!
|
||||
// We store the datablock ID rather than the actual pointer
|
||||
// since the pointer could go bad.
|
||||
SimObjectId dataId = item.getData()->getId();
|
||||
|
||||
mItems.push_back( item );
|
||||
mItems.last().setData( (ForestItemData*)dataId );
|
||||
mData->removeItem( item.getKey(), item.getPosition() );
|
||||
}
|
||||
|
||||
void ForestDeleteUndoAction::removeItem( const Vector<ForestItem> &itemList )
|
||||
{
|
||||
for ( S32 i = 0; i < itemList.size(); i++ )
|
||||
removeItem( itemList[i] );
|
||||
}
|
||||
|
||||
void ForestDeleteUndoAction::redo()
|
||||
{
|
||||
for ( U32 i = 0; i < mItems.size(); i++ )
|
||||
{
|
||||
const ForestItem &item = mItems[i];
|
||||
mData->removeItem( item.getKey(), item.getPosition() );
|
||||
}
|
||||
|
||||
mEditor->onUndoAction();
|
||||
}
|
||||
|
||||
void ForestDeleteUndoAction::undo()
|
||||
{
|
||||
for ( S32 i = mItems.size()-1; i >= 0; i-- )
|
||||
{
|
||||
const ForestItem &item = mItems[i];
|
||||
|
||||
// We store the datablock ID rather than the actual pointer
|
||||
// since the pointer could go bad.
|
||||
ForestItemData *data;
|
||||
if ( !Sim::findObject( (SimObjectId)(item.getData()), data ) )
|
||||
{
|
||||
Con::errorf( "ForestDeleteUndoAction::undo() - ForestItemData for item to restore does not seem to exist. Undo stack may be hosed." );
|
||||
continue;
|
||||
}
|
||||
|
||||
mData->addItem( item.getKey(),
|
||||
data,
|
||||
item.getTransform(),
|
||||
item.getScale() );
|
||||
}
|
||||
|
||||
mEditor->onUndoAction();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ForestUpdateAction::ForestUpdateAction( const Resource<ForestData> &data,
|
||||
ForestEditorCtrl *editor )
|
||||
: ForestUndoAction( data, editor, "Update Forest Items" )
|
||||
{
|
||||
}
|
||||
|
||||
void ForestUpdateAction::saveItem( const ForestItem &item )
|
||||
{
|
||||
// We just store the current state... we undo it later.
|
||||
mItems.push_back( item );
|
||||
|
||||
// We store the datablock ID rather than the actual pointer
|
||||
// since the pointer could go bad.
|
||||
SimObjectId dataId = item.getData()->getId();
|
||||
mItems.last().setData( (ForestItemData*)dataId );
|
||||
}
|
||||
|
||||
void ForestUpdateAction::_swapState()
|
||||
{
|
||||
Vector<ForestItem> prevItems = mItems;
|
||||
mItems.clear();
|
||||
|
||||
for ( U32 i=0; i < prevItems.size(); i++ )
|
||||
{
|
||||
const ForestItem &item = prevItems[i];
|
||||
|
||||
// Save the current state so we can reverse this swap.
|
||||
//
|
||||
// Note that we do 'not' want the const ref returned by findItem
|
||||
// because when we call updateItem below we would lose our copy
|
||||
// of the items state before the call.
|
||||
//
|
||||
ForestItem newItem = mData->findItem( item.getKey() );
|
||||
|
||||
if ( !newItem.isValid() )
|
||||
{
|
||||
Con::errorf( "ForestUpdateAction::_swapState() - saved item no longer exists. Undo stack may be hosed." );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not 64bit safe!
|
||||
// We store the datablock ID rather than the actual pointer
|
||||
// since the pointer could go bad.
|
||||
ForestItemData *data;
|
||||
if ( !Sim::findObject( (SimObjectId)(item.getData()), data ) )
|
||||
{
|
||||
Con::errorf( "ForestUpdateAction::_swapState() - ForestItemData for item to restore does not seem to exist. Undo stack may be hosed." );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now revert to the old state.
|
||||
mData->updateItem( item.getKey(),
|
||||
item.getPosition(),
|
||||
data,
|
||||
item.getTransform(),
|
||||
item.getScale() );
|
||||
|
||||
// Save the state before this swap for the next swap.
|
||||
newItem.setData( (ForestItemData*)data->getId() );
|
||||
mItems.push_back( newItem );
|
||||
}
|
||||
|
||||
mEditor->onUndoAction();
|
||||
}
|
||||
121
Engine/source/forest/editor/forestUndo.h
Normal file
121
Engine/source/forest/editor/forestUndo.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _FOREST_EDITOR_UNDO_H_
|
||||
#define _FOREST_EDITOR_UNDO_H_
|
||||
|
||||
#ifndef _UNDO_H_
|
||||
#include "util/undo.h"
|
||||
#endif
|
||||
#ifndef _FORESTITEM_H_
|
||||
#include "forest/forestItem.h"
|
||||
#endif
|
||||
#ifndef __RESOURCE_H__
|
||||
#include "core/resource.h"
|
||||
#endif
|
||||
|
||||
class ForestData;
|
||||
class ForestEditorCtrl;
|
||||
|
||||
class ForestUndoAction : public UndoAction
|
||||
{
|
||||
typedef UndoAction Parent;
|
||||
|
||||
public:
|
||||
|
||||
ForestUndoAction( const Resource<ForestData> &data, ForestEditorCtrl *editor, const char *description );
|
||||
|
||||
// UndoAction
|
||||
virtual void undo() {}
|
||||
virtual void redo() {}
|
||||
|
||||
protected:
|
||||
|
||||
ForestEditorCtrl *mEditor;
|
||||
Vector<ForestItem> mItems;
|
||||
Resource<ForestData> mData;
|
||||
};
|
||||
|
||||
class ForestCreateUndoAction : public ForestUndoAction
|
||||
{
|
||||
typedef ForestUndoAction Parent;
|
||||
|
||||
public:
|
||||
|
||||
ForestCreateUndoAction( const Resource<ForestData> &data,
|
||||
ForestEditorCtrl *editor );
|
||||
|
||||
/// Adds the item to the Forest and stores
|
||||
/// its info for undo later.
|
||||
void addItem( ForestItemData *data,
|
||||
const Point3F &position,
|
||||
F32 rotation,
|
||||
F32 scale );
|
||||
|
||||
// UndoAction
|
||||
virtual void undo();
|
||||
virtual void redo();
|
||||
};
|
||||
|
||||
|
||||
class ForestDeleteUndoAction : public ForestUndoAction
|
||||
{
|
||||
typedef ForestUndoAction Parent;
|
||||
|
||||
public:
|
||||
|
||||
ForestDeleteUndoAction( const Resource<ForestData> &data,
|
||||
ForestEditorCtrl *editor );
|
||||
|
||||
///
|
||||
void removeItem( const ForestItem &item );
|
||||
void removeItem( const Vector<ForestItem> &itemList );
|
||||
|
||||
// UndoAction
|
||||
virtual void undo();
|
||||
virtual void redo();
|
||||
};
|
||||
|
||||
|
||||
class ForestUpdateAction : public ForestUndoAction
|
||||
{
|
||||
typedef ForestUndoAction Parent;
|
||||
|
||||
public:
|
||||
|
||||
ForestUpdateAction( const Resource<ForestData> &data,
|
||||
ForestEditorCtrl *editor );
|
||||
|
||||
void saveItem( const ForestItem &item );
|
||||
|
||||
virtual void undo() { _swapState(); }
|
||||
virtual void redo() { _swapState(); }
|
||||
|
||||
protected:
|
||||
|
||||
void _swapState();
|
||||
};
|
||||
|
||||
#endif // _FOREST_EDITOR_UNDO_H_
|
||||
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue