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,475 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/creator.h"
#include "gfx/gfxDrawUtil.h"
IMPLEMENT_CONOBJECT(CreatorTree);
ConsoleDocClass( CreatorTree,
"@brief Creator tree from old editor. Not used in current editor.\n\n"
"@internal"
);
//------------------------------------------------------------------------------
// Class CreatorTree::Node
//------------------------------------------------------------------------------
CreatorTree::Node::Node() :
mFlags(0),
mParent(0),
mName(0),
mValue(0),
mId(0),
mTab(0)
{
VECTOR_SET_ASSOCIATION(mChildren);
}
CreatorTree::Node::~Node()
{
for(U32 i = 0; i < mChildren.size(); i++)
delete mChildren[i];
}
//------------------------------------------------------------------------------
void CreatorTree::Node::expand(bool exp)
{
if(exp)
{
if(mParent)
mParent->expand(exp);
mFlags.set(Node::Expanded);
}
else if(!isRoot())
{
if(isGroup())
for(U32 i = 0; i < mChildren.size(); i++)
mChildren[i]->expand(exp);
mFlags.clear(Selected);
mFlags.clear(Expanded);
}
}
//------------------------------------------------------------------------------
CreatorTree::Node * CreatorTree::Node::find(S32 id)
{
if(mId == id)
return(this);
if(!isGroup())
return(0);
for(U32 i = 0; i < mChildren.size(); i++)
{
Node * node = mChildren[i]->find(id);
if(node)
return(node);
}
return(0);
}
//------------------------------------------------------------------------------
bool CreatorTree::Node::isFirst()
{
AssertFatal(!isRoot(), "CreatorTree::Node::isFirst - cannot call on root node");
return(this == mParent->mChildren[0]);
}
bool CreatorTree::Node::isLast()
{
AssertFatal(!isRoot(), "CreatorTree::Node::isLast - cannot call on root node");
return(this == mParent->mChildren[mParent->mChildren.size()-1]);
}
bool CreatorTree::Node::hasChildItem()
{
for(U32 i = 0; i < mChildren.size(); i++)
{
if(mChildren[i]->isGroup() && mChildren[i]->hasChildItem())
return(true);
if(!mChildren[i]->isGroup())
return(true);
}
return(false);
}
S32 CreatorTree::Node::getSelected()
{
for(U32 i = 0; i < mChildren.size(); i++)
{
if(mChildren[i]->isSelected())
return(mChildren[i]->mId);
else if(mChildren[i]->isGroup())
{
S32 ret = mChildren[i]->getSelected();
if(ret != -1)
return(ret);
}
}
return(-1);
}
//------------------------------------------------------------------------------
// Class CreatorTree
//------------------------------------------------------------------------------
CreatorTree::CreatorTree() :
mCurId(0),
mTxtOffset(5),
mRoot(0)
{
VECTOR_SET_ASSOCIATION(mNodeList);
clear();
}
CreatorTree::~CreatorTree()
{
delete mRoot;
}
//------------------------------------------------------------------------------
CreatorTree::Node * CreatorTree::createNode(const char * name, const char * value, bool group, Node * parent)
{
Node * node = new Node();
node->mId = mCurId++;
node->mName = name ? StringTable->insert(name) : 0;
node->mValue = value ? StringTable->insert(value) : 0;
node->mFlags.set(Node::Group, group);
// add to the parent group
if(parent)
{
node->mParent = parent;
if(!addNode(parent, node))
{
delete node;
return(0);
}
}
return(node);
}
//------------------------------------------------------------------------------
void CreatorTree::clear()
{
delete mRoot;
mCurId = 0;
mRoot = createNode(0, 0, true);
mRoot->mFlags.set(Node::Root | Node::Expanded);
mSize = Point2I(1,0);
}
//------------------------------------------------------------------------------
bool CreatorTree::addNode(Node * parent, Node * node)
{
if(!parent->isGroup())
return(false);
//
parent->mChildren.push_back(node);
return(true);
}
//------------------------------------------------------------------------------
CreatorTree::Node * CreatorTree::findNode(S32 id)
{
return(mRoot->find(id));
}
//------------------------------------------------------------------------------
void CreatorTree::sort()
{
// groups then items by alpha
}
//------------------------------------------------------------------------------
ConsoleMethod( CreatorTree, addGroup, S32, 4, 4, "(string group, string name, string value)")
{
CreatorTree::Node * grp = object->findNode(dAtoi(argv[2]));
if(!grp || !grp->isGroup())
return(-1);
// return same named group if found...
for(U32 i = 0; i < grp->mChildren.size(); i++)
if(!dStricmp(argv[3], grp->mChildren[i]->mName))
return(grp->mChildren[i]->mId);
CreatorTree::Node * node = object->createNode(argv[3], 0, true, grp);
object->build();
return(node ? node->getId() : -1);
}
ConsoleMethod( CreatorTree, addItem, S32, 5, 5, "(Node group, string name, string value)")
{
CreatorTree::Node * grp = object->findNode(dAtoi(argv[2]));
if(!grp || !grp->isGroup())
return -1;
CreatorTree::Node * node = object->createNode(argv[3], argv[4], false, grp);
object->build();
return(node ? node->getId() : -1);
}
//------------------------------------------------------------------------------
ConsoleMethod( CreatorTree, fileNameMatch, bool, 5, 5, "(string world, string type, string filename)"){
// argv[2] - world short
// argv[3] - type short
// argv[4] - filename
// interior filenames
// 0 - world short ('b', 'x', ...)
// 1-> - type short ('towr', 'bunk', ...)
U32 typeLen = dStrlen(argv[3]);
if(dStrlen(argv[4]) < (typeLen + 1))
return(false);
// world
if(dToupper(argv[4][0]) != dToupper(argv[2][0]))
return(false);
return(!dStrnicmp(argv[4]+1, argv[3], typeLen));
}
ConsoleMethod( CreatorTree, getSelected, S32, 2, 2, "Return a handle to the currently selected item.")
{
return(object->getSelected());
}
ConsoleMethod( CreatorTree, isGroup, bool, 3, 3, "(Group g)")
{
CreatorTree::Node * node = object->findNode(dAtoi(argv[2]));
if(node && node->isGroup())
return(true);
return(false);
}
ConsoleMethod( CreatorTree, getName, const char*, 3, 3, "(Node item)")
{
CreatorTree::Node * node = object->findNode(dAtoi(argv[2]));
return(node ? node->mName : 0);
}
ConsoleMethod( CreatorTree, getValue, const char*, 3, 3, "(Node n)")
{
CreatorTree::Node * node = object->findNode(dAtoi(argv[2]));
return(node ? node->mValue : 0);
}
ConsoleMethod( CreatorTree, clear, void, 2, 2, "Clear the tree.")
{
object->clear();
}
ConsoleMethod( CreatorTree, getParent, S32, 3, 3, "(Node n)")
{
CreatorTree::Node * node = object->findNode(dAtoi(argv[2]));
if(node && node->mParent)
return(node->mParent->getId());
return(-1);
}
//------------------------------------------------------------------------------
void CreatorTree::buildNode(Node * node, U32 tab)
{
if(node->isExpanded())
for(U32 i = 0; i < node->mChildren.size(); i++)
{
Node * child = node->mChildren[i];
child->mTab = tab;
child->select(false);
mNodeList.push_back(child);
// grab width
if(bool(mProfile->mFont) && child->mName)
{
S32 width = (tab + 1) * mTabSize + mProfile->mFont->getStrWidth(child->mName) + mTxtOffset;
if(width > mMaxWidth)
mMaxWidth = width;
}
if(node->mChildren[i]->isGroup())
buildNode(node->mChildren[i], tab+1);
}
}
//------------------------------------------------------------------------------
void CreatorTree::build()
{
mMaxWidth = 0;
mNodeList.clear();
buildNode(mRoot, 0);
mCellSize.set( mMaxWidth + 1, 11 );
setSize(Point2I(1, mNodeList.size()));
}
//------------------------------------------------------------------------------
bool CreatorTree::onWake()
{
if(!Parent::onWake())
return(false);
mTabSize = 11;
//
build();
mCellSize.set( mMaxWidth + 1, 11 );
setSize(Point2I(1, mNodeList.size()));
return true;
}
//------------------------------------------------------------------------------
void CreatorTree::onMouseUp(const GuiEvent & event)
{
onAction();
}
void CreatorTree::onMouseDown(const GuiEvent & event)
{
Point2I pos = globalToLocalCoord(event.mousePoint);
bool dblClick = event.mouseClickCount > 1;
// determine cell
Point2I cell(pos.x < 0 ? -1 : pos.x / mCellSize.x, pos.y < 0 ? -1 : pos.y / mCellSize.y);
if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y)
{
Node * node = mNodeList[cell.y];
S32 offset = mTabSize * node->mTab;
if(node->isGroup() && node->mChildren.size() && pos.x >= offset && pos.x <= (offset + mTabSize))
{
node->expand(!node->isExpanded());
build();
dblClick = false;
}
if(pos.x >= offset)
{
if(dblClick)
node->expand(!node->isExpanded());
build();
node->select(true);
}
}
}
//------------------------------------------------------------------------------
void CreatorTree::onMouseDragged(const GuiEvent & event)
{
TORQUE_UNUSED(event);
}
//------------------------------------------------------------------------------
void CreatorTree::onRenderCell(Point2I offset, Point2I cell, bool, bool)
{
Point2I cellOffset = offset;
Node *node = mNodeList[cell.y];
// Get our points
Point2I boxStart( cellOffset.x + mTabSize * node->mTab, cellOffset.y );
boxStart.x += 2;
boxStart.y += 1;
Point2I boxEnd = Point2I( boxStart );
boxEnd.x += 8;
boxEnd.y += 8;
GFXDrawUtil *drawer = GFX->getDrawUtil();
// Start drawing stuff
if( node->isGroup() )
{
// If we need a box...
drawer->drawRectFill( boxStart, boxEnd, mProfile->mFillColor ); // Box background
drawer->drawRect( boxStart, boxEnd, mProfile->mFontColor ); // Border
// Cross line
drawer->drawLine( boxStart.x + 2, boxStart.y + 4, boxStart.x + 7, boxStart.y + 4, mProfile->mFontColor );
if( !node->isExpanded() ) // If it's a [+] draw down line
drawer->drawLine( boxStart.x + 4, boxStart.y + 2, boxStart.x + 4, boxStart.y + 7, mProfile->mFontColor );
}
else
{
// Draw horizontal line
drawer->drawLine( boxStart.x + 4, boxStart.y + 4, boxStart.x + 9, boxStart.y + 4, mProfile->mFontColor );
if( !node->isLast() ) // If it's a continuing one, draw a long down line
drawer->drawLine( boxStart.x + 4, boxStart.y - 6, boxStart.x + 4, boxStart.y + 10, mProfile->mFontColor );
else // Otherwise, just a small one
drawer->drawLine( boxStart.x + 4, boxStart.y - 2, boxStart.x + 4, boxStart.y + 4, mProfile->mFontColor );
}
//draw in all the required continuation lines
Node *parent = node->mParent;
while( !parent->isRoot() )
{
if( !parent->isLast() )
{
drawer->drawLine( cellOffset.x + ( parent->mTab * mTabSize ) + 6,
cellOffset.y - 2,
cellOffset.x + ( parent->mTab * mTabSize ) + 6,
cellOffset.y + 11,
mProfile->mFontColor );
}
parent = parent->mParent;
}
ColorI fontColor = mProfile->mFontColor;
if( node->isSelected() )
fontColor = mProfile->mFontColorHL;
else if( node->isGroup() && node->hasChildItem() )
fontColor.set( 128, 0, 0 );
else if( !node->isGroup() )
fontColor.set( 0, 0, 128 );
drawer->setBitmapModulation(fontColor); //node->isSelected() ? mProfile->mFontColorHL : mProfile->mFontColor);
drawer->drawText( mProfile->mFont,
Point2I( offset.x + mTxtOffset + mTabSize * ( node->mTab + 1 ), offset.y ),
node->mName);
}

View file

@ -0,0 +1,120 @@
//-----------------------------------------------------------------------------
// 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 _CREATOR_H_
#define _CREATOR_H_
#ifndef _GUIARRAYCTRL_H_
#include "gui/core/guiArrayCtrl.h"
#endif
/// Creator tree from old editor. Not used in current editor.
class CreatorTree : public GuiArrayCtrl
{
typedef GuiArrayCtrl Parent;
public:
class Node
{
public:
Node();
~Node();
enum {
Group = BIT(0),
Expanded = BIT(1),
Selected = BIT(2),
Root = BIT(3)
};
BitSet32 mFlags;
S32 mId;
U32 mTab;
Node * mParent;
Vector<Node*> mChildren;
StringTableEntry mName;
StringTableEntry mValue;
void expand(bool exp);
void select(bool sel){mFlags.set(Selected, sel);}
Node * find(S32 id);
//
bool isGroup(){return(mFlags.test(Group));}
bool isExpanded(){return(mFlags.test(Expanded));}
bool isSelected(){return(mFlags.test(Selected));}
bool isRoot(){return(mFlags.test(Root));}
S32 getId(){return(mId);}
bool hasChildItem();
S32 getSelected();
//
bool isFirst();
bool isLast();
};
CreatorTree();
~CreatorTree();
//
S32 mCurId;
Node * mRoot;
Vector<Node*> mNodeList;
//
void buildNode(Node * node, U32 tab);
void build();
//
bool addNode(Node * parent, Node * node);
Node * createNode(const char * name, const char * value, bool group = false, Node * parent = 0);
Node * findNode(S32 id);
S32 getSelected(){return(mRoot->getSelected());}
//
void expandNode(Node * node, bool expand);
void selectNode(Node * node, bool select);
//
void sort();
void clear();
S32 mTabSize;
S32 mMaxWidth;
S32 mTxtOffset;
// GuiControl
void onMouseDown(const GuiEvent & event);
void onMouseDragged(const GuiEvent & event);
void onMouseUp(const GuiEvent & event);
bool onWake();
// GuiArrayCtrl
void onRenderCell(Point2I offset, Point2I cell, bool, bool);
DECLARE_CONOBJECT(CreatorTree);
DECLARE_CATEGORY( "Gui Editor" );
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,218 @@
//-----------------------------------------------------------------------------
// 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 _EDITTSCTRL_H_
#define _EDITTSCTRL_H_
#ifndef _GUITSCONTROL_H_
#include "gui/3d/guiTSControl.h"
#endif
#ifndef _GIZMO_H_
#include "gizmo.h"
#endif
class TerrainBlock;
class MissionArea;
class Gizmo;
class EditManager;
struct ObjectRenderInst;
class SceneRenderState;
class BaseMatInstance;
class EditTSCtrl : public GuiTSCtrl
{
typedef GuiTSCtrl Parent;
protected:
void make3DMouseEvent(Gui3DMouseEvent & gui3Devent, const GuiEvent &event);
// GuiControl
virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent);
virtual void onMouseUp(const GuiEvent & event);
virtual void onMouseDown(const GuiEvent & event);
virtual void onMouseMove(const GuiEvent & event);
virtual void onMouseDragged(const GuiEvent & event);
virtual void onMouseEnter(const GuiEvent & event);
virtual void onMouseLeave(const GuiEvent & event);
virtual void onRightMouseDown(const GuiEvent & event);
virtual void onRightMouseUp(const GuiEvent & event);
virtual void onRightMouseDragged(const GuiEvent & event);
virtual void onMiddleMouseDown(const GuiEvent & event);
virtual void onMiddleMouseUp(const GuiEvent & event);
virtual void onMiddleMouseDragged(const GuiEvent & event);
virtual bool onInputEvent(const InputEventInfo & event);
virtual bool onMouseWheelUp(const GuiEvent &event);
virtual bool onMouseWheelDown(const GuiEvent &event);
virtual void updateGuiInfo() {};
virtual void renderScene(const RectI &){};
void renderMissionArea();
virtual void renderCameraAxis();
virtual void renderGrid();
// GuiTSCtrl
void renderWorld(const RectI & updateRect);
void _renderScene(ObjectRenderInst*, SceneRenderState *state, BaseMatInstance*);
/// Zoom in/out in ortho views by "steps".
void orthoZoom( F32 steps );
protected:
enum DisplayType
{
DisplayTypeTop,
DisplayTypeBottom,
DisplayTypeFront,
DisplayTypeBack,
DisplayTypeLeft,
DisplayTypeRight,
DisplayTypePerspective,
DisplayTypeIsometric,
};
S32 mDisplayType;
F32 mOrthoFOV;
Point3F mOrthoCamTrans;
EulerF mIsoCamRot;
Point3F mIsoCamRotCenter;
F32 mIsoCamAngle;
Point3F mRawCamPos;
Point2I mLastMousePos;
bool mLastMouseClamping;
bool mAllowBorderMove;
S32 mMouseMoveBorder;
F32 mMouseMoveSpeed;
U32 mLastBorderMoveTime;
Gui3DMouseEvent mLastEvent;
bool mLeftMouseDown;
bool mRightMouseDown;
bool mMiddleMouseDown;
bool mMiddleMouseTriggered;
bool mMouseLeft;
SimObjectPtr<Gizmo> mGizmo;
GizmoProfile *mGizmoProfile;
public:
EditTSCtrl();
~EditTSCtrl();
// SimObject
bool onAdd();
void onRemove();
//
bool mRenderMissionArea;
ColorI mMissionAreaFillColor;
ColorI mMissionAreaFrameColor;
F32 mMissionAreaHeightAdjust;
//
ColorI mConsoleFrameColor;
ColorI mConsoleFillColor;
S32 mConsoleSphereLevel;
S32 mConsoleCircleSegments;
S32 mConsoleLineWidth;
static void initPersistFields();
static void consoleInit();
//
bool mConsoleRendering;
bool mRightMousePassThru;
bool mMiddleMousePassThru;
// all editors will share a camera
static Point3F smCamPos;
static MatrixF smCamMatrix;
static bool smCamOrtho;
static F32 smCamNearPlane;
static F32 smCamFOV;
static F32 smVisibleDistanceScale;
static U32 smSceneBoundsMask;
static Point3F smMinSceneBounds;
bool mRenderGridPlane;
ColorI mGridPlaneColor;
F32 mGridPlaneSize;
F32 mGridPlaneSizePixelBias;
S32 mGridPlaneMinorTicks;
ColorI mGridPlaneMinorTickColor;
ColorI mGridPlaneOriginColor;
GFXStateBlockRef mBlendSB;
// GuiTSCtrl
virtual bool getCameraTransform(MatrixF* cameraMatrix);
virtual void computeSceneBounds(Box3F& bounds);
bool processCameraQuery(CameraQuery * query);
// guiControl
virtual void onRender(Point2I offset, const RectI &updateRect);
virtual void on3DMouseUp(const Gui3DMouseEvent &){};
virtual void on3DMouseDown(const Gui3DMouseEvent &){};
virtual void on3DMouseMove(const Gui3DMouseEvent &){};
virtual void on3DMouseDragged(const Gui3DMouseEvent &){};
virtual void on3DMouseEnter(const Gui3DMouseEvent &){};
virtual void on3DMouseLeave(const Gui3DMouseEvent &){};
virtual void on3DRightMouseDown(const Gui3DMouseEvent &){};
virtual void on3DRightMouseUp(const Gui3DMouseEvent &){};
virtual void on3DRightMouseDragged(const Gui3DMouseEvent &){};
virtual void on3DMouseWheelUp(const Gui3DMouseEvent &){};
virtual void on3DMouseWheelDown(const Gui3DMouseEvent &){};
virtual void get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &);
virtual bool isMiddleMouseDown() {return mMiddleMouseDown;}
S32 getDisplayType() const {return mDisplayType;}
virtual void setDisplayType(S32 type);
/// Return true if the current view is an ortho projection along one of the world axes.
bool isOrthoDisplayType() const { return ( mDisplayType != DisplayTypePerspective && mDisplayType != DisplayTypeIsometric ); }
F32 getOrthoFOV() const { return mOrthoFOV; }
void setOrthoFOV( F32 fov ) { mOrthoFOV = fov; }
virtual TerrainBlock* getActiveTerrain();
virtual void calcOrthoCamOffset(F32 mousex, F32 mousey, U8 modifier=0);
Gizmo* getGizmo() { return mGizmo; }
/// Set flags or other Gizmo state appropriate for the current situation.
/// For example derived classes may override this to disable certain
/// axes of modes of manipulation.
virtual void updateGizmo();
DECLARE_CONOBJECT(EditTSCtrl);
DECLARE_CATEGORY( "Gui Editor" );
};
#endif // _EDITTSCTRL_H_

View file

