Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CCmpObstructionManager.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 "ICmpObstructionManager.h"
22 
28 
29 #include "graphics/Overlay.h"
30 #include "graphics/Terrain.h"
31 #include "maths/MathUtil.h"
32 #include "ps/Overlay.h"
33 #include "ps/Profile.h"
34 #include "renderer/Scene.h"
35 #include "ps/CLogger.h"
36 
37 // Externally, tags are opaque non-zero positive integers.
38 // Internally, they are tagged (by shape) indexes into shape lists.
39 // idx must be non-zero.
40 #define TAG_IS_VALID(tag) ((tag).valid())
41 #define TAG_IS_UNIT(tag) (((tag).n & 1) == 0)
42 #define TAG_IS_STATIC(tag) (((tag).n & 1) == 1)
43 #define UNIT_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 0)
44 #define STATIC_INDEX_TO_TAG(idx) tag_t(((idx) << 1) | 1)
45 #define TAG_TO_INDEX(tag) ((tag).n >> 1)
46 
47 /**
48  * Internal representation of axis-aligned sometimes-square sometimes-circle shapes for moving units
49  */
50 struct UnitShape
51 {
54  entity_pos_t r; // radius of circle, or half width of square
56  entity_id_t group; // control group (typically the owner entity, or a formation controller entity) (units ignore collisions with others in the same group)
57 };
58 
59 /**
60  * Internal representation of arbitrary-rotation static square shapes for buildings
61  */
63 {
65  entity_pos_t x, z; // world-space coordinates
66  CFixedVector2D u, v; // orthogonal unit vectors - axes of local coordinate space
67  entity_pos_t hw, hh; // half width/height in local coordinate space
71 };
72 
73 /**
74  * Serialization helper template for UnitShape
75  */
77 {
78  template<typename S>
79  void operator()(S& serialize, const char* UNUSED(name), UnitShape& value)
80  {
81  serialize.NumberU32_Unbounded("entity", value.entity);
82  serialize.NumberFixed_Unbounded("x", value.x);
83  serialize.NumberFixed_Unbounded("z", value.z);
84  serialize.NumberFixed_Unbounded("r", value.r);
85  serialize.NumberU8_Unbounded("flags", value.flags);
86  serialize.NumberU32_Unbounded("group", value.group);
87  }
88 };
89 
90 /**
91  * Serialization helper template for StaticShape
92  */
94 {
95  template<typename S>
96  void operator()(S& serialize, const char* UNUSED(name), StaticShape& value)
97  {
98  serialize.NumberU32_Unbounded("entity", value.entity);
99  serialize.NumberFixed_Unbounded("x", value.x);
100  serialize.NumberFixed_Unbounded("z", value.z);
101  serialize.NumberFixed_Unbounded("u.x", value.u.X);
102  serialize.NumberFixed_Unbounded("u.y", value.u.Y);
103  serialize.NumberFixed_Unbounded("v.x", value.v.X);
104  serialize.NumberFixed_Unbounded("v.y", value.v.Y);
105  serialize.NumberFixed_Unbounded("hw", value.hw);
106  serialize.NumberFixed_Unbounded("hh", value.hh);
107  serialize.NumberU8_Unbounded("flags", value.flags);
108  serialize.NumberU32_Unbounded("group", value.group);
109  serialize.NumberU32_Unbounded("group2", value.group2);
110  }
111 };
112 
114 {
115 public:
116  static void ClassInit(CComponentManager& componentManager)
117  {
118  componentManager.SubscribeToMessageType(MT_RenderSubmit); // for debug overlays
119  }
120 
121  DEFAULT_COMPONENT_ALLOCATOR(ObstructionManager)
122 
126 
129 
130  // TODO: using std::map is a bit inefficient; is there a better way to store these?
132  std::map<u32, StaticShape> m_StaticShapes;
133  u32 m_UnitShapeNext; // next allocated id
135 
137 
142 
143  static std::string GetSchema()
144  {
145  return "<a:component type='system'/><empty/>";
146  }
147 
148  virtual void Init(const CParamNode& UNUSED(paramNode))
149  {
150  m_DebugOverlayEnabled = false;
151  m_DebugOverlayDirty = true;
152 
153  m_UnitShapeNext = 1;
154  m_StaticShapeNext = 1;
155 
156  m_DirtyID = 1; // init to 1 so default-initialised grids are considered dirty
157 
158  m_PassabilityCircular = false;
159 
161 
162  // Initialise with bogus values (these will get replaced when
163  // SetBounds is called)
165  }
166 
167  virtual void Deinit()
168  {
169  }
170 
171  template<typename S>
172  void SerializeCommon(S& serialize)
173  {
174  SerializeSpatialSubdivision()(serialize, "unit subdiv", m_UnitSubdivision);
175  SerializeSpatialSubdivision()(serialize, "static subdiv", m_StaticSubdivision);
176 
179  serialize.NumberU32_Unbounded("unit shape next", m_UnitShapeNext);
180  serialize.NumberU32_Unbounded("static shape next", m_StaticShapeNext);
181 
182  serialize.Bool("circular", m_PassabilityCircular);
183 
184  serialize.NumberFixed_Unbounded("world x0", m_WorldX0);
185  serialize.NumberFixed_Unbounded("world z0", m_WorldZ0);
186  serialize.NumberFixed_Unbounded("world x1", m_WorldX1);
187  serialize.NumberFixed_Unbounded("world z1", m_WorldZ1);
188  }
189 
190  virtual void Serialize(ISerializer& serialize)
191  {
192  // TODO: this could perhaps be optimised by not storing all the obstructions,
193  // and instead regenerating them from the other entities on Deserialize
194 
195  SerializeCommon(serialize);
196  }
197 
198  virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
199  {
200  Init(paramNode);
201 
202  SerializeCommon(deserialize);
203  }
204 
205  virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
206  {
207  switch (msg.GetType())
208  {
209  case MT_RenderSubmit:
210  {
211  const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
212  RenderSubmit(msgData.collector);
213  break;
214  }
215  }
216  }
217 
219  {
220  m_WorldX0 = x0;
221  m_WorldZ0 = z0;
222  m_WorldX1 = x1;
223  m_WorldZ1 = z1;
224  MakeDirtyAll();
225 
226  // Subdivision system bounds:
227  ENSURE(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
228  ResetSubdivisions(x1, z1);
229  }
230 
232  {
233  // Use 8x8 tile subdivisions
234  // (TODO: find the optimal number instead of blindly guessing)
237 
238  for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
239  {
240  CFixedVector2D center(it->second.x, it->second.z);
241  CFixedVector2D halfSize(it->second.r, it->second.r);
242  m_UnitSubdivision.Add(it->first, center - halfSize, center + halfSize);
243  }
244 
245  for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
246  {
247  CFixedVector2D center(it->second.x, it->second.z);
248  CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, CFixedVector2D(it->second.hw, it->second.hh));
249  m_StaticSubdivision.Add(it->first, center - bbHalfSize, center + bbHalfSize);
250  }
251  }
252 
254  {
255  UnitShape shape = { ent, x, z, r, flags, group };
256  u32 id = m_UnitShapeNext++;
257  m_UnitShapes[id] = shape;
258  MakeDirtyUnit(flags);
259 
260  m_UnitSubdivision.Add(id, CFixedVector2D(x - r, z - r), CFixedVector2D(x + r, z + r));
261 
262  return UNIT_INDEX_TO_TAG(id);
263  }
264 
265  virtual tag_t AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group, entity_id_t group2 /* = INVALID_ENTITY */)
266  {
267  fixed s, c;
268  sincos_approx(a, s, c);
269  CFixedVector2D u(c, -s);
270  CFixedVector2D v(s, c);
271 
272  StaticShape shape = { ent, x, z, u, v, w/2, h/2, flags, group, group2 };
273  u32 id = m_StaticShapeNext++;
274  m_StaticShapes[id] = shape;
275  MakeDirtyStatic(flags);
276 
277  CFixedVector2D center(x, z);
278  CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(w/2, h/2));
279  m_StaticSubdivision.Add(id, center - bbHalfSize, center + bbHalfSize);
280 
281  return STATIC_INDEX_TO_TAG(id);
282  }
283 
285  {
288  ObstructionSquare o = { x, z, u, v, r, r };
289  return o;
290  }
291 
293  {
294  fixed s, c;
295  sincos_approx(a, s, c);
296  CFixedVector2D u(c, -s);
297  CFixedVector2D v(s, c);
298 
299  ObstructionSquare o = { x, z, u, v, w/2, h/2 };
300  return o;
301  }
302 
304  {
305  ENSURE(TAG_IS_VALID(tag));
306 
307  if (TAG_IS_UNIT(tag))
308  {
309  UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
310 
312  CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
313  CFixedVector2D(shape.x + shape.r, shape.z + shape.r),
314  CFixedVector2D(x - shape.r, z - shape.r),
315  CFixedVector2D(x + shape.r, z + shape.r));
316 
317  shape.x = x;
318  shape.z = z;
319 
320  MakeDirtyUnit(shape.flags);
321  }
322  else
323  {
324  fixed s, c;
325  sincos_approx(a, s, c);
326  CFixedVector2D u(c, -s);
327  CFixedVector2D v(s, c);
328 
329  StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
330 
331  CFixedVector2D fromBbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
332  CFixedVector2D toBbHalfSize = Geometry::GetHalfBoundingBox(u, v, CFixedVector2D(shape.hw, shape.hh));
334  CFixedVector2D(shape.x, shape.z) - fromBbHalfSize,
335  CFixedVector2D(shape.x, shape.z) + fromBbHalfSize,
336  CFixedVector2D(x, z) - toBbHalfSize,
337  CFixedVector2D(x, z) + toBbHalfSize);
338 
339  shape.x = x;
340  shape.z = z;
341  shape.u = u;
342  shape.v = v;
343 
344  MakeDirtyStatic(shape.flags);
345  }
346  }
347 
348  virtual void SetUnitMovingFlag(tag_t tag, bool moving)
349  {
350  ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag));
351 
352  if (TAG_IS_UNIT(tag))
353  {
354  UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
355  if (moving)
356  shape.flags |= FLAG_MOVING;
357  else
358  shape.flags &= (flags_t)~FLAG_MOVING;
359 
360  MakeDirtyDebug();
361  }
362  }
363 
364  virtual void SetUnitControlGroup(tag_t tag, entity_id_t group)
365  {
366  ENSURE(TAG_IS_VALID(tag) && TAG_IS_UNIT(tag));
367 
368  if (TAG_IS_UNIT(tag))
369  {
370  UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
371  shape.group = group;
372  }
373  }
374 
375  virtual void SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2)
376  {
377  ENSURE(TAG_IS_VALID(tag) && TAG_IS_STATIC(tag));
378 
379  if (TAG_IS_STATIC(tag))
380  {
381  StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
382  shape.group = group;
383  shape.group2 = group2;
384  }
385  }
386 
387  virtual void RemoveShape(tag_t tag)
388  {
389  ENSURE(TAG_IS_VALID(tag));
390 
391  if (TAG_IS_UNIT(tag))
392  {
393  UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
395  CFixedVector2D(shape.x - shape.r, shape.z - shape.r),
396  CFixedVector2D(shape.x + shape.r, shape.z + shape.r));
397 
398  MakeDirtyUnit(shape.flags);
399  m_UnitShapes.erase(TAG_TO_INDEX(tag));
400  }
401  else
402  {
403  StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
404 
405  CFixedVector2D center(shape.x, shape.z);
406  CFixedVector2D bbHalfSize = Geometry::GetHalfBoundingBox(shape.u, shape.v, CFixedVector2D(shape.hw, shape.hh));
407  m_StaticSubdivision.Remove(TAG_TO_INDEX(tag), center - bbHalfSize, center + bbHalfSize);
408 
409  MakeDirtyStatic(shape.flags);
410  m_StaticShapes.erase(TAG_TO_INDEX(tag));
411  }
412  }
413 
415  {
416  ENSURE(TAG_IS_VALID(tag));
417 
418  if (TAG_IS_UNIT(tag))
419  {
420  UnitShape& shape = m_UnitShapes[TAG_TO_INDEX(tag)];
423  ObstructionSquare o = { shape.x, shape.z, u, v, shape.r, shape.r };
424  return o;
425  }
426  else
427  {
428  StaticShape& shape = m_StaticShapes[TAG_TO_INDEX(tag)];
429  ObstructionSquare o = { shape.x, shape.z, shape.u, shape.v, shape.hw, shape.hh };
430  return o;
431  }
432  }
433 
434  virtual bool TestLine(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r);
435  virtual bool TestStaticShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector<entity_id_t>* out);
436  virtual bool TestUnitShape(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector<entity_id_t>* out);
437 
438  virtual bool Rasterise(Grid<u8>& grid);
439  virtual void GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares);
440  virtual bool FindMostImportantObstruction(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, ObstructionSquare& square);
441 
442  virtual void SetPassabilityCircular(bool enabled)
443  {
444  m_PassabilityCircular = enabled;
445  MakeDirtyAll();
446  }
447 
448  virtual void SetDebugOverlay(bool enabled)
449  {
450  m_DebugOverlayEnabled = enabled;
451  m_DebugOverlayDirty = true;
452  if (!enabled)
453  m_DebugOverlayLines.clear();
454  }
455 
456  void RenderSubmit(SceneCollector& collector);
457 
458 private:
459  // To support lazy updates of grid rasterisations of obstruction data,
460  // we maintain a DirtyID here and increment it whenever obstructions change;
461  // if a grid has a lower DirtyID then it needs to be updated.
462 
463  size_t m_DirtyID;
464 
465  /**
466  * Mark all previous Rasterise()d grids as dirty, and the debug display.
467  * Call this when the world bounds have changed.
468  */
470  {
471  ++m_DirtyID;
472  m_DebugOverlayDirty = true;
473  }
474 
475  /**
476  * Mark the debug display as dirty.
477  * Call this when nothing has changed except a unit's 'moving' flag.
478  */
480  {
481  m_DebugOverlayDirty = true;
482  }
483 
484  /**
485  * Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
486  * Call this when a static shape has changed.
487  */
489  {
491  ++m_DirtyID;
492 
493  m_DebugOverlayDirty = true;
494  }
495 
496  /**
497  * Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
498  * Call this when a unit shape has changed.
499  */
500  void MakeDirtyUnit(flags_t flags)
501  {
503  ++m_DirtyID;
504 
505  m_DebugOverlayDirty = true;
506  }
507 
508  /**
509  * Test whether a Rasterise()d grid is dirty and needs updating
510  */
511  template<typename T>
512  bool IsDirty(const Grid<T>& grid)
513  {
514  return grid.m_DirtyID < m_DirtyID;
515  }
516 
517  /**
518  * Return whether the given point is within the world bounds by at least r
519  */
521  {
522  return (m_WorldX0+r <= x && x <= m_WorldX1-r && m_WorldZ0+r <= z && z <= m_WorldZ1-r);
523  }
524 
525  /**
526  * Return whether the given point is within the world bounds
527  */
529  {
530  return (m_WorldX0 <= p.X && p.X <= m_WorldX1 && m_WorldZ0 <= p.Y && p.Y <= m_WorldZ1);
531  }
532 };
533 
534 REGISTER_COMPONENT_TYPE(ObstructionManager)
535 
537 {
538  PROFILE("TestLine");
539 
540  // Check that both end points are within the world (which means the whole line must be)
541  if (!IsInWorld(x0, z0, r) || !IsInWorld(x1, z1, r))
542  return true;
543 
544  CFixedVector2D posMin (std::min(x0, x1) - r, std::min(z0, z1) - r);
545  CFixedVector2D posMax (std::max(x0, x1) + r, std::max(z0, z1) + r);
546 
547  SpatialQueryArray unitShapes;
548  m_UnitSubdivision.GetInRange(unitShapes, posMin, posMax);
549  for (int i = 0; i < unitShapes.size(); ++i)
550  {
551  std::map<u32, UnitShape>::iterator it = m_UnitShapes.find(unitShapes[i]);
552  ENSURE(it != m_UnitShapes.end());
553 
554  if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY))
555  continue;
556 
557  CFixedVector2D center(it->second.x, it->second.z);
558  CFixedVector2D halfSize(it->second.r + r, it->second.r + r);
559  if (Geometry::TestRayAASquare(CFixedVector2D(x0, z0) - center, CFixedVector2D(x1, z1) - center, halfSize))
560  return true;
561  }
562 
563  SpatialQueryArray staticShapes;
564  m_StaticSubdivision.GetInRange(staticShapes, posMin, posMax);
565  for (int i = 0; i < staticShapes.size(); ++i)
566  {
567  std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(staticShapes[i]);
568  ENSURE(it != m_StaticShapes.end());
569 
570  if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2))
571  continue;
572 
573  CFixedVector2D center(it->second.x, it->second.z);
574  CFixedVector2D halfSize(it->second.hw + r, it->second.hh + r);
575  if (Geometry::TestRaySquare(CFixedVector2D(x0, z0) - center, CFixedVector2D(x1, z1) - center, it->second.u, it->second.v, halfSize))
576  return true;
577  }
578 
579  return false;
580 }
581 
584  std::vector<entity_id_t>* out)
585 {
586  PROFILE("TestStaticShape");
587 
588  // TODO: should use the subdivision stuff here, if performance is non-negligible
589 
590  if (out)
591  out->clear();
592 
593  fixed s, c;
594  sincos_approx(a, s, c);
595  CFixedVector2D u(c, -s);
596  CFixedVector2D v(s, c);
597  CFixedVector2D center(x, z);
598  CFixedVector2D halfSize(w/2, h/2);
599 
600  // Check that all corners are within the world (which means the whole shape must be)
601  if (!IsInWorld(center + u.Multiply(halfSize.X) + v.Multiply(halfSize.Y)) ||
602  !IsInWorld(center + u.Multiply(halfSize.X) - v.Multiply(halfSize.Y)) ||
603  !IsInWorld(center - u.Multiply(halfSize.X) + v.Multiply(halfSize.Y)) ||
604  !IsInWorld(center - u.Multiply(halfSize.X) - v.Multiply(halfSize.Y)))
605  {
606  if (out)
607  out->push_back(INVALID_ENTITY); // no entity ID, so just push an arbitrary marker
608  else
609  return true;
610  }
611 
612  for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
613  {
614  if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY))
615  continue;
616 
617  CFixedVector2D center1(it->second.x, it->second.z);
618 
619  if (Geometry::PointIsInSquare(center1 - center, u, v, CFixedVector2D(halfSize.X + it->second.r, halfSize.Y + it->second.r)))
620  {
621  if (out)
622  out->push_back(it->second.entity);
623  else
624  return true;
625  }
626  }
627 
628  for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
629  {
630  if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2))
631  continue;
632 
633  CFixedVector2D center1(it->second.x, it->second.z);
634  CFixedVector2D halfSize1(it->second.hw, it->second.hh);
635  if (Geometry::TestSquareSquare(center, u, v, halfSize, center1, it->second.u, it->second.v, halfSize1))
636  {
637  if (out)
638  out->push_back(it->second.entity);
639  else
640  return true;
641  }
642  }
643 
644  if (out)
645  return !out->empty(); // collided if the list isn't empty
646  else
647  return false; // didn't collide, if we got this far
648 }
649 
652  std::vector<entity_id_t>* out)
653 {
654  PROFILE("TestUnitShape");
655 
656  // TODO: should use the subdivision stuff here, if performance is non-negligible
657 
658  // Check that the shape is within the world
659  if (!IsInWorld(x, z, r))
660  {
661  if (out)
662  out->push_back(INVALID_ENTITY); // no entity ID, so just push an arbitrary marker
663  else
664  return true;
665  }
666 
667  CFixedVector2D center(x, z);
668 
669  for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
670  {
671  if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY))
672  continue;
673 
674  entity_pos_t r1 = it->second.r;
675 
676  if (!(it->second.x + r1 < x - r || it->second.x - r1 > x + r || it->second.z + r1 < z - r || it->second.z - r1 > z + r))
677  {
678  if (out)
679  out->push_back(it->second.entity);
680  else
681  return true;
682  }
683  }
684 
685  for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
686  {
687  if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2))
688  continue;
689 
690  CFixedVector2D center1(it->second.x, it->second.z);
691  if (Geometry::PointIsInSquare(center1 - center, it->second.u, it->second.v, CFixedVector2D(it->second.hw + r, it->second.hh + r)))
692  {
693  if (out)
694  out->push_back(it->second.entity);
695  else
696  return true;
697 
698  }
699  }
700 
701  if (out)
702  return !out->empty(); // collided if the list isn't empty
703  else
704  return false; // didn't collide, if we got this far
705 }
706 
707 /**
708  * Compute the tile indexes on the grid nearest to a given point
709  */
710 static void NearestTile(entity_pos_t x, entity_pos_t z, u16& i, u16& j, u16 w, u16 h)
711 {
712  i = (u16)clamp((x / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, w-1);
713  j = (u16)clamp((z / (int)TERRAIN_TILE_SIZE).ToInt_RoundToZero(), 0, h-1);
714 }
715 
716 /**
717  * Returns the position of the center of the given tile
718  */
719 static void TileCenter(u16 i, u16 j, entity_pos_t& x, entity_pos_t& z)
720 {
723 }
724 
726 {
727  if (!IsDirty(grid))
728  return false;
729 
730  PROFILE("Rasterise");
731 
732  grid.m_DirtyID = m_DirtyID;
733 
734  // TODO: this is all hopelessly inefficient
735  // What we should perhaps do is have some kind of quadtree storing Shapes so it's
736  // quick to invalidate and update small numbers of tiles
737 
738  grid.reset();
739 
740  // For tile-based pathfinding:
741  // Since we only count tiles whose centers are inside the square,
742  // we maybe want to expand the square a bit so we're less likely to think there's
743  // free space between buildings when there isn't. But this is just a random guess
744  // and needs to be tweaked until everything works nicely.
745  //entity_pos_t expandPathfinding = entity_pos_t::FromInt(TERRAIN_TILE_SIZE / 2);
746  // Actually that's bad because units get stuck when the A* pathfinder thinks they're
747  // blocked on all sides, so it's better to underestimate
748  entity_pos_t expandPathfinding = entity_pos_t::FromInt(0);
749 
750  // For AI building foundation planning, we want to definitely block all
751  // potentially-obstructed tiles (so we don't blindly build on top of an obstruction),
752  // so we need to expand by at least 1/sqrt(2) of a tile
753  entity_pos_t expandFoundation = (entity_pos_t::FromInt(TERRAIN_TILE_SIZE) * 3) / 4;
754 
755  for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
756  {
757  CFixedVector2D center(it->second.x, it->second.z);
758 
759  if (it->second.flags & FLAG_BLOCK_PATHFINDING)
760  {
761  CFixedVector2D halfSize(it->second.hw + expandPathfinding, it->second.hh + expandPathfinding);
762  CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, halfSize);
763 
764  u16 i0, j0, i1, j1;
765  NearestTile(center.X - halfBound.X, center.Y - halfBound.Y, i0, j0, grid.m_W, grid.m_H);
766  NearestTile(center.X + halfBound.X, center.Y + halfBound.Y, i1, j1, grid.m_W, grid.m_H);
767  for (u16 j = j0; j <= j1; ++j)
768  {
769  for (u16 i = i0; i <= i1; ++i)
770  {
771  entity_pos_t x, z;
772  TileCenter(i, j, x, z);
773  if (Geometry::PointIsInSquare(CFixedVector2D(x, z) - center, it->second.u, it->second.v, halfSize))
774  grid.set(i, j, grid.get(i, j) | TILE_OBSTRUCTED_PATHFINDING);
775  }
776  }
777  }
778 
779  if (it->second.flags & FLAG_BLOCK_FOUNDATION)
780  {
781  CFixedVector2D halfSize(it->second.hw + expandFoundation, it->second.hh + expandFoundation);
782  CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(it->second.u, it->second.v, halfSize);
783 
784  u16 i0, j0, i1, j1;
785  NearestTile(center.X - halfBound.X, center.Y - halfBound.Y, i0, j0, grid.m_W, grid.m_H);
786  NearestTile(center.X + halfBound.X, center.Y + halfBound.Y, i1, j1, grid.m_W, grid.m_H);
787  for (u16 j = j0; j <= j1; ++j)
788  {
789  for (u16 i = i0; i <= i1; ++i)
790  {
791  entity_pos_t x, z;
792  TileCenter(i, j, x, z);
793  if (Geometry::PointIsInSquare(CFixedVector2D(x, z) - center, it->second.u, it->second.v, halfSize))
794  grid.set(i, j, grid.get(i, j) | TILE_OBSTRUCTED_FOUNDATION);
795  }
796  }
797  }
798  }
799 
800  for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
801  {
802  CFixedVector2D center(it->second.x, it->second.z);
803 
804  if (it->second.flags & FLAG_BLOCK_PATHFINDING)
805  {
806  entity_pos_t r = it->second.r + expandPathfinding;
807 
808  u16 i0, j0, i1, j1;
809  NearestTile(center.X - r, center.Y - r, i0, j0, grid.m_W, grid.m_H);
810  NearestTile(center.X + r, center.Y + r, i1, j1, grid.m_W, grid.m_H);
811  for (u16 j = j0; j <= j1; ++j)
812  for (u16 i = i0; i <= i1; ++i)
813  grid.set(i, j, grid.get(i, j) | TILE_OBSTRUCTED_PATHFINDING);
814  }
815 
816  if (it->second.flags & FLAG_BLOCK_FOUNDATION)
817  {
818  entity_pos_t r = it->second.r + expandFoundation;
819 
820  u16 i0, j0, i1, j1;
821  NearestTile(center.X - r, center.Y - r, i0, j0, grid.m_W, grid.m_H);
822  NearestTile(center.X + r, center.Y + r, i1, j1, grid.m_W, grid.m_H);
823  for (u16 j = j0; j <= j1; ++j)
824  for (u16 i = i0; i <= i1; ++i)
825  grid.set(i, j, grid.get(i, j) | TILE_OBSTRUCTED_FOUNDATION);
826  }
827  }
828 
829  // Any tiles outside or very near the edge of the map are impassable
830 
831  // WARNING: CCmpRangeManager::LosIsOffWorld needs to be kept in sync with this
832  const u16 edgeSize = 3; // number of tiles around the edge that will be off-world
833 
835 
837  {
838  for (u16 j = 0; j < grid.m_H; ++j)
839  {
840  for (u16 i = 0; i < grid.m_W; ++i)
841  {
842  // Based on CCmpRangeManager::LosIsOffWorld
843  // but tweaked since it's tile-based instead.
844  // (We double all the values so we can handle half-tile coordinates.)
845  // This needs to be slightly tighter than the LOS circle,
846  // else units might get themselves lost in the SoD around the edge.
847 
848  ssize_t dist2 = (i*2 + 1 - grid.m_W)*(i*2 + 1 - grid.m_W)
849  + (j*2 + 1 - grid.m_H)*(j*2 + 1 - grid.m_H);
850 
851  if (dist2 >= (grid.m_W - 2*edgeSize) * (grid.m_H - 2*edgeSize))
852  grid.set(i, j, edgeFlags);
853  }
854  }
855  }
856  else
857  {
858  u16 i0, j0, i1, j1;
859  NearestTile(m_WorldX0, m_WorldZ0, i0, j0, grid.m_W, grid.m_H);
860  NearestTile(m_WorldX1, m_WorldZ1, i1, j1, grid.m_W, grid.m_H);
861 
862  for (u16 j = 0; j < grid.m_H; ++j)
863  for (u16 i = 0; i < i0+edgeSize; ++i)
864  grid.set(i, j, edgeFlags);
865  for (u16 j = 0; j < grid.m_H; ++j)
866  for (u16 i = (u16)(i1-edgeSize+1); i < grid.m_W; ++i)
867  grid.set(i, j, edgeFlags);
868  for (u16 j = 0; j < j0+edgeSize; ++j)
869  for (u16 i = (u16)(i0+edgeSize); i < i1-edgeSize+1; ++i)
870  grid.set(i, j, edgeFlags);
871  for (u16 j = (u16)(j1-edgeSize+1); j < grid.m_H; ++j)
872  for (u16 i = (u16)(i0+edgeSize); i < i1-edgeSize+1; ++i)
873  grid.set(i, j, edgeFlags);
874  }
875 
876  return true;
877 }
878 
879 void CCmpObstructionManager::GetObstructionsInRange(const IObstructionTestFilter& filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector<ObstructionSquare>& squares)
880 {
881  PROFILE("GetObstructionsInRange");
882 
883  ENSURE(x0 <= x1 && z0 <= z1);
884 
885  SpatialQueryArray unitShapes;
886  m_UnitSubdivision.GetInRange(unitShapes, CFixedVector2D(x0, z0), CFixedVector2D(x1, z1));
887  for (int i = 0; i < unitShapes.size(); ++i)
888  {
889  std::map<u32, UnitShape>::iterator it = m_UnitShapes.find(unitShapes[i]);
890  ENSURE(it != m_UnitShapes.end());
891 
892  if (!filter.TestShape(UNIT_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, INVALID_ENTITY))
893  continue;
894 
895  entity_pos_t r = it->second.r;
896 
897  // Skip this object if it's completely outside the requested range
898  if (it->second.x + r < x0 || it->second.x - r > x1 || it->second.z + r < z0 || it->second.z - r > z1)
899  continue;
900 
903  ObstructionSquare s = { it->second.x, it->second.z, u, v, r, r };
904  squares.push_back(s);
905  }
906 
907  SpatialQueryArray staticShapes;
908  m_StaticSubdivision.GetInRange(staticShapes, CFixedVector2D(x0, z0), CFixedVector2D(x1, z1));
909  for (int i = 0; i < staticShapes.size(); ++i)
910  {
911  std::map<u32, StaticShape>::iterator it = m_StaticShapes.find(staticShapes[i]);
912  ENSURE(it != m_StaticShapes.end());
913 
914  if (!filter.TestShape(STATIC_INDEX_TO_TAG(it->first), it->second.flags, it->second.group, it->second.group2))
915  continue;
916 
917  entity_pos_t r = it->second.hw + it->second.hh; // overestimate the max dist of an edge from the center
918 
919  // Skip this object if its overestimated bounding box is completely outside the requested range
920  if (it->second.x + r < x0 || it->second.x - r > x1 || it->second.z + r < z0 || it->second.z - r > z1)
921  continue;
922 
923  // TODO: maybe we should use Geometry::GetHalfBoundingBox to be more precise?
924 
925  ObstructionSquare s = { it->second.x, it->second.z, it->second.u, it->second.v, it->second.hw, it->second.hh };
926  squares.push_back(s);
927  }
928 }
929 
931 {
932  std::vector<ObstructionSquare> squares;
933 
934  CFixedVector2D center(x, z);
935 
936  // First look for obstructions that are covering the exact target point
937  GetObstructionsInRange(filter, x, z, x, z, squares);
938  // Building squares are more important but returned last, so check backwards
939  for (std::vector<ObstructionSquare>::reverse_iterator it = squares.rbegin(); it != squares.rend(); ++it)
940  {
941  CFixedVector2D halfSize(it->hw, it->hh);
942  if (Geometry::PointIsInSquare(CFixedVector2D(it->x, it->z) - center, it->u, it->v, halfSize))
943  {
944  square = *it;
945  return true;
946  }
947  }
948 
949  // Then look for obstructions that cover the target point when expanded by r
950  // (i.e. if the target is not inside an object but closer than we can get to it)
951 
952  GetObstructionsInRange(filter, x-r, z-r, x+r, z+r, squares);
953  // Building squares are more important but returned last, so check backwards
954  for (std::vector<ObstructionSquare>::reverse_iterator it = squares.rbegin(); it != squares.rend(); ++it)
955  {
956  CFixedVector2D halfSize(it->hw + r, it->hh + r);
957  if (Geometry::PointIsInSquare(CFixedVector2D(it->x, it->z) - center, it->u, it->v, halfSize))
958  {
959  square = *it;
960  return true;
961  }
962  }
963 
964  return false;
965 }
966 
968 {
970  return;
971 
972  CColor defaultColour(0, 0, 1, 1);
973  CColor movingColour(1, 0, 1, 1);
974  CColor boundsColour(1, 1, 0, 1);
975 
976  // If the shapes have changed, then regenerate all the overlays
978  {
979  m_DebugOverlayLines.clear();
980 
981  m_DebugOverlayLines.push_back(SOverlayLine());
982  m_DebugOverlayLines.back().m_Color = boundsColour;
984  (m_WorldX0+m_WorldX1).ToFloat()/2.f, (m_WorldZ0+m_WorldZ1).ToFloat()/2.f,
985  (m_WorldX1-m_WorldX0).ToFloat(), (m_WorldZ1-m_WorldZ0).ToFloat(),
986  0, m_DebugOverlayLines.back(), true);
987 
988  for (std::map<u32, UnitShape>::iterator it = m_UnitShapes.begin(); it != m_UnitShapes.end(); ++it)
989  {
990  m_DebugOverlayLines.push_back(SOverlayLine());
991  m_DebugOverlayLines.back().m_Color = ((it->second.flags & FLAG_MOVING) ? movingColour : defaultColour);
992  SimRender::ConstructSquareOnGround(GetSimContext(), it->second.x.ToFloat(), it->second.z.ToFloat(), it->second.r.ToFloat()*2, it->second.r.ToFloat()*2, 0, m_DebugOverlayLines.back(), true);
993  }
994 
995  for (std::map<u32, StaticShape>::iterator it = m_StaticShapes.begin(); it != m_StaticShapes.end(); ++it)
996  {
997  m_DebugOverlayLines.push_back(SOverlayLine());
998  m_DebugOverlayLines.back().m_Color = defaultColour;
999  float a = atan2f(it->second.v.X.ToFloat(), it->second.v.Y.ToFloat());
1000  SimRender::ConstructSquareOnGround(GetSimContext(), it->second.x.ToFloat(), it->second.z.ToFloat(), it->second.hw.ToFloat()*2, it->second.hh.ToFloat()*2, a, m_DebugOverlayLines.back(), true);
1001  }
1002 
1003  m_DebugOverlayDirty = false;
1004  }
1005 
1006  for (size_t i = 0; i < m_DebugOverlayLines.size(); ++i)
1007  collector.Submit(&m_DebugOverlayLines[i]);
1008 }
An entity initialisation parameter node.
Definition: ParamNode.h:112
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
#define u8
Definition: types.h:39
A simple fixed-point number class.
Definition: Fixed.h:115
virtual void SetDebugOverlay(bool enabled)
Toggle the rendering of debug info.
virtual bool TestLine(const IObstructionTestFilter &filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, entity_pos_t r)
Collision test a flat-ended thick line against the current set of shapes.
#define TAG_IS_VALID(tag)
int size() const
Definition: Spatial.h:37
Interface for ICmpObstructionManager Test functions to filter out unwanted shapes.
#define REGISTER_COMPONENT_TYPE(cname)
Definition: Component.h:30
void GetInRange(SpatialQueryArray &out, CFixedVector2D posMin, CFixedVector2D posMax)
Returns a sorted list of unique items that includes all items within the given axis-aligned square ra...
Definition: Spatial.h:290
void MakeDirtyUnit(flags_t flags)
Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
Helper templates for serializing/deserializing common objects.
virtual tag_t AddUnitShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_pos_t r, flags_t flags, entity_id_t group)
Register a unit shape.
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
Line-based overlay, with world-space coordinates, rendered in the world potentially behind other obje...
Definition: Overlay.h:36
static void ClassInit(CComponentManager &componentManager)
virtual void HandleMessage(const CMessage &msg, bool global)
static CFixed Zero()
Definition: Fixed.h:127
const ssize_t TERRAIN_TILE_SIZE
metres [world space units] per tile in x and z
Definition: Terrain.h:40
virtual void Serialize(ISerializer &serialize)
void Remove(uint32_t item, CFixedVector2D fromMin, CFixedVector2D fromMax)
Remove an item with the given &#39;from&#39; size.
Definition: Spatial.h:222
std::vector< SOverlayLine > m_DebugOverlayLines
void Reset(entity_pos_t maxX, entity_pos_t maxZ, entity_pos_t divisionSize)
Definition: Spatial.h:186
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1)
Set the bounds of the world.
Definition: Overlay.h:34
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
u16 m_H
Definition: Grid.h:101
virtual bool TestStaticShape(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, std::vector< entity_id_t > *out)
Collision test a static square shape against the current set of shapes.
Add renderable objects to the scene collector.
Definition: MessageTypes.h:145
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
void MakeDirtyDebug()
Mark the debug display as dirty.
virtual void SetUnitControlGroup(tag_t tag, entity_id_t group)
Set the control group of a unit shape.
Serialization helper template for StaticShape.
This interface accepts renderable objects.
Definition: Scene.h:82
virtual void SetStaticControlGroup(tag_t tag, entity_id_t group, entity_id_t group2)
Sets the control group of a static shape.
bool IsInWorld(CFixedVector2D p)
Return whether the given point is within the world bounds.
size_t m_DirtyID
Definition: Grid.h:104
virtual void SetUnitMovingFlag(tag_t tag, bool moving)
Set whether a unit shape is moving or stationary.
virtual bool TestShape(tag_t tag, flags_t flags, entity_id_t group, entity_id_t group2) const =0
Return true if the shape with the specified parameters should be tested for collisions.
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
virtual void RemoveShape(tag_t tag)
Remove an existing shape.
virtual int GetType() const =0
Serialization helper template for UnitShape.
Internal representation of axis-aligned sometimes-square sometimes-circle shapes for moving units...
std::map< u32, UnitShape > m_UnitShapes
void reset()
Definition: Grid.h:79
u8 flags_t
Bitmask of EFlag values.
virtual ObstructionSquare GetStaticShapeObstruction(entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h)
void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
#define TAG_TO_INDEX(tag)
#define TAG_IS_UNIT(tag)
void SerializeCommon(S &serialize)
void operator()(S &serialize, const char *name, StaticShape &value)
virtual void MoveShape(tag_t tag, entity_pos_t x, entity_pos_t z, entity_angle_t a)
Adjust the position and angle of an existing shape.
std::map< u32, StaticShape > m_StaticShapes
#define PROFILE(name)
Definition: Profile.h:195
#define DEFAULT_COMPONENT_ALLOCATOR(cname)
Definition: Component.h:44
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.
SpatialSubdivision m_StaticSubdivision
const CSimContext & GetSimContext() const
Definition: IComponent.h:52
virtual void GetObstructionsInRange(const IObstructionTestFilter &filter, entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, std::vector< ObstructionSquare > &squares)
Find all the obstructions that are inside (or partially inside) the given range.
bool TestRayAASquare(CFixedVector2D a, CFixedVector2D b, CFixedVector2D halfSize)
Definition: Geometry.cpp:241
bool TestRaySquare(CFixedVector2D a, CFixedVector2D b, CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:193
intptr_t ssize_t
Definition: wposix_types.h:82
Internal representation of arbitrary-rotation static square shapes for buildings. ...
static CFixed FromInt(int n)
Definition: Fixed.h:136
bool IsDirty(const Grid< T > &grid)
Test whether a Rasterise()d grid is dirty and needs updating.
Helper functions related to rendering.
SpatialSubdivision m_UnitSubdivision
CFixedVector2D Multiply(fixed n) const
Multiply by a CFixed.
Definition: FixedVector2D.h:86
#define u16
Definition: types.h:40
A very basic subdivision scheme for finding items in ranges.
Definition: Spatial.h:63
bool TestSquareSquare(CFixedVector2D c0, CFixedVector2D u0, CFixedVector2D v0, CFixedVector2D halfSize0, CFixedVector2D c1, CFixedVector2D u1, CFixedVector2D v1, CFixedVector2D halfSize1)
Definition: Geometry.cpp:305
virtual void SetPassabilityCircular(bool enabled)
Set the passability to be restricted to a circular map.
virtual tag_t AddStaticShape(entity_id_t ent, entity_pos_t x, entity_pos_t z, entity_angle_t a, entity_pos_t w, entity_pos_t h, flags_t flags, entity_id_t group, entity_id_t group2)
Register a static shape.
#define u32
Definition: types.h:41
virtual ObstructionSquare GetUnitShapeObstruction(entity_pos_t x, entity_pos_t z, entity_pos_t r)
bool IsZero() const
Returns true if the number is precisely 0.
Definition: Fixed.h:199
SceneCollector & collector
Definition: MessageTypes.h:155
virtual ObstructionSquare GetObstruction(tag_t tag)
Get the obstruction square representing the given shape.
T & get(int i, int j) const
Definition: Grid.h:93
void RenderSubmit(SceneCollector &collector)
void ConstructSquareOnGround(const CSimContext &context, float x, float z, float w, float h, float a, SOverlayLine &overlay, bool floating, float heightOffset=0.25f)
Constructs overlay line as rectangle with given center and dimensions, conforming to terrain...
Definition: Render.cpp:121
#define TAG_IS_STATIC(tag)
bool IsInWorld(entity_pos_t x, entity_pos_t z, entity_pos_t r)
Return whether the given point is within the world bounds by at least r.
ICmpObstructionManager::flags_t flags
void sincos_approx(CFixed_15_16 a, CFixed_15_16 &sin_out, CFixed_15_16 &cos_out)
Compute sin(a) and cos(a).
Definition: Fixed.cpp:187
virtual bool TestUnitShape(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, std::vector< entity_id_t > *out)
Collision test a unit shape against the current set of registered shapes, and optionally writes a lis...
A simple fixed-size array that works similar to an std::vector but is obviously limited in its max it...
Definition: Spatial.h:28
void Move(uint32_t item, CFixedVector2D fromMin, CFixedVector2D fromMax, CFixedVector2D toMin, CFixedVector2D toMax)
Equivalent to Remove() then Add(), but potentially faster.
Definition: Spatial.h:251
Serialization helper template for SpatialSubdivision.
Definition: Spatial.h:361
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)
virtual bool Rasterise(Grid< u8 > &grid)
Convert the current set of shapes onto a grid.
u16 m_W
Definition: Grid.h:101
void Add(uint32_t item, CFixedVector2D toMin, CFixedVector2D toMax)
Add an item with the given &#39;to&#39; size.
Definition: Spatial.h:200
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
const entity_id_t INVALID_ENTITY
Invalid entity ID.
Definition: Entity.h:36
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
static std::string GetSchema()
virtual void Submit(CPatch *patch)=0
Submit a terrain patch that is part of the scene.
void operator()(S &serialize, const char *name, UnitShape &value)
void MakeDirtyAll()
Mark all previous Rasterise()d grids as dirty, and the debug display.
#define STATIC_INDEX_TO_TAG(idx)
T clamp(T value, T min, T max)
Definition: MathUtil.h:32
#define UNIT_INDEX_TO_TAG(idx)
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
CFixedVector2D GetHalfBoundingBox(CFixedVector2D u, CFixedVector2D v, CFixedVector2D halfSize)
Definition: Geometry.cpp:40
Obstruction manager: provides efficient spatial queries over objects in the world.
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.
ICmpObstructionManager::flags_t flags
void MakeDirtyStatic(flags_t flags)
Mark all previous Rasterise()d grids as dirty, if they depend on this shape.
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34
External identifiers for shapes.
virtual bool FindMostImportantObstruction(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, ObstructionSquare &square)
Find a single obstruction that blocks a unit at the given point with the given radius.
virtual void Init(const CParamNode &paramNode)