Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View 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 );
}

View 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_

View 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(&currentTree);
// 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();
}

View 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_

View 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();
}

View 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_

View 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();
}

View 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_

View 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();
}

View 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_

View 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();
}

View 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_