@ -0,0 +1,165 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/editor.h"
#include "console/console.h"
#include "console/consoleInternal.h"
#include "gui/controls/guiTextListCtrl.h"
#include "T3D/shapeBase.h"
#include "T3D/gameBase/gameConnection.h"
#ifndef TORQUE_PLAYER
// See matching #ifdef in app/game.cpp
bool gEditingMission = false;
#endif
//------------------------------------------------------------------------------
// Class EditManager
//------------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(EditManager);
ConsoleDocClass( EditManager,
"@brief For Editor use only, deprecated\n\n"
"@internal"
);
EditManager::EditManager()
{
for(U32 i = 0; i < 10; i++)
mBookmarks[i] = MatrixF(true);
}
EditManager::~EditManager()
{
}
//------------------------------------------------------------------------------
bool EditManager::onWake()
{
if(!Parent::onWake())
return(false);
return(true);
}
void EditManager::onSleep()
{
Parent::onSleep();
}
//------------------------------------------------------------------------------
bool EditManager::onAdd()
{
if(!Parent::onAdd())
return(false);
// hook the namespace
const char * name = getName();
if(name && name[0] && getClassRep())
{
Namespace * parent = getClassRep()->getNameSpace();
Con::linkNamespaces(parent->mName, name);
mNameSpace = Con::lookupNamespace(name);
}
return(true);
}
//------------------------------------------------------------------------------
// NOTE: since EditManager is not actually used as a gui anymore, onWake/Sleep
// were never called, which broke anyone hooking into onEditorEnable/onEditorDisable
// and gEditingMission. So, moved these to happen in response to console methods
// which should be called at the appropriate time.
//
// This is a quick fix and this system is still "begging" for a remake.
void EditManager::editorEnabled()
{
for(SimGroupIterator itr(Sim::getRootGroup()); *itr; ++itr)
(*itr)->onEditorEnable();
gEditingMission = true;
}
void EditManager::editorDisabled()
{
for(SimGroupIterator itr(Sim::getRootGroup()); *itr; ++itr)
{
SimObject *so = *itr;
AssertFatal(so->isProperlyAdded() && !so->isRemoved(), "bad");
so->onEditorDisable();
}
gEditingMission = false;
}
//------------------------------------------------------------------------------
static GameBase * getControlObj()
{
GameConnection * connection = GameConnection::getLocalClientConnection();
ShapeBase* control = 0;
if(connection)
control = dynamic_cast<ShapeBase*>(connection->getControlObject());
return(control);
}
ConsoleMethod( EditManager, setBookmark, void, 3, 3, "(int slot)")
{
S32 val = dAtoi(argv[2]);
if(val < 0 || val > 9)
return;
GameBase * control = getControlObj();
if(control)
object->mBookmarks[val] = control->getTransform();
}
ConsoleMethod( EditManager, gotoBookmark, void, 3, 3, "(int slot)")
{
S32 val = dAtoi(argv[2]);
if(val < 0 || val > 9)
return;
GameBase * control = getControlObj();
if(control)
control->setTransform(object->mBookmarks[val]);
}
ConsoleMethod( EditManager, editorEnabled, void, 2, 2, "Perform the onEditorEnabled callback on all SimObjects and set gEditingMission true" )
{
object->editorEnabled();
}
ConsoleMethod( EditManager, editorDisabled, void, 2, 2, "Perform the onEditorDisabled callback on all SimObjects and set gEditingMission false" )
{
object->editorDisabled();
}
ConsoleMethod( EditManager, isEditorEnabled, bool, 2, 2, "Return the value of gEditingMission." )
{
return gEditingMission;
}

View file

@ -0,0 +1,69 @@
//-----------------------------------------------------------------------------
// 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 _EDITOR_H_
#define _EDITOR_H_
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
class GameBase;
//------------------------------------------------------------------------------
class EditManager : public GuiControl
{
private:
typedef GuiControl Parent;
public:
EditManager();
~EditManager();
bool onWake();
void onSleep();
// SimObject
bool onAdd();
/// Perform the onEditorEnabled callback on all SimObjects
/// and set gEditingMission true.
void editorEnabled();
/// Perform the onEditorDisabled callback on all SimObjects
/// and set gEditingMission false.
void editorDisabled();
MatrixF mBookmarks[10];
DECLARE_CONOBJECT(EditManager);
DECLARE_CATEGORY( "Gui Editor" );
};
extern bool gEditingMission;
//------------------------------------------------------------------------------
#endif

View file

@ -0,0 +1,225 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/editorIconRegistry.h"
#include "console/console.h"
#include "console/simBase.h"
EditorIconRegistry gEditorIcons;
ConsoleDoc(
"@class EditorIconRegistry\n"
"@brief This class is used to find the correct icon file path for different SimObject class types.\n\n"
"It is typically used by the editors, not intended for actual game development\n\n"
"@internal"
);
EditorIconRegistry::EditorIconRegistry()
{
}
EditorIconRegistry::~EditorIconRegistry()
{
clear();
}
void EditorIconRegistry::loadFromPath( const String &path, bool overwrite )
{
AbstractClassRep *classRep = AbstractClassRep::getClassList();
while ( classRep )
{
String iconFile = String::ToString( "%s%s", path.c_str(), classRep->getClassName() );
add( classRep->getClassName(), iconFile.c_str(), overwrite );
classRep = classRep->getNextClass();
}
String defaultIconFile = path + "default";
mDefaultIcon.set( defaultIconFile,
&GFXDefaultPersistentProfile,
avar("%s() - mIcons[] (line %d)",
__FUNCTION__, __LINE__) );
}
void EditorIconRegistry::add( const String &className, const String &imageFile, bool overwrite )
{
// First see if we can load the image.
GFXTexHandle icon( imageFile, &GFXDefaultPersistentProfile,
avar("%s() - mIcons[] (line %d)", __FUNCTION__, __LINE__) );
if ( icon.isNull() )
return;
// Look it up in the map.
StringNoCase key( className );
IconMap::Iterator iter = mIcons.find( key );
if ( iter != mIcons.end() )
{
if ( !overwrite )
return;
iter->value = icon;
}
else
mIcons.insertUnique( key, icon );
}
GFXTexHandle EditorIconRegistry::findIcon( AbstractClassRep *classRep )
{
while ( classRep )
{
StringNoCase key( classRep->getClassName() );
IconMap::Iterator icon = mIcons.find( key );
if ( icon != mIcons.end() && icon->value.isValid() )
return icon->value;
classRep = classRep->getParentClass();
}
return mDefaultIcon;
}
GFXTexHandle EditorIconRegistry::findIcon( const SimObject *object )
{
if( object == NULL )
return mDefaultIcon;
AbstractClassRep *classRep = object->getClassRep();
return findIcon( classRep );
}
GFXTexHandle EditorIconRegistry::findIcon( const char *className )
{
// On the chance we have this className already in the map,
// check there first because its a lot faster...
StringNoCase key( className );
IconMap::Iterator icon = mIcons.find( key );
if ( icon != mIcons.end() && icon->value.isValid() )
return icon->value;
// Well, we could still have an icon for a parent class,
// so find the AbstractClassRep for the className.
//
// Unfortunately the only way to do this is looping through
// the AbstractClassRep linked list.
bool found = false;
AbstractClassRep* pClassRep = AbstractClassRep::getClassList();
while ( pClassRep )
{
if ( key.equal( pClassRep->getClassName(), String::NoCase ) )
{
found = true;
break;
}
pClassRep = pClassRep->getNextClass();
}
if ( !found )
{
Con::errorf( "EditorIconRegistry::findIcon, passed className %s was not an AbstractClassRep!", key.c_str() );
return mDefaultIcon;
}
// Now do a find by AbstractClassRep recursively up the class tree...
return findIcon( pClassRep );
}
bool EditorIconRegistry::hasIconNoRecurse( const SimObject *object )
{
AbstractClassRep *classRep = object->getClassRep();
StringNoCase key( classRep->getClassName() );
IconMap::Iterator icon = mIcons.find( key );
return icon != mIcons.end();
}
void EditorIconRegistry::clear()
{
mIcons.clear();
mDefaultIcon.free();
}
ConsoleStaticMethod( EditorIconRegistry, add, void, 3, 4, "( String className, String imageFile [, bool overwrite = true] )"
"@internal")
{
bool overwrite = true;
if ( argc > 3 )
overwrite = dAtob( argv[3] );
gEditorIcons.add( argv[1], argv[2], overwrite );
}
ConsoleStaticMethod( EditorIconRegistry, loadFromPath, void, 2, 3, "( String imagePath [, bool overwrite = true] )"
"@internal")
{
bool overwrite = true;
if ( argc > 2 )
overwrite = dAtob( argv[2] );
gEditorIcons.loadFromPath( argv[1], overwrite );
}
ConsoleStaticMethod( EditorIconRegistry, clear, void, 1, 1, ""
"@internal")
{
gEditorIcons.clear();
}
ConsoleStaticMethod( EditorIconRegistry, findIconByClassName, const char*, 2, 2, "( String className )\n"
"Returns the file path to the icon file if found."
"@internal")
{
GFXTexHandle icon = gEditorIcons.findIcon( argv[1] );
if ( icon.isNull() )
return NULL;
return icon->mPath;
}
ConsoleStaticMethod( EditorIconRegistry, findIconBySimObject, const char*, 2, 2, "( SimObject )\n"
"Returns the file path to the icon file if found."
"@internal")
{
SimObject *obj = NULL;
if ( !Sim::findObject( argv[1], obj ) )
{
Con::warnf( "EditorIconRegistry::findIcon, parameter %d was not a SimObject!", argv[1] );
return NULL;
}
GFXTexHandle icon = gEditorIcons.findIcon( obj );
if ( icon.isNull() )
return NULL;
return icon->mPath;
}

View file

