/* Copyright (C) 2015 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 . */ #ifndef INCLUDED_GRID #define INCLUDED_GRID #include #ifdef NDEBUG #define GRID_BOUNDS_DEBUG 0 #else #define GRID_BOUNDS_DEBUG 1 #endif /** * Basic 2D array, intended for storing tile data, plus support for lazy updates * by ICmpObstructionManager. * @c T must be a POD type that can be initialised with 0s. */ template class Grid { public: Grid() : m_W(0), m_H(0), m_Data(NULL), m_DirtyID(0) { } Grid(u16 w, u16 h) : m_W(w), m_H(h), m_Data(NULL), m_DirtyID(0) { if (m_W || m_H) m_Data = new T[m_W * m_H]; reset(); } Grid(const Grid& g) { m_Data = NULL; *this = g; } Grid& operator=(const Grid& g) { if (this != &g) { m_W = g.m_W; m_H = g.m_H; m_DirtyID = g.m_DirtyID; delete[] m_Data; if (g.m_Data) { m_Data = new T[m_W * m_H]; memcpy(m_Data, g.m_Data, m_W*m_H*sizeof(T)); } else m_Data = NULL; } return *this; } ~Grid() { delete[] m_Data; } void reset() { if (m_Data) memset(m_Data, 0, m_W*m_H*sizeof(T)); } // Add two grids of the same size void add(const Grid& g) { #if GRID_BOUNDS_DEBUG ENSURE(g.m_W == m_W && g.m_H == m_H); #endif for (int i=0; i < m_H*m_W; ++i) m_Data[i] += g.m_Data[i]; } void set(int i, int j, const T& value) { #if GRID_BOUNDS_DEBUG ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); #endif m_Data[j*m_W + i] = value; } T& get(int i, int j) const { #if GRID_BOUNDS_DEBUG ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); #endif return m_Data[j*m_W + i]; } u16 m_W, m_H; T* m_Data; size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated }; /** * Similar to Grid, except optimised for sparse usage (the grid is subdivided into * buckets whose contents are only initialised on demand, to save on memset cost). */ template class SparseGrid { NONCOPYABLE(SparseGrid); enum { BucketBits = 4, BucketSize = 1 << BucketBits }; T* GetBucket(int i, int j) { size_t b = (j >> BucketBits) * m_BW + (i >> BucketBits); if (!m_Data[b]) { m_Data[b] = new T[BucketSize*BucketSize]; memset(m_Data[b], 0, BucketSize*BucketSize*sizeof(T)); } return m_Data[b]; } public: SparseGrid(u16 w, u16 h) : m_W(w), m_H(h), m_DirtyID(0) { ENSURE(m_W && m_H); m_BW = (u16)((m_W + BucketSize-1) >> BucketBits); m_BH = (u16)((m_H + BucketSize-1) >> BucketBits); m_Data = new T*[m_BW*m_BH]; memset(m_Data, 0, m_BW*m_BH*sizeof(T*)); } ~SparseGrid() { reset(); delete[] m_Data; } void reset() { for (size_t i = 0; i < (size_t)(m_BW*m_BH); ++i) delete[] m_Data[i]; memset(m_Data, 0, m_BW*m_BH*sizeof(T*)); } void set(int i, int j, const T& value) { #if GRID_BOUNDS_DEBUG ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); #endif GetBucket(i, j)[(j % BucketSize)*BucketSize + (i % BucketSize)] = value; } T& get(int i, int j) { #if GRID_BOUNDS_DEBUG ENSURE(0 <= i && i < m_W && 0 <= j && j < m_H); #endif return GetBucket(i, j)[(j % BucketSize)*BucketSize + (i % BucketSize)]; } u16 m_W, m_H; u16 m_BW, m_BH; T** m_Data; size_t m_DirtyID; // if this is < the id maintained by ICmpObstructionManager then it needs to be updated }; /** * Structure holding grid dirtiness informations, for clever updates * * Note: globallyDirty should be used to know which parts of the grid have been changed after an update, * whereas globalRecompute should be used during the update itself, to avoid unnecessary recomputations. */ struct GridUpdateInformation { bool dirty; bool globallyDirty; bool globalRecompute; Grid dirtinessGrid; /** * Mark everything as clean */ void Clean() { dirty = false; globallyDirty = false; globalRecompute = false; dirtinessGrid.reset(); } }; #endif // INCLUDED_GRID