From 40e268e06a1ac7948ed3b11533ec88bcbdb27d88 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Thu, 5 Aug 2010 20:43:31 +0000 Subject: [PATCH] # Add terrain smoothing tool to Atlas, based on patch from DigitalSeraphim. Fixes #516. This was SVN commit r7854. --- .../tools/atlas/scripts/section/terrain.js | 1 + .../tools/atlas/toolbar/smoothelevation.png | 3 + .../AtlasUI/ScenarioEditor/ScenarioEditor.cpp | 1 + .../Sections/Terrain/Terrain.cpp | 1 + .../ScenarioEditor/Tools/SmoothElevation.cpp | 140 ++++++++++++++++++ .../Handlers/ElevationHandlers.cpp | 113 ++++++++++++++ source/tools/atlas/GameInterface/Messages.h | 5 + 7 files changed, 264 insertions(+) create mode 100644 binaries/data/tools/atlas/toolbar/smoothelevation.png create mode 100644 source/tools/atlas/AtlasUI/ScenarioEditor/Tools/SmoothElevation.cpp diff --git a/binaries/data/tools/atlas/scripts/section/terrain.js b/binaries/data/tools/atlas/scripts/section/terrain.js index 05d77a664b..9d3e6b9d21 100644 --- a/binaries/data/tools/atlas/scripts/section/terrain.js +++ b/binaries/data/tools/atlas/scripts/section/terrain.js @@ -129,6 +129,7 @@ function init(window, bottomWindow) var tools = [ { label: 'Modify', name: 'AlterElevation' }, + { label: 'Smooth', name: 'SmoothElevation' }, { label: 'Flatten', name: 'FlattenElevation' }, { label: 'Paint', name: 'PaintTerrain' }, ]; diff --git a/binaries/data/tools/atlas/toolbar/smoothelevation.png b/binaries/data/tools/atlas/toolbar/smoothelevation.png new file mode 100644 index 0000000000..6fc36ef433 --- /dev/null +++ b/binaries/data/tools/atlas/toolbar/smoothelevation.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a94e39d63db9302d0bbbb5b6e1dcc43a426d87823639156a7d489d6111d6764 +size 2002 diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp index d5fb7559e5..27f7f7ae49 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/ScenarioEditor.cpp @@ -474,6 +474,7 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac toolbar->AddToolButton(_("Default"), _("Default"), _T("default.png"), _T(""), _T("")); toolbar->AddToolButton(_("Move"), _("Move/rotate object"), _T("moveobject.png"), _T("TransformObject"), _T("")/*_T("ObjectSidebar")*/); toolbar->AddToolButton(_("Elevation"), _("Alter terrain elevation"), _T("alterelevation.png"), _T("AlterElevation"), _T("")/*_T("TerrainSidebar")*/); + toolbar->AddToolButton(_("Smooth"), _("Smooth terrain elevation"), _T("smoothelevation.png"), _T("SmoothElevation"), _T("")/*_T("TerrainSidebar")*/); toolbar->AddToolButton(_("Flatten"), _("Flatten terrain elevation"), _T("flattenelevation.png"), _T("FlattenElevation"), _T("")/*_T("TerrainSidebar")*/); toolbar->AddToolButton(_("Paint Terrain"), _("Paint terrain texture"), _T("paintterrain.png"), _T("PaintTerrain"), _T("")/*_T("TerrainSidebar")*/); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp index 9c88b7fe4d..4f7b9835f7 100644 --- a/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Sections/Terrain/Terrain.cpp @@ -56,6 +56,7 @@ TerrainSidebar::TerrainSidebar(ScenarioEditor& scenarioEditor, wxWindow* sidebar { wxSizer* sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Elevation tools")); sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Modify"), _T("AlterElevation"), wxSize(50,20))); + sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Smooth"), _T("SmoothElevation"), wxSize(50,20))); sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Flatten"), _T("FlattenElevation"), wxSize(50,20))); // sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Smooth"), _T(""), wxSize(50,20))); // sizer->Add(new ToolButton(scenarioEditor.GetToolManager(), this, _("Sample"), _T(""), wxSize(50,20))); diff --git a/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/SmoothElevation.cpp b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/SmoothElevation.cpp new file mode 100644 index 0000000000..cdc5a799b0 --- /dev/null +++ b/source/tools/atlas/AtlasUI/ScenarioEditor/Tools/SmoothElevation.cpp @@ -0,0 +1,140 @@ +/* Copyright (C) 2010 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "ScenarioEditor/ScenarioEditor.h" +#include "Common/Tools.h" +#include "Common/Brushes.h" +#include "GameInterface/Messages.h" + + +using AtlasMessage::Position; + +class SmoothElevation : public StateDrivenTool +{ + DECLARE_DYNAMIC_CLASS(SmoothElevation); + + int m_Direction; // +1 = smoother, -1 = rougher + Position m_Pos; + +public: + SmoothElevation() + { + SetState(&Waiting); + } + + + void OnEnable() + { + g_Brush_Elevation.MakeActive(); + } + + void OnDisable() + { + POST_MESSAGE(BrushPreview, (false, Position())); + } + + + struct sWaiting : public State + { + bool OnMouse(SmoothElevation* obj, wxMouseEvent& evt) + { + if (evt.LeftDown()) + { + obj->m_Pos = Position(evt.GetPosition()); + SET_STATE(Smoothing); + return true; + } + else if (evt.RightDown()) + { + obj->m_Pos = Position(evt.GetPosition()); + SET_STATE(Roughing); + return true; + } + else if (evt.Moving()) + { + POST_MESSAGE(BrushPreview, (true, Position(evt.GetPosition()))); + return true; + } + else + { + return false; + } + } + } + Waiting; + + + struct sSmoothing_common : public State + { + void OnEnter(SmoothElevation* obj) + { + POST_MESSAGE(BrushPreview, (true, obj->m_Pos)); + } + + void OnLeave(SmoothElevation*) + { + ScenarioEditor::GetCommandProc().FinaliseLastCommand(); + } + + bool OnMouse(SmoothElevation* obj, wxMouseEvent& evt) + { + if (IsMouseUp(evt)) + { + 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(SmoothElevation* obj, float dt) + { + POST_COMMAND(SmoothElevation, (obj->m_Pos, dt*4096.f*GetDirection()*g_Brush_Elevation.GetStrength())); + obj->m_Pos = Position::Unchanged(); + } + + virtual bool IsMouseUp(wxMouseEvent& evt) = 0; + virtual int GetDirection() = 0; + }; + + struct sSmoothing : public sSmoothing_common + { + bool IsMouseUp(wxMouseEvent& evt) { return evt.LeftUp(); } + int GetDirection() { return +1; } + } + Smoothing; + + struct sRoughing : public sSmoothing_common + { + bool IsMouseUp(wxMouseEvent& evt) { return evt.RightUp(); } + int GetDirection() { return -1; } + } + Roughing; +}; + +IMPLEMENT_DYNAMIC_CLASS(SmoothElevation, StateDrivenTool); diff --git a/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp b/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp index 2c55e621c6..acd0c3344e 100644 --- a/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp +++ b/source/tools/atlas/GameInterface/Handlers/ElevationHandlers.cpp @@ -96,6 +96,7 @@ protected: ssize_t m_VertsPerSide; }; +////////////////////////////////////////////////////////////////////////// BEGIN_COMMAND(AlterElevation) { @@ -177,6 +178,118 @@ BEGIN_COMMAND(AlterElevation) }; END_COMMAND(AlterElevation) +////////////////////////////////////////////////////////////////////////// + +BEGIN_COMMAND(SmoothElevation) +{ + TerrainArray m_TerrainDelta; + ssize_t m_i0, m_j0, m_i1, m_j1; + + cSmoothElevation() + { + m_TerrainDelta.Init(); + } + + void MakeDirty() + { + g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES); + CmpPtr cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY); + if (!cmpTerrain.null()) + cmpTerrain->MakeDirty(m_i0, m_j0, m_i1, m_j1); + } + + void Do() + { + int amount = (int)msg->amount; + + // If the framerate is very high, 'amount' is often very + // small (even zero) so the integer truncation is significant + static float roundingError = 0.0; + roundingError += msg->amount - (float)amount; + if (roundingError >= 1.f) + { + amount += (int)roundingError; + roundingError -= (float)(int)roundingError; + } + + static CVector3D previousPosition; + g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace(previousPosition); + previousPosition = g_CurrentBrush.m_Centre; + + ssize_t x0, y0; + g_CurrentBrush.GetBottomLeft(x0, y0); + + if (g_CurrentBrush.m_H > 2) + { + std::vector terrainDeltas; + ssize_t num = (g_CurrentBrush.m_H - 2) * (g_CurrentBrush.m_W - 2); + terrainDeltas.resize(num); + + // For each vertex, compute the average of the 9 adjacent vertices + for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy) + { + for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx) + { + float delta = m_TerrainDelta.GetVertex(x0+dx, y0+dy) / 9.0f; + ssize_t x1_min = std::max((ssize_t)1, dx - 1); + ssize_t x1_max = std::min(dx + 1, g_CurrentBrush.m_W - 2); + ssize_t y1_min = std::max((ssize_t)1, dy - 1); + ssize_t y1_max = std::min(dy + 1, g_CurrentBrush.m_H - 2); + + for (ssize_t yy = y1_min; yy <= y1_max; ++yy) + { + for (ssize_t xx = x1_min; xx <= x1_max; ++xx) + { + ssize_t index = (yy-1)*(g_CurrentBrush.m_W-2) + (xx-1); + terrainDeltas[index] += delta; + } + } + } + } + + // Move each vertex towards the computed average of its neighbours + for (ssize_t dy = 1; dy < g_CurrentBrush.m_H - 1; ++dy) + { + for (ssize_t dx = 1; dx < g_CurrentBrush.m_W - 1; ++dx) + { + ssize_t index = (dy-1)*(g_CurrentBrush.m_W-2) + (dx-1); + float b = g_CurrentBrush.Get(dx, dy); + if (b) + m_TerrainDelta.MoveVertexTowards(x0+dx, y0+dy, (int)terrainDeltas[index], (int)(amount*b)); + } + } + } + + m_i0 = x0; + m_j0 = y0; + m_i1 = x0 + g_CurrentBrush.m_W; + m_j1 = y0 + g_CurrentBrush.m_H; + MakeDirty(); + } + + void Undo() + { + m_TerrainDelta.Undo(); + MakeDirty(); + } + + void Redo() + { + m_TerrainDelta.Redo(); + MakeDirty(); + } + + void MergeIntoPrevious(cSmoothElevation* prev) + { + prev->m_TerrainDelta.OverlayWith(m_TerrainDelta); + prev->m_i0 = std::min(prev->m_i0, m_i0); + prev->m_j0 = std::min(prev->m_j0, m_j0); + prev->m_i1 = std::max(prev->m_i1, m_i1); + prev->m_j1 = std::max(prev->m_j1, m_j1); + } +}; +END_COMMAND(SmoothElevation) + ////////////////////////////////////////////////////////////////////////// BEGIN_COMMAND(FlattenElevation) diff --git a/source/tools/atlas/GameInterface/Messages.h b/source/tools/atlas/GameInterface/Messages.h index 0b7d819b65..398872fd3a 100644 --- a/source/tools/atlas/GameInterface/Messages.h +++ b/source/tools/atlas/GameInterface/Messages.h @@ -372,6 +372,11 @@ COMMAND(AlterElevation, MERGE, ((float, amount)) ); +COMMAND(SmoothElevation, MERGE, + ((Position, pos)) + ((float, amount)) + ); + COMMAND(FlattenElevation, MERGE, ((Position, pos)) ((float, amount))