@ -0,0 +1,82 @@
//-----------------------------------------------------------------------------
// 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 _EDITORICONREGISTRY_H_
#define _EDITORICONREGISTRY_H_
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
class SimObject;
class AbstractClassRep;
/// This class is used to find the correct icon file
/// path for different SimObject class types. It is
/// typically used by the editors.
class EditorIconRegistry
{
public:
EditorIconRegistry();
~EditorIconRegistry();
/// Loops thru all the AbstractClassReps looking for icons in the path.
void loadFromPath( const String &path, bool overwrite );
/// Adds a single icon to the registry.
void add( const String &className, const String &imageFile, bool overwrite );
/// Clears all the icons from the registry.
void clear();
/// Looks up an icon given an AbstractClassRep.
/// Other findIcon methods redirect to this.
GFXTexHandle findIcon( AbstractClassRep *classRep );
/// Looks up an icon given a SimObject.
GFXTexHandle findIcon( const SimObject *object );
/// Looks up an icon given a className.
GFXTexHandle findIcon( const char *className );
/// Returns true if an icon is defined this object's class.
/// Does not recurse up the class hierarchy.
bool hasIconNoRecurse( const SimObject *object );
protected:
typedef HashTable<StringNoCase,GFXTexHandle> IconMap;
IconMap mIcons;
/// The default icon returned when no matching icon is found.
GFXTexHandle mDefaultIcon;
};
/// The global registry of editor icons.
extern EditorIconRegistry gEditorIcons;
#endif // _EDITORICONREGISTRY_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,418 @@
//-----------------------------------------------------------------------------
// 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 _GIZMO_H_
#define _GIZMO_H_
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/core/guiTypes.h"
#endif
#ifndef _MATHUTILS_H_
#include "math/mathUtils.h"
#endif
#ifndef _DYNAMIC_CONSOLETYPES_H_
#include "console/dynamicTypes.h"
#endif
enum GizmoMode
{
NoneMode = 0,
MoveMode, // 1
RotateMode, // 2
ScaleMode, // 3
ModeEnumCount
};
enum GizmoAlignment
{
World = 0,
Object,
AlignEnumCount
};
DefineEnumType( GizmoMode );
DefineEnumType( GizmoAlignment );
//
class GizmoProfile : public SimObject
{
typedef SimObject Parent;
public:
GizmoProfile();
virtual ~GizmoProfile() {}
DECLARE_CONOBJECT( GizmoProfile );
virtual bool onAdd();
static void initPersistFields();
static void consoleInit();
/// Set flags to default values.
void restoreDefaultState();
// Data Fields
GizmoMode mode;
GizmoAlignment alignment;
F32 rotateScalar;
F32 scaleScalar;
U32 screenLen;
ColorI axisColors[3];
ColorI activeColor;
ColorI inActiveColor;
ColorI centroidColor;
ColorI centroidHighlightColor;
Resource<GFont> font;
bool snapToGrid;
F32 scaleSnap;
bool allowSnapScale;
F32 rotationSnap;
bool allowSnapRotations;
bool renderWhenUsed;
bool renderInfoText;
Point3F gridSize;
bool renderPlane;
bool renderPlaneHashes;
ColorI gridColor;
F32 planeDim;
bool renderSolid;
/// Whether to render a transparent grid overlay when using the move gizmo.
bool renderMoveGrid;
enum Flags {
CanRotate = 1 << 0, // 0
CanRotateX = 1 << 1,
CanRotateY = 1 << 2,
CanRotateZ = 1 << 3,
CanRotateScreen = 1 << 4,
CanRotateUniform = 1 << 5,
CanScale = 1 << 6,
CanScaleX = 1 << 7,
CanScaleY = 1 << 8,
CanScaleZ = 1 << 9,
CanScaleUniform = 1 << 10,
CanTranslate = 1 << 11,
CanTranslateX = 1 << 12,
CanTranslateY = 1 << 13,
CanTranslateZ = 1 << 14,
CanTranslateUniform = 1 << 15,
PlanarHandlesOn = 1 << 16
};
S32 flags;
bool hideDisabledAxes;
bool allAxesScaleUniform;
};
// This class contains code for rendering and manipulating a 3D gizmo, it
// is usually used as a helper within a TSEdit-derived control.
//
// The Gizmo has a MatrixF transform and Point3F scale on which it will
// operate by passing it Gui3DMouseEvent(s).
//
// The idea is to set the Gizmo transform/scale to that of another 3D object
// which is being manipulated, pass mouse events into the Gizmo, read the
// new transform/scale out, and set it to onto the object.
// And of course the Gizmo can be rendered.
//
// Gizmo derives from SimObject only because this allows its properties
// to be initialized directly from script via fields.
class Gizmo : public SimObject
{
typedef SimObject Parent;
friend class WorldEditor;
public:
enum Selection {
None = -1,
Axis_X = 0,
Axis_Y = 1,
Axis_Z = 2,
Plane_XY = 3, // Normal = Axis_Z
Plane_XZ = 4, // Normal = Axis_Y
Plane_YZ = 5, // Normal = Axis_X
Centroid = 6,
Custom1 = 7, // screen-aligned rotation
Custom2 = 8
};
Gizmo();
~Gizmo();
DECLARE_CONOBJECT( Gizmo );
// SimObject
bool onAdd();
void onRemove();
static void initPersistFields();
// Mutators
void set( const MatrixF &objMat, const Point3F &worldPos, const Point3F &objScale );
void setProfile( GizmoProfile *profile )
{
AssertFatal( profile != NULL, "NULL passed to Gizmo::setProfile - Gizmo must always have a profile!" );
mProfile = profile;
}
// Accessors
GizmoProfile* getProfile() { return mProfile; }
GizmoMode getMode() const { return mCurrentMode; }
GizmoAlignment getAlignment() const { return mCurrentAlignment; }
/// Returns current object to world transform of the object being manipulated.
const MatrixF& getTransform() const { return mCurrentTransform; }
Point3F getPosition() const { return mCurrentTransform.getPosition(); }
const Point3F& getScale() const { return mScale; }
// Returns change in position in last call to on3DMouseDragged.
const Point3F& getOffset() const { return mDeltaPos; }
// Returns change is position since on3DMouseDown.
const Point3F& getTotalOffset() const { return mDeltaTotalPos; }
const Point3F& getDeltaScale() const { return mDeltaScale; }
const Point3F& getDeltaTotalScale() const { return mDeltaTotalScale; }
const Point3F& getDeltaRot() const { return mDeltaRot; }
const Point3F& getDeltaTotalRot() const { return mDeltaTotalRot; }
/// Set whether to render the grid plane.
void setGridPlaneEnabled( bool value ) { mGridPlaneEnabled = value; }
/// Set whether to a transparent grid overlay when using the move gizmo.
void setMoveGridEnabled( bool value ) { mMoveGridEnabled = value; }
/// Set the size of the move grid along one dimension. The total size of the
/// move grid is @a value * @a value.
void setMoveGridSize( F32 value ) { mMoveGridSize = value; }
/// Set the spacing between grid lines on the move grid.
void setMoveGridSpacing( F32 value ) { mMoveGridSpacing = value; }
// Gizmo Interface methods...
// Set the current highlight mode on the gizmo's centroid handle
void setCentroidHandleHighlight( bool state ) { mHighlightCentroidHandle = state; }
// Must be called before on3DMouseDragged to save state
void on3DMouseDown( const Gui3DMouseEvent &event );
// So Gizmo knows the current mouse button state.
void on3DMouseUp( const Gui3DMouseEvent &event );
// Test Gizmo for collisions and set the Gizmo Selection (the part under the cursor)
void on3DMouseMove( const Gui3DMouseEvent &event );
// Make changes to the Gizmo transform/scale (depending on mode)
void on3DMouseDragged( const Gui3DMouseEvent &event );
// Returns an enum describing the part of the Gizmo that is Selected
// ( under the cursor ). This should be called AFTER calling onMouseMove
// or collideAxisGizmo
//
// -1 None
// 0 Axis_X
// 1 Axis_Y
// 2 Axis_Z
// 3 Plane_XY
// 4 Plane_XZ
// 5 Plane_YZ
Selection getSelection();
void setSelection( Selection sel ) { mSelectionIdx = sel; }
// Returns the object space vector corresponding to a Selection.
Point3F selectionToAxisVector( Selection axis );
// These provide the user an easy way to check if the Gizmo's transform
// or scale have changed by calling markClean prior to calling
// on3DMouseDragged, and calling isDirty after.
bool isDirty() { return mDirty; }
void markClean() { mDirty = false; }
// Renders the 3D Gizmo in the scene, GFX must be setup for proper
// 3D rendering before calling this!
// Calling this will change the GFXStateBlock!
void renderGizmo( const MatrixF &cameraTransform, F32 camerFOV = 1.5f );
// Renders text associated with the Gizmo, GFX must be setup for proper
// 2D rendering before calling this!
// Calling this will change the GFXStateBlock!
void renderText( const RectI &viewPort, const MatrixF &modelView, const MatrixF &projection );
// Returns true if the mouse event collides with any part of the Gizmo
// and sets the Gizmo's current Selection.
// You can call this or on3DMouseMove, they are identical
bool collideAxisGizmo( const Gui3DMouseEvent & event );
protected:
void _calcAxisInfo();
void _setStateBlock();
void _renderPrimaryAxis();
void _renderAxisArrows();
void _renderAxisBoxes();
void _renderAxisCircles();
void _renderAxisText();
void _renderPlane();
Point3F _snapPoint( const Point3F &pnt ) const;
F32 _snapFloat( const F32 &val, const F32 &snap ) const;
GizmoAlignment _filteredAlignment();
void _updateState( bool collideGizmo = true );
void _updateEnabledAxices();
F32 _getProjectionLength( F32 dist ) const
{
if( GFX->isFrustumOrtho() )
return mLastCameraFOV * dist * 0.002f;
else
{
Point3F dir = mOrigin - mCameraPos;
return ( dist * dir.len() ) / mLastWorldToScreenScale.y;
}
}
protected:
GizmoProfile *mProfile;
MatrixF mObjectMat;
MatrixF mObjectMatInv;
MatrixF mTransform;
MatrixF mCurrentTransform;
MatrixF mSavedTransform;
GizmoAlignment mCurrentAlignment;
GizmoMode mCurrentMode;
MatrixF mCameraMat;
Point3F mCameraPos;
Point3F mScale;
Point3F mSavedScale;
Point3F mDeltaScale;
Point3F mDeltaTotalScale;
Point3F mLastScale;
Point3F mScaleInfluence;
EulerF mRot;
EulerF mSavedRot;
EulerF mDeltaRot;
EulerF mDeltaTotalRot;
F32 mDeltaAngle;
F32 mLastAngle;
Point2I mMouseDownPos;
Point3F mMouseDownProjPnt;
Point3F mDeltaPos;
Point3F mDeltaTotalPos;
Point3F mProjPnt;
Point3F mOrigin;
Point3F mProjAxisVector[3];
F32 mProjLen;
S32 mSelectionIdx;
bool mDirty;
Gui3DMouseEvent mLastMouseEvent;
GFXStateBlockRef mStateBlock;
GFXStateBlockRef mSolidStateBlock;
PlaneF mMouseCollidePlane;
MathUtils::Line mMouseCollideLine;
bool mMouseDown;
F32 mSign;
/// If false, don't render the grid plane even if it is enabled in the profile.
bool mGridPlaneEnabled;
/// If false, don't render a transparent grid overlay when using the move gizmo.
bool mMoveGridEnabled;
/// Size of the move grid along one dimension.
F32 mMoveGridSize;
/// Spacing between grid lines on the move grid.
U32 mMoveGridSpacing;
bool mAxisEnabled[3];
bool mUniformHandleEnabled;
bool mScreenRotateHandleEnabled;
// Used to override rendering of handles.
bool mHighlightCentroidHandle;
bool mHighlightAll;
// Initialized in renderGizmo and saved for later use when projecting
// to screen space for selection testing.
MatrixF mLastWorldMat;
MatrixF mLastProjMat;
RectI mLastViewport;
Point2F mLastWorldToScreenScale;
F32 mLastCameraFOV;
// Screenspace cursor collision information used in rotation mode.
Point3F mElipseCursorCollidePntSS;
Point3F mElipseCursorCollideVecSS;
/// A large hard coded distance used to test
/// gizmo axis selection.
static F32 smProjectDistance;
};
#endif // _GIZMO_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,294 @@
//-----------------------------------------------------------------------------
// 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 _GUICONVEXSHAPEEDITORCTRL_H_
#define _GUICONVEXSHAPEEDITORCTRL_H_
#ifndef _EDITTSCTRL_H_
#include "gui/worldEditor/editTSCtrl.h"
#endif
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
#ifndef _GIZMO_H_
#include "gui/worldEditor/gizmo.h"
#endif
#ifndef _CONVEXSHAPE_H_
#include "T3D/convexShape.h"
#endif
class GameBase;
class GuiConvexEditorUndoAction;
class ConvexEditorTool;
class ConvexEditorCreateTool;
class GuiConvexEditorCtrl : public EditTSCtrl
{
typedef EditTSCtrl Parent;
friend class GuiConvexEditorUndoAction;
public:
GuiConvexEditorCtrl();
virtual ~GuiConvexEditorCtrl();
DECLARE_CONOBJECT( GuiConvexEditorCtrl );
// SimObject
virtual bool onAdd();
virtual void onRemove();
static void initPersistFields();
// GuiControl
virtual bool onWake();
virtual void onSleep();
virtual void setVisible(bool value);
// EditTSCtrl
bool onKeyDown( const GuiEvent &event );
bool onKeyUp( const GuiEvent &event );
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 );
void renderScene(const RectI & updateRect);
void updateGizmo();
void updateShape( ConvexShape *shape, S32 offsetFace = -1 );
static void synchClientObject( const ConvexShape *serverConvex );
void updateGizmoPos();
bool setActiveTool( ConvexEditorTool *tool );
void drawFacePlane( ConvexShape *shape, S32 faceId );
void scaleFace( ConvexShape *shape, S32 faceId, Point3F scale );
void translateFace( ConvexShape *shape, S32 faceId, const Point3F &displace );
void updateModifiedFace( ConvexShape *shape, S32 faceId );
bool isShapeValid( ConvexShape *shape );
void setupShape( ConvexShape *shape );
void setPivotPos( ConvexShape *shape, S32 faceId, const Gui3DMouseEvent &event );
void cleanMatrix( MatrixF &mat );
S32 getEdgeByPoints( ConvexShape *shape, S32 faceId, S32 pId0, S32 pId1 );
bool getEdgesTouchingPoint( ConvexShape *shape, S32 faceId, S32 pId, Vector< U32 > &edgeIdxList, S32 excludeEdge = -1 );
void hollowSelection();
void hollowShape( ConvexShape *shape, F32 thickness );
void recenterSelection();
void recenterShape( ConvexShape *shape );
void dropSelectionAtScreenCenter();
void splitSelectedFace();
/// Interface with Tools.
/// @{
MatrixF getCameraMat() const { return mLastCameraQuery.cameraMatrix; }
enum UndoType
{
ModifyShape = 0,
CreateShape,
DeleteShape,
HollowShape
};
void submitUndo( UndoType type, ConvexShape *shape );
void submitUndo( UndoType type, const Vector< ConvexShape* > &shape );
/// @}
bool hasSelection() const;
void clearSelection();
void setSelection( ConvexShape *shape, S32 faceId );
void handleDeselect();
bool handleEscape();
bool handleDelete();
bool handleTab();
public:
StringTableEntry mMaterialName;
protected:
void _prepRenderImage( SceneManager* sceneGraph, const SceneRenderState* sceneState );
void _renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst );
bool _cursorCast( const Gui3DMouseEvent &event, ConvexShape **hitShape, S32 *hitFace );
static bool _cursorCastCallback( RayInfo* ri );
protected:
bool mIsDirty;
U32 mSavedGizmoFlags;
/// The selected ConvexShape.
SimObjectPtr<ConvexShape> mConvexSEL;
/// The highlighted ConvexShape ( mouse over ).
SimObjectPtr<ConvexShape> mConvexHL;
S32 mFaceSEL;
S32 mFaceHL;
MatrixF mFaceSavedXfm;
ConvexShape::Geometry mSavedGeometry;
Vector< MatrixF > mSavedSurfaces;
Vector< MatrixF > mLastValidShape;
Point3F mSavedPivotPos;
bool mCtrlDown;
bool mSavedUndo;
bool mHasGeometry;
bool mDragging;
bool mMouseDown;
bool mHasCopied;
RayInfo mLastRayInfo;
Gui3DMouseEvent mMouseDownEvent;
Point3F mGizmoMatOffset;
Point3F mPivotPos;
bool mUsingPivot;
bool mSettingPivot;
UndoAction *mLastUndo;
UndoManager *mUndoManager;
ConvexEditorTool *mActiveTool;
ConvexEditorCreateTool *mCreateTool;
};
class GuiConvexEditorUndoAction : public UndoAction
{
friend class GuiConvexEditorCtrl;
public:
GuiConvexEditorUndoAction( const UTF8* actionName ) : UndoAction( actionName )
{
}
GuiConvexEditorCtrl *mEditor;
SimObjectId mObjId;
Vector< MatrixF > mSavedSurfaces;
MatrixF mSavedObjToWorld;
Point3F mSavedScale;
virtual void undo();
virtual void redo() { undo(); }
};
class ConvexEditorTool
{
public:
enum EventResult
{
NotHandled = 0,
Handled = 1,
Done = 2,
Failed = 3
};
ConvexEditorTool( GuiConvexEditorCtrl *editor )
: mEditor( editor ), mDone( false ) {}
virtual ~ConvexEditorTool() {}
virtual void onActivated( ConvexEditorTool *prevTool ) {}
virtual void onDeactivated( ConvexEditorTool *newTool ) {}
virtual EventResult onKeyDown( const GuiEvent &event ) { return NotHandled; }
virtual EventResult on3DMouseDown( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual EventResult on3DMouseUp( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual EventResult on3DMouseMove( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual EventResult on3DMouseDragged( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual EventResult on3DMouseEnter( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual EventResult on3DMouseLeave( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual EventResult on3DRightMouseDown( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual EventResult on3DRightMouseUp( const Gui3DMouseEvent &event ) { return NotHandled; }
virtual void renderScene(const RectI & updateRect) {}
virtual void render2D() {}
bool isDone() { return mDone; }
public:
GuiConvexEditorCtrl *mEditor;
protected:
bool mDone;
};
class ConvexEditorCreateTool : public ConvexEditorTool
{
typedef ConvexEditorTool Parent;
public:
ConvexEditorCreateTool( GuiConvexEditorCtrl *editor );
virtual ~ConvexEditorCreateTool() {}
virtual void onActivated( ConvexEditorTool *prevTool );
virtual void onDeactivated( ConvexEditorTool *newTool );
virtual EventResult on3DMouseDown( const Gui3DMouseEvent &event );
virtual EventResult on3DMouseUp( const Gui3DMouseEvent &event );
virtual EventResult on3DMouseMove( const Gui3DMouseEvent &event );
virtual EventResult on3DMouseDragged( const Gui3DMouseEvent &event );
virtual void renderScene(const RectI & updateRect);
ConvexShape* extrudeShapeFromFace( ConvexShape *shape, S32 face );
protected:
S32 mStage;
ConvexShape *mNewConvex;
PlaneF mCreatePlane;
MatrixF mTransform;
Point3F mStart;
Point3F mEnd;
Point3F mPlaneSizes;
};
#endif // _GUICONVEXSHAPEEDITORCTRL_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,231 @@
//-----------------------------------------------------------------------------
// 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 _GUIDECALEDITORCTRL_H_
#define _GUIDECALEDITORCTRL_H_
#ifndef _EDITTSCTRL_H_
#include "gui/worldEditor/editTSCtrl.h"
#endif
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
#ifndef _DECALINSTANCE_H_
#include "T3D/decal/decalInstance.h"
#endif
class GameBase;
class Gizmo;
struct RayInfo;
class DecalInstance;
class DecalData;
class GuiDecalEditorCtrl : public EditTSCtrl
{
typedef EditTSCtrl Parent;
public:
GuiDecalEditorCtrl();
~GuiDecalEditorCtrl();
DECLARE_CONOBJECT(GuiDecalEditorCtrl);
// SimObject
bool onAdd();
static void initPersistFields();
static void consoleInit();
void onEditorDisable();
// GuiControl
virtual bool onWake();
virtual void onSleep();
virtual void onRender(Point2I offset, const RectI &updateRect);
// 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);
void updateGuiInfo();
void renderScene(const RectI & updateRect);
void renderGui(Point2I offset, const RectI &updateRect);
/// Find clicked point on "static collision" objects.
bool getRayInfo( const Gui3DMouseEvent &event, RayInfo *rInfo );
void selectDecal( DecalInstance *inst );
void deleteSelectedDecal();
void deleteDecalDatablock( String lookupName );
void retargetDecalDatablock( String dbFrom, String dbTo );
void setMode( String mode, bool sourceShortcut );
void forceRedraw( DecalInstance * decalInstance );
void setGizmoFocus( DecalInstance * decalInstance );
public:
String mMode;
DecalInstance *mSELDecal;
DecalInstance *mHLDecal;
static bool smRenderDecalPixelSize;
protected:
bool mPerformedDragCopy;
DecalData *mCurrentDecalData;
Vector<Point3F> mSELEdgeVerts;
Vector<Point3F> mHLEdgeVerts;
void _renderDecalEdge( const Vector<Point3F> &verts, const ColorI &color );
};
//Decal Instance Create Undo Actions
class DICreateUndoAction : public UndoAction
{
typedef UndoAction Parent;
public:
GuiDecalEditorCtrl *mEditor;
protected:
/// The captured object state.
DecalInstance mDecalInstance;
S32 mDatablockId;
public:
DECLARE_CONOBJECT( DICreateUndoAction );
static void initPersistFields();
DICreateUndoAction( const UTF8* actionName = "Create Decal " );
virtual ~DICreateUndoAction();
void addDecal( DecalInstance decal );
// UndoAction
virtual void undo();
virtual void redo();
};
//Decal Instance Delete Undo Actions
class DIDeleteUndoAction : public UndoAction
{
typedef UndoAction Parent;
public:
GuiDecalEditorCtrl *mEditor;
protected:
/// The captured object state.
DecalInstance mDecalInstance;
S32 mDatablockId;
public:
DECLARE_CONOBJECT( DIDeleteUndoAction );
static void initPersistFields();
DIDeleteUndoAction( const UTF8* actionName = "Delete Decal" );
virtual ~DIDeleteUndoAction();
///
void deleteDecal( DecalInstance decal );
// UndoAction
virtual void undo();
virtual void redo();
};
//Decal Datablock Delete Undo Actions
class DBDeleteUndoAction : public UndoAction
{
typedef UndoAction Parent;
public:
GuiDecalEditorCtrl *mEditor;
S32 mDatablockId;
protected:
// The captured decalInstance states
Vector<DecalInstance> mDecalInstanceVec;
public:
DECLARE_CONOBJECT( DBDeleteUndoAction );
static void initPersistFields();
DBDeleteUndoAction( const UTF8* actionName = "Delete Decal Datablock" );
virtual ~DBDeleteUndoAction();
void deleteDecal( DecalInstance decal );
// UndoAction
virtual void undo();
virtual void redo();
};
//Decal Datablock Retarget Undo Actions
class DBRetargetUndoAction : public UndoAction
{
typedef UndoAction Parent;
public:
GuiDecalEditorCtrl *mEditor;
S32 mDBFromId;
S32 mDBToId;
protected:
// The captured decalInstance states
Vector<DecalInstance*> mDecalInstanceVec;
public:
DECLARE_CONOBJECT( DBRetargetUndoAction );
static void initPersistFields();
DBRetargetUndoAction( const UTF8* actionName = "Retarget Decal Datablock" );
virtual ~DBRetargetUndoAction();
void retargetDecal( DecalInstance* decal );
// UndoAction
virtual void undo();
virtual void redo();
};
#endif // _GUIDECALEDITORCTRL_H_

View file

@ -0,0 +1,720 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/guiMissionArea.h"
#include "console/consoleTypes.h"
#include "console/engineAPI.h"
#include "gfx/gfxDrawUtil.h"
#include "gfx/primBuilder.h"
#include "gfx/bitmap/gBitmap.h"
#include "gui/3d/guiTSControl.h"
#include "T3D/gameFunctions.h"
#include "terrain/terrData.h"
namespace {
F32 round_local(F32 val)
{
if(val >= 0.f)
{
F32 floor = mFloor(val);
if((val - floor) >= 0.5f)
return(floor + 1.f);
return(floor);
}
else
{
F32 ceil = mCeil(val);
if((val - ceil) <= -0.5f)
return(ceil - 1.f);
return(ceil);
}
}
}
IMPLEMENT_CONOBJECT(GuiMissionAreaCtrl);
ConsoleDocClass( GuiMissionAreaCtrl,
"@brief Visual representation of Mission Area Editor.\n\n"
"@internal"
);
GuiMissionAreaCtrl::GuiMissionAreaCtrl()
{
mHandleBitmap = StringTable->EmptyString();
mHandleTexture = NULL;
mHandleTextureSize = Point2I::Zero;
mHandleTextureHalfSize = Point2F::Zero;
mSquareBitmap = true;
mMissionArea = 0;
mTerrainBlock = 0;
mMissionBoundsColor.set(255,0,0);
mCameraColor.set(255,0,0);
mBlendStateBlock = NULL;
mSolidStateBlock = NULL;
mLastHitMode = Handle_None;
mSavedDrag = false;
}
GuiMissionAreaCtrl::~GuiMissionAreaCtrl()
{
}
//------------------------------------------------------------------------------
void GuiMissionAreaCtrl::initPersistFields()
{
addField( "squareBitmap", TypeBool, Offset(mSquareBitmap, GuiMissionAreaCtrl));
addField( "handleBitmap", TypeFilename, Offset( mHandleBitmap, GuiMissionAreaCtrl ),
"Bitmap file for the mission area handles.\n");
addField( "missionBoundsColor", TypeColorI, Offset(mMissionBoundsColor, GuiMissionAreaCtrl));
addField( "cameraColor", TypeColorI, Offset(mCameraColor, GuiMissionAreaCtrl));
Parent::initPersistFields();
}
//------------------------------------------------------------------------------
bool GuiMissionAreaCtrl::onAdd()
{
if(!Parent::onAdd())
return(false);
GFXStateBlockDesc desc;
desc.setCullMode(GFXCullNone);
desc.setZReadWrite(false);
desc.setBlend(false, GFXBlendOne, GFXBlendZero);
mSolidStateBlock = GFX->createStateBlock( desc );
desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
mBlendStateBlock = GFX->createStateBlock( desc );
if (*mHandleBitmap)
{
mHandleTexture = GFXTexHandle( mHandleBitmap, &GFXDefaultPersistentProfile, avar("%s() - mHandleTexture (line %d)", __FUNCTION__, __LINE__) );
mHandleTextureSize = Point2I( mHandleTexture->getWidth(), mHandleTexture->getHeight() );
mHandleTextureHalfSize = Point2F(mHandleTextureSize.x, mHandleTextureSize.y) * 0.5f;
}
else
{
mHandleTexture = NULL;
mHandleTextureSize = Point2I::Zero;
mHandleTextureHalfSize = Point2F::Zero;
}
return(true);
}
bool GuiMissionAreaCtrl::onWake()
{
if(!Parent::onWake())
return(false);
//mMissionArea = const_cast<MissionArea*>(MissionArea::getServerObject());
//if(!bool(mMissionArea))
// Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no MissionArea object.");
//mTerrainBlock = getTerrainObj();
//if(!bool(mTerrainBlock))
// Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no TerrainBlock object.");
//if ( !bool(mMissionArea) || !bool(mTerrainBlock) )
// return true;
updateTerrainBitmap();
// make sure mission area is clamped
setArea(getArea());
//onUpdate();
setActive(true);
return(true);
}
void GuiMissionAreaCtrl::onSleep()
{
mTextureObject = NULL;
mMissionArea = 0;
mTerrainBlock = 0;
Parent::onSleep();
}
//------------------------------------------------------------------------------
void GuiMissionAreaCtrl::onMouseUp(const GuiEvent & event)
{
if(!bool(mMissionArea))
return;
RectI box;
getScreenMissionArea(box);
S32 hit = getHitHandles(event.mousePoint, box);
// set the current cursor
//updateCursor(hit);
mLastHitMode = hit;
if(mSavedDrag)
{
// Let the script get a chance at it.
Con::executef( this, "onMissionAreaModified" );
}
mSavedDrag = false;
}
void GuiMissionAreaCtrl::onMouseDown(const GuiEvent & event)
{
if(!bool(mMissionArea))
return;
RectI box;
getScreenMissionArea(box);
mLastHitMode = getHitHandles(event.mousePoint, box);
//if(mLastHitMode == Handle_Middle)
// setCursor(GrabCursor);
mLastMousePoint = event.mousePoint;
}
void GuiMissionAreaCtrl::onMouseMove(const GuiEvent & event)
{
if(!bool(mMissionArea))
return;
RectI box;
getScreenMissionArea(box);
S32 hit = getHitHandles(event.mousePoint, box);
// set the current cursor...
//updateCursor(hit);
mLastHitMode = hit;
}
void GuiMissionAreaCtrl::onMouseDragged(const GuiEvent & event)
{
if(!bool(mMissionArea))
return;
if(mLastHitMode == Handle_None)
return;
// If we haven't already saved,
// save an undo action to get back to this state,
// before we make any modifications.
if ( !mSavedDrag )
{
submitUndo( "Modify Node" );
mSavedDrag = true;
}
RectI box;
getScreenMissionArea(box);
Point2I mouseDiff(event.mousePoint.x - mLastMousePoint.x,
event.mousePoint.y - mLastMousePoint.y);
// what we drag'n?
RectI area = getArea();
Point2I wp = screenDeltaToWorldDelta(mouseDiff);
if (mLastHitMode == Handle_Middle)
{
area.point += wp;
}
if (mLastHitMode & Handle_Left)
{
if ((area.extent.x - wp.x) >= 1)
{
area.point.x += wp.x;
area.extent.x -= wp.x;
}
}
if (mLastHitMode & Handle_Right)
{
if ((area.extent.x + wp.x) >= 1)
{
area.extent.x += wp.x;
}
}
if (mLastHitMode & Handle_Bottom)
{
if ((area.extent.y - wp.y) >= 1)
{
area.point.y += wp.y;
area.extent.y -= wp.y;
}
}
if (mLastHitMode & Handle_Top)
{
if ((area.extent.y + wp.y) >= 1)
{
area.extent.y += wp.y;
}
}
setArea(area);
mLastMousePoint = event.mousePoint;
}
void GuiMissionAreaCtrl::onMouseEnter(const GuiEvent &)
{
mLastHitMode = Handle_None;
//setCursor(DefaultCursor);
}
void GuiMissionAreaCtrl::onMouseLeave(const GuiEvent &)
{
mLastHitMode = Handle_None;
//setCursor(DefaultCursor);
}
//------------------------------------------------------------------------------
void GuiMissionAreaCtrl::submitUndo( const UTF8 *name )
{
// Grab the mission editor undo manager.
UndoManager *undoMan = NULL;
if ( !Sim::findObject( "EUndoManager", undoMan ) )
{
Con::errorf( "GuiRiverEditorCtrl::submitUndo() - EUndoManager not found!" );
return;
}
// Setup the action.
GuiMissionAreaUndoAction *action = new GuiMissionAreaUndoAction( name );
action->mMissionAreaEditor = this;
action->mObjId = mMissionArea->getId();
action->mArea = mMissionArea->getArea();
undoMan->addAction( action );
}
//------------------------------------------------------------------------------
void GuiMissionAreaCtrl::updateTerrain()
{
mTerrainBlock = getTerrainObj();
updateTerrainBitmap();
}
TerrainBlock * GuiMissionAreaCtrl::getTerrainObj()
{
SimSet * scopeAlwaysSet = Sim::getGhostAlwaysSet();
for(SimSet::iterator itr = scopeAlwaysSet->begin(); itr != scopeAlwaysSet->end(); itr++)
{
TerrainBlock * terrain = dynamic_cast<TerrainBlock*>(*itr);
if(terrain)
return(terrain);
}
return(0);
}
void GuiMissionAreaCtrl::updateTerrainBitmap()
{
GBitmap * bitmap = createTerrainBitmap();
if( bitmap )
setBitmapHandle( GFXTexHandle( bitmap, &GFXDefaultGUIProfile, true, String("Terrain Bitmap Update") ) );
else
setBitmap( "" );
}
GBitmap * GuiMissionAreaCtrl::createTerrainBitmap()
{
if(!mTerrainBlock)
return NULL;
GBitmap * bitmap = new GBitmap(mTerrainBlock->getBlockSize(), mTerrainBlock->getBlockSize(), false, GFXFormatR8G8B8 );
if(!bitmap)
return NULL;
// get the min/max
F32 min, max;
mTerrainBlock->getMinMaxHeight(&min, &max);
F32 diff = max - min;
F32 colRange = 255.0f / diff;
// This method allocates it's bitmap above, and does all assignment
// in the following loop. It is not subject to 24-bit -> 32-bit conversion
// problems, because the texture handle creation is where the conversion would
// occur, if it occurs. Since the data in the texture is never read back, and
// the bitmap is deleted after texture-upload, this is not a problem.
for(S32 y = 0; y < mTerrainBlock->getBlockSize() ; y++)
{
for(S32 x = 0; x < mTerrainBlock->getBlockSize(); x++)
{
F32 height;
height = mTerrainBlock->getHeight(Point2I(x, y));
U8 col = U8((height - min) * colRange);
ColorI color(col, col, col);
bitmap->setColor(x, y, color);
}
}
return(bitmap);
}
//------------------------------------------------------------------------------
void GuiMissionAreaCtrl::setMissionArea( MissionArea* area )
{
mMissionArea = area;
if( mMissionArea )
{
setArea(getArea());
}
}
const RectI & GuiMissionAreaCtrl::getArea()
{
if( !bool(mMissionArea) )
return(MissionArea::smMissionArea);
return(mMissionArea->getArea());
}
void GuiMissionAreaCtrl::setArea(const RectI & area)
{
if( bool(mMissionArea) )
{
mMissionArea->setArea(area);
mMissionArea->inspectPostApply();
//onUpdate();
}
}
//------------------------------------------------------------------------------
void GuiMissionAreaCtrl::drawHandle(const Point2F & pos)
{
Point2F pnt(pos.x-mHandleTextureHalfSize.x, pos.y-mHandleTextureHalfSize.y);
GFX->getDrawUtil()->drawBitmap(mHandleTexture, pnt);
}
void GuiMissionAreaCtrl::drawHandles(RectI & box)
{
F32 fillOffset = GFX->getFillConventionOffset();
F32 lx = box.point.x + fillOffset, rx = box.point.x + box.extent.x + fillOffset;
F32 cx = (lx + rx) * 0.5f;
F32 by = box.point.y + fillOffset, ty = box.point.y + box.extent.y + fillOffset;
F32 cy = (ty + by) * 0.5f;
GFX->getDrawUtil()->clearBitmapModulation();
drawHandle(Point2F(lx, ty));
drawHandle(Point2F(lx, cy));
drawHandle(Point2F(lx, by));
drawHandle(Point2F(rx, ty));
drawHandle(Point2F(rx, cy));
drawHandle(Point2F(rx, by));
drawHandle(Point2F(cx, ty));
drawHandle(Point2F(cx, by));
}
bool GuiMissionAreaCtrl::testWithinHandle(const Point2I & testPoint, S32 handleX, S32 handleY)
{
S32 dx = testPoint.x - handleX;
S32 dy = testPoint.y - handleY;
return dx <= Handle_Pixel_Size && dx >= -Handle_Pixel_Size && dy <= Handle_Pixel_Size && dy >= -Handle_Pixel_Size;
}
S32 GuiMissionAreaCtrl::getHitHandles(const Point2I & mousePnt, const RectI & box)
{
S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1;
S32 cx = (lx + rx) >> 1;
S32 by = box.point.y, ty = box.point.y + box.extent.y - 1;
S32 cy = (ty + by) >> 1;
if (testWithinHandle(mousePnt, lx, ty))
return Handle_Left | Handle_Top;
if (testWithinHandle(mousePnt, cx, ty))
return Handle_Top;
if (testWithinHandle(mousePnt, rx, ty))
return Handle_Right | Handle_Top;
if (testWithinHandle(mousePnt, lx, by))
return Handle_Left | Handle_Bottom;
if (testWithinHandle(mousePnt, cx, by))
return Handle_Bottom;
if (testWithinHandle(mousePnt, rx, by))
return Handle_Right | Handle_Bottom;
if (testWithinHandle(mousePnt, lx, cy))
return Handle_Left;
if (testWithinHandle(mousePnt, rx, cy))
return Handle_Right;
if(mousePnt.x >= lx && mousePnt.x <= rx &&
mousePnt.y >= ty && mousePnt.y <= by)
return(Handle_Middle);
return Handle_None;
}
//------------------------------------------------------------------------------
Point2F GuiMissionAreaCtrl::worldToScreen(const Point2F & pos)
{
return(Point2F(mCenterPos.x + (pos.x * mScale.x), mCenterPos.y + (pos.y * mScale.y)));
}
Point2I GuiMissionAreaCtrl::worldToScreen(const Point2I &pos)
{
return(Point2I(S32(mCenterPos.x + (pos.x * mScale.x)), S32(mCenterPos.y + (pos.y * mScale.y))));
}
Point2F GuiMissionAreaCtrl::screenToWorld(const Point2F & pos)
{
return(Point2F((pos.x - mCenterPos.x) / mScale.x, (pos.y - mCenterPos.y) / mScale.y));
}
Point2I GuiMissionAreaCtrl::screenToWorld(const Point2I &pos)
{
return(Point2I(S32((pos.x - mCenterPos.x) / mScale.x), S32((pos.y - mCenterPos.y) / mScale.y)));
}
Point2I GuiMissionAreaCtrl::screenDeltaToWorldDelta(const Point2I &screenPoint)
{
return(Point2I(S32(screenPoint.x / mScale.x), S32(screenPoint.y / mScale.y)));
}
void GuiMissionAreaCtrl::setupScreenTransform(const Point2I & offset)
{
const MatrixF & terrMat = mTerrainBlock->getTransform();
Point3F terrPos;
terrMat.getColumn(3, &terrPos);
terrPos.z = 0;
F32 terrDim = mTerrainBlock->getWorldBlockSize();
const Point2I& extenti = getExtent( );
Point2F extent( static_cast<F32>( extenti.x ), static_cast<F32>( extenti.y ) );
if(mSquareBitmap)
extent.x > extent.y ? extent.x = extent.y : extent.y = extent.x;
// We need to negate the y-axis so we are correctly oriented with
// positive y increase up the screen.
mScale.set(extent.x / terrDim, -extent.y / terrDim, 0);
Point3F terrOffset = -terrPos;
terrOffset.convolve(mScale);
// We need to add the y extent so we start from the bottom left of the control
// rather than the top left.
mCenterPos.set(terrOffset.x + F32(offset.x), terrOffset.y + F32(offset.y) + extent.y);
}
void GuiMissionAreaCtrl::getScreenMissionArea(RectI & rect)
{
RectI area = mMissionArea->getArea();
Point2F pos = worldToScreen(Point2F(F32(area.point.x), F32(area.point.y)));
Point2F end = worldToScreen(Point2F(F32(area.point.x + area.extent.x), F32(area.point.y + area.extent.y)));
//
rect.point.x = S32(round_local(pos.x));
rect.point.y = S32(round_local(pos.y));
rect.extent.x = S32(round_local(end.x - pos.x));
rect.extent.y = S32(round_local(end.y - pos.y));
}
void GuiMissionAreaCtrl::getScreenMissionArea(RectF & rect)
{
RectI area = mMissionArea->getArea();
Point2F pos = worldToScreen(Point2F(F32(area.point.x), F32(area.point.y)));
Point2F end = worldToScreen(Point2F(F32(area.point.x + area.extent.x), F32(area.point.y + area.extent.y)));
//
rect.point.x = pos.x;
rect.point.y = pos.y;
rect.extent.x = end.x - pos.x;
rect.extent.y = end.y - pos.y;
}
Point2I GuiMissionAreaCtrl::convertOrigin(const Point2I &pos)
{
// Convert screen point to our bottom left origin
Point2I pnt = globalToLocalCoord(pos);
const Point2I& extent = getExtent( );
pnt.y = extent.y - pnt.y;
Point2I pt = localToGlobalCoord(pnt);
return pt;
}
void GuiMissionAreaCtrl::onRender(Point2I offset, const RectI & updateRect)
{
RectI rect(offset, getExtent());
F32 fillOffset = GFX->getFillConventionOffset();
setUpdate();
// draw an x
if(!bool(mMissionArea) || !bool(mTerrainBlock))
{
GFX->setStateBlock(mSolidStateBlock);
PrimBuild::color3i( 0, 0, 0 );
PrimBuild::begin( GFXLineList, 4 );
PrimBuild::vertex2f( rect.point.x + fillOffset, updateRect.point.y + fillOffset );
PrimBuild::vertex2f( rect.point.x + updateRect.extent.x + fillOffset, updateRect.point.y + updateRect.extent.y + fillOffset );
PrimBuild::vertex2f( rect.point.x + fillOffset, updateRect.point.y + updateRect.extent.y + fillOffset );
PrimBuild::vertex2f( rect.point.x + updateRect.extent.x + fillOffset, updateRect.point.y + fillOffset );
PrimBuild::end();
return;
}
//
setupScreenTransform(offset);
// draw the terrain
if(mSquareBitmap)
rect.extent.x > rect.extent.y ? rect.extent.x = rect.extent.y : rect.extent.y = rect.extent.x;
GFXDrawUtil *drawer = GFX->getDrawUtil();
drawer->clearBitmapModulation();
drawer->drawBitmapStretch(mTextureObject, rect, GFXBitmapFlip_Y, GFXTextureFilterLinear, false);
GFX->setStateBlock(mSolidStateBlock);
drawer->clearBitmapModulation();
// draw the reference axis
PrimBuild::begin( GFXLineList, 4 );
PrimBuild::color3i( 255, 0, 0 );
PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset );
PrimBuild::vertex2f( rect.point.x + 25 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset );
PrimBuild::color3i( 0, 255, 0 );
PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset );
PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 25 + fillOffset );
PrimBuild::end();
RectF area;
getScreenMissionArea(area);
// render the mission area box
PrimBuild::color( mMissionBoundsColor );
PrimBuild::begin( GFXLineStrip, 5 );
PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + fillOffset);
PrimBuild::vertex2f(area.point.x + area.extent.x + fillOffset, area.point.y + fillOffset);
PrimBuild::vertex2f(area.point.x + area.extent.x + fillOffset, area.point.y + area.extent.y + fillOffset);
PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + area.extent.y + fillOffset);
PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + fillOffset);
PrimBuild::end();
// render the camera
//if(mRenderCamera)
{
CameraQuery camera;
GameProcessCameraQuery(&camera);
// farplane too far, 90' looks wrong...
camera.fov = mDegToRad(60.f);
camera.farPlane = 500.f;
//
F32 rot = camera.fov / 2;
//
VectorF ray;
VectorF projRayA, projRayB;
ray.set(camera.farPlane * -mSin(rot), camera.farPlane * mCos(rot), 0);
camera.cameraMatrix.mulV(ray, &projRayA);
ray.set(camera.farPlane * -mSin(-rot), camera.farPlane * mCos(-rot), 0);
camera.cameraMatrix.mulV(ray, &projRayB);
Point3F camPos;
camera.cameraMatrix.getColumn(3, &camPos);
Point2F s = worldToScreen(Point2F(camPos.x, camPos.y));
Point2F e1 = worldToScreen(Point2F(camPos.x + projRayA.x, camPos.y + projRayA.y));
Point2F e2 = worldToScreen(Point2F(camPos.x + projRayB.x, camPos.y + projRayB.y));
PrimBuild::color( mCameraColor );
PrimBuild::begin( GFXLineList, 4 );
PrimBuild::vertex2f( s.x + fillOffset, s.y + fillOffset );
PrimBuild::vertex2f( e1.x + fillOffset, e1.y + fillOffset );
PrimBuild::vertex2f( s.x + fillOffset, s.y + fillOffset );
PrimBuild::vertex2f( e2.x + fillOffset, e2.y + fillOffset );
PrimBuild::end();
}
// render the handles
RectI iArea;
getScreenMissionArea(iArea);
drawHandles(iArea);
renderChildControls(offset, updateRect);
}
//------------------------------------------------------------------------------
DefineEngineMethod( GuiMissionAreaCtrl, setMissionArea, void, ( MissionArea* area ),,
"@brief Set the MissionArea to edit.\n\n")
{
object->setMissionArea( area );
}
DefineEngineMethod( GuiMissionAreaCtrl, updateTerrain, void, ( ),,
"@brief Update the terrain bitmap.\n\n")
{
object->updateTerrain();
}
//------------------------------------------------------------------------------
void GuiMissionAreaUndoAction::undo()
{
MissionArea *ma = NULL;
if ( !Sim::findObject( mObjId, ma ) )
return;
// Temporarily save the MissionArea's current data.
RectI area = ma->getArea();
// Restore the MissionArea properties saved in the UndoAction
ma->setArea( mArea );
ma->inspectPostApply();
// Now save the previous Mission data in this UndoAction
// since an undo action must become a redo action and vice-versa
mArea = area;
// Let the script get a chance at it.
Con::executef( mMissionAreaEditor, "onUndo" );
}

View file

@ -0,0 +1,158 @@
//-----------------------------------------------------------------------------
// 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 _GUIMISSIONAREA_H_
#define _GUIMISSIONAREA_H_
#ifndef _GUIBITMAPCTRL_H_
#include "gui/controls/guiBitmapCtrl.h"
#endif
#ifndef _GUITYPES_H_
#include "gui/guiTypes.h"
#endif
#ifndef _MISSIONAREA_H_
#include "T3D/missionArea.h"
#endif
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
class GBitmap;
class TerrainBlock;
class GuiMissionAreaCtrl : public GuiBitmapCtrl
{
typedef GuiBitmapCtrl Parent;
protected:
enum HandleInfo
{
// Handle in use
Handle_None = 0,
Handle_Left = BIT(0),
Handle_Right = BIT(1),
Handle_Top = BIT(2),
Handle_Bottom = BIT(3),
Handle_Middle = BIT(4), // Used to drag the whole area
Handle_Pixel_Size = 3,
};
SimObjectPtr<MissionArea> mMissionArea;
SimObjectPtr<TerrainBlock> mTerrainBlock;
GFXStateBlockRef mBlendStateBlock;
GFXStateBlockRef mSolidStateBlock;
StringTableEntry mHandleBitmap;
GFXTexHandle mHandleTexture;
Point2I mHandleTextureSize;
Point2F mHandleTextureHalfSize;
ColorI mMissionBoundsColor;
ColorI mCameraColor;
bool mSquareBitmap;
VectorF mScale;
Point2F mCenterPos;
S32 mLastHitMode;
Point2I mLastMousePoint;
bool mSavedDrag;
void submitUndo( const UTF8 *name = "Action" );
TerrainBlock * getTerrainObj();
GBitmap * createTerrainBitmap();
void updateTerrainBitmap();
//void onUpdate();
void setupScreenTransform(const Point2I & offset);
Point2F worldToScreen(const Point2F &);
Point2F screenToWorld(const Point2F &);
Point2I worldToScreen(const Point2I &);
Point2I screenToWorld(const Point2I &);
Point2I screenDeltaToWorldDelta(const Point2I &screenPoint);
void getScreenMissionArea(RectI & rect);
void getScreenMissionArea(RectF & rect);
Point2I convertOrigin(const Point2I &);
void drawHandle(const Point2F & pos);
void drawHandles(RectI & box);
bool testWithinHandle(const Point2I & testPoint, S32 handleX, S32 handleY);
S32 getHitHandles(const Point2I & mousePnt, const RectI & box);
public:
GuiMissionAreaCtrl();
virtual ~GuiMissionAreaCtrl();
DECLARE_CONOBJECT(GuiMissionAreaCtrl);
// SimObject
bool onAdd();
static void initPersistFields();
// GuiControl
void onRender(Point2I offset, const RectI &updateRect);
bool onWake();
void onSleep();
virtual void onMouseUp(const GuiEvent & event);
virtual void onMouseDown(const GuiEvent & event);
virtual void onMouseMove(const GuiEvent & event);
virtual void onMouseDragged(const GuiEvent & event);
virtual void onMouseEnter(const GuiEvent & event);
virtual void onMouseLeave(const GuiEvent & event);
void setMissionArea( MissionArea* area );
void updateTerrain();
const RectI & getArea();
void setArea(const RectI & area);
};
class GuiMissionAreaUndoAction : public UndoAction
{
public:
GuiMissionAreaUndoAction( const UTF8* actionName ) : UndoAction( actionName )
{
}
GuiMissionAreaCtrl *mMissionAreaEditor;
SimObjectId mObjId;
RectI mArea;
virtual void undo();
virtual void redo() { undo(); }
};
#endif // _GUIMISSIONAREA_H_

View file

@ -0,0 +1,116 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/guiMissionAreaEditor.h"
#include "gui/core/guiCanvas.h"
IMPLEMENT_CONOBJECT(GuiMissionAreaEditorCtrl);
ConsoleDocClass( GuiMissionAreaEditorCtrl,
"@brief Specialized GUI used for editing the MissionArea in a level\n\n"
"Editor use only.\n\n"
"@internal"
);
GuiMissionAreaEditorCtrl::GuiMissionAreaEditorCtrl()
{
}
GuiMissionAreaEditorCtrl::~GuiMissionAreaEditorCtrl()
{
}
bool GuiMissionAreaEditorCtrl::onAdd()
{
if( !Parent::onAdd() )
return false;
return true;
}
void GuiMissionAreaEditorCtrl::initPersistFields()
{
Parent::initPersistFields();
}
void GuiMissionAreaEditorCtrl::get3DCursor( GuiCursor *&cursor,
bool &visible,
const Gui3DMouseEvent &event_ )
{
//cursor = mAddNodeCursor;
//visible = false;
cursor = NULL;
visible = false;
GuiCanvas *root = getRoot();
if ( !root )
return;
S32 currCursor = PlatformCursorController::curArrow;
if ( root->mCursorChanged == currCursor )
return;
PlatformWindow *window = root->getPlatformWindow();
PlatformCursorController *controller = window->getCursorController();
// We've already changed the cursor,
// so set it back before we change it again.
if( root->mCursorChanged != -1)
controller->popCursor();
// Now change the cursor shape
controller->pushCursor(currCursor);
root->mCursorChanged = currCursor;
}
void GuiMissionAreaEditorCtrl::setSelectedMissionArea( MissionArea *missionArea )
{
mSelMissionArea = missionArea;
if ( mSelMissionArea != NULL )
Con::executef( this, "onMissionAreaSelected", missionArea->getIdString() );
else
Con::executef( this, "onMissionAreaSelected" );
}
ConsoleMethod( GuiMissionAreaEditorCtrl, setSelectedMissionArea, void, 2, 3, "" )
{
if ( argc == 2 )
object->setSelectedMissionArea(NULL);
else
{
MissionArea *missionArea = NULL;
if ( Sim::findObject( argv[2], missionArea ) )
object->setSelectedMissionArea(missionArea);
}
}
ConsoleMethod( GuiMissionAreaEditorCtrl, getSelectedMissionArea, const char*, 2, 2, "" )
{
MissionArea *missionArea = object->getSelectedMissionArea();
if ( !missionArea )
return NULL;
return missionArea->getIdString();
}

View file

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// 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 _GUIMISSIONAREAEDITORCTRL_H_
#define _GUIMISSIONAREAEDITORCTRL_H_
#ifndef _EDITTSCTRL_H_
#include "gui/worldEditor/editTSCtrl.h"
#endif
#ifndef _MISSIONAREA_H_
#include "T3D/missionArea.h"
#endif
class GuiMissionAreaEditorCtrl : public EditTSCtrl
{
typedef EditTSCtrl Parent;
protected:
SimObjectPtr<MissionArea> mSelMissionArea;
public:
GuiMissionAreaEditorCtrl();
virtual ~GuiMissionAreaEditorCtrl();
DECLARE_CONOBJECT(GuiMissionAreaEditorCtrl);
// SimObject
bool onAdd();
static void initPersistFields();
// EditTSCtrl
void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ );
void setSelectedMissionArea( MissionArea *missionArea );
MissionArea* getSelectedMissionArea() { return mSelMissionArea; };
};
#endif // _GUIMISSIONAREAEDITORCTRL_H_

