Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CCmpFootprint.cpp
Go to the documentation of this file.
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 
21 #include "ICmpFootprint.h"
22 
29 #include "graphics/Terrain.h" // For TERRAIN_TILE_SIZE
30 #include "maths/FixedVector2D.h"
31 
33 {
34 public:
35  static void ClassInit(CComponentManager& UNUSED(componentManager))
36  {
37  }
38 
40 
42  entity_pos_t m_Size0; // width/radius
43  entity_pos_t m_Size1; // height/radius
45 
46  static std::string GetSchema()
47  {
48  return
49  "<a:help>Approximation of the entity's shape, for collision detection and outline rendering. "
50  "Shapes are flat horizontal squares or circles, extended vertically to a given height.</a:help>"
51  "<a:example>"
52  "<Square width='3.0' height='3.0'/>"
53  "<Height>0.0</Height>"
54  "</a:example>"
55  "<a:example>"
56  "<Circle radius='0.5'/>"
57  "<Height>0.0</Height>"
58  "</a:example>"
59  "<choice>"
60  "<element name='Square' a:help='Set the footprint to a square of the given size'>"
61  "<attribute name='width' a:help='Size of the footprint along the left/right direction (in metres)'>"
62  "<ref name='positiveDecimal'/>"
63  "</attribute>"
64  "<attribute name='depth' a:help='Size of the footprint along the front/back direction (in metres)'>"
65  "<ref name='positiveDecimal'/>"
66  "</attribute>"
67  "</element>"
68  "<element name='Circle' a:help='Set the footprint to a circle of the given size'>"
69  "<attribute name='radius' a:help='Radius of the footprint (in metres)'>"
70  "<ref name='positiveDecimal'/>"
71  "</attribute>"
72  "</element>"
73  "</choice>"
74  "<element name='Height' a:help='Vertical extent of the footprint (in metres)'>"
75  "<ref name='nonNegativeDecimal'/>"
76  "</element>";
77  }
78 
79  virtual void Init(const CParamNode& paramNode)
80  {
81  if (paramNode.GetChild("Square").IsOk())
82  {
83  m_Shape = SQUARE;
84  m_Size0 = paramNode.GetChild("Square").GetChild("@width").ToFixed();
85  m_Size1 = paramNode.GetChild("Square").GetChild("@depth").ToFixed();
86  }
87  else if (paramNode.GetChild("Circle").IsOk())
88  {
89  m_Shape = CIRCLE;
90  m_Size0 = m_Size1 = paramNode.GetChild("Circle").GetChild("@radius").ToFixed();
91  }
92  else
93  {
94  // Error - pick some default
95  m_Shape = CIRCLE;
97  }
98 
99  m_Height = paramNode.GetChild("Height").ToFixed();
100  }
101 
102  virtual void Deinit()
103  {
104  }
105 
106  virtual void Serialize(ISerializer& UNUSED(serialize))
107  {
108  // No dynamic state to serialize
109  }
110 
111  virtual void Deserialize(const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
112  {
113  Init(paramNode);
114  }
115 
116  virtual void GetShape(EShape& shape, entity_pos_t& size0, entity_pos_t& size1, entity_pos_t& height)
117  {
118  shape = m_Shape;
119  size0 = m_Size0;
120  size1 = m_Size1;
121  height = m_Height;
122  }
123 
125  {
126  // Try to find a free space around the building's footprint.
127  // (Note that we use the footprint, not the obstruction shape - this might be a bit dodgy
128  // because the footprint might be inside the obstruction, but it hopefully gives us a nicer
129  // shape.)
130 
132 
133  CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
134  if (!cmpPosition || !cmpPosition->IsInWorld())
135  return error;
136 
137  CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity());
138  if (!cmpObstructionManager)
139  return error;
140 
141  entity_pos_t spawnedRadius;
143 
144  CmpPtr<ICmpObstruction> cmpSpawnedObstruction(GetSimContext(), spawned);
145  if (cmpSpawnedObstruction)
146  {
147  spawnedRadius = cmpSpawnedObstruction->GetUnitRadius();
148  spawnedTag = cmpSpawnedObstruction->GetObstruction();
149  }
150  // else use zero radius
151 
152  // Get passability class from UnitMotion
153  CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), spawned);
154  if (!cmpUnitMotion)
155  return error;
156 
157  ICmpPathfinder::pass_class_t spawnedPass = cmpUnitMotion->GetPassabilityClass();
158  CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity());
159  if (!cmpPathfinder)
160  return error;
161 
162  CFixedVector2D initialPos = cmpPosition->GetPosition2D();
163  entity_angle_t initialAngle = cmpPosition->GetRotation().Y;
164 
165  // Max spawning distance in tiles
166  const i32 maxSpawningDistance = 4;
167 
168  if (m_Shape == CIRCLE)
169  {
170  // Expand outwards from foundation
171  for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
172  {
173  // The spawn point should be far enough from this footprint to fit the unit, plus a little gap
174  entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist);
175  entity_pos_t radius = m_Size0 + clearance;
176 
177  // Try equally-spaced points around the circle in alternating directions, starting from the front
178  const i32 numPoints = 31 + 2*dist;
179  for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
180  {
181  entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints);
182 
183  fixed s, c;
184  sincos_approx(angle, s, c);
185 
186  CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius));
187 
188  SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
189  if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
190  return pos; // this position is okay, so return it
191  }
192  }
193  }
194  else
195  {
196  fixed s, c;
197  sincos_approx(initialAngle, s, c);
198 
199  // Expand outwards from foundation
200  for (i32 dist = 0; dist <= maxSpawningDistance; ++dist)
201  {
202  // The spawn point should be far enough from this footprint to fit the unit, plus a little gap
203  entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist);
204 
205  for (i32 edge = 0; edge < 4; ++edge)
206  {
207  // Try equally-spaced points along the edge in alternating directions, starting from the middle
208  const i32 numPoints = 9 + 2*dist;
209 
210  // Compute the direction and length of the current edge
211  CFixedVector2D dir;
212  fixed sx, sy;
213  switch (edge)
214  {
215  case 0:
216  dir = CFixedVector2D(c, -s);
217  sx = m_Size0;
218  sy = m_Size1;
219  break;
220  case 1:
221  dir = CFixedVector2D(-s, -c);
222  sx = m_Size1;
223  sy = m_Size0;
224  break;
225  case 2:
226  dir = CFixedVector2D(s, c);
227  sx = m_Size1;
228  sy = m_Size0;
229  break;
230  case 3:
231  dir = CFixedVector2D(-c, s);
232  sx = m_Size0;
233  sy = m_Size1;
234  break;
235  }
236  CFixedVector2D center = initialPos - dir.Perpendicular().Multiply(sy/2 + clearance);
237  dir = dir.Multiply((sx + clearance*2) / (int)(numPoints-1));
238 
239  for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2]
240  {
241  CFixedVector2D pos (center + dir*i);
242 
243  SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity
244  if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS)
245  return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it
246  }
247  }
248  }
249  }
250 
251  return error;
252  }
253 };
254 
255 REGISTER_COMPONENT_TYPE(Footprint)
An entity initialisation parameter node.
Definition: ParamNode.h:112
A simple fixed-point number class.
Definition: Fixed.h:115
#define REGISTER_COMPONENT_TYPE(cname)
Definition: Component.h:30
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
entity_pos_t m_Size0
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
bool IsOk() const
Returns true if this is a valid CParamNode, false if it represents a non-existent node...
Definition: ParamNode.cpp:193
#define i32
Definition: types.h:36
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
virtual CFixedVector3D GetRotation()=0
Returns the current rotation (relative to the upwards axis), as Euler angles with X=pitch...
static std::string GetSchema()
static CFixed Pi()
Definition: Fixed.cpp:182
virtual bool IsInWorld()=0
Returns true if the entity currently exists at a defined position in the world.
virtual void GetShape(EShape &shape, entity_pos_t &size0, entity_pos_t &size1, entity_pos_t &height)
Return the shape of this footprint.
CEntityHandle GetEntityHandle() const
Definition: IComponent.h:45
fixed ToFixed() const
Parses the content of this node as a fixed-point number.
Definition: ParamNode.cpp:222
static void ClassInit(CComponentManager &componentManager)
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)
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
CFixedVector2D Perpendicular()
virtual ICmpObstruction::EFoundationCheck CheckUnitPlacement(const IObstructionTestFilter &filter, entity_pos_t x, entity_pos_t z, entity_pos_t r, pass_class_t passClass)=0
Check whether a unit placed here is valid and doesn&#39;t hit any obstructions or impassable terrain...
CFixed Multiply(CFixed n) const
Multiply by a CFixed.
Definition: Fixed.h:290
virtual CFixedVector2D GetPosition2D()=0
Returns the current x,z position (no interpolation).
#define DEFAULT_COMPONENT_ALLOCATOR(cname)
Definition: Component.h:44
const CSimContext & GetSimContext() const
Definition: IComponent.h:52
virtual void Serialize(ISerializer &serialize)
virtual void Init(const CParamNode &paramNode)
entity_pos_t m_Height
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
CEntityHandle GetSystemEntity() const
Definition: IComponent.h:50
static CFixed FromInt(int n)
Definition: Fixed.h:136
virtual ICmpPathfinder::pass_class_t GetPassabilityClass()=0
Get the unit&#39;s passability class.
virtual entity_pos_t GetUnitRadius()=0
CFixedVector2D Multiply(fixed n) const
Multiply by a CFixed.
Definition: FixedVector2D.h:86
entity_pos_t m_Size1
Obstruction test filter that will test only against shapes that do not have the specified tag set...
JSBool error(JSContext *cx, uintN argc, jsval *vp)
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
Footprints - an approximation of the entity&#39;s shape, used for collision detection and for rendering s...
Definition: ICmpFootprint.h:33
virtual CFixedVector3D PickSpawnPoint(entity_id_t spawned)
Pick a sensible position to place a newly-spawned entity near this footprint, such that it won&#39;t be i...
virtual void Deinit()
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
virtual ICmpObstructionManager::tag_t GetObstruction()=0
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34
External identifiers for shapes.