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))