View file

@ -0,0 +1,366 @@
//-----------------------------------------------------------------------------
// 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 "console/console.h"
#include "console/consoleTypes.h"
#include "terrain/terrData.h"
#include "gui/worldEditor/guiTerrPreviewCtrl.h"
#include "gfx/primBuilder.h"
#include "T3D/gameFunctions.h"
IMPLEMENT_CONOBJECT(GuiTerrPreviewCtrl);
ConsoleDocClass( GuiTerrPreviewCtrl,
"@brief Very old GUI used for terrain preview\n\n"
"Deprecated\n\n"
"@internal"
);
GuiTerrPreviewCtrl::GuiTerrPreviewCtrl(void) : mTerrainEditor(NULL), mTerrainSize(2048.0f)
{
mRoot.set( 0, 0 );
mOrigin.set( 0, 0 );
mWorldScreenCenter.set( mTerrainSize*0.5f, mTerrainSize*0.5f );
mControlsStateBlock = NULL;
mTerrainBitmapStateBlock = NULL;
}
bool GuiTerrPreviewCtrl::onAdd()
{
if(Parent::onAdd() == false)
{
return false;
}
SimObject* inTerrEditor = Sim::findObject("ETerrainEditor");
if(!inTerrEditor)
{
Con::errorf(ConsoleLogEntry::General, "TerrainEditor::onAdd: failed to load Terrain Editor");
return false;
}
mTerrainEditor = dynamic_cast<TerrainEditor*>(inTerrEditor);
GFXStateBlockDesc desc;
desc.setBlend(false, GFXBlendOne, GFXBlendZero);
desc.samplersDefined = true;
desc.samplers[0].addressModeU = GFXAddressWrap;
desc.samplers[0].addressModeV = GFXAddressWrap;
desc.samplers[0].textureColorOp = GFXTOPSelectARG1;
desc.samplers[0].colorArg1 = GFXTATexture;
desc.setCullMode(GFXCullNone);
desc.setZReadWrite(false);
mTerrainBitmapStateBlock = GFX->createStateBlock(desc);
desc.samplers[0].textureColorOp = GFXTOPDisable;
mControlsStateBlock = GFX->createStateBlock(desc);
return true;
}
void GuiTerrPreviewCtrl::initPersistFields()
{
Parent::initPersistFields();
}
ConsoleMethod( GuiTerrPreviewCtrl, reset, void, 2, 2, "Reset the view of the terrain.")
{
object->reset();
}
ConsoleMethod( GuiTerrPreviewCtrl, setRoot, void, 2, 2, "Add the origin to the root and reset the origin.")
{
object->setRoot();
}
ConsoleMethod( GuiTerrPreviewCtrl, getRoot, const char *, 2, 2, "Return a Point2F representing the position of the root.")
{
Point2F p = object->getRoot();
static char rootbuf[32];
dSprintf(rootbuf,sizeof(rootbuf),"%g %g", p.x, -p.y);
return rootbuf;
}
ConsoleMethod( GuiTerrPreviewCtrl, setOrigin, void, 4, 4, "(float x, float y)"
"Set the origin of the view.")
{
object->setOrigin( Point2F( dAtof(argv[2]), -dAtof(argv[3]) ) );
}
ConsoleMethod( GuiTerrPreviewCtrl, getOrigin, const char*, 2, 2, "Return a Point2F containing the position of the origin.")
{
Point2F p = object->getOrigin();
static char originbuf[32];
dSprintf(originbuf,sizeof(originbuf),"%g %g", p.x, -p.y);
return originbuf;
}
ConsoleMethod( GuiTerrPreviewCtrl, getValue, const char*, 2, 2, "Returns a 4-tuple containing: root_x root_y origin_x origin_y")
{
Point2F r = object->getRoot();
Point2F o = object->getOrigin();
static char valuebuf[64];
dSprintf(valuebuf,sizeof(valuebuf),"%g %g %g %g", r.x, -r.y, o.x, -o.y);
return valuebuf;
}
ConsoleMethod( GuiTerrPreviewCtrl, setValue, void, 3, 3, "Accepts a 4-tuple in the same form as getValue returns.\n\n"
"@see GuiTerrPreviewCtrl::getValue()")
{
Point2F r,o;
dSscanf(argv[2],"%g %g %g %g", &r.x, &r.y, &o.x, &o.y);
r.y = -r.y;
o.y = -o.y;
object->reset();
object->setRoot(r);
object->setOrigin(o);
}
bool GuiTerrPreviewCtrl::onWake()
{
if (! Parent::onWake())
return false;
return true;
}
void GuiTerrPreviewCtrl::onSleep()
{
Parent::onSleep();
}
void GuiTerrPreviewCtrl::setBitmap(const GFXTexHandle &handle)
{
mTextureHandle = handle;
}
void GuiTerrPreviewCtrl::reset()
{
mRoot.set(0,0);
mOrigin.set(0,0);
}
void GuiTerrPreviewCtrl::setRoot()
{
mRoot += mOrigin;
mOrigin.set(0,0);
}
void GuiTerrPreviewCtrl::setRoot(const Point2F &p)
{
mRoot = p;
}
void GuiTerrPreviewCtrl::setOrigin(const Point2F &p)
{
mOrigin = p;
}
Point2F& GuiTerrPreviewCtrl::wrap(const Point2F &p)
{
static Point2F result;
result = p;
while (result.x < 0.0f)
result.x += mTerrainSize;
while (result.x > mTerrainSize)
result.x -= mTerrainSize;
while (result.y < 0.0f)
result.y += mTerrainSize;
while (result.y > mTerrainSize)
result.y -= mTerrainSize;
return result;
}
Point2F& GuiTerrPreviewCtrl::worldToTexture(const Point2F &p)
{
static Point2F result;
result = wrap( p + mRoot ) / mTerrainSize;
return result;
}
Point2F& GuiTerrPreviewCtrl::worldToCtrl(const Point2F &p)
{
static Point2F result;
result = wrap( p - mCamera - mWorldScreenCenter );
result *= getWidth() / mTerrainSize;
return result;
}
void GuiTerrPreviewCtrl::onPreRender()
{
setUpdate();
}
void GuiTerrPreviewCtrl::onRender(Point2I offset, const RectI &updateRect)
{
CameraQuery query;
GameProcessCameraQuery(&query);
Point3F cameraRot;
TerrainBlock *terrBlock = NULL;
MatrixF matrix = query.cameraMatrix;
matrix.getColumn(3,&cameraRot); // get Camera translation
mCamera.set(cameraRot.x, -cameraRot.y);
matrix.getRow(1,&cameraRot); // get camera rotation
if (mTerrainEditor != NULL)
terrBlock = mTerrainEditor->getActiveTerrain();
if (!terrBlock)
return;
for(U32 i = 0; i < GFX->getNumSamplers(); i++)
GFX->setTexture(i, NULL);
GFX->disableShaders();
Point2F terrPos(terrBlock->getPosition().x, terrBlock->getPosition().y);
mTerrainSize = terrBlock->getWorldBlockSize();
//----------------------------------------- RENDER the Terrain Bitmap
if (mTextureHandle)
{
GFXTextureObject *texture = (GFXTextureObject*)mTextureHandle;
if (texture)
{
//GFX->setLightingEnable(false);
GFX->setStateBlock(mTerrainBitmapStateBlock);
GFX->setTexture(0, texture);
Point2F screenP1(offset.x - 0.5f, offset.y + 0.5f);
Point2F screenP2(offset.x + getWidth() - 0.5f, offset.y + getWidth() + 0.5f);
Point2F textureP1( worldToTexture( mCamera - terrPos ) - Point2F(0.5f, 0.5f));
Point2F textureP2(textureP1 + Point2F(1.0f, 1.0f));
// the texture if flipped horz to reflect how the terrain is really drawn
PrimBuild::color3f(1.0f, 1.0f, 1.0f);
PrimBuild::begin(GFXTriangleFan, 4);
PrimBuild::texCoord2f(textureP1.x, textureP2.y);
PrimBuild::vertex2f(screenP1.x, screenP2.y); // left bottom
PrimBuild::texCoord2f(textureP2.x, textureP2.y);
PrimBuild::vertex2f(screenP2.x, screenP2.y); // right bottom
PrimBuild::texCoord2f(textureP2.x, textureP1.y);
PrimBuild::vertex2f(screenP2.x, screenP1.y); // right top
PrimBuild::texCoord2f(textureP1.x, textureP1.y);
PrimBuild::vertex2f(screenP1.x, screenP1.y); // left top
PrimBuild::end();
}
}
//Draw blank texture
else
{
RectI rect(offset.x, offset.y, getWidth(), getHeight());
GFX->getDrawUtil()->drawRect(rect, ColorI(0,0,0));
}
GFX->setStateBlock(mControlsStateBlock);
//----------------------------------------- RENDER the '+' at the center of the Block
PrimBuild::color4f(1.0f, 1.0f, 1.0f, 1.0f);
Point2F center( worldToCtrl(terrPos + Point2F(mTerrainSize * 0.5f, mTerrainSize * 0.5f)) );
S32 y;
for (y=-1; y<=1; y++)
{
F32 yoffset = offset.y + y*256.0f;
for (S32 x=-1; x<=1; x++)
{
F32 xoffset = offset.x + x*256.0f;
PrimBuild::begin(GFXLineList, 4);
PrimBuild::vertex2f(xoffset + center.x, yoffset + center.y-5);
PrimBuild::vertex2f(xoffset + center.x, yoffset + center.y+6);
PrimBuild::vertex2f(xoffset + center.x-5, yoffset + center.y);
PrimBuild::vertex2f(xoffset + center.x+6, yoffset + center.y);
PrimBuild::end();
}
}
//----------------------------------------- RENDER the Block Corners
Point2F cornerf( worldToCtrl(terrPos) + Point2F(0.125f, 0.125f));
Point2I corner=Point2I((S32)cornerf.x,(S32)cornerf.y);
for (y=-1; y<=1; y++)
{
S32 yoffset = offset.y + y*256;
for (S32 x=-1; x<=1; x++)
{
S32 xoffset = offset.x + x*256;
PrimBuild::begin(GFXLineStrip, 3);
PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f);
PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y-128);
PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.7f);
PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y);
PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f);
PrimBuild::vertex2i(xoffset + corner.x+128, yoffset + corner.y);
PrimBuild::end();
PrimBuild::begin(GFXLineStrip, 3);
PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f);
PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y+128);
PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.7f);
PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y);
PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f);
PrimBuild::vertex2i(xoffset + corner.x-128, yoffset + corner.y);
PrimBuild::end();
}
}
//----------------------------------------- RENDER the Viewcone
Point2F pointA(cameraRot.x * -40, cameraRot.y * -40);
Point2F pointB(-pointA.y, pointA.x);
F32 tann = mTan(0.5f);
Point2F point1( pointA + pointB * tann );
Point2F point2( pointA - pointB * tann );
center.set((F32)(offset.x + getWidth() / 2), (F32)(offset.y + getHeight() / 2 ));
PrimBuild::begin(GFXLineStrip, 3);
PrimBuild::color4f(1.0f, 0.0f, 0.0f, 0.7f);
PrimBuild::vertex2i((S32)(center.x + point1.x), (S32)(center.y + point1.y));
PrimBuild::color4f(1.0f, 0.0f, 0.0f, 1.0f);
PrimBuild::vertex2i((S32)center.x,(S32)center.y);
PrimBuild::color4f(1.0f, 0.0f, 0.0f, 0.7f);
PrimBuild::vertex2i((S32)(center.x + point2.x), (S32)(center.y + point2.y));
PrimBuild::end();
renderChildControls(offset, updateRect);
}

