Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CCmpTerritoryManager.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2012 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 
21 #include "ICmpTerritoryManager.h"
22 
23 #include "graphics/Overlay.h"
24 #include "graphics/Terrain.h"
27 #include "maths/MathUtil.h"
28 #include "maths/Vector2D.h"
29 #include "ps/Overlay.h"
30 #include "renderer/Renderer.h"
31 #include "renderer/Scene.h"
48 
50 
52 {
54 public:
56 
58  virtual void StartRender();
59  virtual void ProcessTile(ssize_t i, ssize_t j);
60 };
61 
63 {
64 public:
65  static void ClassInit(CComponentManager& componentManager)
66  {
70  componentManager.SubscribeToMessageType(MT_TerrainChanged);
71  componentManager.SubscribeToMessageType(MT_Update);
72  componentManager.SubscribeToMessageType(MT_Interpolate);
73  componentManager.SubscribeToMessageType(MT_RenderSubmit);
74  }
75 
76  DEFAULT_COMPONENT_ALLOCATOR(TerritoryManager)
77 
78  static std::string GetSchema()
79  {
80  return "<a:component type='system'/><empty/>";
81  }
82 
86 
87  // Player ID in bits 0-5 (TERRITORY_PLAYER_MASK);
88  // connected flag in bit 6 (TERRITORY_CONNECTED_MASK);
89  // processed flag in bit 7 (TERRITORY_PROCESSED_MASK)
91 
92  // Set to true when territories change; will send a TerritoriesChanged message
93  // during the Update phase
95 
97  {
98  bool connected;
101  };
102 
103  std::vector<SBoundaryLine> m_BoundaryLines;
105 
106  double m_AnimTime; // time since start of rendering, in seconds
107 
109 
110  bool m_EnableLineDebugOverlays; ///< Enable node debugging overlays for boundary lines?
111  std::vector<SOverlayLine> m_DebugBoundaryLineNodes;
112 
113  virtual void Init(const CParamNode& UNUSED(paramNode))
114  {
115  m_Territories = NULL;
116  m_DebugOverlay = NULL;
117 // m_DebugOverlay = new TerritoryOverlay(*this);
118  m_BoundaryLinesDirty = true;
119  m_TriggerEvent = true;
121  m_DirtyID = 1;
122 
123  m_AnimTime = 0.0;
124 
125  CParamNode externalParamNode;
126  CParamNode::LoadXML(externalParamNode, L"simulation/data/territorymanager.xml");
127 
128  int impassableCost = externalParamNode.GetChild("TerritoryManager").GetChild("ImpassableCost").ToInt();
129  ENSURE(0 <= impassableCost && impassableCost <= 255);
130  m_ImpassableCost = (u8)impassableCost;
131  m_BorderThickness = externalParamNode.GetChild("TerritoryManager").GetChild("BorderThickness").ToFixed().ToFloat();
132  m_BorderSeparation = externalParamNode.GetChild("TerritoryManager").GetChild("BorderSeparation").ToFixed().ToFloat();
133  }
134 
135  virtual void Deinit()
136  {
139  }
140 
141  virtual void Serialize(ISerializer& UNUSED(serialize))
142  {
143  // Territory state can be recomputed as required, so we don't need to serialize any of it.
144  // TODO: do we ever need to serialize m_TriggerEvent to prevent lost messages?
145  }
146 
147  virtual void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
148  {
149  Init(paramNode);
150  }
151 
152  virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
153  {
154  switch (msg.GetType())
155  {
156  case MT_OwnershipChanged:
157  {
158  const CMessageOwnershipChanged& msgData = static_cast<const CMessageOwnershipChanged&> (msg);
160  break;
161  }
162  case MT_PositionChanged:
163  {
164  const CMessagePositionChanged& msgData = static_cast<const CMessagePositionChanged&> (msg);
166  break;
167  }
169  {
170  const CMessageTechnologyModification& msgData = static_cast<const CMessageTechnologyModification&> (msg);
171  if (msgData.component == L"TerritoryInfluence")
172  MakeDirty();
173  break;
174  }
175  case MT_TerrainChanged:
176  {
177  MakeDirty();
178  break;
179  }
180  case MT_Update:
181  {
182  if (m_TriggerEvent)
183  {
184  m_TriggerEvent = false;
187  }
188  break;
189  }
190  case MT_Interpolate:
191  {
192  const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
193  Interpolate(msgData.deltaSimTime, msgData.offset);
194  break;
195  }
196  case MT_RenderSubmit:
197  {
198  const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
199  RenderSubmit(msgData.collector);
200  break;
201  }
202  }
203  }
204 
205  // Check whether the entity is either a settlement or territory influence;
206  // ignore any others
208  {
209  CmpPtr<ICmpSettlement> cmpSettlement(GetSimContext(), ent);
210  if (cmpSettlement)
211  MakeDirty();
212 
213  CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), ent);
214  if (cmpTerritoryInfluence)
215  MakeDirty();
216  }
217 
218  virtual const Grid<u8>& GetTerritoryGrid()
219  {
222  return *m_Territories;
223  }
224 
226  virtual bool IsConnected(entity_pos_t x, entity_pos_t z);
227 
228  // To support lazy updates of territory render data,
229  // we maintain a DirtyID here and increment it whenever territories change;
230  // if a caller has a lower DirtyID then it needs to be updated.
231 
232  size_t m_DirtyID;
233 
234  void MakeDirty()
235  {
237  ++m_DirtyID;
238  m_BoundaryLinesDirty = true;
239  m_TriggerEvent = true;
240  }
241 
242  virtual bool NeedUpdate(size_t* dirtyID)
243  {
244  if (*dirtyID != m_DirtyID)
245  {
246  *dirtyID = m_DirtyID;
247  return true;
248  }
249  return false;
250  }
251 
252  void CalculateTerritories();
253 
254  /**
255  * Updates @p grid based on the obstruction shapes of all entities with
256  * a TerritoryInfluence component. Grid cells are 0 if no influence,
257  * or 1+c if the influence have cost c (assumed between 0 and 254).
258  */
260 
261  std::vector<STerritoryBoundary> ComputeBoundaries();
262 
263  void UpdateBoundaryLines();
264 
265  void Interpolate(float frameTime, float frameOffset);
266 
267  void RenderSubmit(SceneCollector& collector);
268 };
269 
270 REGISTER_COMPONENT_TYPE(TerritoryManager)
271 
272 /*
273 We compute the territory influence of an entity with a kind of best-first search,
274 storing an 'open' list of tiles that have not yet been processed,
275 then taking the highest-weight tile (closest to origin) and updating the weight
276 of extending to each neighbour (based on radius-determining 'falloff' value,
277 adjusted by terrain movement cost), and repeating until all tiles are processed.
278 */
279 
280 typedef PriorityQueueHeap<std::pair<u16, u16>, u32, std::greater<u32> > OpenQueue;
281 
282 static void ProcessNeighbour(u32 falloff, u16 i, u16 j, u32 pg, bool diagonal,
283  Grid<u32>& grid, OpenQueue& queue, const Grid<u8>& costGrid)
284 {
285  u32 dg = falloff * costGrid.get(i, j);
286  if (diagonal)
287  dg = (dg * 362) / 256;
288 
289  // Stop if new cost g=pg-dg is not better than previous value for that tile
290  // (arranged to avoid underflow if pg < dg)
291  if (pg <= grid.get(i, j) + dg)
292  return;
293 
294  u32 g = pg - dg; // cost to this tile = cost to predecessor - falloff from predecessor
295 
296  grid.set(i, j, g);
297  OpenQueue::Item tile = { std::make_pair(i, j), g };
298  queue.push(tile);
299 }
300 
301 static void FloodFill(Grid<u32>& grid, Grid<u8>& costGrid, OpenQueue& openTiles, u32 falloff)
302 {
303  u16 tilesW = grid.m_W;
304  u16 tilesH = grid.m_H;
305 
306  while (!openTiles.empty())
307  {
308  OpenQueue::Item tile = openTiles.pop();
309 
310  // Process neighbours (if they're not off the edge of the map)
311  u16 x = tile.id.first;
312  u16 z = tile.id.second;
313  if (x > 0)
314  ProcessNeighbour(falloff, (u16)(x-1), z, tile.rank, false, grid, openTiles, costGrid);
315  if (x < tilesW-1)
316  ProcessNeighbour(falloff, (u16)(x+1), z, tile.rank, false, grid, openTiles, costGrid);
317  if (z > 0)
318  ProcessNeighbour(falloff, x, (u16)(z-1), tile.rank, false, grid, openTiles, costGrid);
319  if (z < tilesH-1)
320  ProcessNeighbour(falloff, x, (u16)(z+1), tile.rank, false, grid, openTiles, costGrid);
321  if (x > 0 && z > 0)
322  ProcessNeighbour(falloff, (u16)(x-1), (u16)(z-1), tile.rank, true, grid, openTiles, costGrid);
323  if (x > 0 && z < tilesH-1)
324  ProcessNeighbour(falloff, (u16)(x-1), (u16)(z+1), tile.rank, true, grid, openTiles, costGrid);
325  if (x < tilesW-1 && z > 0)
326  ProcessNeighbour(falloff, (u16)(x+1), (u16)(z-1), tile.rank, true, grid, openTiles, costGrid);
327  if (x < tilesW-1 && z < tilesH-1)
328  ProcessNeighbour(falloff, (u16)(x+1), (u16)(z+1), tile.rank, true, grid, openTiles, costGrid);
329  }
330 }
331 
333 {
334  if (m_Territories)
335  return;
336 
337  PROFILE("CalculateTerritories");
338 
339  CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
340 
341  // If the terrain hasn't been loaded (e.g. this is called during map initialisation),
342  // abort the computation (and assume callers can cope with m_Territories == NULL)
343  if (!cmpTerrain->IsLoaded())
344  return;
345 
346  u16 tilesW = cmpTerrain->GetTilesPerSide();
347  u16 tilesH = cmpTerrain->GetTilesPerSide();
348 
349  m_Territories = new Grid<u8>(tilesW, tilesH);
350 
351  // Compute terrain-passability-dependent costs per tile
352  Grid<u8> influenceGrid(tilesW, tilesH);
353 
354  CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
355  ICmpPathfinder::pass_class_t passClassDefault = cmpPathfinder->GetPassabilityClass("default");
356  ICmpPathfinder::pass_class_t passClassUnrestricted = cmpPathfinder->GetPassabilityClass("unrestricted");
357 
358  const Grid<u16>& passGrid = cmpPathfinder->GetPassabilityGrid();
359  for (u16 j = 0; j < tilesH; ++j)
360  {
361  for (u16 i = 0; i < tilesW; ++i)
362  {
363  u16 g = passGrid.get(i, j);
364  u8 cost;
365  if (g & passClassUnrestricted)
366  cost = 255; // off the world; use maximum cost
367  else if (g & passClassDefault)
368  cost = m_ImpassableCost;
369  else
370  cost = 1;
371  influenceGrid.set(i, j, cost);
372  }
373  }
374 
375  // Find all territory influence entities
377 
378  // Allow influence entities to override the terrain costs
379  RasteriseInfluences(influences, influenceGrid);
380 
381  // Split influence entities into per-player lists, ignoring any with invalid properties
382  std::map<player_id_t, std::vector<entity_id_t> > influenceEntities;
383  std::vector<entity_id_t> rootInfluenceEntities;
384  for (CComponentManager::InterfaceList::iterator it = influences.begin(); it != influences.end(); ++it)
385  {
386  // Ignore any with no weight or radius (to avoid divide-by-zero later)
387  ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second);
388  if (cmpTerritoryInfluence->GetWeight() == 0 || cmpTerritoryInfluence->GetRadius() == 0)
389  continue;
390 
391  CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), it->first);
392  if (!cmpOwnership)
393  continue;
394 
395  // Ignore Gaia and unassigned
396  player_id_t owner = cmpOwnership->GetOwner();
397  if (owner <= 0)
398  continue;
399 
400  // We only have so many bits to store tile ownership, so ignore unrepresentable players
401  if (owner > TERRITORY_PLAYER_MASK)
402  continue;
403 
404  // Ignore if invalid position
405  CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), it->first);
406  if (!cmpPosition || !cmpPosition->IsInWorld())
407  continue;
408 
409  influenceEntities[owner].push_back(it->first);
410 
411  if (cmpTerritoryInfluence->IsRoot())
412  rootInfluenceEntities.push_back(it->first);
413  }
414 
415  // For each player, store the sum of influences on each tile
416  std::vector<std::pair<player_id_t, Grid<u32> > > playerGrids;
417  // TODO: this is a large waste of memory; we don't really need to store
418  // all the intermediate grids
419 
420  for (std::map<player_id_t, std::vector<entity_id_t> >::iterator it = influenceEntities.begin(); it != influenceEntities.end(); ++it)
421  {
422  Grid<u32> playerGrid(tilesW, tilesH);
423 
424  std::vector<entity_id_t>& ents = it->second;
425  for (std::vector<entity_id_t>::iterator eit = ents.begin(); eit != ents.end(); ++eit)
426  {
427  // Compute the influence map of the current entity, then add it to the player grid
428 
429  Grid<u32> entityGrid(tilesW, tilesH);
430 
431  CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *eit);
432  CFixedVector2D pos = cmpPosition->GetPosition2D();
433  u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);
434  u16 j = (u16)clamp((pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1);
435 
436  CmpPtr<ICmpTerritoryInfluence> cmpTerritoryInfluence(GetSimContext(), *eit);
437  u32 weight = cmpTerritoryInfluence->GetWeight();
438  u32 radius = cmpTerritoryInfluence->GetRadius() / TERRAIN_TILE_SIZE;
439  u32 falloff = weight / radius; // earlier check for GetRadius() == 0 prevents divide-by-zero
440 
441  // TODO: we should have some maximum value on weight, to avoid overflow
442  // when doing all the sums
443 
444  // Initialise the tile under the entity
445  entityGrid.set(i, j, weight);
446  OpenQueue openTiles;
447  OpenQueue::Item tile = { std::make_pair((u16)i, (i16)j), weight };
448  openTiles.push(tile);
449 
450  // Expand influences outwards
451  FloodFill(entityGrid, influenceGrid, openTiles, falloff);
452 
453  // TODO: we should do a sparse grid and only add the non-zero regions, for performance
454  for (u16 j = 0; j < entityGrid.m_H; ++j)
455  for (u16 i = 0; i < entityGrid.m_W; ++i)
456  playerGrid.set(i, j, playerGrid.get(i, j) + entityGrid.get(i, j));
457  }
458 
459  playerGrids.push_back(std::make_pair(it->first, playerGrid));
460  }
461 
462  // Set m_Territories to the player ID with the highest influence for each tile
463  for (u16 j = 0; j < tilesH; ++j)
464  {
465  for (u16 i = 0; i < tilesW; ++i)
466  {
467  u32 bestWeight = 0;
468  for (size_t k = 0; k < playerGrids.size(); ++k)
469  {
470  u32 w = playerGrids[k].second.get(i, j);
471  if (w > bestWeight)
472  {
473  player_id_t id = playerGrids[k].first;
474  m_Territories->set(i, j, (u8)id);
475  bestWeight = w;
476  }
477  }
478  }
479  }
480 
481  // Detect territories connected to a 'root' influence (typically a civ center)
482  // belonging to their player, and mark them with the connected flag
483  for (std::vector<entity_id_t>::iterator it = rootInfluenceEntities.begin(); it != rootInfluenceEntities.end(); ++it)
484  {
485  // (These components must be valid else the entities wouldn't be added to this list)
486  CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), *it);
487  CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), *it);
488 
489  CFixedVector2D pos = cmpPosition->GetPosition2D();
490  u16 i = (u16)clamp((pos.X / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesW-1);
491  u16 j = (u16)clamp((pos.Y / (int)TERRAIN_TILE_SIZE).ToInt_RoundToNegInfinity(), 0, tilesH-1);
492 
493  u8 owner = (u8)cmpOwnership->GetOwner();
494 
495  if (m_Territories->get(i, j) != owner)
496  continue;
497 
498  // TODO: would be nice to refactor some of the many flood fill
499  // algorithms in this component
500 
501  Grid<u8>& grid = *m_Territories;
502 
503  u16 maxi = (u16)(grid.m_W-1);
504  u16 maxj = (u16)(grid.m_H-1);
505 
506  std::vector<std::pair<u16, u16> > tileStack;
507 
508 #define MARK_AND_PUSH(i, j) STMT(grid.set(i, j, owner | TERRITORY_CONNECTED_MASK); tileStack.push_back(std::make_pair(i, j)); )
509 
510  MARK_AND_PUSH(i, j);
511  while (!tileStack.empty())
512  {
513  int ti = tileStack.back().first;
514  int tj = tileStack.back().second;
515  tileStack.pop_back();
516 
517  if (ti > 0 && grid.get(ti-1, tj) == owner)
518  MARK_AND_PUSH(ti-1, tj);
519  if (ti < maxi && grid.get(ti+1, tj) == owner)
520  MARK_AND_PUSH(ti+1, tj);
521  if (tj > 0 && grid.get(ti, tj-1) == owner)
522  MARK_AND_PUSH(ti, tj-1);
523  if (tj < maxj && grid.get(ti, tj+1) == owner)
524  MARK_AND_PUSH(ti, tj+1);
525 
526  if (ti > 0 && tj > 0 && grid.get(ti-1, tj-1) == owner)
527  MARK_AND_PUSH(ti-1, tj-1);
528  if (ti > 0 && tj < maxj && grid.get(ti-1, tj+1) == owner)
529  MARK_AND_PUSH(ti-1, tj+1);
530  if (ti < maxi && tj > 0 && grid.get(ti+1, tj-1) == owner)
531  MARK_AND_PUSH(ti+1, tj-1);
532  if (ti < maxi && tj < maxj && grid.get(ti+1, tj+1) == owner)
533  MARK_AND_PUSH(ti+1, tj+1);
534  }
535 
536 #undef MARK_AND_PUSH
537  }
538 }
539 
540 /**
541  * Compute the tile indexes on the grid nearest to a given point
542  */
543 static void NearestTile(entity_pos_t x, entity_pos_t z, u16& i, u16& j, u16 w, u16 h)
544 {
545  i = (u16)clamp((x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, w-1);
546  j = (u16)clamp((z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, h-1);
547 }
548 
549 /**
550  * Returns the position of the center of the given tile
551  */
552 static void TileCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z)
553 {
556 }
557 
558 // TODO: would be nice not to duplicate those two functions from CCmpObstructionManager.cpp
559 
560 
562 {
563  for (CComponentManager::InterfaceList::iterator it = infls.begin(); it != infls.end(); ++it)
564  {
565  ICmpTerritoryInfluence* cmpTerritoryInfluence = static_cast<ICmpTerritoryInfluence*>(it->second);
566 
567  i32 cost = cmpTerritoryInfluence->GetCost();
568  if (cost == -1)
569  continue;
570 
571  CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), it->first);
572  if (!cmpObstruction)
573  continue;
574 
576  if (!cmpObstruction->GetObstructionSquare(square))
577  continue;
578 
579  CFixedVector2D halfSize(square.hw, square.hh);
580  CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(square.u, square.v, halfSize);
581 
582  u16 i0, j0, i1, j1;
583  NearestTile(square.x - halfBound.X, square.z - halfBound.Y, i0, j0, grid.m_W, grid.m_H);
584  NearestTile(square.x + halfBound.X, square.z + halfBound.Y, i1, j1, grid.m_W, grid.m_H);
585  for (u16 j = j0; j <= j1; ++j)
586  {
587  for (u16 i = i0; i <= i1; ++i)
588  {
589  entity_pos_t x, z;
590  TileCenter(i, j, x, z);
591  if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize))
592  grid.set(i, j, (u8)cost);
593  }
594  }
595 
596  }
597 }
598 
599 std::vector<STerritoryBoundary> CCmpTerritoryManager::ComputeBoundaries()
600 {
601  PROFILE("ComputeBoundaries");
602 
605 
607 }
608 
610 {
611  PROFILE("update boundary lines");
612 
613  m_BoundaryLines.clear();
614  m_DebugBoundaryLineNodes.clear();
615 
617  return;
618 
619  std::vector<STerritoryBoundary> boundaries = ComputeBoundaries();
620 
621  CTextureProperties texturePropsBase("art/textures/misc/territory_border.png");
622  texturePropsBase.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
623  texturePropsBase.SetMaxAnisotropy(2.f);
624  CTexturePtr textureBase = g_Renderer.GetTextureManager().CreateTexture(texturePropsBase);
625 
626  CTextureProperties texturePropsMask("art/textures/misc/territory_border_mask.png");
627  texturePropsMask.SetWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_EDGE);
628  texturePropsMask.SetMaxAnisotropy(2.f);
629  CTexturePtr textureMask = g_Renderer.GetTextureManager().CreateTexture(texturePropsMask);
630 
631  CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSystemEntity());
632  if (!cmpPlayerManager)
633  return;
634 
635  for (size_t i = 0; i < boundaries.size(); ++i)
636  {
637  if (boundaries[i].points.empty())
638  continue;
639 
640  CColor color(1, 0, 1, 1);
641  CmpPtr<ICmpPlayer> cmpPlayer(GetSimContext(), cmpPlayerManager->GetPlayerByID(boundaries[i].owner));
642  if (cmpPlayer)
643  color = cmpPlayer->GetColour();
644 
645  m_BoundaryLines.push_back(SBoundaryLine());
646  m_BoundaryLines.back().connected = boundaries[i].connected;
647  m_BoundaryLines.back().color = color;
648  m_BoundaryLines.back().overlay.m_SimContext = &GetSimContext();
649  m_BoundaryLines.back().overlay.m_TextureBase = textureBase;
650  m_BoundaryLines.back().overlay.m_TextureMask = textureMask;
651  m_BoundaryLines.back().overlay.m_Color = color;
652  m_BoundaryLines.back().overlay.m_Thickness = m_BorderThickness;
653  m_BoundaryLines.back().overlay.m_Closed = true;
654 
655  SimRender::SmoothPointsAverage(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed);
656  SimRender::InterpolatePointsRNS(boundaries[i].points, m_BoundaryLines.back().overlay.m_Closed, m_BorderSeparation);
657 
658  std::vector<float>& points = m_BoundaryLines.back().overlay.m_Coords;
659  for (size_t j = 0; j < boundaries[i].points.size(); ++j)
660  {
661  points.push_back(boundaries[i].points[j].X);
662  points.push_back(boundaries[i].points[j].Y);
663 
665  {
666  const size_t numHighlightNodes = 7; // highlight the X last nodes on either end to see where they meet (if closed)
667  SOverlayLine overlayNode;
668  if (j > boundaries[i].points.size() - 1 - numHighlightNodes)
669  overlayNode.m_Color = CColor(1.f, 0.f, 0.f, 1.f);
670  else if (j < numHighlightNodes)
671  overlayNode.m_Color = CColor(0.f, 1.f, 0.f, 1.f);
672  else
673  overlayNode.m_Color = CColor(1.0f, 1.0f, 1.0f, 1.0f);
674 
675  overlayNode.m_Thickness = 1;
676  SimRender::ConstructCircleOnGround(GetSimContext(), boundaries[i].points[j].X, boundaries[i].points[j].Y, 0.1f, overlayNode, true);
677  m_DebugBoundaryLineNodes.push_back(overlayNode);
678  }
679  }
680 
681  }
682 }
683 
684 void CCmpTerritoryManager::Interpolate(float frameTime, float UNUSED(frameOffset))
685 {
686  m_AnimTime += frameTime;
687 
689  {
691  m_BoundaryLinesDirty = false;
692  }
693 
694  for (size_t i = 0; i < m_BoundaryLines.size(); ++i)
695  {
696  if (!m_BoundaryLines[i].connected)
697  {
698  CColor c = m_BoundaryLines[i].color;
699  c.a *= 0.2f + 0.8f * fabsf((float)cos(m_AnimTime * M_PI)); // TODO: should let artists tweak this
700  m_BoundaryLines[i].overlay.m_Color = c;
701  }
702  }
703 }
704 
706 {
707  for (size_t i = 0; i < m_BoundaryLines.size(); ++i)
708  collector.Submit(&m_BoundaryLines[i].overlay);
709 
710  for (size_t i = 0; i < m_DebugBoundaryLineNodes.size(); ++i)
711  collector.Submit(&m_DebugBoundaryLineNodes[i]);
712 
713 }
714 
716 {
717  u16 i, j;
719  if (!m_Territories)
720  return 0;
721 
723  return m_Territories->get(i, j) & TERRITORY_PLAYER_MASK;
724 }
725 
727 {
728  u16 i, j;
730  if (!m_Territories)
731  return false;
732 
734  return (m_Territories->get(i, j) & TERRITORY_CONNECTED_MASK) != 0;
735 }
736 
738  : TerrainOverlay(manager.GetSimContext()), m_TerritoryManager(manager)
739 { }
740 
742 {
744 }
745 
747 {
749  return;
750 
752 
753  float a = 0.2f;
754  switch (id)
755  {
756  case 0: break;
757  case 1: RenderTile(CColor(1, 0, 0, a), false); break;
758  case 2: RenderTile(CColor(0, 1, 0, a), false); break;
759  case 3: RenderTile(CColor(0, 0, 1, a), false); break;
760  case 4: RenderTile(CColor(1, 1, 0, a), false); break;
761  case 5: RenderTile(CColor(0, 1, 1, a), false); break;
762  case 6: RenderTile(CColor(1, 0, 1, a), false); break;
763  default: RenderTile(CColor(1, 1, 1, a), false); break;
764  }
765 }
#define M_PI
Definition: wposix.h:64
virtual void HandleMessage(const CMessage &msg, bool global)
An entity initialisation parameter node.
Definition: ParamNode.h:112
virtual pass_class_t GetPassabilityClass(const std::string &name)=0
Get the tag for a given passability class name.
virtual player_id_t GetOwner(entity_pos_t x, entity_pos_t z)
Get owner of territory at given position.
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
virtual void ProcessTile(ssize_t i, ssize_t j)
Override to perform processing of each tile.
#define u8
Definition: types.h:39
A simple fixed-point number class.
Definition: Fixed.h:115
Definition: Decompose.h:22
virtual const Grid< u16 > & GetPassabilityGrid()=0
#define REGISTER_COMPONENT_TYPE(cname)
Definition: Component.h:30
std::vector< std::pair< entity_id_t, IComponent * > > InterfaceList
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
CCmpTerritoryManager & m_TerritoryManager
Line-based overlay, with world-space coordinates, rendered in the world potentially behind other obje...
Definition: Overlay.h:36
virtual bool IsConnected(entity_pos_t x, entity_pos_t z)
Get whether territory at given position is connected to a root object (civ center etc) owned by that ...
const ssize_t TERRAIN_TILE_SIZE
metres [world space units] per tile in x and z
Definition: Terrain.h:40
Definition: Decompose.h:22
Definition: Overlay.h:34
#define i32
Definition: types.h:36
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
u16 m_H
Definition: Grid.h:101
Represents the filename and GL parameters of a texture, for passing to CTextureManager::CreateTexture...
Add renderable objects to the scene collector.
Definition: MessageTypes.h:145
Priority queue implemented as a binary heap.
Definition: PriorityQueue.h:61
void InterpolatePointsRNS(std::vector< CVector2D > &points, bool closed, float offset, int segmentSamples=4)
Updates the given points to include intermediate points interpolating between the original control po...
Definition: Render.cpp:347
virtual bool GetObstructionSquare(ICmpObstructionManager::ObstructionSquare &out)=0
Gets the square corresponding to this obstruction shape.
std::vector< SOverlayLine > m_DebugBoundaryLineNodes
int ToInt() const
Parses the content of this node as an integer.
Definition: ParamNode.cpp:213
virtual u32 GetRadius()=0
Base class for (relatively) simple drawing of data onto terrain tiles, intended for debugging purpose...
virtual bool IsInWorld()=0
Returns true if the entity currently exists at a defined position in the world.
#define i16
Definition: types.h:35
float deltaSimTime
Elapsed simulation time since previous interpolate, in seconds.
Definition: MessageTypes.h:134
static void ProcessNeighbour(u32 falloff, u16 i, u16 j, u32 pg, bool diagonal, Grid< u32 > &grid, OpenQueue &queue, const Grid< u8 > &costGrid)
int32_t player_id_t
valid player IDs are non-negative (see ICmpOwnership)
Definition: Player.h:24
This interface accepts renderable objects.
Definition: Scene.h:82
#define g_Renderer
Definition: Renderer.h:61
void RenderTile(const CColor &colour, bool draw_hidden)
Draw a filled quad on top of the current tile.
bool m_EnableLineDebugOverlays
Enable node debugging overlays for boundary lines?
fixed ToFixed() const
Parses the content of this node as a fixed-point number.
Definition: ParamNode.cpp:222
static void LoadXML(CParamNode &ret, const XMBFile &file, const wchar_t *sourceIdentifier=NULL)
Loads the XML data specified by file into the node ret.
Definition: ParamNode.cpp:47
static void TileCenter(u16 i, u16 j, entity_pos_t &x, entity_pos_t &z)
Returns the position of the center of the given tile.
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
TerritoryOverlay * m_DebugOverlay
void ConstructCircleOnGround(const CSimContext &context, float x, float z, float radius, SOverlayLine &overlay, bool floating, float heightOffset=0.25f)
Constructs overlay line as a circle with given center and radius, conforming to terrain.
Definition: Render.cpp:70
virtual int GetType() const =0
std::vector< SBoundaryLine > m_BoundaryLines
InterfaceList GetEntitiesWithInterface(InterfaceId iid) const
const CParamNode & GetChild(const char *name) const
Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none...
Definition: ParamNode.cpp:185
Textured line overlay, with world-space coordinates, rendered in the world onto the terrain...
Definition: Overlay.h:62
void Interpolate(float frameTime, float frameOffset)
static void ClassInit(CComponentManager &componentManager)
NONCOPYABLE(TerritoryOverlay)
virtual void Init(const CParamNode &paramNode)
virtual void Serialize(ISerializer &serialize)
virtual entity_id_t GetPlayerByID(int32_t id)=0
float ToFloat() const
Convert to float. May be lossy - float can&#39;t represent all values.
Definition: Fixed.h:161
static std::string GetSchema()
virtual CFixedVector2D GetPosition2D()=0
Returns the current x,z position (no interpolation).
static void FloodFill(Grid< u32 > &grid, Grid< u8 > &costGrid, OpenQueue &openTiles, u32 falloff)
float a
Definition: Overlay.h:57
#define SAFE_DELETE(p)
delete memory ensuing from new and set the pointer to zero (thus making double-frees safe / a no-op) ...
virtual player_id_t GetOwner()=0
float offset
Range [0, 1] (inclusive); fractional time of current frame between previous/next simulation turns...
Definition: MessageTypes.h:136
virtual const Grid< u8 > & GetTerritoryGrid()
For each tile, the TERRITORY_PLAYER_MASK bits are player ID; TERRITORY_CONNECTED_MASK is set if the t...
#define PROFILE(name)
Definition: Profile.h:195
virtual bool NeedUpdate(size_t *dirtyID)
#define DEFAULT_COMPONENT_ALLOCATOR(cname)
Definition: Component.h:44
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)
const CSimContext & GetSimContext() const
Definition: IComponent.h:52
Sent during TurnStart.
Definition: MessageTypes.h:245
static const int TERRITORY_CONNECTED_MASK
static bool IsInitialised()
Definition: Singleton.h:63
void SubscribeGloballyToMessageType(MessageTypeId mtid)
Subscribe the current component type to all messages of the given message type.
void RasteriseInfluences(CComponentManager::InterfaceList &infls, Grid< u8 > &grid)
Updates grid based on the obstruction shapes of all entities with a TerritoryInfluence component...
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
std::vector< STerritoryBoundary > ComputeBoundaries()
intptr_t ssize_t
Definition: wposix_types.h:82
static CFixed FromInt(int n)
Definition: Fixed.h:136
CEntityHandle GetSystemEntity() const
Definition: IComponent.h:50
Helper functions related to rendering.
#define MARK_AND_PUSH(i, j)
#define u16
Definition: types.h:40
void SetMaxAnisotropy(float aniso)
Set maximum anisotropy value.
#define u32
Definition: types.h:41
void MakeDirtyIfRelevantEntity(entity_id_t ent)
static const int TERRITORY_PLAYER_MASK
u8 m_Thickness
Definition: Overlay.h:42
SceneCollector & collector
Definition: MessageTypes.h:155
void RenderSubmit(SceneCollector &collector)
Sent by technology manager when a technology is researched that modifies a component.
Definition: MessageTypes.h:371
void BroadcastMessage(const CMessage &msg) const
Send a message, not targeted at any particular entity.
T & get(int i, int j) const
Definition: Grid.h:93
virtual bool IsLoaded()=0
static void NearestTile(entity_pos_t x, entity_pos_t z, u16 &i, u16 &j, u16 w, u16 h)
Compute the tile indexes on the grid nearest to a given point.
void SmoothPointsAverage(std::vector< CVector2D > &points, bool closed)
Updates the given points so each point is averaged with its neighbours, resulting in a somewhat smoot...
Definition: Render.cpp:305
static std::vector< STerritoryBoundary > ComputeBoundaries(const Grid< u8 > *territories)
Computes and returns all territory boundaries on the provided territory map (see STerritoryBoundary f...
virtual u16 GetTilesPerSide()=0
Returns number of tiles per side on the terrain.
u16 m_W
Definition: Grid.h:101
Sent when territory assignments have changed.
Definition: MessageTypes.h:298
Prepare for rendering a new frame (set up model positions etc).
Definition: MessageTypes.h:122
bool PointIsInSquare(CFixedVector2D point, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Checks if a point is inside the given rotated square or rectangle.
Definition: Geometry.cpp:28
TerritoryOverlay(CCmpTerritoryManager &manager)
CColor m_Color
Definition: Overlay.h:40
virtual u32 GetWeight()=0
void SetWrap(GLint wrap)
Set wrapping mode (typically GL_REPEAT, GL_CLAMP_TO_EDGE, etc).
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
virtual void Submit(CPatch *patch)=0
Submit a terrain patch that is part of the scene.
void push(const Item &item)
Definition: PriorityQueue.h:70
virtual bool IsRoot()=0
virtual void StartRender()
Override to perform processing at the start of the overlay rendering, before the ProcessTile calls...
T clamp(T value, T min, T max)
Definition: MathUtil.h:32
Standard representation for all types of shapes, for use with geometry processing code...
Helper functions related to geometry algorithms.
void set(int i, int j, const T &value)
Definition: Grid.h:85
CComponentManager & GetComponentManager() const
Definition: SimContext.cpp:35
CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:40
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34
virtual i32 GetCost()=0
Returns either -1 to indicate no special terrain cost, or a value in [0, 255] to indicate overriding ...
shared_ptr< CTexture > CTexturePtr
Definition: Texture.h:22