From 525081439739d4fe4ffd111c58ccb792bda739f3 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Thu, 8 Dec 2005 02:50:55 +0000 Subject: [PATCH] Atlas: Fixed entities-don't-move-when-terrain-is-altered. Unbroke and fixed entity memory management. Added flatten tool. Added object movement/rotation/deletion tool - but not yet finished, since 'redo' usually crashes (for known reasons). This was SVN commit r3207. --- source/simulation/Entity.cpp | 8 +- source/simulation/Entity.h | 6 +- source/simulation/EntityManager.cpp | 22 +- source/simulation/EntityManager.h | 1 + .../AtlasUI/ScenarioEditor/ScenarioEditor.cpp | 3 +- .../ScenarioEditor/Sections/Object/Object.cpp | 2 +- .../Sections/Terrain/Terrain.cpp | 1 + .../ScenarioEditor/Tools/Common/Tools.cpp | 9 +- .../ScenarioEditor/Tools/FlattenElevation.cpp | 98 ++++++ .../ScenarioEditor/Tools/TransformObject.cpp | 97 ++++++ source/tools/atlas/GameInterface/Brushes.cpp | 13 +- source/tools/atlas/GameInterface/Brushes.h | 1 + .../tools/atlas/GameInterface/CommandProc.cpp | 5 + .../tools/atlas/GameInterface/CommandProc.h | 5 + source/tools/atlas/GameInterface/GameLoop.cpp | 6 + .../GameInterface/Handlers/Elevation.cpp | 153 +++++++-- .../GameInterface/Handlers/GraphicsSetup.cpp | 6 + .../GameInterface/Handlers/ObjectHandlers.cpp | 290 +++++++++++++++++- source/tools/atlas/GameInterface/Messages.h | 35 +++ 19 files changed, 709 insertions(+), 52 deletions(-) create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index b48829f084..1433e3fa0a 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -154,7 +154,7 @@ void CEntity::kill() m_bounds = NULL; m_destroyed = true; - Shutdown(); + //Shutdown(); // PT: tentatively removed - this seems to be called by ~CJSComplex, and we don't want to do it twice if( m_actor ) { @@ -617,6 +617,11 @@ void CEntity::interpolate( float relativeoffset ) } } +void CEntity::invalidateActor() +{ + m_actor_transform_valid = false; +} + void CEntity::render() { if( !m_orderQueue.empty() ) @@ -988,6 +993,7 @@ bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ) break; case CEntityOrder::ORDER_ATTACK_MELEE: case CEntityOrder::ORDER_GATHER: + case CEntityOrder::ORDER_HEAL: if( argc < 1 ) { JS_ReportError( cx, "Too few parameters" ); diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index cbc22c2567..e1856ceb5d 100755 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -112,7 +112,7 @@ public: float m_graphics_orientation; // If the actor's current transform data is valid (i.e. the entity hasn't - // moved since it was last calculated). + // moved since it was last calculated, and the terrain hasn't been changed). bool m_actor_transform_valid; //-- Scripts @@ -167,6 +167,10 @@ public: // Updates graphical information for a point between the last and current simulation frame; 0 < relativeoffset < 1. void interpolate( float relativeoffset ); + // Forces update of actor information during next call to 'interpolate'. + // (Necessary when terrain might move underneath the actor.) + void invalidateActor(); + // Updates auras void UpdateAuras( size_t timestep_millis ); diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp index 60218a3b01..4e61c696ec 100755 --- a/source/simulation/EntityManager.cpp +++ b/source/simulation/EntityManager.cpp @@ -36,6 +36,13 @@ CEntityManager::~CEntityManager() m_entities[i].m_entity = 0; m_entities[i].m_refcount = 0; } + + // Delete entities that were killed, but not yet reaped by a call to updateAll, + // to avoid memory leak warnings upon exiting + std::vector::iterator it; + for( it = m_reaper.begin(); it < m_reaper.end(); it++ ) + delete( *it ); + m_reaper.clear(); } void CEntityManager::deleteAll() @@ -45,6 +52,7 @@ void CEntityManager::deleteAll() if( m_entities[i].m_refcount ) { delete( m_entities[i].m_entity ); + m_entities[i].m_entity = 0; m_entities[i].m_refcount = 0; } m_nextalloc = 0; @@ -179,13 +187,17 @@ void CEntityManager::renderAll() m_entities[i].m_entity->render(); } +void CEntityManager::invalidateAll() +{ + for( int i = 0; i < MAX_HANDLES; i++ ) + if( m_entities[i].m_refcount && !m_entities[i].m_entity->m_destroyed ) + m_entities[i].m_entity->invalidateActor(); +} + void CEntityManager::destroy( u16 handle ) { - if (m_entities[handle].m_entity->me.m_handle != INVALID_HANDLE) - { - m_reaper.push_back( m_entities[handle].m_entity ); - m_entities[handle].m_entity->me.m_handle = INVALID_HANDLE; - } + m_reaper.push_back( m_entities[handle].m_entity ); + m_entities[handle].m_entity->me.m_handle = INVALID_HANDLE; } bool CEntityManager::m_extant = false; diff --git a/source/simulation/EntityManager.h b/source/simulation/EntityManager.h index 8fa79ddab5..77053299b8 100755 --- a/source/simulation/EntityManager.h +++ b/source/simulation/EntityManager.h @@ -53,6 +53,7 @@ public: void InitializeAll(); void TickAll(); void renderAll(); + void invalidateAll(); void deleteAll(); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index 7dd3357ef4..02a19c0bed 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -393,8 +393,9 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent) ToolButtonBar* toolbar = new ToolButtonBar(this, ID_Toolbar); // (button label; tooltip text; image; internal tool name) toolbar->AddToolButton(_("Default"), _("Default"), _T("default.png"), _T("")); - toolbar->AddToolButton(_("Move"), _("Move/rotate object"), _T("moveobject.png"), _T("MoveObject")); + toolbar->AddToolButton(_("Move"), _("Move/rotate object"), _T("moveobject.png"), _T("TransformObject")); toolbar->AddToolButton(_("Elevation"), _("Alter terrain elevation"), _T("alterelevation.png"), _T("AlterElevation")); + toolbar->AddToolButton(_("Flatten"), _("Flatten terrain elevation"), _T("flattenelevation.png"), _T("FlattenElevation")); toolbar->AddToolButton(_("Paint Terrain"), _("Paint terrain texture"), _T("paintterrain.png"), _T("PaintTerrain")); toolbar->Realize(); SetToolBar(toolbar); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp index 72d8c78d37..9c3ab3ac06 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Object/Object.cpp @@ -38,7 +38,7 @@ ObjectSidebar::ObjectSidebar(wxWindow* parent) } -wxWindow* ObjectSidebar::GetBottomBar(wxWindow* parent) +wxWindow* ObjectSidebar::GetBottomBar(wxWindow* WXUNUSED(parent)) { if (m_BottomBar) return m_BottomBar; diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp index 88b6969f42..089a9295de 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp @@ -23,6 +23,7 @@ TerrainSidebar::TerrainSidebar(wxWindow* parent) { wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools")); sizer->Add(new ToolButton(this, _("Modify"), _T("AlterElevation"), wxSize(50,20))); + sizer->Add(new ToolButton(this, _("Flatten"), _T("FlattenElevation"), wxSize(50,20))); // sizer->Add(new ToolButton(this, _("Smooth"), _T(""), wxSize(50,20))); // sizer->Add(new ToolButton(this, _("Sample"), _T(""), wxSize(50,20))); sizer->Add(new ToolButton(this, _("Paint"), _T("PaintTerrain"), wxSize(50,20))); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp index 54ffe1c1ac..9420823770 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/Common/Tools.cpp @@ -29,6 +29,7 @@ void SetCurrentTool(const wxString& name, void* initData) { g_CurrentTool->Shutdown(); delete g_CurrentTool; + g_CurrentTool = &dummy; } SetActive(false, g_CurrentToolName); @@ -38,13 +39,13 @@ void SetCurrentTool(const wxString& name, void* initData) { tool = wxDynamicCast(wxCreateDynamicObject(name), ITool); wxASSERT(tool); - tool->Init(initData); } - if (tool == NULL) - g_CurrentTool = &dummy; - else + if (tool) + { g_CurrentTool = tool; + tool->Init(initData); + } g_CurrentToolName = name; SetActive(true, g_CurrentToolName); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp new file mode 100644 index 0000000000..cbc756c38e --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/FlattenElevation.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" + +#include "Common/Tools.h" +#include "Common/Brushes.h" +#include "GameInterface/Messages.h" + +using AtlasMessage::Position; + +class FlattenElevation : public StateDrivenTool +{ + DECLARE_DYNAMIC_CLASS(FlattenElevation); + + Position m_Pos; + +public: + FlattenElevation() + { + SetState(&Waiting); + } + + + void OnEnable() + { + g_Brush_Elevation.MakeActive(); + } + + void OnDisable() + { + POST_MESSAGE(BrushPreview(false, Position())); + } + + + struct sWaiting : public State + { + bool OnMouse(FlattenElevation* obj, wxMouseEvent& evt) + { + if (evt.LeftDown()) + { + obj->m_Pos = Position(evt.GetPosition()); + SET_STATE(Flattening); + return true; + } + else if (evt.Moving()) + { + POST_MESSAGE(BrushPreview(true, Position(evt.GetPosition()))); + return true; + } + else + { + return false; + } + } + } + Waiting; + + + struct sFlattening : public State + { + void OnEnter(FlattenElevation* obj) + { + POST_MESSAGE(BrushPreview(true, obj->m_Pos)); + } + + void OnLeave(FlattenElevation*) + { + ScenarioEditor::GetCommandProc().FinaliseLastCommand(); + } + + bool OnMouse(FlattenElevation* obj, wxMouseEvent& evt) + { + if (evt.LeftUp()) + { + SET_STATE(Waiting); + return true; + } + else if (evt.Dragging()) + { + wxPoint pos = evt.GetPosition(); + obj->m_Pos = Position(pos); + POST_MESSAGE(BrushPreview(true, obj->m_Pos)); + return true; + } + else + { + return false; + } + } + + void OnTick(FlattenElevation* obj, float dt) + { + POST_COMMAND(FlattenElevation, (obj->m_Pos, dt*4096.f*g_Brush_Elevation.GetStrength())); + obj->m_Pos = Position::Unchanged(); + } + } + Flattening; +}; + +IMPLEMENT_DYNAMIC_CLASS(FlattenElevation, StateDrivenTool); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp new file mode 100644 index 0000000000..c47b05922e --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/TransformObject.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" + +#include "Common/Tools.h" +#include "Common/Brushes.h" +#include "Common/MiscState.h" +#include "GameInterface/Messages.h" + +using AtlasMessage::Position; + +class TransformObject : public StateDrivenTool +{ + DECLARE_DYNAMIC_CLASS(TransformObject); + + std::vector m_Selection; + +public: + TransformObject() + { + SetState(&Waiting); + } + + void OnDisable() + { + m_Selection.clear(); + POST_MESSAGE(SetSelectionPreview(m_Selection)); + } + + + // TODO: keys to rotate/move object? + + struct sWaiting : public State + { + bool OnMouse(TransformObject* obj, wxMouseEvent& evt) + { + if (evt.LeftDown()) + { + // TODO: multiple selection + AtlasMessage::qSelectObject qry(Position(evt.GetPosition())); + qry.Post(); + obj->m_Selection.clear(); + obj->m_Selection.push_back(qry.id); + POST_MESSAGE(SetSelectionPreview(obj->m_Selection)); + ScenarioEditor::GetCommandProc().FinaliseLastCommand(); + SET_STATE(Dragging); + return true; + } + else if (evt.Dragging() && evt.RightIsDown() || evt.RightDown()) + { + Position pos (evt.GetPosition()); + for (size_t i = 0; i < obj->m_Selection.size(); ++i) + POST_COMMAND(RotateObject, (obj->m_Selection[i], true, pos, 0.f)); + return true; + } + else + return false; + } + + bool OnKey(TransformObject* obj, wxKeyEvent& evt, KeyEventType type) + { + if (type == KEY_CHAR && evt.GetKeyCode() == WXK_DELETE) + { + for (size_t i = 0; i < obj->m_Selection.size(); ++i) + POST_COMMAND(DeleteObject, (obj->m_Selection[i])); + obj->m_Selection.clear(); + POST_MESSAGE(SetSelectionPreview(obj->m_Selection)); + return true; + } + else + return false; + } + } + Waiting; + + struct sDragging : public State + { + bool OnMouse(TransformObject* obj, wxMouseEvent& evt) + { + if (evt.LeftUp()) + { + SET_STATE(Waiting); + return true; + } + else if (evt.Dragging()) + { + Position pos (evt.GetPosition()); + for (size_t i = 0; i < obj->m_Selection.size(); ++i) + POST_COMMAND(MoveObject, (obj->m_Selection[i], pos)); + return true; + } + else + return false; + } + } + Dragging; +}; + +IMPLEMENT_DYNAMIC_CLASS(TransformObject, StateDrivenTool); diff --git a/source/tools/atlas/GameInterface/Brushes.cpp b/source/tools/atlas/GameInterface/Brushes.cpp index 4ad3dfc5e9..735bc9bb5b 100644 --- a/source/tools/atlas/GameInterface/Brushes.cpp +++ b/source/tools/atlas/GameInterface/Brushes.cpp @@ -28,7 +28,7 @@ void Brush::SetData(int w, int h, const float* data) m_Data = data; } -void Brush::GetBottomRight(int& x, int& y) const +void Brush::GetCentre(int& x, int& y) const { CVector3D c = m_Centre; if (m_W % 2) c.X += CELL_SIZE/2.f; @@ -36,8 +36,15 @@ void Brush::GetBottomRight(int& x, int& y) const i32 cx, cy; CTerrain::CalcFromPosition(c, cx, cy); - x = cx - (m_W-1)/2; - y = cy - (m_H-1)/2; + x = cx; + y = cy; +} + +void Brush::GetBottomRight(int& x, int& y) const +{ + GetCentre(x, y); + x -= (m_W-1)/2; + y -= (m_H-1)/2; } void Brush::SetRenderEnabled(bool enabled) diff --git a/source/tools/atlas/GameInterface/Brushes.h b/source/tools/atlas/GameInterface/Brushes.h index 539fcdaec3..aa31d76fba 100644 --- a/source/tools/atlas/GameInterface/Brushes.h +++ b/source/tools/atlas/GameInterface/Brushes.h @@ -12,6 +12,7 @@ struct Brush void SetRenderEnabled(bool enabled); // initial state is disabled void Render(); // only does anything if enabled + void GetCentre(int& x, int& y) const; void GetBottomRight(int& x, int& y) const; float Get(int x, int y) const diff --git a/source/tools/atlas/GameInterface/CommandProc.cpp b/source/tools/atlas/GameInterface/CommandProc.cpp index 680ed6bdf9..21e2baae3f 100644 --- a/source/tools/atlas/GameInterface/CommandProc.cpp +++ b/source/tools/atlas/GameInterface/CommandProc.cpp @@ -44,6 +44,11 @@ CommandProc::CommandProc() } CommandProc::~CommandProc() +{ + debug_assert(!m_Commands.size()); +} + +void CommandProc::Destroy() { for_each(m_Commands.begin(), m_Commands.end(), delete_fn); m_Commands.clear(); diff --git a/source/tools/atlas/GameInterface/CommandProc.h b/source/tools/atlas/GameInterface/CommandProc.h index bdd4927d0d..6766422a96 100644 --- a/source/tools/atlas/GameInterface/CommandProc.h +++ b/source/tools/atlas/GameInterface/CommandProc.h @@ -20,6 +20,10 @@ public: CommandProc(); ~CommandProc(); + // Should be called before shutting down, so it can free + // references to entities/etc that are stored in commands + void Destroy(); + void Submit(Command* cmd); void Undo(); @@ -42,6 +46,7 @@ struct DataCommand : public Command // so commands can optionally override (De|C { void Destruct() {}; void Construct() {}; + // MergeWithSelf should update 'prev' to include the effects of 'this' void MergeWithSelf(void*) { debug_warn("MergeWithSelf unimplemented in some command"); } }; diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index 0b0cf53bb8..977a936ad3 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -22,10 +22,16 @@ using namespace AtlasMessage; +namespace AtlasMessage +{ + extern void AtlasRenderSelection(); +} + void AtlasRender() { Render(); g_CurrentBrush.Render(); + AtlasRenderSelection(); } diff --git a/source/tools/atlas/GameInterface/Handlers/Elevation.cpp b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp index 005f812b3d..ef3d729e78 100644 --- a/source/tools/atlas/GameInterface/Handlers/Elevation.cpp +++ b/source/tools/atlas/GameInterface/Handlers/Elevation.cpp @@ -7,45 +7,76 @@ #include "graphics/Terrain.h" #include "ps/Game.h" #include "maths/MathUtil.h" +#include "simulation/EntityManager.h" #include "../Brushes.h" #include "../DeltaArray.h" namespace AtlasMessage { -BEGIN_COMMAND(AlterElevation) - - class TerrainArray : public DeltaArray2D +class TerrainArray : public DeltaArray2D +{ +public: + void Init() { - public: - void Init() - { - m_Heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap(); - m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); - } + m_Heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap(); + m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide(); + } - void RaiseVertex(int x, int y, int amount) - { - // Ignore out-of-bounds vertices - if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide) - return; + void RaiseVertex(int x, int y, int amount) + { + // Ignore out-of-bounds vertices + if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide) + return; - set(x,y, (u16)clamp(get(x,y) + amount, 0, 65535)); - } + set(x,y, (u16)clamp(get(x,y) + amount, 0, 65535)); + } - protected: - u16 getOld(int x, int y) - { - return m_Heightmap[y*m_VertsPerSide + x]; - } - void setNew(int x, int y, const u16& val) - { - m_Heightmap[y*m_VertsPerSide + x] = val; - } + void MoveVertexTowards(int x, int y, int target, int amount) + { + if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide) + return; - u16* m_Heightmap; - size_t m_VertsPerSide; - }; + int h = get(x,y); + if (h < target) + h = std::min(target, h + amount); + else if (h > target) + h = std::max(target, h - amount); + else + return; + + set(x,y, (u16)clamp(h, 0, 65535)); + } + + void SetVertex(int x, int y, u16 value) + { + if ((unsigned)x >= m_VertsPerSide || (unsigned)y >= m_VertsPerSide) + return; + + set(x,y, value); + } + + u16 GetVertex(int x, int y) + { + return get(clamp(x, 0, (int)m_VertsPerSide-1), clamp(y, 0, (int)m_VertsPerSide-1)); + } + +protected: + u16 getOld(int x, int y) + { + return m_Heightmap[y*m_VertsPerSide + x]; + } + void setNew(int x, int y, const u16& val) + { + m_Heightmap[y*m_VertsPerSide + x] = val; + } + + u16* m_Heightmap; + size_t m_VertsPerSide; +}; + + +BEGIN_COMMAND(AlterElevation) TerrainArray m_TerrainDelta; @@ -59,7 +90,6 @@ BEGIN_COMMAND(AlterElevation) void Do() { - int amount = (int)d->amount; // If the framerate is very high, 'amount' is often very @@ -89,18 +119,21 @@ BEGIN_COMMAND(AlterElevation) } g_Game->GetWorld()->GetTerrain()->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H, RENDERDATA_UPDATE_VERTICES); + g_EntityManager.invalidateAll(); } void Undo() { m_TerrainDelta.Undo(); g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES); + g_EntityManager.invalidateAll(); } void Redo() { m_TerrainDelta.Redo(); g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES); + g_EntityManager.invalidateAll(); } void MergeWithSelf(cAlterElevation* prev) @@ -110,4 +143,66 @@ BEGIN_COMMAND(AlterElevation) END_COMMAND(AlterElevation) +////////////////////////////////////////////////////////////////////////// + +BEGIN_COMMAND(FlattenElevation) + + TerrainArray m_TerrainDelta; + + void Construct() + { + m_TerrainDelta.Init(); + } + void Destruct() + { + } + + void Do() + { + int amount = (int)d->amount; + + static CVector3D previousPosition; + d->pos.GetWorldSpace(g_CurrentBrush.m_Centre, previousPosition); + previousPosition = g_CurrentBrush.m_Centre; + + int xc, yc; + g_CurrentBrush.GetCentre(xc, yc); + u16 height = m_TerrainDelta.GetVertex(xc, yc); + + int x0, y0; + g_CurrentBrush.GetBottomRight(x0, y0); + + for (int dy = 0; dy < g_CurrentBrush.m_H; ++dy) + for (int dx = 0; dx < g_CurrentBrush.m_W; ++dx) + { + float b = g_CurrentBrush.Get(dx, dy); + if (b) + m_TerrainDelta.MoveVertexTowards(x0+dx, y0+dy, height, 1 + (int)(b*amount)); + } + + g_Game->GetWorld()->GetTerrain()->MakeDirty(x0, y0, x0+g_CurrentBrush.m_W, y0+g_CurrentBrush.m_H, RENDERDATA_UPDATE_VERTICES); + g_EntityManager.invalidateAll(); + } + + void Undo() + { + m_TerrainDelta.Undo(); + g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES); + g_EntityManager.invalidateAll(); + } + + void Redo() + { + m_TerrainDelta.Redo(); + g_Game->GetWorld()->GetTerrain()->MakeDirty(RENDERDATA_UPDATE_VERTICES); + g_EntityManager.invalidateAll(); + } + + void MergeWithSelf(cFlattenElevation* prev) + { + prev->m_TerrainDelta.OverlayWith(m_TerrainDelta); + } + +END_COMMAND(FlattenElevation) + } diff --git a/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp b/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp index 876893e992..1b848a72f3 100644 --- a/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp +++ b/source/tools/atlas/GameInterface/Handlers/GraphicsSetup.cpp @@ -2,6 +2,7 @@ #include "MessageHandler.h" #include "../GameLoop.h" +#include "../CommandProc.h" #include "renderer/Renderer.h" #include "gui/GUI.h" @@ -34,7 +35,12 @@ MESSAGEHANDLER_STR(init) MESSAGEHANDLER_STR(shutdown) { + // Empty the CommandProc, to get rid of its references to entities before + // we kill the EntityManager + GetCommandProc().Destroy(); + Shutdown(); + g_GameLoop->rendering = false; } diff --git a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp index b67535f50e..8a94ee7d97 100644 --- a/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -11,6 +11,7 @@ #include "maths/Matrix3D.h" #include "ps/CLogger.h" #include "ps/Game.h" +#include "lib/ogl.h" #define LOG_CATEGORY "editor" @@ -31,6 +32,47 @@ QUERYHANDLER(GetEntitiesList) } +static std::vector g_Selection; +void AtlasRenderSelection() +{ + glDisable(GL_DEPTH_TEST); + for (size_t i = 0; i < g_Selection.size(); ++i) + { + if (g_Selection[i]) + { + CUnit* unit = static_cast(g_Selection[i]); + if (unit->GetEntity()) + unit->GetEntity()->renderSelectionOutline(); + else if (unit->GetModel()) + { + const CBound& bound = unit->GetModel()->GetBounds(); + // Expand bounds by 10% around the centre + CVector3D centre; + bound.GetCentre(centre); + CVector3D a = (bound[0] - centre) * 1.1f + centre; + CVector3D b = (bound[1] - centre) * 1.1f + centre; + float h = g_Game->GetWorld()->GetTerrain()->getExactGroundLevel(centre.X, centre.Z); + glColor3f(0.8f, 0.8f, 0.8f); + glBegin(GL_LINE_LOOP); + glVertex3f(a.X, h, a.Z); + glVertex3f(a.X, h, b.Z); + glVertex3f(b.X, h, b.Z); + glVertex3f(b.X, h, a.Z); + glEnd(); + } + } + } + glEnable(GL_DEPTH_TEST); +} + +MESSAGEHANDLER(SetSelectionPreview) +{ + g_Selection = msg->ids; +} + +////////////////////////////////////////////////////////////////////////// + + static CUnit* g_PreviewUnit = NULL; static CStrW g_PreviewUnitID; @@ -104,13 +146,6 @@ BEGIN_COMMAND(CreateEntity) CVector3D m_Pos; float m_Angle; - void Construct() - { - } - void Destruct() - { - } - void Do() { m_Pos = GetUnitPos(d->pos); @@ -160,4 +195,245 @@ BEGIN_COMMAND(CreateEntity) END_COMMAND(CreateEntity) +QUERYHANDLER(SelectObject) +{ + float x, y; + msg->pos.GetScreenSpace(x, y); + + CVector3D rayorigin, raydir; + g_Game->GetView()->GetCamera()->BuildCameraRay(x, y, rayorigin, raydir); + + CUnit* target = g_UnitMan.PickUnit(rayorigin, raydir); + + msg->id = static_cast(target); +} + + +BEGIN_COMMAND(MoveObject) + + CVector3D m_PosOld, m_PosNew; + + void Do() + { + if (! d->id) + return; + + CUnit* unit = static_cast(d->id); + + if (unit->GetEntity()) + { + m_PosOld = unit->GetEntity()->m_position; + } + else if (unit->GetModel()) + { + CMatrix3D m = unit->GetModel()->GetTransform(); + m_PosOld = m.GetTranslation(); + } + + m_PosNew = GetUnitPos(d->pos); + + SetPos(m_PosNew); + } + + void SetPos(CVector3D& pos) + { + if (! d->id) + return; + + CUnit* unit = static_cast(d->id); + + if (unit->GetEntity()) + { + unit->GetEntity()->m_position = pos; + } + else if (unit->GetModel()) + { + CMatrix3D m = unit->GetModel()->GetTransform(); + m.Translate(pos - m.GetTranslation()); + unit->GetModel()->SetTransform(m); + } + } + + void Redo() + { + SetPos(m_PosNew); + } + + void Undo() + { + SetPos(m_PosOld); + } + + void MergeWithSelf(cMoveObject* prev) + { + // TODO: merge correctly when prev unit != this unit + prev->m_PosNew = m_PosNew; + } + +END_COMMAND(MoveObject) + + +BEGIN_COMMAND(RotateObject) + + float m_AngleOld, m_AngleNew; + CMatrix3D m_TransformOld, m_TransformNew; + + void Do() + { + if (! d->id) + return; + + CUnit* unit = static_cast(d->id); + + if (unit->GetEntity()) + { + m_AngleOld = unit->GetEntity()->m_orientation; + if (d->usetarget) + { + CVector3D& pos = unit->GetEntity()->m_position; + CVector3D target; + d->target.GetWorldSpace(target, pos.Y); + CVector2D dir(target.X-pos.X, target.Z-pos.Z); + m_AngleNew = atan2(dir.x, dir.y); + } + else + { + m_AngleNew = d->angle; + } + } + else if (unit->GetModel()) + { + m_TransformOld = unit->GetModel()->GetTransform(); + + CVector3D pos = unit->GetModel()->GetTransform().GetTranslation(); + + float s, c; + if (d->usetarget) + { + CVector3D target; + d->target.GetWorldSpace(target, pos.Y); + CVector2D dir(target.X-pos.X, target.Z-pos.Z); + dir = dir.normalize(); + s = dir.x; + c = dir.y; + } + else + { + s = sinf(d->angle); + c = cosf(d->angle); + } + CMatrix3D& m = m_TransformNew; + m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = pos.X; + m._21 = 0.0f; m._22 = 1.0f; m._23 = 0.0f; m._24 = pos.Y; + m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = pos.Z; + m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f; + } + + SetAngle(m_AngleNew, m_TransformNew); + } + + void SetAngle(float angle, CMatrix3D& transform) + { + if (! d->id) + return; + + CUnit* unit = static_cast(d->id); + + if (unit->GetEntity()) + { + unit->GetEntity()->m_orientation = angle; + } + else if (unit->GetModel()) + { + unit->GetModel()->SetTransform(transform); + } + } + + void Redo() + { + SetAngle(m_AngleNew, m_TransformNew); + } + + void Undo() + { + SetAngle(m_AngleOld, m_TransformOld); + } + + void MergeWithSelf(cRotateObject* prev) + { + // TODO: merge correctly when prev unit != this unit + prev->m_AngleNew = m_AngleNew; + prev->m_TransformNew = m_TransformNew; + } + +END_COMMAND(RotateObject) + + +BEGIN_COMMAND(DeleteObject) + + bool m_ObjectAlive; + + void Construct() + { + m_ObjectAlive = true; + } + + void Destruct() + { + if (! m_ObjectAlive) + { + if (! d->id) + return; + + CUnit* unit = static_cast(d->id); + + if (unit->GetEntity()) + unit->GetEntity()->kill(); + else + { + g_UnitMan.RemoveUnit(unit); + delete unit; + } + } + } + + void Do() + { + Redo(); + } + + void Redo() + { + if (! d->id) + return; + + CUnit* unit = static_cast(d->id); + + if (unit->GetEntity()) + // HACK: I don't know the proper way of undoably deleting entities... + unit->GetEntity()->m_destroyed = true; + + g_UnitMan.RemoveUnit(unit); + + m_ObjectAlive = false; + } + + void Undo() + { + if (! d->id) + return; + + CUnit* unit = static_cast(d->id); + + if (unit->GetEntity()) + unit->GetEntity()->m_destroyed = false; + + g_UnitMan.AddUnit(unit); + + m_ObjectAlive = true; + } + +END_COMMAND(DeleteObject) + + } diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 1a2d4a758e..32efb45597 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -153,6 +153,11 @@ COMMAND(AlterElevation, MERGE, ((float, amount)) ); +COMMAND(FlattenElevation, MERGE, + ((Position, pos)) + ((float, amount)) + ); + struct ePaintTerrainPriority { enum { HIGH, LOW }; }; COMMAND(PaintTerrain, MERGE, ((Position, pos)) @@ -162,6 +167,36 @@ COMMAND(PaintTerrain, MERGE, ////////////////////////////////////////////////////////////////////////// +typedef void* ObjectID; + +QUERY(SelectObject, + ((Position, pos)) + , + ((ObjectID, id)) + ); + +COMMAND(MoveObject, MERGE, + ((ObjectID, id)) + ((Position, pos)) + ); + +COMMAND(RotateObject, MERGE, + ((ObjectID, id)) + ((bool, usetarget)) // true => use 'target' for orientation; false => use 'angle' + ((Position, target)) + ((float, angle)) + ); + +COMMAND(DeleteObject, NOMERGE, + ((ObjectID, id)) + ); + +MESSAGE(SetSelectionPreview, + ((std::vector, ids)) + ); + +////////////////////////////////////////////////////////////////////////// + #include "MessagesSetup.h" #endif // MESSAGES_H__