View file

@ -0,0 +1,88 @@
//-----------------------------------------------------------------------------
// 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 _GUITERRPREVIEWCTRL_H_
#define _GUITERRPREVIEWCTRL_H_
#ifndef _GUICONTROL_H_
#include "gui/core/guiControl.h"
#endif
#ifndef _GUITSCONTROL_H_
#include "gui/3d/guiTSControl.h"
#endif
#ifndef _GFX_GFXDRAWER_H_
#include "gfx/gfxDrawUtil.h"
#endif
#ifndef _TERRAINEDITOR_H_
#include "gui/worldEditor/terrainEditor.h"
#endif
class GuiTerrPreviewCtrl : public GuiControl
{
private:
typedef GuiControl Parent;
GFXTexHandle mTextureHandle;
GFXStateBlockRef mTerrainBitmapStateBlock;
GFXStateBlockRef mControlsStateBlock;
Point2F mRoot;
Point2F mOrigin;
Point2F mWorldScreenCenter;
Point2F mCamera;
F32 mTerrainSize;
TerrainEditor* mTerrainEditor;
Point2F& wrap(const Point2F &p);
Point2F& worldToTexture(const Point2F &p);
Point2F& worldToCtrl(const Point2F &p);
public:
//creation methods
DECLARE_CONOBJECT(GuiTerrPreviewCtrl);
DECLARE_CATEGORY( "Gui Editor" );
GuiTerrPreviewCtrl();
static void initPersistFields();
//Parental methods
bool onWake();
void onSleep();
bool onAdd();
void setBitmap(const GFXTexHandle&);
void reset();
void setRoot();
void setRoot(const Point2F &root);
void setOrigin(const Point2F &origin);
const Point2F& getRoot() { return mRoot; }
const Point2F& getOrigin() { return mOrigin; }
//void setValue(const Point2F *center, const Point2F *camera);
//const char *getScriptValue();
void onPreRender();
void onRender(Point2I offset, const RectI &updateRect);
};
#endif

View file

@ -0,0 +1,25 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/tSelection.h"

View file

@ -0,0 +1,91 @@
//-----------------------------------------------------------------------------
// 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 _TSELECTION_H_
#define _TSELECTION_H_
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
template <class T>
class Selection : public Vector<T>
{
public:
// Use explicit specialization to define these for your type.
MatrixF getOrientation() { return MatrixF(); }
Point3F getOrigin() { return Point3F(); }
Point3F getScale() { return Point3F(); }
void offset( const Point3F &delta );
void rotate( const EulerF &delta );
void scale( const Point3F &delta );
protected:
// Use explicit specialization to define these for your type.
virtual void offsetObject( T &object, const Point3F &delta ) {}
virtual void rotateObject( T &object, const EulerF &delta, const Point3F &origin ) {}
virtual void scaleObject( T &object, const Point3F &delta ) {}
protected:
//Point3F mCentroid;
//Point3F mBoxCentroid;
//Box3F mBoxBounds;
//bool mCentroidValid;
};
template<class T> inline void Selection<T>::offset( const Point3F &delta )
{
typename Selection<T>::iterator itr = this->begin();
for ( ; itr != this->end(); itr++ )
offsetObject( *itr, delta );
}
template<class T> inline void Selection<T>::rotate( const EulerF &delta )
{
typename Selection<T>::iterator itr = this->begin();
Point3F origin = getOrigin();
for ( ; itr != this->end(); itr++ )
rotateObject( *itr, delta, origin );
}
template<class T> inline void Selection<T>::scale( const Point3F &delta )
{
// Can only scale a single selection.
if ( this->size() != 1 )
return;
scaleObject( this->mArray[0], delta );
}
#endif // _TSELECTION_H_

View file

