Ticket #2264: TerrainFlattening.2.diff

File TerrainFlattening.2.diff, 17.3 KB (added by wraitii, 10 years ago)
  • source/simulation2/components/ICmpTerrainModifier.h

     
     1/* Copyright (C) 2014 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#ifndef INCLUDED_ICMPTERRAINMODIFIER
     19#define INCLUDED_ICMPTERRAINMODIFIER
     20
     21#include "simulation2/system/Interface.h"
     22
     23/**
     24 * Handles terrain changes around an entity
     25 * Currently only handles flattening on building placement.
     26 */
     27class ICmpTerrainModifier : public IComponent
     28{
     29public:
     30    virtual void ApplyTerrainModification() = 0;
     31   
     32    DECLARE_INTERFACE_TYPE(TerrainModifier)
     33};
     34
     35#endif // INCLUDED_ICMPTERRAINMODIFIER
  • source/simulation2/components/CCmpTerrainModifier.cpp

    Property changes on: source/simulation2/components/ICmpTerrainModifier.h
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1/* Copyright (C) 2013 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 "simulation2/system/Component.h"
     21#include "ICmpTerrainModifier.h"
     22
     23#include "graphics/Terrain.h"
     24#include "ps/CLogger.h"
     25#include "simulation2/MessageTypes.h"
     26#include "simulation2/serialization/SerializeTemplates.h"
     27
     28#include "simulation2/components/ICmpFootprint.h"
     29#include "simulation2/components/ICmpPosition.h"
     30#include "simulation2/components/ICmpTerrain.h"
     31#include "simulation2/helpers/Geometry.h"
     32
     33
     34/**
     35 * Handles terrain changes around an entity
     36 * Currently only handles flattening on building placement.
     37 */
     38class CCmpTerrainModifier : public ICmpTerrainModifier
     39{
     40private:
     41    // used to expand outwards.
     42    struct Point {
     43        Point() : X(0), Y(0), parentX(0), parentY(0), generation(1), originalHeight(0)
     44        { }
     45        Point(int x, int y, u16 height) : X(x), Y(y), parentX(x), parentY(y), generation(1), originalHeight(height)
     46        {}
     47        Point(int x, int y, int px, int py, int Generation, u16 height) : X(x), Y(y), parentX(px), parentY(py), generation(Generation), originalHeight(height)
     48        {}
     49
     50        int X,Y;
     51        int parentX,parentY;
     52        int generation;
     53        u16 originalHeight;
     54    };
     55   
     56public:
     57    static void ClassInit(CComponentManager& componentManager)
     58    {
     59        componentManager.SubscribeToMessageType(MT_Destroy);
     60    }
     61
     62    DEFAULT_COMPONENT_ALLOCATOR(TerrainModifier)
     63
     64    // Dynamic state:
     65
     66    static std::string GetSchema()
     67    {
     68        return
     69            "<a:example/>"
     70            "<a:help>Causes the entity to flatten the terrain when placed on the map, if possible.</a:help>"
     71            "<empty/>";
     72    }
     73
     74    virtual void Init(const CParamNode& UNUSED(paramNode))
     75    {
     76    }
     77
     78    virtual void Deinit()
     79    {
     80    }
     81
     82    template<typename S>
     83    void SerializeCommon(S& UNUSED(serialize))
     84    {
     85    }
     86
     87    virtual void Serialize(ISerializer& serialize)
     88    {
     89        SerializeCommon(serialize);
     90    }
     91
     92    virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
     93    {
     94        Init(paramNode);
     95
     96        SerializeCommon(deserialize);
     97    }
     98
     99    virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
     100    {
     101        switch (msg.GetType())
     102        {
     103        case MT_Destroy:
     104        {
     105            break;
     106        }
     107        }
     108    }
     109   
     110    // Try to modifiy the terrain around us.
     111    virtual void ApplyTerrainModification()
     112    {
     113        FlattenTerrainAroundCenter();
     114    }
     115   
     116    // Flatten the terrain around us.
     117    void FlattenTerrainAroundCenter()
     118    {
     119        CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
     120        if (!cmpPosition)
     121            return;
     122
     123        CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
     124        if (!cmpTerrain)
     125            return;
     126
     127        CmpPtr<ICmpFootprint> cmpFootprint(GetEntityHandle());
     128        if (!cmpFootprint)
     129            return;
     130       
     131        // grab the shape and dimensions of the footprint
     132        entity_pos_t footprintSize0, footprintSize1, footprintHeight;
     133        ICmpFootprint::EShape footprintShape;
     134        cmpFootprint->GetShape(footprintShape, footprintSize0, footprintSize1, footprintHeight);
     135               
     136        CFixedVector2D position = cmpPosition->GetPosition2D();
     137       
     138        fixed originX = (position.X/4);
     139        fixed originY = (position.Y/4);
     140        int posX = originX.ToInt_RoundToZero();
     141        int posY = originY.ToInt_RoundToZero();
     142               
     143        // copy the heightmap
     144        CTerrain* terrain = cmpTerrain->GetCTerrain();
     145        ssize_t width = terrain->GetVerticesPerSide();
     146        u16* heightmap = terrain->GetHeightMap();
     147       
     148        // we'll be hotmodifying the heightmap.
     149       
     150        /* Algorithm is somewhat simple:
     151         * We start from the 4 vertices surrounding the center point
     152         * For each vertice
     153         *      If we've already been added to the closed list, we remove us from the open list
     154         *      Depending on the quadrant (of a circle around the center point) they are in, we add 2 points that go away from the center
     155         */
     156#define tou16(a) (a*(int)HEIGHT_UNITS_PER_METRE).ToInt_RoundToNearest();
     157
     158        u16 originalHeight = tou16(terrain->GetExactGroundLevelFixed(position.X,position.Y));
     159
     160        // let's actually flatten the terrain.
     161        // So what we actually need here is to be quite sure we don't make a passable tile impassable.
     162        // because that has all sorts of weird gameplay side-effects which we do not want.
     163        // so we need to refrain from moving vertices too much.
     164       
     165        // define the 4 corners as base pixels. I'll assume we can't be exactly on a point here, which probably holds true.
     166        std::vector<Point> openList;
     167       
     168        std::stack<Point> lastPoints;
     169       
     170        // acts as a global list of points I've studied.
     171        std::map<std::pair<int,int>, Point> closedList;
     172       
     173        Point basePointA(posX,posY, originalHeight);
     174        Point basePointB(posX,posY+1, originalHeight);
     175        Point basePointC(posX+1,posY, originalHeight);
     176        Point basePointD(posX+1,posY+1, originalHeight);
     177        openList.push_back(basePointA);
     178        openList.push_back(basePointB);
     179        openList.push_back(basePointC);
     180        openList.push_back(basePointD);
     181       
     182       
     183        // for each base pixel on the openList, update
     184        while (openList.size() > 0)
     185        {
     186            Point curPoint = openList[0];
     187            // disregard already moved points or points not on the map (includes the edges, so we don't need to clamp)
     188            if (closedList.find(std::make_pair(curPoint.X,curPoint.Y)) != closedList.end())
     189            {
     190                openList.erase(openList.begin());
     191                continue;
     192            }
     193            if (curPoint.X <= 0 || curPoint.X >= width-1
     194                || curPoint.Y <= 0 || curPoint.Y >= width-1)
     195            {
     196                lastPoints.push(curPoint);
     197                closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
     198                openList.erase(openList.begin());
     199                continue;
     200            }
     201
     202            CFixedVector2D path(fixed::FromInt(curPoint.X) - originX,fixed::FromInt(curPoint.Y) - originY);
     203            fixed distance = path.Length();
     204           
     205            // if we're too far away
     206            if (distance > footprintSize0/2)
     207            {
     208                lastPoints.push(curPoint);
     209                closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
     210                openList.erase(openList.begin());
     211                continue;
     212            }
     213            else if(distance >= footprintSize0/5 && abs(heightmap[curPoint.X + curPoint.Y*width] - originalHeight) < 175)
     214            {
     215                // here we're not under the foundation and it's still flat, so disregard going further.
     216                lastPoints.push(curPoint);
     217                closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
     218                openList.erase(openList.begin());
     219                continue;
     220
     221            }
     222           
     223            path.Normalize();
     224
     225#define checkHeight(X,Y,changeX,changeY) abs(heightmap[X+changeX + (Y+changeY)*width]-heightmap[X + Y*width]) < 4000
     226#define addPoint(id, X,Y,changeX,changeY, generation) id = Point(X+changeX,Y+changeY,X,Y,generation, heightmap[X+changeX + (Y+changeY)*width]);\
     227++addedPoints;\
     228openList.push_back(id);
     229           
     230            int newSquares[2][2] = { {0,0}, {0,0}};
     231
     232            if (path.X <= fixed::Zero() && path.Y <= fixed::Zero())
     233            {
     234                // lower left quadrant
     235                newSquares[0][0] = -1;
     236                newSquares[1][1] = -1;
     237            }
     238            else if (path.X <= fixed::Zero() && path.Y >= fixed::Zero())
     239            {
     240                // upper left quadrant
     241                newSquares[0][0] = -1;
     242                newSquares[1][1] = +1;
     243            }
     244            else if (path.X >= fixed::Zero() && path.Y <= fixed::Zero())
     245            {
     246                // lower right quadrant
     247                newSquares[0][0] = +1;
     248                newSquares[1][1] = -1;
     249            }
     250            else if (path.X >= fixed::Zero() && path.Y >= fixed::Zero())
     251            {
     252                // upper right quadrant
     253                newSquares[0][0] = +1;
     254                newSquares[1][1] = +1;
     255            }
     256            int generation = 1;
     257            if(distance >= footprintSize0/5 && distance <= footprintSize0/2)
     258                generation = curPoint.generation+1;
     259           
     260            int addedPoints = 0;
     261            Point newA;
     262            Point newB;
     263            // let's check that the slope isn't too big in that direction.
     264            if (checkHeight(curPoint.X,curPoint.Y,newSquares[0][0],newSquares[0][1]))
     265                addPoint(newA,curPoint.X,curPoint.Y,newSquares[0][0],newSquares[0][1], generation);
     266            if (checkHeight(curPoint.X,curPoint.Y,newSquares[1][0],newSquares[1][1]))
     267                addPoint(newB,curPoint.X,curPoint.Y,newSquares[1][0],newSquares[1][1], generation);
     268
     269#undef addPoint
     270#undef checkHeight
     271            if (addedPoints == 0)
     272                lastPoints.push(curPoint);
     273           
     274            closedList[std::make_pair(curPoint.X,curPoint.Y)] = curPoint;
     275            openList.erase(openList.begin());
     276        }
     277        // Now for each point, we'll move towards the last parent, flattening a bit each time.
     278        // we'll do it linearly for now, since we now of many points we'll go through till the center.
     279        while (lastPoints.size() > 0)
     280        {
     281            Point curPoint = lastPoints.top();
     282            lastPoints.pop();
     283            int startGeneration = curPoint.generation;
     284            do
     285            {
     286                u16 myHeight = curPoint.originalHeight;
     287                // target height is original height
     288                fixed coeff = fixed::FromInt(curPoint.generation) / startGeneration;
     289                fixed height = coeff * (int)myHeight;
     290                height += (fixed::FromInt(1) - coeff) * (int)originalHeight;
     291                heightmap[width*curPoint.Y + curPoint.X] = height.ToInt_RoundToNearest();
     292               
     293                curPoint = closedList.find(std::make_pair(curPoint.parentX,curPoint.parentY))->second;
     294            } while(curPoint.X != curPoint.parentX || curPoint.Y != curPoint.parentY);
     295        }
     296
     297        // upload the modified heightmap.
     298        terrain->SetHeightMap(heightmap);
     299        cmpTerrain->MakeDirty(position.X.ToInt_RoundToZero(), position.X.ToInt_RoundToInfinity(),
     300                              position.Y.ToInt_RoundToZero(), position.Y.ToInt_RoundToInfinity());
     301    }
     302};
     303
     304REGISTER_COMPONENT_TYPE(TerrainModifier)
  • source/simulation2/components/ICmpTerrainModifier.cpp

    Property changes on: source/simulation2/components/CCmpTerrainModifier.cpp
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
     1/* Copyright (C) 2014 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 "ICmpTerrainModifier.h"
     21
     22#include "simulation2/system/InterfaceScripted.h"
     23#include "simulation2/system/SimContext.h"
     24
     25BEGIN_INTERFACE_WRAPPER(TerrainModifier)
     26DEFINE_INTERFACE_METHOD_0("ApplyTerrainModification", void, ICmpTerrainModifier, ApplyTerrainModification)
     27/*DEFINE_INTERFACE_METHOD_2("CheckFoundation", std::string, ICmpObstruction, CheckFoundation_wrapper, std::string, bool)
     28DEFINE_INTERFACE_METHOD_0("CheckDuplicateFoundation", bool, ICmpObstruction, CheckDuplicateFoundation)
     29DEFINE_INTERFACE_METHOD_2("GetEntityCollisions", std::vector<entity_id_t>, ICmpObstruction, GetEntityCollisions, bool, bool)
     30DEFINE_INTERFACE_METHOD_1("SetActive", void, ICmpObstruction, SetActive, bool)
     31DEFINE_INTERFACE_METHOD_3("SetDisableBlockMovementPathfinding", void, ICmpObstruction, SetDisableBlockMovementPathfinding, bool, bool, int32_t)
     32DEFINE_INTERFACE_METHOD_0("GetBlockMovementFlag", bool, ICmpObstruction, GetBlockMovementFlag)
     33DEFINE_INTERFACE_METHOD_1("SetControlGroup", void, ICmpObstruction, SetControlGroup, entity_id_t)
     34DEFINE_INTERFACE_METHOD_0("GetControlGroup", entity_id_t, ICmpObstruction, GetControlGroup)
     35DEFINE_INTERFACE_METHOD_1("SetControlGroup2", void, ICmpObstruction, SetControlGroup2, entity_id_t)
     36DEFINE_INTERFACE_METHOD_0("GetControlGroup2", entity_id_t, ICmpObstruction, GetControlGroup2)
     37*/END_INTERFACE_WRAPPER(TerrainModifier)
  • binaries/data/mods/public/simulation/helpers/Commands.js

    Property changes on: source/simulation2/components/ICmpTerrainModifier.cpp
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
     
    876876    var cmpFoundation = Engine.QueryInterface(ent, IID_Foundation);
    877877    cmpFoundation.InitialiseConstruction(player, cmd.template);
    878878
     879
     880    // If it modifies the terrain, apply the changes
     881    var CmpTerrainModifier = Engine.QueryInterface(ent, IID_TerrainModifier);
     882    if (CmpTerrainModifier)
     883        CmpTerrainModifier.ApplyTerrainModification();
     884
    879885    // send Metadata info if any
    880886    if (cmd.metadata)
    881887        Engine.PostMessage(ent, MT_AIMetadata, { "id": ent, "metadata" : cmd.metadata, "owner" : player } );
  • binaries/data/mods/public/simulation/templates/template_structure.xml

     
    9797    <BarHeight>0.6</BarHeight>
    9898    <HeightOffset>12.0</HeightOffset>
    9999  </StatusBars>
     100  <TerrainModifier/>
    100101  <TerritoryDecay>
    101102    <HealthDecayRate>5</HealthDecayRate>
    102103  </TerritoryDecay>
  • source/simulation2/TypeList.h

     
    149149INTERFACE(Terrain)
    150150COMPONENT(Terrain)
    151151
     152INTERFACE(TerrainModifier)
     153COMPONENT(TerrainModifier)
     154
    152155INTERFACE(TerritoryInfluence)
    153156COMPONENT(TerritoryInfluence)
    154157
  • source/simulation2/components/CCmpTemplateManager.cpp

     
    572572    permittedComponentTypes.insert("AIProxy");
    573573    permittedComponentTypes.insert("RallyPoint");
    574574    permittedComponentTypes.insert("RallyPointRenderer");
     575    permittedComponentTypes.insert("TerrainModifier");
    575576
    576577    CParamNode::LoadXMLString(out, "<Entity/>");
    577578    out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
  • source/simulation2/components/CCmpTerrain.cpp

     
    2525#include "simulation2/MessageTypes.h"
    2626
    2727#include "graphics/Terrain.h"
     28#include "graphics/RenderableObject.h"
     29#include "ps/Game.h"
     30#include "ps/World.h"
    2831#include "renderer/Renderer.h"
    2932#include "renderer/WaterManager.h"
    3033#include "maths/Vector3D.h"
     
    140143
    141144        MakeDirty(0, 0, tiles+1, tiles+1);
    142145    }
    143 
     146   
    144147    virtual void MakeDirty(i32 i0, i32 j0, i32 i1, i32 j1)
    145148    {
    146149        CMessageTerrainChanged msg(i0, j0, i1, j1);