LCOV - code coverage report
Current view: top level - source/tools/atlas/GameInterface/Handlers - ElevationHandlers.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 241 0.0 %
Date: 2023-01-19 00:18:29 Functions: 0 81 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "MessageHandler.h"
      21             : 
      22             : #include "../CommandProc.h"
      23             : 
      24             : #include "graphics/RenderableObject.h"
      25             : #include "graphics/Terrain.h"
      26             : #include "graphics/UnitManager.h"
      27             : #include "ps/CStr.h"
      28             : #include "ps/Game.h"
      29             : #include "ps/World.h"
      30             : #include "maths/MathUtil.h"
      31             : #include "simulation2/Simulation2.h"
      32             : #include "simulation2/components/ICmpTerrain.h"
      33             : 
      34             : #include "../Brushes.h"
      35             : #include "../DeltaArray.h"
      36             : 
      37             : namespace AtlasMessage {
      38             : 
      39           0 : class TerrainArray : public DeltaArray2D<u16>
      40             : {
      41             : public:
      42           0 :     void Init()
      43             :     {
      44           0 :         m_Heightmap = g_Game->GetWorld()->GetTerrain()->GetHeightMap();
      45           0 :         m_VertsPerSide = g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide();
      46           0 :     }
      47             : 
      48           0 :     void RaiseVertex(ssize_t x, ssize_t y, int amount)
      49             :     {
      50             :         // Ignore out-of-bounds vertices
      51           0 :         if (size_t(x) >= size_t(m_VertsPerSide) || size_t(y) >= size_t(m_VertsPerSide))
      52           0 :             return;
      53             : 
      54           0 :         set(x, y, static_cast<u16>(Clamp(get(x,y) + amount, 0, 65535)));
      55             :     }
      56             : 
      57           0 :     void MoveVertexTowards(ssize_t x, ssize_t y, int target, int amount)
      58             :     {
      59           0 :         if (size_t(x) >= size_t(m_VertsPerSide) || size_t(y) >= size_t(m_VertsPerSide))
      60           0 :             return;
      61             : 
      62           0 :         int h = get(x,y);
      63           0 :         if (h < target)
      64           0 :             h = std::min(target, h + amount);
      65           0 :         else if (h > target)
      66           0 :             h = std::max(target, h - amount);
      67             :         else
      68           0 :             return;
      69             : 
      70           0 :         set(x, y, static_cast<u16>(Clamp(h, 0, 65535)));
      71             :     }
      72             : 
      73             :     void SetVertex(ssize_t x, ssize_t y, u16 value)
      74             :     {
      75             :         if (size_t(x) >= size_t(m_VertsPerSide) || size_t(y) >= size_t(m_VertsPerSide))
      76             :             return;
      77             : 
      78             :         set(x,y, value);
      79             :     }
      80             : 
      81           0 :     u16 GetVertex(ssize_t x, ssize_t y)
      82             :     {
      83           0 :         return get(Clamp<ssize_t>(x, 0, m_VertsPerSide - 1), Clamp<ssize_t>(y, 0, m_VertsPerSide - 1));
      84             :     }
      85             : 
      86             : protected:
      87           0 :     u16 getOld(ssize_t x, ssize_t y)
      88             :     {
      89           0 :         return m_Heightmap[y*m_VertsPerSide + x];
      90             :     }
      91           0 :     void setNew(ssize_t x, ssize_t y, const u16& val)
      92             :     {
      93           0 :         m_Heightmap[y*m_VertsPerSide + x] = val;
      94           0 :     }
      95             : 
      96             :     u16* m_Heightmap;
      97             :     ssize_t m_VertsPerSide;
      98             : };
      99             : 
     100             : //////////////////////////////////////////////////////////////////////////
     101             : 
     102           0 : BEGIN_COMMAND(AlterElevation)
     103             : {
     104             :     TerrainArray m_TerrainDelta;
     105             :     ssize_t m_i0, m_j0, m_i1, m_j1; // dirtied tiles (inclusive lower bound, exclusive upper)
     106             : 
     107           0 :     cAlterElevation()
     108           0 :     {
     109           0 :         m_TerrainDelta.Init();
     110           0 :     }
     111             : 
     112           0 :     void MakeDirty()
     113             :     {
     114           0 :         g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     115           0 :         g_Game->GetWorld()->GetUnitManager().MakeTerrainDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     116           0 :         CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     117           0 :         if (cmpTerrain)
     118           0 :             cmpTerrain->MakeDirty(m_i0, m_j0, m_i1, m_j1);
     119           0 :     }
     120             : 
     121           0 :     void Do()
     122             :     {
     123           0 :         int amount = (int)msg->amount;
     124             : 
     125             :         // If the framerate is very high, 'amount' is often very
     126             :         // small (even zero) so the integer truncation is significant
     127             :         static float roundingError = 0.0;
     128           0 :         roundingError += msg->amount - (float)amount;
     129           0 :         if (roundingError >= 1.f)
     130             :         {
     131           0 :             amount += (int)roundingError;
     132           0 :             roundingError -= (float)(int)roundingError;
     133             :         }
     134             : 
     135           0 :         static CVector3D previousPosition;
     136           0 :         g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace(previousPosition);
     137           0 :         previousPosition = g_CurrentBrush.m_Centre;
     138             : 
     139             :         ssize_t x0, y0;
     140           0 :         g_CurrentBrush.GetBottomLeft(x0, y0);
     141             : 
     142           0 :         for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy)
     143             :         {
     144           0 :             for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx)
     145             :             {
     146             :                 // TODO: proper variable raise amount (store floats in terrain delta array?)
     147           0 :                 float b = g_CurrentBrush.Get(dx, dy);
     148           0 :                 if (b)
     149           0 :                     m_TerrainDelta.RaiseVertex(x0+dx, y0+dy, (int)(amount*b));
     150             :             }
     151             :         }
     152             : 
     153           0 :         m_i0 = x0 - 1;
     154           0 :         m_j0 = y0 - 1;
     155           0 :         m_i1 = x0 + g_CurrentBrush.m_W;
     156           0 :         m_j1 = y0 + g_CurrentBrush.m_H;
     157           0 :         MakeDirty();
     158           0 :     }
     159             : 
     160           0 :     void Undo()
     161             :     {
     162           0 :         m_TerrainDelta.Undo();
     163           0 :         MakeDirty();
     164           0 :     }
     165             : 
     166           0 :     void Redo()
     167             :     {
     168           0 :         m_TerrainDelta.Redo();
     169           0 :         MakeDirty();
     170           0 :     }
     171             : 
     172           0 :     void MergeIntoPrevious(cAlterElevation* prev)
     173             :     {
     174           0 :         prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
     175           0 :         prev->m_i0 = std::min(prev->m_i0, m_i0);
     176           0 :         prev->m_j0 = std::min(prev->m_j0, m_j0);
     177           0 :         prev->m_i1 = std::max(prev->m_i1, m_i1);
     178           0 :         prev->m_j1 = std::max(prev->m_j1, m_j1);
     179           0 :     }
     180             : };
     181           0 : END_COMMAND(AlterElevation)
     182             : 
     183             : //////////////////////////////////////////////////////////////////////////
     184             : 
     185           0 : BEGIN_COMMAND(SmoothElevation)
     186             : {
     187             :     TerrainArray m_TerrainDelta;
     188             :     ssize_t m_i0, m_j0, m_i1, m_j1; // dirtied tiles (inclusive lower bound, exclusive upper)
     189             : 
     190           0 :     cSmoothElevation()
     191           0 :     {
     192           0 :         m_TerrainDelta.Init();
     193           0 :     }
     194             : 
     195           0 :     void MakeDirty()
     196             :     {
     197           0 :         g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     198           0 :         g_Game->GetWorld()->GetUnitManager().MakeTerrainDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     199           0 :         CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     200           0 :         if (cmpTerrain)
     201           0 :             cmpTerrain->MakeDirty(m_i0, m_j0, m_i1, m_j1);
     202           0 :     }
     203             : 
     204           0 :     void Do()
     205             :     {
     206           0 :         int amount = (int)msg->amount;
     207             : 
     208             :         // If the framerate is very high, 'amount' is often very
     209             :         // small (even zero) so the integer truncation is significant
     210             :         static float roundingError = 0.0;
     211           0 :         roundingError += msg->amount - (float)amount;
     212           0 :         if (roundingError >= 1.f)
     213             :         {
     214           0 :             amount += (int)roundingError;
     215           0 :             roundingError -= (float)(int)roundingError;
     216             :         }
     217             : 
     218           0 :         static CVector3D previousPosition;
     219           0 :         g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace(previousPosition);
     220           0 :         previousPosition = g_CurrentBrush.m_Centre;
     221             : 
     222             :         ssize_t x0, y0;
     223           0 :         g_CurrentBrush.GetBottomLeft(x0, y0);
     224             : 
     225           0 :         if (g_CurrentBrush.m_H > 2)
     226             :         {
     227           0 :             std::vector<float> terrainDeltas;
     228           0 :             ssize_t num = (g_CurrentBrush.m_H - 2) * (g_CurrentBrush.m_W - 2);
     229           0 :             terrainDeltas.resize(num);
     230             : 
     231             :             // For each vertex, compute the average of the 9 adjacent vertices
     232           0 :             for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy)
     233             :             {
     234           0 :                 for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx)
     235             :                 {
     236           0 :                     float delta = m_TerrainDelta.GetVertex(x0+dx, y0+dy) / 9.0f;
     237           0 :                     ssize_t x1_min = std::max((ssize_t)1, dx - 1);
     238           0 :                     ssize_t x1_max = std::min(dx + 1, g_CurrentBrush.m_W - 2);
     239           0 :                     ssize_t y1_min = std::max((ssize_t)1, dy - 1);
     240           0 :                     ssize_t y1_max = std::min(dy + 1, g_CurrentBrush.m_H - 2);
     241             : 
     242           0 :                     for (ssize_t yy = y1_min; yy <= y1_max; ++yy)
     243             :                     {
     244           0 :                         for (ssize_t xx = x1_min; xx <= x1_max; ++xx)
     245             :                         {
     246           0 :                             ssize_t index = (yy-1)*(g_CurrentBrush.m_W-2) + (xx-1);
     247           0 :                             terrainDeltas[index] += delta;
     248             :                         }
     249             :                     }
     250             :                 }
     251             :             }
     252             : 
     253             :             // Move each vertex towards the computed average of its neighbours
     254           0 :             for (ssize_t dy = 1; dy < g_CurrentBrush.m_H - 1; ++dy)
     255             :             {
     256           0 :                 for (ssize_t dx = 1; dx < g_CurrentBrush.m_W - 1; ++dx)
     257             :                 {
     258           0 :                     ssize_t index = (dy-1)*(g_CurrentBrush.m_W-2) + (dx-1);
     259           0 :                     float b = g_CurrentBrush.Get(dx, dy);
     260           0 :                     if (b)
     261           0 :                         m_TerrainDelta.MoveVertexTowards(x0+dx, y0+dy, (int)terrainDeltas[index], (int)(amount*b));
     262             :                 }
     263             :             }
     264             :         }
     265             : 
     266           0 :         m_i0 = x0;
     267           0 :         m_j0 = y0;
     268           0 :         m_i1 = x0 + g_CurrentBrush.m_W - 1;
     269           0 :         m_j1 = y0 + g_CurrentBrush.m_H - 1;
     270           0 :         MakeDirty();
     271           0 :     }
     272             : 
     273           0 :     void Undo()
     274             :     {
     275           0 :         m_TerrainDelta.Undo();
     276           0 :         MakeDirty();
     277           0 :     }
     278             : 
     279           0 :     void Redo()
     280             :     {
     281           0 :         m_TerrainDelta.Redo();
     282           0 :         MakeDirty();
     283           0 :     }
     284             : 
     285           0 :     void MergeIntoPrevious(cSmoothElevation* prev)
     286             :     {
     287           0 :         prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
     288           0 :         prev->m_i0 = std::min(prev->m_i0, m_i0);
     289           0 :         prev->m_j0 = std::min(prev->m_j0, m_j0);
     290           0 :         prev->m_i1 = std::max(prev->m_i1, m_i1);
     291           0 :         prev->m_j1 = std::max(prev->m_j1, m_j1);
     292           0 :     }
     293             : };
     294           0 : END_COMMAND(SmoothElevation)
     295             : 
     296             : //////////////////////////////////////////////////////////////////////////
     297             : 
     298           0 : BEGIN_COMMAND(FlattenElevation)
     299             : {
     300             :     TerrainArray m_TerrainDelta;
     301             :     ssize_t m_i0, m_j0, m_i1, m_j1; // dirtied tiles (inclusive lower bound, exclusive upper)
     302             : 
     303           0 :     cFlattenElevation()
     304           0 :     {
     305           0 :         m_TerrainDelta.Init();
     306           0 :     }
     307             : 
     308           0 :     void MakeDirty()
     309             :     {
     310           0 :         g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     311           0 :         g_Game->GetWorld()->GetUnitManager().MakeTerrainDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     312           0 :         CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     313           0 :         if (cmpTerrain)
     314           0 :             cmpTerrain->MakeDirty(m_i0, m_j0, m_i1, m_j1);
     315           0 :     }
     316             : 
     317           0 :     void Do()
     318             :     {
     319           0 :         int amount = (int)msg->amount;
     320             : 
     321           0 :         static CVector3D previousPosition;
     322           0 :         g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace(previousPosition);
     323           0 :         previousPosition = g_CurrentBrush.m_Centre;
     324             : 
     325             :         ssize_t xc, yc;
     326           0 :         g_CurrentBrush.GetCentre(xc, yc);
     327           0 :         u16 height = m_TerrainDelta.GetVertex(xc, yc);
     328             : 
     329             :         ssize_t x0, y0;
     330           0 :         g_CurrentBrush.GetBottomLeft(x0, y0);
     331             : 
     332           0 :         for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy)
     333             :         {
     334           0 :             for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx)
     335             :             {
     336           0 :                 float b = g_CurrentBrush.Get(dx, dy);
     337           0 :                 if (b)
     338           0 :                     m_TerrainDelta.MoveVertexTowards(x0+dx, y0+dy, height, 1 + (int)(b*amount));
     339             :             }
     340             :         }
     341             : 
     342           0 :         m_i0 = x0 - 1;
     343           0 :         m_j0 = y0 - 1;
     344           0 :         m_i1 = x0 + g_CurrentBrush.m_W;
     345           0 :         m_j1 = y0 + g_CurrentBrush.m_H;
     346           0 :         MakeDirty();
     347           0 :     }
     348             : 
     349           0 :     void Undo()
     350             :     {
     351           0 :         m_TerrainDelta.Undo();
     352           0 :         MakeDirty();
     353           0 :     }
     354             : 
     355           0 :     void Redo()
     356             :     {
     357           0 :         m_TerrainDelta.Redo();
     358           0 :         MakeDirty();
     359           0 :     }
     360             : 
     361           0 :     void MergeIntoPrevious(cFlattenElevation* prev)
     362             :     {
     363           0 :         prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
     364           0 :         prev->m_i0 = std::min(prev->m_i0, m_i0);
     365           0 :         prev->m_j0 = std::min(prev->m_j0, m_j0);
     366           0 :         prev->m_i1 = std::max(prev->m_i1, m_i1);
     367           0 :         prev->m_j1 = std::max(prev->m_j1, m_j1);
     368           0 :     }
     369             : };
     370           0 : END_COMMAND(FlattenElevation)
     371             : 
     372             : 
     373           0 : BEGIN_COMMAND(PikeElevation)
     374             : {
     375             :     TerrainArray m_TerrainDelta;
     376             :     ssize_t m_i0, m_j0, m_i1, m_j1; // dirtied tiles (inclusive lower bound, exclusive upper)
     377             : 
     378           0 :     cPikeElevation()
     379           0 :     {
     380           0 :         m_TerrainDelta.Init();
     381           0 :     }
     382             : 
     383           0 :     void MakeDirty()
     384             :     {
     385           0 :         g_Game->GetWorld()->GetTerrain()->MakeDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     386           0 :         g_Game->GetWorld()->GetUnitManager().MakeTerrainDirty(m_i0, m_j0, m_i1, m_j1, RENDERDATA_UPDATE_VERTICES);
     387           0 :         CmpPtr<ICmpTerrain> cmpTerrain(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
     388           0 :         if (cmpTerrain)
     389           0 :             cmpTerrain->MakeDirty(m_i0, m_j0, m_i1, m_j1);
     390           0 :     }
     391             : 
     392           0 :     void Do()
     393             :     {
     394           0 :         int amount = (int)msg->amount;
     395             : 
     396             :         // If the framerate is very high, 'amount' is often very
     397             :         // small (even zero) so the integer truncation is significant
     398             :         static float roundingError = 0.0;
     399           0 :         roundingError += msg->amount - (float)amount;
     400           0 :         if (roundingError >= 1.f)
     401             :         {
     402           0 :             amount += (int)roundingError;
     403           0 :             roundingError -= (float)(int)roundingError;
     404             :         }
     405             : 
     406           0 :         static CVector3D previousPosition;
     407           0 :         g_CurrentBrush.m_Centre = msg->pos->GetWorldSpace(previousPosition);
     408           0 :         previousPosition = g_CurrentBrush.m_Centre;
     409             : 
     410             :         ssize_t x0, y0;
     411           0 :         g_CurrentBrush.GetBottomLeft(x0, y0);
     412           0 :         float h = ((float) g_CurrentBrush.m_H - 1) / 2.f;
     413             : 
     414           0 :         for (ssize_t dy = 0; dy < g_CurrentBrush.m_H; ++dy)
     415             :         {
     416           0 :             for (ssize_t dx = 0; dx < g_CurrentBrush.m_W; ++dx)
     417             :             {
     418           0 :                 float b = g_CurrentBrush.Get(dx, dy);
     419           0 :                 if (b)
     420             :                 {
     421           0 :                     float x = (float)dx - ((float)g_CurrentBrush.m_H - 1) / 2.f;
     422           0 :                     float y = (float)dy - ((float)g_CurrentBrush.m_W - 1) / 2.f;
     423           0 :                     float distance = Clamp(1 - static_cast<float>(sqrt(x * x + y * y)) / h, 0.01f, 1.0f);
     424           0 :                     distance *= distance;
     425           0 :                     m_TerrainDelta.RaiseVertex(x0 + dx, y0 + dy, (int)(amount * distance));
     426             :                 }
     427             :             }
     428             :         }
     429           0 :         m_i0 = x0 - 1;
     430           0 :         m_j0 = y0 - 1;
     431           0 :         m_i1 = x0 + g_CurrentBrush.m_W;
     432           0 :         m_j1 = y0 + g_CurrentBrush.m_H;
     433           0 :         MakeDirty();
     434           0 :     }
     435             : 
     436           0 :     void Undo()
     437             :     {
     438           0 :         m_TerrainDelta.Undo();
     439           0 :         MakeDirty();
     440           0 :     }
     441             : 
     442           0 :     void Redo()
     443             :     {
     444           0 :         m_TerrainDelta.Redo();
     445           0 :         MakeDirty();
     446           0 :     }
     447             : 
     448           0 :     void MergeIntoPrevious(cPikeElevation* prev)
     449             :     {
     450           0 :         prev->m_TerrainDelta.OverlayWith(m_TerrainDelta);
     451           0 :         prev->m_i0 = std::min(prev->m_i0, m_i0);
     452           0 :         prev->m_j0 = std::min(prev->m_j0, m_j0);
     453           0 :         prev->m_i1 = std::max(prev->m_i1, m_i1);
     454           0 :         prev->m_j1 = std::max(prev->m_j1, m_j1);
     455           0 :     }
     456             : };
     457           0 : END_COMMAND(PikeElevation)
     458             : 
     459             : }

Generated by: LCOV version 1.13