@ -0,0 +1,795 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/terrainActions.h"
#include "gui/core/guiCanvas.h"
//------------------------------------------------------------------------------
void SelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type)
{
if(sel == mTerrainEditor->getCurrentSel())
return;
if(type == Process)
return;
if(selChanged)
{
if(event.modifier & SI_MULTISELECT)
{
for(U32 i = 0; i < sel->size(); i++)
mTerrainEditor->getCurrentSel()->remove((*sel)[i]);
}
else
{
for(U32 i = 0; i < sel->size(); i++)
{
GridInfo gInfo;
if(mTerrainEditor->getCurrentSel()->getInfo((*sel)[i].mGridPoint.gridPos, gInfo))
{
if(!gInfo.mPrimarySelect)
gInfo.mPrimarySelect = (*sel)[i].mPrimarySelect;
if(gInfo.mWeight < (*sel)[i].mWeight)
gInfo.mWeight = (*sel)[i].mWeight;
mTerrainEditor->getCurrentSel()->setInfo(gInfo);
}
else
mTerrainEditor->getCurrentSel()->add((*sel)[i]);
}
}
}
}
void DeselectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type)
{
if(sel == mTerrainEditor->getCurrentSel())
return;
if(type == Process)
return;
if(selChanged)
{
for(U32 i = 0; i < sel->size(); i++)
mTerrainEditor->getCurrentSel()->remove((*sel)[i]);
}
}
//------------------------------------------------------------------------------
void SoftSelectAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
{
TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain();
if ( !terrBlock )
return;
// allow process of current selection
Selection tmpSel;
if(sel == mTerrainEditor->getCurrentSel())
{
tmpSel = *sel;
sel = &tmpSel;
}
if(type == Begin || type == Process)
mFilter.set(1, &mTerrainEditor->mSoftSelectFilter);
//
if(selChanged)
{
F32 radius = mTerrainEditor->mSoftSelectRadius;
if(radius == 0.f)
return;
S32 squareSize = terrBlock->getSquareSize();
U32 offset = U32(radius / F32(squareSize)) + 1;
for(U32 i = 0; i < sel->size(); i++)
{
GridInfo & info = (*sel)[i];
info.mPrimarySelect = true;
info.mWeight = mFilter.getValue(0);
if(!mTerrainEditor->getCurrentSel()->add(info))
mTerrainEditor->getCurrentSel()->setInfo(info);
Point2F infoPos((F32)info.mGridPoint.gridPos.x, (F32)info.mGridPoint.gridPos.y);
//
for(S32 x = info.mGridPoint.gridPos.x - offset; x < info.mGridPoint.gridPos.x + (offset << 1); x++)
for(S32 y = info.mGridPoint.gridPos.y - offset; y < info.mGridPoint.gridPos.y + (offset << 1); y++)
{
//
Point2F pos((F32)x, (F32)y);
F32 dist = Point2F(pos - infoPos).len() * F32(squareSize);
if(dist > radius)
continue;
F32 weight = mFilter.getValue(dist / radius);
//
GridInfo gInfo;
GridPoint gridPoint = info.mGridPoint;
gridPoint.gridPos.set(x, y);
if(mTerrainEditor->getCurrentSel()->getInfo(Point2I(x, y), gInfo))
{
if(gInfo.mPrimarySelect)
continue;
if(gInfo.mWeight < weight)
{
gInfo.mWeight = weight;
mTerrainEditor->getCurrentSel()->setInfo(gInfo);
}
}
else
{
Vector<GridInfo> gInfos;
mTerrainEditor->getGridInfos(gridPoint, gInfos);
for (U32 z = 0; z < gInfos.size(); z++)
{
gInfos[z].mWeight = weight;
gInfos[z].mPrimarySelect = false;
mTerrainEditor->getCurrentSel()->add(gInfos[z]);
}
}
}
}
}
}
//------------------------------------------------------------------------------
void OutlineSelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type)
{
TORQUE_UNUSED(sel); TORQUE_UNUSED(event); TORQUE_UNUSED(type);
switch(type)
{
case Begin:
if(event.modifier & SI_SHIFT)
break;
mTerrainEditor->getCurrentSel()->reset();
break;
case End:
case Update:
default:
return;
}
mLastEvent = event;
}
//------------------------------------------------------------------------------
void PaintMaterialAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
S32 mat = mTerrainEditor->getPaintMaterialIndex();
if ( !selChanged || mat < 0 )
return;
const bool slopeLimit = mTerrainEditor->mSlopeMinAngle > 0.0f || mTerrainEditor->mSlopeMaxAngle < 90.0f;
const F32 minSlope = mSin( mDegToRad( 90.0f - mTerrainEditor->mSlopeMinAngle ) );
const F32 maxSlope = mSin( mDegToRad( 90.0f - mTerrainEditor->mSlopeMaxAngle ) );
const TerrainBlock *terrain = mTerrainEditor->getActiveTerrain();
const F32 squareSize = terrain->getSquareSize();
Point2F p;
Point3F norm;
for( U32 i = 0; i < sel->size(); i++ )
{
GridInfo &inf = (*sel)[i];
if ( slopeLimit )
{
p.x = inf.mGridPoint.gridPos.x * squareSize;
p.y = inf.mGridPoint.gridPos.y * squareSize;
if ( !terrain->getNormal( p, &norm, true ) )
continue;
if ( norm.z > minSlope ||
norm.z < maxSlope )
continue;
}
// If grid is already set to our material, or it is an
// empty grid spot, then skip painting.
if ( inf.mMaterial == mat || inf.mMaterial == U8_MAX )
continue;
if ( mRandF() > mTerrainEditor->getBrushPressure() )
continue;
inf.mMaterialChanged = true;
mTerrainEditor->getUndoSel()->add(inf);
// Painting is really simple now... set the one mat index.
inf.mMaterial = mat;
mTerrainEditor->setGridInfo(inf, true);
}
mTerrainEditor->scheduleMaterialUpdate();
}
//------------------------------------------------------------------------------
void ClearMaterialsAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if(selChanged)
{
for(U32 i = 0; i < sel->size(); i++)
{
GridInfo &inf = (*sel)[i];
mTerrainEditor->getUndoSel()->add(inf);
inf.mMaterialChanged = true;
// Reset to the first texture layer.
inf.mMaterial = 0;
mTerrainEditor->setGridInfo(inf);
}
mTerrainEditor->scheduleMaterialUpdate();
}
}
//------------------------------------------------------------------------------
void RaiseHeightAction::process( Selection *sel, const Gui3DMouseEvent &evt, bool selChanged, Type type )
{
// ok the raise height action is our "dirt pour" action
// only works on brushes...
Brush *brush = dynamic_cast<Brush*>(sel);
if ( !brush )
return;
if ( type == End )
return;
Point2I brushPos = brush->getPosition();
Point2I brushSize = brush->getSize();
GridPoint brushGridPoint = brush->getGridPoint();
Vector<GridInfo> cur; // the height at the brush position
mTerrainEditor->getGridInfos(brushGridPoint, cur);
if ( cur.size() == 0 )
return;
// we get 30 process actions per second (at least)
F32 heightAdjust = mTerrainEditor->mAdjustHeightVal / 30;
// nothing can get higher than the current brush pos adjusted height
F32 maxHeight = cur[0].mHeight + heightAdjust;
for ( U32 i = 0; i < sel->size(); i++ )
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
if ( (*sel)[i].mHeight < maxHeight )
{
(*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
if ( (*sel)[i].mHeight > maxHeight )
(*sel)[i].mHeight = maxHeight;
}
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
//------------------------------------------------------------------------------
void LowerHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
{
// ok the lower height action is our "dirt dig" action
// only works on brushes...
Brush *brush = dynamic_cast<Brush *>(sel);
if(!brush)
return;
if ( type == End )
return;
Point2I brushPos = brush->getPosition();
Point2I brushSize = brush->getSize();
GridPoint brushGridPoint = brush->getGridPoint();
Vector<GridInfo> cur; // the height at the brush position
mTerrainEditor->getGridInfos(brushGridPoint, cur);
if (cur.size() == 0)
return;
// we get 30 process actions per second (at least)
F32 heightAdjust = -mTerrainEditor->mAdjustHeightVal / 30;
// nothing can get higher than the current brush pos adjusted height
F32 maxHeight = cur[0].mHeight + heightAdjust;
if(maxHeight < 0)
maxHeight = 0;
for(U32 i = 0; i < sel->size(); i++)
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
if((*sel)[i].mHeight > maxHeight)
{
(*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight;
if((*sel)[i].mHeight < maxHeight)
(*sel)[i].mHeight = maxHeight;
}
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
//------------------------------------------------------------------------------
void SetHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if(selChanged)
{
for(U32 i = 0; i < sel->size(); i++)
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
(*sel)[i].mHeight = mTerrainEditor->mSetHeightVal;
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
}
//------------------------------------------------------------------------------
void SetEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if ( !selChanged )
return;
mTerrainEditor->setMissionDirty();
for ( U32 i = 0; i < sel->size(); i++ )
{
GridInfo &inf = (*sel)[i];
// Skip already empty blocks.
if ( inf.mMaterial == U8_MAX )
continue;
// The change flag needs to be set on the undo
// so that it knows to restore materials.
inf.mMaterialChanged = true;
mTerrainEditor->getUndoSel()->add( inf );
// Set the material to empty.
inf.mMaterial = -1;
mTerrainEditor->setGridInfo( inf );
}
mTerrainEditor->scheduleGridUpdate();
}
//------------------------------------------------------------------------------
void ClearEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if ( !selChanged )
return;
mTerrainEditor->setMissionDirty();
for ( U32 i = 0; i < sel->size(); i++ )
{
GridInfo &inf = (*sel)[i];
// Skip if not empty.
if ( inf.mMaterial != U8_MAX )
continue;
// The change flag needs to be set on the undo
// so that it knows to restore materials.
inf.mMaterialChanged = true;
mTerrainEditor->getUndoSel()->add( inf );
// Set the material
inf.mMaterial = 0;
mTerrainEditor->setGridInfo( inf );
}
mTerrainEditor->scheduleGridUpdate();
}
//------------------------------------------------------------------------------
void ScaleHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if(selChanged)
{
for(U32 i = 0; i < sel->size(); i++)
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
(*sel)[i].mHeight *= mTerrainEditor->mScaleVal;
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
}
void BrushAdjustHeightAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type)
{
if(type == Process)
return;
TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain();
if ( !terrBlock )
return;
if(type == Begin)
{
mTerrainEditor->lockSelection(true);
mTerrainEditor->getRoot()->mouseLock(mTerrainEditor);
// the way this works is:
// construct a plane that goes through the collision point
// with one axis up the terrain Z, and horizontally parallel to the
// plane of projection
// the cross of the camera ffdv and the terrain up vector produces
// the cross plane vector.
// all subsequent mouse actions are collided against the plane and the deltaZ
// from the previous position is used to delta the selection up and down.
Point3F cameraDir;
EditTSCtrl::smCamMatrix.getColumn(1, &cameraDir);
terrBlock->getTransform().getColumn(2, &mTerrainUpVector);
// ok, get the cross vector for the plane:
Point3F planeCross;
mCross(cameraDir, mTerrainUpVector, &planeCross);
planeCross.normalize();
Point3F planeNormal;
Point3F intersectPoint;
mTerrainEditor->collide(event, intersectPoint);
mCross(mTerrainUpVector, planeCross, &planeNormal);
mIntersectionPlane.set(intersectPoint, planeNormal);
// ok, we have the intersection point...
// project the collision point onto the up vector of the terrain
mPreviousZ = mDot(mTerrainUpVector, intersectPoint);
// add to undo
// and record the starting heights
for(U32 i = 0; i < sel->size(); i++)
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
(*sel)[i].mStartHeight = (*sel)[i].mHeight;
}
}
else if(type == Update)
{
// ok, collide the ray from the event with the intersection plane:
Point3F intersectPoint;
Point3F start = event.pos;
Point3F end = start + event.vec * 1000;
F32 t = mIntersectionPlane.intersect(start, end);
m_point3F_interpolate( start, end, t, intersectPoint);
F32 currentZ = mDot(mTerrainUpVector, intersectPoint);
F32 diff = currentZ - mPreviousZ;
for(U32 i = 0; i < sel->size(); i++)
{
(*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight;
// clamp it
if((*sel)[i].mHeight < 0.f)
(*sel)[i].mHeight = 0.f;
if((*sel)[i].mHeight > 2047.f)
(*sel)[i].mHeight = 2047.f;
mTerrainEditor->setGridInfoHeight((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
else if(type == End)
{
mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor);
}
}
//------------------------------------------------------------------------------
AdjustHeightAction::AdjustHeightAction(TerrainEditor * editor) :
BrushAdjustHeightAction(editor)
{
mCursor = 0;
}
void AdjustHeightAction::process(Selection *sel, const Gui3DMouseEvent & event, bool b, Type type)
{
Selection * curSel = mTerrainEditor->getCurrentSel();
BrushAdjustHeightAction::process(curSel, event, b, type);
}
//------------------------------------------------------------------------------
// flatten the primary selection then blend in the rest...
void FlattenHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if(!sel->size())
return;
if(selChanged)
{
F32 average = 0.f;
// get the average height
U32 cPrimary = 0;
for(U32 k = 0; k < sel->size(); k++)
if((*sel)[k].mPrimarySelect)
{
cPrimary++;
average += (*sel)[k].mHeight;
}
average /= cPrimary;
// set it
for(U32 i = 0; i < sel->size(); i++)
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
//
if((*sel)[i].mPrimarySelect)
(*sel)[i].mHeight = average;
else
{
F32 h = average - (*sel)[i].mHeight;
(*sel)[i].mHeight += (h * (*sel)[i].mWeight);
}
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
}
//------------------------------------------------------------------------------
void SmoothHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if(!sel->size())
return;
if(selChanged)
{
F32 avgHeight = 0.f;
for(U32 k = 0; k < sel->size(); k++)
{
mTerrainEditor->getUndoSel()->add((*sel)[k]);
avgHeight += (*sel)[k].mHeight;
}
avgHeight /= sel->size();
// clamp the terrain smooth factor...
if(mTerrainEditor->mSmoothFactor < 0.f)
mTerrainEditor->mSmoothFactor = 0.f;
if(mTerrainEditor->mSmoothFactor > 1.f)
mTerrainEditor->mSmoothFactor = 1.f;
// linear
for(U32 i = 0; i < sel->size(); i++)
{
(*sel)[i].mHeight += (avgHeight - (*sel)[i].mHeight) * mTerrainEditor->mSmoothFactor * (*sel)[i].mWeight;
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
}
void PaintNoiseAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type)
{
// If this is the ending
// mouse down event, then
// update the noise values.
if ( type == Begin )
{
mNoise.setSeed( Sim::getCurrentTime() );
mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f );
mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize );
mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y);
}
if( selChanged )
{
for( U32 i = 0; i < sel->size(); i++ )
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
const F32 noiseVal = mNoiseData[ ( gridPos.x % mNoiseSize ) +
( ( gridPos.y % mNoiseSize ) * mNoiseSize ) ];
(*sel)[i].mHeight += (noiseVal - mMinMaxNoise.y * mScale) * (*sel)[i].mWeight * mTerrainEditor->mNoiseFactor;
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->scheduleGridUpdate();
}
}
/*
void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type)
{
if( selChanged )
{
TerrainBlock *tblock = mTerrainEditor->getActiveTerrain();
if ( !tblock )
return;
F32 height = 0;
F32 maxHeight = 0;
U32 shift = getBinLog2( TerrainBlock::BlockSize );
for ( U32 x = 0; x < TerrainBlock::BlockSize; x++ )
{
for ( U32 y = 0; y < TerrainBlock::BlockSize; y++ )
{
height = fixedToFloat( tblock->getHeight( x, y ) );
mTerrainHeights[ x + (y << 8)] = height;
if ( height > maxHeight )
maxHeight = height;
}
}
//mNoise.erodeThermal( &mTerrainHeights, &mNoiseData, 30.0f, 5.0f, 5, TerrainBlock::BlockSize, tblock->getSquareSize(), maxHeight );
mNoise.erodeHydraulic( &mTerrainHeights, &mNoiseData, 1, TerrainBlock::BlockSize );
F32 heightDiff = 0;
for( U32 i = 0; i < sel->size(); i++ )
{
mTerrainEditor->getUndoSel()->add((*sel)[i]);
const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos;
// Need to get the height difference
// between the current height and the
// erosion height to properly apply the
// softness and pressure settings of the brush
// for this selection.
heightDiff = (*sel)[i].mHeight - mNoiseData[ gridPos.x + (gridPos.y << shift)];
(*sel)[i].mHeight -= (heightDiff * (*sel)[i].mWeight);
mTerrainEditor->setGridInfo((*sel)[i]);
}
mTerrainEditor->gridUpdateComplete();
}
}
*/
IMPLEMENT_CONOBJECT( TerrainSmoothAction );
ConsoleDocClass( TerrainSmoothAction,
"@brief Terrain action used for leveling varying terrain heights smoothly.\n\n"
"Editor use only.\n\n"
"@internal"
);
TerrainSmoothAction::TerrainSmoothAction()
: UndoAction( "Terrain Smoothing" )
{
}
void TerrainSmoothAction::initPersistFields()
{
Parent::initPersistFields();
}
void TerrainSmoothAction::smooth( TerrainBlock *terrain, F32 factor, U32 steps )
{
AssertFatal( terrain, "TerrainSmoothAction::smooth() - Got null object!" );
// Store our input parameters.
mTerrainId = terrain->getId();
mSteps = steps;
mFactor = factor;
// The redo can do the rest.
redo();
}
ConsoleMethod( TerrainSmoothAction, smooth, void, 5, 5, "( TerrainBlock obj, F32 factor, U32 steps )")
{
TerrainBlock *terrain = NULL;
if ( Sim::findObject( argv[2], terrain ) && terrain )
object->smooth( terrain, dAtof( argv[3] ), mClamp( dAtoi( argv[4] ), 1, 13 ) );
}
void TerrainSmoothAction::undo()
{
// First find the terrain from the id.
TerrainBlock *terrain;
if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
return;
// Get the terrain file.
TerrainFile *terrFile = terrain->getFile();
// Copy our stored heightmap to the file.
terrFile->setHeightMap( mUnsmoothedHeights, false );
// Tell the terrain to update itself.
terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
}
void TerrainSmoothAction::redo()
{
// First find the terrain from the id.
TerrainBlock *terrain;
if ( !Sim::findObject( mTerrainId, terrain ) || !terrain )
return;
// Get the terrain file.
TerrainFile *terrFile = terrain->getFile();
// First copy the heightmap state.
mUnsmoothedHeights = terrFile->getHeightMap();
// Do the smooth.
terrFile->smooth( mFactor, mSteps, false );
// Tell the terrain to update itself.
terrain->updateGrid( Point2I::Zero, Point2I::Max, true );
}

View file

@ -0,0 +1,351 @@
//-----------------------------------------------------------------------------
// 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 _TERRAINACTIONS_H_
#define _TERRAINACTIONS_H_
#ifndef _TERRAINEDITOR_H_
#include "gui/worldEditor/terrainEditor.h"
#endif
#ifndef _GUIFILTERCTRL_H_
#include "gui/editor/guiFilterCtrl.h"
#endif
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
#ifndef _NOISE2D_H_
#include "util/noise2d.h"
#endif
class TerrainAction
{
protected:
TerrainEditor * mTerrainEditor;
public:
virtual ~TerrainAction(){};
TerrainAction(TerrainEditor * editor) : mTerrainEditor(editor){}
virtual StringTableEntry getName() = 0;
enum Type {
Begin = 0,
Update,
End,
Process
};
//
virtual void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type) = 0;
virtual bool useMouseBrush() { return(true); }
};
//------------------------------------------------------------------------------
class SelectAction : public TerrainAction
{
public:
SelectAction(TerrainEditor * editor) : TerrainAction(editor){};
StringTableEntry getName(){return("select");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
class DeselectAction : public TerrainAction
{
public:
DeselectAction(TerrainEditor * editor) : TerrainAction(editor){};
StringTableEntry getName(){return("deselect");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
class ClearAction : public TerrainAction
{
public:
ClearAction(TerrainEditor * editor) : TerrainAction(editor){};
StringTableEntry getName(){return("clear");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type) {};
bool useMouseBrush() { mTerrainEditor->getCurrentSel()->reset(); return true; }
};
class SoftSelectAction : public TerrainAction
{
public:
SoftSelectAction(TerrainEditor * editor) : TerrainAction(editor){};
StringTableEntry getName(){return("softSelect");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
Filter mFilter;
};
//------------------------------------------------------------------------------
class OutlineSelectAction : public TerrainAction
{
public:
OutlineSelectAction(TerrainEditor * editor) : TerrainAction(editor){};
StringTableEntry getName(){return("outlineSelect");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
bool useMouseBrush() { return(false); }
private:
Gui3DMouseEvent mLastEvent;
};
//------------------------------------------------------------------------------
class PaintMaterialAction : public TerrainAction
{
public:
PaintMaterialAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("paintMaterial");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class ClearMaterialsAction : public TerrainAction
{
public:
ClearMaterialsAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("clearMaterials");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class RaiseHeightAction : public TerrainAction
{
public:
RaiseHeightAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("raiseHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class LowerHeightAction : public TerrainAction
{
public:
LowerHeightAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("lowerHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class SetHeightAction : public TerrainAction
{
public:
SetHeightAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("setHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class SetEmptyAction : public TerrainAction
{
public:
SetEmptyAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("setEmpty");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class ClearEmptyAction : public TerrainAction
{
public:
ClearEmptyAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("clearEmpty");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class ScaleHeightAction : public TerrainAction
{
public:
ScaleHeightAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("scaleHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
//------------------------------------------------------------------------------
class BrushAdjustHeightAction : public TerrainAction
{
public:
BrushAdjustHeightAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("brushAdjustHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
private:
PlaneF mIntersectionPlane;
Point3F mTerrainUpVector;
F32 mPreviousZ;
};
class AdjustHeightAction : public BrushAdjustHeightAction
{
public:
AdjustHeightAction(TerrainEditor * editor);
StringTableEntry getName(){return("adjustHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
bool useMouseBrush() { return(false); }
private:
//
Point3F mHitPos;
Point3F mLastPos;
SimObjectPtr<GuiCursor> mCursor;
};
//------------------------------------------------------------------------------
class FlattenHeightAction : public TerrainAction
{
public:
FlattenHeightAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("flattenHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
class SmoothHeightAction : public TerrainAction
{
public:
SmoothHeightAction(TerrainEditor * editor) : TerrainAction(editor){}
StringTableEntry getName(){return("smoothHeight");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
};
class PaintNoiseAction : public TerrainAction
{
public:
PaintNoiseAction( TerrainEditor *editor )
: TerrainAction( editor ),
mNoiseSize( 256 )
{
mNoise.setSeed( 5342219 );
mNoiseData.setSize( mNoiseSize * mNoiseSize );
mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f );
//Vector<F32> scratch = mNoiseData;
//mNoise.rigidMultiFractal( &mNoiseData, &scratch, TerrainBlock::BlockSize, 12, 1.0f, 5.0f );
mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize );
mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y);
}
StringTableEntry getName() { return "paintNoise"; }
void process( Selection *sel, const Gui3DMouseEvent &event, bool selChanged, Type type );
protected:
const U32 mNoiseSize;
Noise2D mNoise;
Vector<F32> mNoiseData;
Point2F mMinMaxNoise;
F32 mScale;
};
/*
class ThermalErosionAction : public TerrainAction
{
public:
ThermalErosionAction(TerrainEditor * editor)
: TerrainAction(editor)
{
mNoise.setSeed( 1 );//Sim::getCurrentTime() );
mNoiseData.setSize( TerrainBlock::BlockSize * TerrainBlock::BlockSize );
mTerrainHeights.setSize( TerrainBlock::BlockSize * TerrainBlock::BlockSize );
}
StringTableEntry getName(){return("thermalErode");}
void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type);
Noise2D mNoise;
Vector<F32> mNoiseData;
Vector<F32> mTerrainHeights;
};
*/
/// An undo action used to perform terrain wide smoothing.
class TerrainSmoothAction : public UndoAction
{
typedef UndoAction Parent;
protected:
SimObjectId mTerrainId;
U32 mSteps;
F32 mFactor;
Vector<U16> mUnsmoothedHeights;
public:
TerrainSmoothAction();
// SimObject
DECLARE_CONOBJECT( TerrainSmoothAction );
static void initPersistFields();
// UndoAction
virtual void undo();
virtual void redo();
/// Performs the initial smoothing and stores
/// the heighfield state for later undo.
void smooth( TerrainBlock *terrain, F32 factor, U32 steps );
};
#endif // _TERRAINACTIONS_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,497 @@
//-----------------------------------------------------------------------------
// 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 _TERRAINEDITOR_H_
#define _TERRAINEDITOR_H_
#ifndef _EDITTSCTRL_H_
#include "gui/worldEditor/editTSCtrl.h"
#endif
#ifndef _TERRDATA_H_
#include "terrain/terrData.h"
#endif
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
// Each 2D grid position must be associated with a terrainBlock
struct GridPoint
{
Point2I gridPos;
TerrainBlock* terrainBlock;
GridPoint() { gridPos.set(0, 0); terrainBlock = NULL; };
};
class GridInfo
{
public:
GridPoint mGridPoint;
U8 mMaterial;
F32 mHeight;
F32 mWeight;
F32 mStartHeight;
bool mPrimarySelect;
bool mMaterialChanged;
// hash table
S32 mNext;
S32 mPrev;
};
class Selection : public Vector<GridInfo>
{
private:
StringTableEntry mName;
BitSet32 mUndoFlags;
// hash table
S32 lookup(const Point2I & pos);
void insert(GridInfo info);
U32 getHashIndex(const Point2I & pos);
bool validate();
Vector<S32> mHashLists;
U32 mHashListSize;
public:
Selection();
virtual ~Selection();
void reset();
/// add unique grid info into the selection - test uniqueness by grid position
bool add(const GridInfo &info);
bool getInfo(Point2I pos, GridInfo & info);
bool setInfo(GridInfo & info);
bool remove(const GridInfo &info);
void setName(StringTableEntry name);
StringTableEntry getName(){return(mName);}
F32 getAvgHeight();
F32 getMinHeight();
F32 getMaxHeight();
};
class TerrainEditor;
class Brush : public Selection
{
protected:
TerrainEditor * mTerrainEditor;
Point2I mSize;
GridPoint mGridPoint;
Vector<S32> mRenderList;
public:
enum { MaxBrushDim = 40 };
Brush(TerrainEditor * editor);
virtual ~Brush(){};
virtual const char *getType() const = 0;
// Brush appears to intentionally bypass Selection's hash table, so we
// override validate() here.
bool validate() { return true; }
void setPosition(const Point3F & pos);
void setPosition(const Point2I & pos);
const Point2I & getPosition();
const GridPoint & getGridPoint();
void setTerrain(TerrainBlock* terrain) { mGridPoint.terrainBlock = terrain; };
Point2I getSize() const {return(mSize);}
virtual void setSize(const Point2I & size){mSize = size;}
void update();
void render();
virtual void rebuild() = 0;
virtual void _renderOutline() = 0;
};
class BoxBrush : public Brush
{
public:
BoxBrush(TerrainEditor * editor) : Brush(editor){}
const char *getType() const { return "box"; }
void rebuild();
protected:
void _renderOutline();
};
class EllipseBrush : public Brush
{
public:
EllipseBrush(TerrainEditor * editor) : Brush(editor){}
const char *getType() const { return "ellipse"; }
void rebuild();
protected:
void _renderOutline();
};
class SelectionBrush : public Brush
{
public:
SelectionBrush(TerrainEditor * editor);
const char *getType() const { return "selection"; }
void rebuild();
void render(Vector<GFXVertexPC> & vertexBuffer, S32 & verts, S32 & elems, S32 & prims, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone) const;
void setSize(const Point2I &){}
protected:
void _renderOutline() {}
};
class TerrainAction;
class TerrainEditor : public EditTSCtrl
{
// XA: This methods where added to replace the friend consoleMethods.
public:
void attachTerrain(TerrainBlock *terrBlock);
void detachTerrain(TerrainBlock *terrBlock);
S32 getTerrainBlockCount() {return mTerrainBlocks.size();}
TerrainBlock* getTerrainBlock(S32 index);
void getTerrainBlocksMaterialList(Vector<StringTableEntry>& list); // Returns consolidated list of all materials used on all terrain blocks
void setBrushType(const char* type);
const char* getBrushType() const;
void setBrushSize(S32 w, S32 h);
const char* getBrushPos();
void setBrushPos(Point2I pos);
void setAction(const char* action);
const char* getActionName(U32 index);
const char* getCurrentAction() const;
S32 getNumActions();
void processAction(const char* sAction);
void resetSelWeights(bool clear);
void clearSelection();
S32 getNumTextures();
void markEmptySquares();
void mirrorTerrain(S32 mirrorIndex);
TerrainBlock* getActiveTerrain() { return mActiveTerrain; };
void scheduleGridUpdate() { mNeedsGridUpdate = true; }
void scheduleMaterialUpdate() { mNeedsMaterialUpdate = true; }
void setGridUpdateMinMax()
{
mGridUpdateMax.set( S32_MAX, S32_MAX );
mGridUpdateMin.set( 0, 0 );
}
void submitMaterialUndo( String actionName );
void onMaterialUndo( TerrainBlock *terr );
private:
typedef EditTSCtrl Parent;
TerrainBlock* mActiveTerrain;
// A list of all of the TerrainBlocks this editor can edit
VectorPtr<TerrainBlock*> mTerrainBlocks;
Point2I mGridUpdateMin;
Point2I mGridUpdateMax;
U32 mMouseDownSeq;
/// If one of these flags when the terrainEditor goes to render
/// an appropriate update method will be called on the terrain.
/// This prevents unnecessary work from happening from directly
/// within an editor event's process method.
bool mNeedsGridUpdate;
bool mNeedsMaterialUpdate;
bool mMouseDown;
PlaneF mMousePlane;
Point3F mMousePos;
Brush * mMouseBrush;
bool mBrushChanged;
bool mRenderBrush;
F32 mBrushPressure;
Point2I mBrushSize;
F32 mBrushSoftness;
Vector<TerrainAction *> mActions;
TerrainAction * mCurrentAction;
bool mInAction;
Selection mDefaultSel;
bool mSelectionLocked;
S32 mPaintIndex;
Selection * mCurrentSel;
class TerrainEditorUndoAction : public UndoAction
{
public:
TerrainEditorUndoAction( const UTF8* actionName )
: UndoAction( actionName ),
mTerrainEditor( NULL ),
mSel( NULL )
{
}
virtual ~TerrainEditorUndoAction()
{
delete mSel;
}
TerrainEditor *mTerrainEditor;
Selection *mSel;
virtual void undo();
virtual void redo() { undo(); }
};
void submitUndo( Selection *sel );
Selection *mUndoSel;
class TerrainMaterialUndoAction : public UndoAction
{
public:
TerrainMaterialUndoAction( const UTF8 *actionName )
: UndoAction( actionName ),
mEditor( NULL ),
mTerrain( NULL )
{
}
TerrainEditor *mEditor;
TerrainBlock *mTerrain;
Vector<U8> mLayerMap;
Vector<TerrainMaterial*> mMaterials;
virtual void undo();
virtual void redo();
};
bool mIsDirty; // dirty flag for writing terrain.
bool mIsMissionDirty; // dirty flag for writing mission.
GFXStateBlockRef mStateBlock;
public:
TerrainEditor();
~TerrainEditor();
// conversion functions
// Returns true if the grid position is on the main tile
bool isMainTile(const GridPoint & gPoint) const;
// Takes a world point and find the "highest" terrain underneath it
// Returns true if the returned GridPoint includes a valid terrain and grid position
TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos);
// Converts a GridPoint to a world position
bool gridToWorld(const GridPoint & gPoint, Point3F & wPos);
bool gridToWorld(const Point2I & gPos, Point3F & wPos, TerrainBlock* terrain);
// Converts a world position to a grid point
// If the grid point does not have a TerrainBlock already it will find the nearest
// terrian under the world position
bool worldToGrid(const Point3F & wPos, GridPoint & gPoint);
bool worldToGrid(const Point3F & wPos, Point2I & gPos, TerrainBlock* terrain = NULL);
// Converts any point that is off of the main tile to its equivalent on the main tile
// Returns true if the point was already on the main tile
bool gridToCenter(const Point2I & gPos, Point2I & cPos) const;
//bool getGridInfo(const Point3F & wPos, GridInfo & info);
// Gets the grid info for a point on a TerrainBlock's grid
bool getGridInfo(const GridPoint & gPoint, GridInfo & info);
bool getGridInfo(const Point2I & gPos, GridInfo & info, TerrainBlock* terrain);
// Returns a list of infos for all points on the terrain that are at that point in space
void getGridInfos(const GridPoint & gPoint, Vector<GridInfo>& infos);
void setGridInfo(const GridInfo & info, bool checkActive = false);
void setGridInfoHeight(const GridInfo & info);
void gridUpdateComplete( bool materialChanged = false );
void materialUpdateComplete();
void processActionTick(U32 sequence);
TerrainBlock* collide(const Gui3DMouseEvent & event, Point3F & pos);
void lockSelection(bool lock) { mSelectionLocked = lock; };
Selection * getUndoSel(){return(mUndoSel);}
Selection * getCurrentSel(){return(mCurrentSel);}
void setCurrentSel(Selection * sel) { mCurrentSel = sel; }
void resetCurrentSel() {mCurrentSel = &mDefaultSel; }
S32 getPaintMaterialIndex() const { return mPaintIndex; }
void setBrushPressure( F32 pressure );
F32 getBrushPressure() const { return mBrushPressure; }
void setBrushSoftness( F32 softness );
F32 getBrushSoftness() const { return mBrushSoftness; }
Point2I getBrushSize() { return(mBrushSize); }
TerrainBlock* getTerrainBlock() const { return mActiveTerrain; }
TerrainBlock* getClientTerrain( TerrainBlock *serverTerrain = NULL ) const;
bool terrainBlockValid() { return(mActiveTerrain ? true : false); }
void setDirty() { mIsDirty = true; }
void setMissionDirty() { mIsMissionDirty = true; }
TerrainAction * lookupAction(const char * name);
private:
// terrain interface functions
// Returns the height at a grid point
F32 getGridHeight(const GridPoint & gPoint);
// Sets a height at a grid point
void setGridHeight(const GridPoint & gPoint, const F32 height);
///
U8 getGridMaterial( const GridPoint &gPoint ) const;
///
void setGridMaterial( const GridPoint & gPoint, U8 index );
// Gets the material group of a specific spot on a TerrainBlock's grid
U8 getGridMaterialGroup(const GridPoint & gPoint);
// Sets a material group for a spot on a TerrainBlock's grid
void setGridMaterialGroup(const GridPoint & gPoint, U8 group);
//
void updateBrush(Brush & brush, const Point2I & gPos);
//
void renderSelection(const Selection & sel, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone, bool renderFill, bool renderFrame);
void renderBrush(const Brush & brush, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone, bool renderFill, bool renderFrame);
void renderBorder();
public:
// persist field data - these are dynamic
bool mRenderBorder;
F32 mBorderHeight;
ColorI mBorderFillColor;
ColorI mBorderFrameColor;
bool mBorderLineMode;
bool mSelectionHidden;
bool mRenderVertexSelection;
bool mRenderSolidBrush;
bool mProcessUsesBrush;
//
F32 mAdjustHeightVal;
F32 mSetHeightVal;
F32 mScaleVal;
F32 mSmoothFactor;
F32 mNoiseFactor;
S32 mMaterialGroup;
F32 mSoftSelectRadius;
StringTableEntry mSoftSelectFilter;
StringTableEntry mSoftSelectDefaultFilter;
F32 mAdjustHeightMouseScale;
Point2I mMaxBrushSize;
F32 mSlopeMinAngle;
F32 mSlopeMaxAngle;
public:
// SimObject
bool onAdd();
void onDeleteNotify(SimObject * object);
static void initPersistFields();
// GuiControl
bool onWake();
void onSleep();
// EditTSCtrl
bool onInputEvent( const InputEventInfo & evt );
void on3DMouseUp( const Gui3DMouseEvent & evt );
void on3DMouseDown( const Gui3DMouseEvent & evt );
void on3DMouseMove( const Gui3DMouseEvent & evt );
void on3DMouseDragged( const Gui3DMouseEvent & evt );
bool onMouseWheelUp( const GuiEvent & evt );
bool onMouseWheelDown( const GuiEvent & evt );
void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &evt );
void onPreRender();
void renderScene(const RectI & updateRect);
void renderGui( Point2I offset, const RectI &updateRect );
void updateGuiInfo();
// Determine if the given grid point is valid within a non-wrap
// around terrain.
bool isPointInTerrain( const GridPoint & gPoint);
/// Reorder material at the given index to the new position, changing the order in which it is rendered / blended
void reorderMaterial( S32 index, S32 orderPos );
//
Point3F getMousePos(){return(mMousePos);};
void renderPoints( const Vector<GFXVertexPCT> &pointList );
DECLARE_CONOBJECT(TerrainEditor);
};
inline void TerrainEditor::setGridInfoHeight(const GridInfo & info)
{
setGridHeight(info.mGridPoint, info.mHeight);
}
#endif

View file

@ -0,0 +1,272 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/undoActions.h"
#include "gui/editor/inspector/field.h"
#include "gui/editor/guiInspector.h"
#include "console/consoleTypes.h"
IMPLEMENT_CONOBJECT( MECreateUndoAction );
ConsoleDocClass( MECreateUndoAction,
"@brief Material Editor create undo instance\n\n"
"Not intended for game development, for editors or internal use only.\n\n "
"@internal");
MECreateUndoAction::MECreateUndoAction( const UTF8* actionName )
: UndoAction( actionName )
{
}
MECreateUndoAction::~MECreateUndoAction()
{
}
void MECreateUndoAction::initPersistFields()
{
Parent::initPersistFields();
}
void MECreateUndoAction::addObject( SimObject *object )
{
AssertFatal( object, "MECreateUndoAction::addObject() - Got null object!" );
mObjects.increment();
mObjects.last().id = object->getId();
}
ConsoleMethod( MECreateUndoAction, addObject, void, 3, 3, "( SimObject obj )")
{
SimObject *obj = NULL;
if ( Sim::findObject( argv[2], obj ) && obj )
object->addObject( obj );
}
void MECreateUndoAction::undo()
{
for ( S32 i= mObjects.size()-1; i >= 0; i-- )
{
ObjectState &state = mObjects[i];
SimObject *object = Sim::findObject( state.id );
if ( !object )
continue;
// Save the state.
if ( !state.memento.hasState() )
state.memento.save( object );
// Store the group.
SimGroup *group = object->getGroup();
if ( group )
state.groupId = group->getId();
// We got what we need... delete it.
object->deleteObject();
}
Con::executef( this, "onUndone" );
}
void MECreateUndoAction::redo()
{
for ( S32 i=0; i < mObjects.size(); i++ )
{
const ObjectState &state = mObjects[i];
// Create the object.
SimObject::setForcedId(state.id); // Restore the object's Id
SimObject *object = state.memento.restore();
if ( !object )
continue;
// Now restore its group.
SimGroup *group;
if ( Sim::findObject( state.groupId, group ) )
group->addObject( object );
}
Con::executef( this, "onRedone" );
}
IMPLEMENT_CONOBJECT( MEDeleteUndoAction );
ConsoleDocClass( MEDeleteUndoAction,
"@brief Material Editor delete undo instance\n\n"
"Not intended for game development, for editors or internal use only.\n\n "
"@internal");
MEDeleteUndoAction::MEDeleteUndoAction( const UTF8 *actionName )
: UndoAction( actionName )
{
}
MEDeleteUndoAction::~MEDeleteUndoAction()
{
}
void MEDeleteUndoAction::initPersistFields()
{
Parent::initPersistFields();
}
void MEDeleteUndoAction::deleteObject( SimObject *object )
{
AssertFatal( object, "MEDeleteUndoAction::deleteObject() - Got null object!" );
AssertFatal( object->isProperlyAdded(),
"MEDeleteUndoAction::deleteObject() - Object should be registered!" );
mObjects.increment();
ObjectState& state = mObjects.last();
// Capture the object id.
state.id = object->getId();
// Save the state.
state.memento.save( object );
// Store the group.
SimGroup *group = object->getGroup();
if ( group )
state.groupId = group->getId();
// Now delete the object.
object->deleteObject();
}
void MEDeleteUndoAction::deleteObject( const Vector<SimObject*> &objectList )
{
for ( S32 i = 0; i < objectList.size(); i++ )
deleteObject( objectList[i] );
}
ConsoleMethod( MEDeleteUndoAction, deleteObject, void, 3, 3, "( SimObject obj )")
{
SimObject *obj = NULL;
if ( Sim::findObject( argv[2], obj ) && obj )
object->deleteObject( obj );
}
void MEDeleteUndoAction::undo()
{
for ( S32 i= mObjects.size()-1; i >= 0; i-- )
{
const ObjectState &state = mObjects[i];
// Create the object.
SimObject::setForcedId(state.id); // Restore the object's Id
SimObject *object = state.memento.restore();
if ( !object )
continue;
// Now restore its group.
SimGroup *group;
if ( Sim::findObject( state.groupId, group ) )
group->addObject( object );
}
Con::executef( this, "onUndone" );
}
void MEDeleteUndoAction::redo()
{
for ( S32 i=0; i < mObjects.size(); i++ )
{
const ObjectState& state = mObjects[i];
SimObject *object = Sim::findObject( state.id );
if ( object )
object->deleteObject();
}
Con::executef( this, "onRedone" );
}
IMPLEMENT_CONOBJECT( InspectorFieldUndoAction );
ConsoleDocClass( InspectorFieldUndoAction,
"@brief Inspector Field undo action instance\n\n"
"Not intended for game development, for editors or internal use only.\n\n "
"@internal");
InspectorFieldUndoAction::InspectorFieldUndoAction()
{
mObjId = 0;
mField = NULL;
mSlotName = StringTable->insert("");
mArrayIdx = StringTable->insert("");
}
InspectorFieldUndoAction::InspectorFieldUndoAction( const UTF8 *actionName )
: UndoAction( actionName )
{
mInspector = NULL;
mObjId = 0;
mField = NULL;
mSlotName = StringTable->insert("");
mArrayIdx = StringTable->insert("");
}
void InspectorFieldUndoAction::initPersistFields()
{
addField( "inspectorGui", TYPEID< GuiInspector >(), Offset( mInspector, InspectorFieldUndoAction ) );
addField( "objectId", TypeS32, Offset( mObjId, InspectorFieldUndoAction ) );
addField( "fieldName", TypeString, Offset( mSlotName, InspectorFieldUndoAction ) );
addField( "fieldValue", TypeRealString, Offset( mData, InspectorFieldUndoAction ) );
addField( "arrayIndex", TypeString, Offset( mArrayIdx, InspectorFieldUndoAction ) );
Parent::initPersistFields();
}
void InspectorFieldUndoAction::undo()
{
SimObject *obj = NULL;
if ( !Sim::findObject( mObjId, obj ) )
return;
if ( mArrayIdx && dStricmp( mArrayIdx, "(null)" ) == 0 )
mArrayIdx = NULL;
// Grab the current data.
String data = obj->getDataField( mSlotName, mArrayIdx );
// Call this to mirror the way field changes are done through the inspector.
obj->inspectPreApply();
// Restore the data from the UndoAction
obj->setDataField( mSlotName, mArrayIdx, mData.c_str() );
// Call this to mirror the way field changes are done through the inspector.
obj->inspectPostApply();
// If the affected object is still being inspected,
// update the InspectorField to reflect the changed value.
if ( mInspector && mInspector->getNumInspectObjects() > 0 && mInspector->getInspectObject() == obj )
mInspector->updateFieldValue( mSlotName, mArrayIdx );
// Now save the previous data in this UndoAction
// since an undo action must become a redo action and vice-versa
mData = data;
}

View file

@ -0,0 +1,135 @@
//-----------------------------------------------------------------------------
// 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 _GUI_WORLDEDITOR_UNDOACTIONS_H_
#define _GUI_WORLDEDITOR_UNDOACTIONS_H_
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
#ifndef _CONSOLE_SIMOBJECTMEMENTO_H_
#include "console/simObjectMemento.h"
#endif
class GuiInspectorField;
class GuiInspector;
class MECreateUndoAction : public UndoAction
{
typedef UndoAction Parent;
protected:
struct ObjectState
{
/// The object we created and will delete in undo.
SimObjectId id;
/// The captured object state.
SimObjectMemento memento;
/// Keep track of the parent group.
SimObjectId groupId;
};
/// All the objects that were created.
Vector<ObjectState> mObjects;
public:
DECLARE_CONOBJECT( MECreateUndoAction );
static void initPersistFields();
MECreateUndoAction( const UTF8* actionName = " " );
virtual ~MECreateUndoAction();
void addObject( SimObject *object );
// UndoAction
virtual void undo();
virtual void redo();
};
class MEDeleteUndoAction : public UndoAction
{
typedef UndoAction Parent;
protected:
struct ObjectState
{
/// The object we deleted and will restore in undo.
SimObjectId id;
/// The captured object state.
SimObjectMemento memento;
/// Keep track of the parent group.
SimObjectId groupId;
};
/// All the objects we're deleting.
Vector<ObjectState> mObjects;
public:
DECLARE_CONOBJECT( MEDeleteUndoAction );
static void initPersistFields();
MEDeleteUndoAction( const UTF8* actionName = "Delete Object" );
virtual ~MEDeleteUndoAction();
///
void deleteObject( SimObject *object );
void deleteObject( const Vector<SimObject*> &objectList );
// UndoAction
virtual void undo();
virtual void redo();
};
class InspectorFieldUndoAction : public UndoAction
{
typedef UndoAction Parent;
public:
InspectorFieldUndoAction();
InspectorFieldUndoAction( const UTF8* actionName );
DECLARE_CONOBJECT( InspectorFieldUndoAction );
static void initPersistFields();
GuiInspector *mInspector;
SimObjectId mObjId;
SimObjectPtr<GuiInspectorField> mField;
StringTableEntry mSlotName;
StringTableEntry mArrayIdx;
String mData;
// UndoAction
virtual void undo();
virtual void redo() { undo(); }
};
#endif // _GUI_WORLDEDITOR_UNDOACTIONS_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,419 @@
//-----------------------------------------------------------------------------
// 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 _WORLDEDITOR_H_
#define _WORLDEDITOR_H_
#ifndef _EDITTSCTRL_H_
#include "gui/worldEditor/editTSCtrl.h"
#endif
#ifndef _CONSOLETYPES_H_
#include "console/consoleTypes.h"
#endif
#ifndef _GFXTEXTUREHANDLE_H_
#include "gfx/gfxTextureHandle.h"
#endif
#ifndef _TSIGNAL_H_
#include "core/util/tSignal.h"
#endif
#ifndef _CONSOLE_SIMOBJECTMEMENTO_H_
#include "console/simObjectMemento.h"
#endif
#ifndef _UNDO_H_
#include "util/undo.h"
#endif
#ifndef _SIMPATH_H_
#include "scene/simPath.h"
#endif
#ifndef _DYNAMIC_CONSOLETYPES_H_
#include "console/dynamicTypes.h"
#endif
class SceneObject;
class WorldEditorSelection;
///
class WorldEditor : public EditTSCtrl
{
typedef EditTSCtrl Parent;
public:
typedef WorldEditorSelection Selection;
struct Triangle
{
Point3F p0;
Point3F p1;
Point3F p2;
};
void ignoreObjClass(U32 argc, const char** argv);
void clearIgnoreList();
static bool setObjectsUseBoxCenter( void *object, const char *index, const char *data ) { static_cast<WorldEditor*>(object)->setObjectsUseBoxCenter( dAtob( data ) ); return false; };
void setObjectsUseBoxCenter(bool state);
bool getObjectsUseBoxCenter() { return mObjectsUseBoxCenter; }
void clearSelection();
void selectObject(SimObject *obj);
void selectObject(const char* obj);
void unselectObject(SimObject *obj);
void unselectObject(const char* obj);
S32 getSelectionSize();
S32 getSelectObject(S32 index);
const Point3F& getSelectionCentroid();
const char* getSelectionCentroidText();
const Box3F& getSelectionBounds();
Point3F getSelectionExtent();
F32 getSelectionRadius();
void dropCurrentSelection( bool skipUndo );
void copyCurrentSelection();
void cutCurrentSelection();
bool canPasteSelection();
bool alignByBounds(S32 boundsAxis);
bool alignByAxis(S32 axis);
void transformSelection(bool position, Point3F& p, bool relativePos, bool rotate, EulerF& r, bool relativeRot, bool rotLocal, S32 scaleType, Point3F& s, bool sRelative, bool sLocal);
void resetSelectedRotation();
void resetSelectedScale();
void addUndoState();
void redirectConsole(S32 objID);
void colladaExportSelection( const String &path );
void makeSelectionPrefab( const char *filename );
void explodeSelectedPrefab();
//
static SceneObject* getClientObj(SceneObject *);
static void markAsSelected( SimObject* object, bool state );
static void setClientObjInfo(SceneObject *, const MatrixF &, const VectorF &);
static void updateClientTransforms(Selection* );
protected:
class WorldEditorUndoAction : public UndoAction
{
public:
WorldEditorUndoAction( const UTF8* actionName ) : UndoAction( actionName )
{
}
WorldEditor *mWorldEditor;
struct Entry
{
MatrixF mMatrix;
VectorF mScale;
// validation
U32 mObjId;
U32 mObjNumber;
};
Vector<Entry> mEntries;
virtual void undo();
virtual void redo() { undo(); }
};
void submitUndo( Selection* sel, const UTF8* label="World Editor Action" );
public:
/// The objects currently in the copy buffer.
Vector<SimObjectMemento> mCopyBuffer;
bool cutSelection(Selection* sel);
bool copySelection(Selection* sel);
bool pasteSelection(bool dropSel=true);
void dropSelection(Selection* sel);
void dropBelowSelection(Selection* sel, const Point3F & centroid, bool useBottomBounds=false);
void terrainSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos, bool forceStick=false);
void softSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos);
Selection* getActiveSelectionSet() const;
void makeActiveSelectionSet( Selection* sel );
void hideObject(SceneObject* obj, bool hide);
// work off of mSelected
void hideSelection(bool hide);
void lockSelection(bool lock);
public:
bool objClassIgnored(const SimObject * obj);
void renderObjectBox(SceneObject * obj, const ColorI & col);
private:
SceneObject * getControlObject();
bool collide(const Gui3DMouseEvent & event, SceneObject **hitObj );
// gfx methods
//void renderObjectBox(SceneObject * obj, const ColorI & col);
void renderObjectFace(SceneObject * obj, const VectorF & normal, const ColorI & col);
void renderSelectionWorldBox(Selection* sel);
void renderPlane(const Point3F & origin);
void renderMousePopupInfo();
void renderScreenObj( SceneObject * obj, const Point3F& sPos, const Point3F& wPos );
void renderPaths(SimObject *obj);
void renderSplinePath(SimPath::Path *path);
// axis gizmo
bool mUsingAxisGizmo;
GFXStateBlockRef mRenderSelectionBoxSB;
GFXStateBlockRef mRenderObjectBoxSB;
GFXStateBlockRef mRenderObjectFaceSB;
GFXStateBlockRef mSplineSB;
//
bool mIsDirty;
//
bool mMouseDown;
SimObjectPtr< Selection > mSelected;
SimObjectPtr< Selection > mDragSelected;
bool mDragSelect;
RectI mDragRect;
Point2I mDragStart;
// modes for when dragging a selection
enum {
Move = 0,
Rotate,
Scale
};
//
//U32 mCurrentMode;
//U32 mDefaultMode;
S32 mRedirectID;
/// @name Object Icons
/// @{
struct IconObject
{
SceneObject *object;
F32 dist;
RectI rect;
U32 alpha;
};
Vector< IconObject > mIcons;
/// If true, icons fade out with distance to mouse cursor.
bool mFadeIcons;
/// Distance at which to start fading out icons.
F32 mFadeIconsDist;
/// @}
SimObjectPtr<SceneObject> mHitObject;
SimObjectPtr<SceneObject> mPossibleHitObject;
bool mMouseDragged;
Gui3DMouseEvent mLastMouseEvent;
Gui3DMouseEvent mLastMouseDownEvent;
//
class ClassInfo
{
public:
~ClassInfo();
struct Entry
{
StringTableEntry mName;
bool mIgnoreCollision;
GFXTexHandle mDefaultHandle;
GFXTexHandle mSelectHandle;
GFXTexHandle mLockedHandle;
};
Vector<Entry*> mEntries;
};
ClassInfo mClassInfo;
ClassInfo::Entry mDefaultClassEntry;
ClassInfo::Entry * getClassEntry(StringTableEntry name);
ClassInfo::Entry * getClassEntry(const SimObject * obj);
bool addClassEntry(ClassInfo::Entry * entry);
// persist field data
public:
enum DropType
{
DropAtOrigin = 0,
DropAtCamera,
DropAtCameraWithRot,
DropBelowCamera,
DropAtScreenCenter,
DropAtCentroid,
DropToTerrain,
DropBelowSelection
};
// Snapping alignment mode
enum AlignmentType
{
AlignNone = 0,
AlignPosX,
AlignPosY,
AlignPosZ,
AlignNegX,
AlignNegY,
AlignNegZ
};
/// A large hard coded distance used to test
/// object and icon selection.
static F32 smProjectDistance;
S32 mDropType;
bool mBoundingBoxCollision;
bool mObjectMeshCollision;
bool mRenderPopupBackground;
ColorI mPopupBackgroundColor;
ColorI mPopupTextColor;
StringTableEntry mSelectHandle;
StringTableEntry mDefaultHandle;
StringTableEntry mLockedHandle;
ColorI mObjectTextColor;
bool mObjectsUseBoxCenter;
ColorI mObjSelectColor;
ColorI mObjMultiSelectColor;
ColorI mObjMouseOverSelectColor;
ColorI mObjMouseOverColor;
bool mShowMousePopupInfo;
ColorI mDragRectColor;
bool mRenderObjText;
bool mRenderObjHandle;
StringTableEntry mObjTextFormat;
ColorI mFaceSelectColor;
bool mRenderSelectionBox;
ColorI mSelectionBoxColor;
bool mSelectionLocked;
bool mPerformedDragCopy;
bool mDragGridSnapToggle; ///< Grid snap setting temporarily toggled during drag.
bool mToggleIgnoreList;
bool mNoMouseDrag;
bool mDropAtBounds;
F32 mDropBelowCameraOffset;
F32 mDropAtScreenCenterScalar;
F32 mDropAtScreenCenterMax;
bool mGridSnap;
bool mStickToGround;
bool mStuckToGround; ///< Selection is stuck to the ground
AlignmentType mTerrainSnapAlignment; ///< How does the stickied object align to the terrain
bool mSoftSnap; ///< Allow soft snapping all of the time
bool mSoftSnapActivated; ///< Soft snap has been activated by the user and allowed by the current rules
bool mSoftSnapIsStuck; ///< Are we snapping?
AlignmentType mSoftSnapAlignment; ///< How does the snapped object align to the snapped surface
bool mSoftSnapRender; ///< Render the soft snapping bounds
bool mSoftSnapRenderTriangle; ///< Render the soft snapped triangle
Triangle mSoftSnapTriangle; ///< The triangle we are snapping to
bool mSoftSnapSizeByBounds; ///< Use the selection bounds for the size
F32 mSoftSnapSize; ///< If not the selection bounds, use this size
Box3F mSoftSnapBounds; ///< The actual bounds used for the soft snap
Box3F mSoftSnapPreBounds; ///< The bounds prior to any soft snapping (used when rendering the snap bounds)
F32 mSoftSnapBackfaceTolerance; ///< Fraction of mSoftSnapSize for backface polys to have an influence
bool mSoftSnapDebugRender; ///< Activates debug rendering
Point3F mSoftSnapDebugPoint; ///< The point we're attempting to snap to
Triangle mSoftSnapDebugSnapTri; ///< The triangle we are snapping to
Vector<Triangle> mSoftSnapDebugTriangles; ///< The triangles that are considered for snapping
protected:
S32 mCurrentCursor;
void setCursor(U32 cursor);
void get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event);
public:
WorldEditor();
~WorldEditor();
void setDirty() { mIsDirty = true; }
// SimObject
virtual bool onAdd();
virtual void onEditorEnable();
// EditTSCtrl
void on3DMouseMove(const Gui3DMouseEvent & event);
void on3DMouseDown(const Gui3DMouseEvent & event);
void on3DMouseUp(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);
void updateGuiInfo();
//
void renderScene(const RectI & updateRect);
static void initPersistFields();
DECLARE_CONOBJECT(WorldEditor);
static Signal<void(WorldEditor*)> smRenderSceneSignal;
};
typedef WorldEditor::DropType WorldEditorDropType;
typedef WorldEditor::AlignmentType WorldEditorAlignmentType;
DefineEnumType( WorldEditorDropType );
DefineEnumType( WorldEditorAlignmentType );
#endif // _WORLDEDITOR_H_

View file

@ -0,0 +1,708 @@
//-----------------------------------------------------------------------------
// 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 "gui/worldEditor/worldEditorSelection.h"
#include "gui/worldEditor/worldEditor.h"
#include "scene/sceneObject.h"
IMPLEMENT_CONOBJECT( WorldEditorSelection );
ConsoleDocClass( WorldEditorSelection,
"@brief Specialized simset that stores the objects selected by the World Editor\n\n"
"Editor use only.\n\n"
"@internal"
);
//-----------------------------------------------------------------------------
WorldEditorSelection::WorldEditorSelection()
: mCentroidValid(false),
mAutoSelect(false),
mPrevCentroid(0.0f, 0.0f, 0.0f),
mContainsGlobalBounds(false)
{
// Selections are transient by default.
setCanSave( false );
setEditorOnly( true );
}
//-----------------------------------------------------------------------------
WorldEditorSelection::~WorldEditorSelection()
{
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::initPersistFields()
{
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::setCanSave( bool value )
{
if( getCanSave() == value )
return;
Parent::setCanSave( value );
// If we went from being transient to being persistent,
// make sure all objects in the selection have persistent IDs.
if( getCanSave() )
for( iterator iter = begin(); iter != end(); ++ iter )
( *iter )->getOrCreatePersistentId();
}
//-----------------------------------------------------------------------------
bool WorldEditorSelection::objInSet( SimObject* obj )
{
if( !mIsResolvingPIDs )
resolvePIDs();
lock();
bool result = false;
for( iterator iter = begin(); iter != end(); ++ iter )
{
if( obj == *iter )
{
result = true;
break;
}
WorldEditorSelection* set = dynamic_cast< WorldEditorSelection* >( *iter );
if( set && set->objInSet( obj ) )
{
result = true;
break;
}
}
unlock();
return result;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::addObject( SimObject* obj )
{
// Return if object is already in selection.
if( objInSet( obj ) )
return;
// Refuse to add object if this selection is locked.
if( isLocked() )
return;
// Prevent adding us to ourselves.
if( obj == this )
return;
// If the object is itself a selection set, make sure we
// don't create a cycle.
WorldEditorSelection* selection = dynamic_cast< WorldEditorSelection* >( obj );
if( selection && !selection->objInSet( this ) )
return;
// Refuse to add any of our parents.
for( SimGroup* group = getGroup(); group != NULL; group = group->getGroup() )
if( obj == group )
return;
invalidateCentroid();
Parent::addObject( obj );
if( mAutoSelect )
WorldEditor::markAsSelected( obj, true );
return;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::removeObject( SimObject* obj )
{
if( !objInSet( obj ) )
return;
// Refuse to remove object if this selection is locked.
if( isLocked() )
return;
invalidateCentroid();
Parent::removeObject( obj );
if( mAutoSelect )
WorldEditor::markAsSelected( obj, false );
return;
}
//-----------------------------------------------------------------------------
bool WorldEditorSelection::containsGlobalBounds()
{
updateCentroid();
return mContainsGlobalBounds;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::updateCentroid()
{
if( mCentroidValid )
return;
resolvePIDs();
mCentroidValid = true;
mCentroid.set(0,0,0);
mBoxCentroid = mCentroid;
mBoxBounds.minExtents.set(1e10, 1e10, 1e10);
mBoxBounds.maxExtents.set(-1e10, -1e10, -1e10);
mContainsGlobalBounds = false;
if( empty() )
return;
//
for( SimSet::iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* obj = dynamic_cast<SceneObject*>( *iter );
if( !obj )
continue;
const MatrixF & mat = obj->getTransform();
Point3F wPos;
mat.getColumn(3, &wPos);
//
mCentroid += wPos;
//
const Box3F& bounds = obj->getWorldBox();
mBoxBounds.minExtents.setMin(bounds.minExtents);
mBoxBounds.maxExtents.setMax(bounds.maxExtents);
if(obj->isGlobalBounds())
mContainsGlobalBounds = true;
}
mCentroid /= (F32) size();
mBoxCentroid = mBoxBounds.getCenter();
}
//-----------------------------------------------------------------------------
const Point3F & WorldEditorSelection::getCentroid()
{
updateCentroid();
return(mCentroid);
}
//-----------------------------------------------------------------------------
const Point3F & WorldEditorSelection::getBoxCentroid()
{
updateCentroid();
return(mBoxCentroid);
}
//-----------------------------------------------------------------------------
const Box3F & WorldEditorSelection::getBoxBounds()
{
updateCentroid();
return(mBoxBounds);
}
//-----------------------------------------------------------------------------
Point3F WorldEditorSelection::getBoxBottomCenter()
{
updateCentroid();
Point3F bottomCenter = mBoxCentroid;
bottomCenter.z -= mBoxBounds.len_z() * 0.5f;
return bottomCenter;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::enableCollision()
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast<SceneObject*>( *iter );
if( object )
object->enableCollision();
}
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::disableCollision()
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( object )
object->disableCollision();
}
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::offset( const Point3F& offset, F32 gridSnap )
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* obj = dynamic_cast<SceneObject*>( *iter );
if( !obj )
continue;
MatrixF mat = obj->getTransform();
Point3F wPos;
mat.getColumn(3, &wPos);
// adjust
wPos += offset;
if( gridSnap != 0.f )
{
wPos.x -= mFmod( wPos.x, gridSnap );
wPos.y -= mFmod( wPos.y, gridSnap );
wPos.z -= mFmod( wPos.z, gridSnap );
}
mat.setColumn(3, wPos);
obj->setTransform(mat);
}
mCentroidValid = false;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::setPosition(const Point3F & pos)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast<SceneObject*>( *iter );
if( object )
object->setPosition(pos);
}
mCentroidValid = false;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::setCentroidPosition(bool useBoxCenter, const Point3F & pos)
{
Point3F centroid;
if( containsGlobalBounds() )
{
centroid = getCentroid();
}
else
{
centroid = useBoxCenter ? getBoxCentroid() : getCentroid();
}
offset(pos - centroid);
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::orient(const MatrixF & rot, const Point3F & center)
{
// Orient all the selected objects to the given rotation
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
MatrixF mat = rot;
mat.setPosition( object->getPosition() );
object->setTransform(mat);
}
mCentroidValid = false;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::rotate(const EulerF &rot)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
MatrixF mat = object->getTransform();
MatrixF transform(rot);
mat.mul(transform);
object->setTransform(mat);
}
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::rotate(const EulerF & rot, const Point3F & center)
{
// single selections will rotate around own axis, multiple about world
if(size() == 1)
{
SceneObject* object = dynamic_cast< SceneObject* >( at( 0 ) );
if( object )
{
MatrixF mat = object->getTransform();
Point3F pos;
mat.getColumn(3, &pos);
// get offset in obj space
Point3F offset = pos - center;
MatrixF wMat = object->getWorldTransform();
wMat.mulV(offset);
//
MatrixF transform(EulerF(0,0,0), -offset);
transform.mul(MatrixF(rot));
transform.mul(MatrixF(EulerF(0,0,0), offset));
mat.mul(transform);
object->setTransform(mat);
}
}
else
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
MatrixF mat = object->getTransform();
Point3F pos;
mat.getColumn(3, &pos);
// get offset in obj space
Point3F offset = pos - center;
MatrixF transform(rot);
Point3F wOffset;
transform.mulV(offset, &wOffset);
MatrixF wMat = object->getWorldTransform();
wMat.mulV(offset);
//
transform.set(EulerF(0,0,0), -offset);
mat.setColumn(3, Point3F(0,0,0));
wMat.setColumn(3, Point3F(0,0,0));
transform.mul(wMat);
transform.mul(MatrixF(rot));
transform.mul(mat);
mat.mul(transform);
mat.normalize();
mat.setColumn(3, wOffset + center);
object->setTransform(mat);
}
}
mCentroidValid = false;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::setRotate(const EulerF & rot)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
MatrixF mat = object->getTransform();
Point3F pos;
mat.getColumn(3, &pos);
MatrixF rmat(rot);
rmat.setPosition(pos);
object->setTransform(rmat);
}
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::scale(const VectorF & scale)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
VectorF current = object->getScale();
current.convolve(scale);
// clamp scale to sensible limits
current.setMax( Point3F( 0.01f ) );
current.setMin( Point3F( 1000.0f ) );
object->setScale(current);
}
mCentroidValid = false;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::scale(const VectorF & scale, const Point3F & center)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
VectorF current = object->getScale();
current.convolve(scale);
// clamp scale to sensible limits
current.setMax( Point3F( 0.01f ) );
current.setMin( Point3F( 1000.0f ) );
// Apply the scale first. If the object's scale doesn't change with
// this operation then this object doesn't scale. In this case
// we don't want to continue with the offset operation.
VectorF prevScale = object->getScale();
object->setScale(current);
if( !object->getScale().equal(current) )
continue;
// determine the actual scale factor to apply to the object offset
// need to account for the scale limiting above to prevent offsets
// being reduced to 0 which then cannot be restored by unscaling
VectorF adjustedScale = current / prevScale;
MatrixF mat = object->getTransform();
Point3F pos;
mat.getColumn(3, &pos);
Point3F offset = pos - center;
offset *= adjustedScale;
object->setPosition(offset + center);
}
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::setScale(const VectorF & scale)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( object )
object->setScale( scale );
}
mCentroidValid = false;
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::setScale(const VectorF & scale, const Point3F & center)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
MatrixF mat = object->getTransform();
Point3F pos;
mat.getColumn(3, &pos);
Point3F offset = pos - center;
offset *= scale;
object->setPosition(offset + center);
object->setScale(scale);
}
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::addSize(const VectorF & newsize)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
if( object->isGlobalBounds() )
continue;
const Box3F& bounds = object->getObjBox();
VectorF extent = bounds.getExtents();
VectorF scaledextent = object->getScale() * extent;
VectorF scale = (newsize + scaledextent) / scaledextent;
object->setScale( object->getScale() * scale );
}
}
//-----------------------------------------------------------------------------
void WorldEditorSelection::setSize(const VectorF & newsize)
{
for( iterator iter = begin(); iter != end(); ++ iter )
{
SceneObject* object = dynamic_cast< SceneObject* >( *iter );
if( !object )
continue;
if( object->isGlobalBounds() )
continue;
const Box3F& bounds = object->getObjBox();
VectorF extent = bounds.getExtents();
VectorF scale = newsize / extent;
object->setScale( scale );
}
}
//=============================================================================
// Console Methods.
//=============================================================================
// MARK: ---- Console Methods ----
//-----------------------------------------------------------------------------
ConsoleMethod( WorldEditorSelection, containsGlobalBounds, bool, 2, 2, "() - True if an object with global bounds is contained in the selection." )
{
return object->containsGlobalBounds();
}
//-----------------------------------------------------------------------------
ConsoleMethod( WorldEditorSelection, getCentroid, const char*, 2, 2, "() - Return the median of all object positions in the selection." )
{
char* buffer = Con::getReturnBuffer( 256 );
const Point3F& centroid = object->getCentroid();
dSprintf( buffer, 256, "%g %g %g", centroid.x, centroid.y, centroid.z );
return buffer;
}
//-----------------------------------------------------------------------------
ConsoleMethod( WorldEditorSelection, getBoxCentroid, const char*, 2, 2, "() - Return the center of the bounding box around the selection." )
{
char* buffer = Con::getReturnBuffer( 256 );
const Point3F& boxCentroid = object->getBoxCentroid();
dSprintf( buffer, 256, "%g %g %g", boxCentroid.x, boxCentroid.y, boxCentroid.z );
return buffer;
}
//-----------------------------------------------------------------------------
ConsoleMethod( WorldEditorSelection, offset, void, 3, 4, "( vector delta, float gridSnap=0 ) - Move all objects in the selection by the given delta." )
{
F32 x, y, z;
dSscanf( argv[ 3 ], "%g %g %g", &x, &y, &z );
F32 gridSnap = 0.f;
if( argc > 3 )
gridSnap = dAtof( argv[ 3 ] );
object->offset( Point3F( x, y, z ), gridSnap );
WorldEditor::updateClientTransforms( object );
}
//-----------------------------------------------------------------------------
ConsoleMethod( WorldEditorSelection, union, void, 3, 3, "( SimSet set ) - Add all objects in the given set to this selection." )
{
SimSet* selection;
if( !Sim::findObject( argv[ 2 ], selection ) )
{
Con::errorf( "WorldEditorSelection::union - no SimSet '%s'", argv[ 2 ] );
return;
}
const U32 numObjects = selection->size();
for( U32 i = 0; i < numObjects; ++ i )
object->addObject( selection->at( i ) );
}
//-----------------------------------------------------------------------------
ConsoleMethod( WorldEditorSelection, subtract, void, 3, 3, "( SimSet ) - Remove all objects in the given set from this selection." )
{
SimSet* selection;
if( !Sim::findObject( argv[ 2 ], selection ) )
{
Con::errorf( "WorldEditorSelection::subtract - no SimSet '%s'", argv[ 2 ] );
return;
}
const U32 numObjects = selection->size();
for( U32 i = 0; i < numObjects; ++ i )
object->removeObject( selection->at( i ) );
}

View file

@ -0,0 +1,150 @@
//-----------------------------------------------------------------------------
// 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 _WORLDEDITORSELECTION_H_
#define _WORLDEDITORSELECTION_H_
#ifndef _SIMPERSISTSET_H_
#include "console/simPersistSet.h"
#endif
#ifndef _MPOINT3_H_
#include "math/mPoint3.h"
#endif
#ifndef _MMATRIX_H_
#include "math/mMatrix.h"
#endif
#ifndef _TVECTOR_H_
#include "core/util/tVector.h"
#endif
class SceneObject;
/// A selection set in the World Editor.
///
/// Selections are by default transient objects, but can be made persistent and
/// saved to disk.
///
class WorldEditorSelection : public SimPersistSet
{
public:
typedef SimPersistSet Parent;
private:
/// The averaged positions of all objects in the selection.
Point3F mCentroid;
/// The center point of the bounding box around the selection.
Point3F mBoxCentroid;
/// The bounding box around the selection.
Box3F mBoxBounds;
///
MatrixF mTransform;
/// If false, the selection has been modified and bounding box values
/// and center points need to be recomputed.
bool mCentroidValid;
/// If true, the selection contains one or more objects that have
/// global bounds enabled.
bool mContainsGlobalBounds;
bool mAutoSelect;
Point3F mPrevCentroid;
void updateCentroid();
public:
WorldEditorSelection();
~WorldEditorSelection();
/// Return true if "object" is contained in the selection.
bool objInSet( SimObject* object );
void storeCurrentCentroid() { mPrevCentroid = getCentroid(); }
bool hasCentroidChanged() { return (mPrevCentroid != getCentroid()); }
bool containsGlobalBounds();
/// @name Transforms
///
/// Note that these methods do not update transforms of client objects.
/// Use WorldEditor::updateClientTransforms to do that.
///
/// @{
///
const Point3F& getCentroid();
const Point3F& getBoxCentroid();
const Box3F& getBoxBounds();
Point3F getBoxBottomCenter();
const MatrixF& getTransform();
//
void offset(const Point3F& delta, F32 gridSnap = 0.f );
void setPosition(const Point3F & pos);
void setCentroidPosition(bool useBoxCenter, const Point3F & pos);
void orient(const MatrixF &, const Point3F &);
void rotate(const EulerF &);
void rotate(const EulerF &, const Point3F &);
void setRotate(const EulerF &);
void scale(const VectorF &);
void scale(const VectorF &, const Point3F &);
void setScale(const VectorF &);
void setScale(const VectorF &, const Point3F &);
void addSize(const VectorF &);
void setSize(const VectorF &);
/// @}
/// Enable collision for all objects in the selection.
void enableCollision();
/// Disable collision for all objects in the selection.
void disableCollision();
//
void setAutoSelect(bool b) { mAutoSelect = b; }
void invalidateCentroid() { mCentroidValid = false; }
// SimSet.
virtual void addObject( SimObject* );
virtual void removeObject( SimObject* );
virtual void setCanSave( bool value );
static void initPersistFields();
DECLARE_CONOBJECT( WorldEditorSelection );
DECLARE_CATEGORY( "Editor World" );
DECLARE_DESCRIPTION( "A selection set for the World Editor." );
};
#endif // !_WORLDEDITORSELECTION_H_