Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CCmpProjectileManager.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 "ICmpProjectileManager.h"
22 
23 #include "ICmpObstruction.h"
24 #include "ICmpObstructionManager.h"
25 #include "ICmpPosition.h"
26 #include "ICmpRangeManager.h"
27 #include "ICmpTerrain.h"
28 #include "ICmpVisual.h"
30 
31 #include "graphics/Frustum.h"
32 #include "graphics/Model.h"
33 #include "graphics/Unit.h"
34 #include "graphics/UnitManager.h"
35 #include "maths/Matrix3D.h"
36 #include "maths/Quaternion.h"
37 #include "maths/Vector3D.h"
38 #include "ps/CLogger.h"
39 #include "renderer/Scene.h"
40 
41 // Time (in seconds) before projectiles that stuck in the ground are destroyed
42 const static float PROJECTILE_DECAY_TIME = 30.f;
43 
45 {
46 public:
47  static void ClassInit(CComponentManager& componentManager)
48  {
49  componentManager.SubscribeToMessageType(MT_Interpolate);
50  componentManager.SubscribeToMessageType(MT_RenderSubmit);
51  }
52 
53  DEFAULT_COMPONENT_ALLOCATOR(ProjectileManager)
54 
55  static std::string GetSchema()
56  {
57  return "<a:component type='system'/><empty/>";
58  }
59 
60  virtual void Init(const CParamNode& UNUSED(paramNode))
61  {
62  m_ActorSeed = 0;
63  m_NextId = 1;
64  }
65 
66  virtual void Deinit()
67  {
68  for (size_t i = 0; i < m_Projectiles.size(); ++i)
70  m_Projectiles.clear();
71  }
72 
73  virtual void Serialize(ISerializer& serialize)
74  {
75  // Because this is just graphical effects, and because it's all non-deterministic floating point,
76  // we don't do much serialization here.
77  // (That means projectiles will vanish if you save/load - is that okay?)
78 
79  // The attack code stores the id so that the projectile gets deleted when it hits the target
80  serialize.NumberU32_Unbounded("next id", m_NextId);
81  }
82 
83  virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
84  {
85  Init(paramNode);
86 
87  // The attack code stores the id so that the projectile gets deleted when it hits the target
88  deserialize.NumberU32_Unbounded("next id", m_NextId);
89  }
90 
91  virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
92  {
93  switch (msg.GetType())
94  {
95  case MT_Interpolate:
96  {
97  const CMessageInterpolate& msgData = static_cast<const CMessageInterpolate&> (msg);
98  Interpolate(msgData.deltaSimTime);
99  break;
100  }
101  case MT_RenderSubmit:
102  {
103  const CMessageRenderSubmit& msgData = static_cast<const CMessageRenderSubmit&> (msg);
104  RenderSubmit(msgData.collector, msgData.frustum, msgData.culling);
105  break;
106  }
107  }
108  }
109 
110  virtual uint32_t LaunchProjectileAtPoint(entity_id_t source, CFixedVector3D target, fixed speed, fixed gravity)
111  {
112  return LaunchProjectile(source, target, speed, gravity);
113  }
114 
115  virtual void RemoveProjectile(uint32_t);
116 
117 private:
118  struct Projectile
119  {
124  float time;
125  float timeHit;
126  float gravity;
127  bool stopped;
129 
131  {
132  float t2 = t;
133  if (t2 > timeHit)
134  t2 = timeHit + logf(1.f + t2 - timeHit);
135 
136  CVector3D ret(origin);
137  ret.X += v.X * t2;
138  ret.Z += v.Z * t2;
139  ret.Y += v.Y * t2 - 0.5f * gravity * t * t;
140  return ret;
141  }
142  };
143 
144  std::vector<Projectile> m_Projectiles;
145 
147 
149 
150  uint32_t LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity);
151 
152  void AdvanceProjectile(Projectile& projectile, float dt);
153 
154  void Interpolate(float frameTime);
155 
156  void RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling);
157 };
158 
159 REGISTER_COMPONENT_TYPE(ProjectileManager)
160 
161 uint32_t CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity)
162 {
163  // This is network synced so don't use GUI checks before incrementing or it breaks any non GUI simulations
164  uint32_t currentId = m_NextId++;
165 
166  if (!GetSimContext().HasUnitManager())
167  return currentId; // do nothing if graphics are disabled
168 
169  CmpPtr<ICmpVisual> cmpSourceVisual(GetSimContext(), source);
170  if (!cmpSourceVisual)
171  return currentId;
172 
173  std::wstring name = cmpSourceVisual->GetProjectileActor();
174  if (name.empty())
175  {
176  // If the actor was actually loaded, complain that it doesn't have a projectile
177  if (!cmpSourceVisual->GetActorShortName().empty())
178  LOGERROR(L"Unit with actor '%ls' launched a projectile but has no actor on 'projectile' attachpoint", cmpSourceVisual->GetActorShortName().c_str());
179  return 0;
180  }
181 
182  Projectile projectile;
183  projectile.id = currentId;
184  projectile.time = 0.f;
185  projectile.stopped = false;
186  projectile.gravity = gravity.ToFloat();
187 
188  projectile.origin = cmpSourceVisual->GetProjectileLaunchPoint();
189  if (!projectile.origin)
190  {
191  // If there's no explicit launch point, take a guess based on the entity position
192  CmpPtr<ICmpPosition> sourcePos(GetSimContext(), source);
193  if (!sourcePos)
194  return 0;
195  projectile.origin = sourcePos->GetPosition();
196  projectile.origin.Y += 3.f;
197  }
198 
199  std::set<CStr> selections;
200  projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, m_ActorSeed++, selections);
201  if (!projectile.unit) // The error will have already been logged
202  return 0;
203 
204  projectile.pos = projectile.origin;
205  CVector3D offset(targetPoint);
206  offset -= projectile.pos;
207  float horizDistance = sqrtf(offset.X*offset.X + offset.Z*offset.Z);
208  projectile.timeHit = horizDistance / speed.ToFloat();
209  projectile.v = offset * (1.f / projectile.timeHit);
210 
211  projectile.v.Y = offset.Y / projectile.timeHit + 0.5f * projectile.gravity * projectile.timeHit;
212 
213  m_Projectiles.push_back(projectile);
214 
215  return projectile.id;
216 }
217 
219 {
220  projectile.time += dt;
221  if (projectile.stopped)
222  return;
223 
224  CVector3D delta;
225  if (dt < 0.1f)
226  delta = projectile.pos;
227  else // For big dt delta is unprecise
228  delta = projectile.position(projectile.time - 0.1f);
229 
230  projectile.pos = projectile.position(projectile.time);
231 
232  delta = projectile.pos - delta;
233 
234  // If we've passed the target position and haven't stopped yet,
235  // carry on until we reach solid land
236  if (projectile.time >= projectile.timeHit)
237  {
238  CmpPtr<ICmpTerrain> cmpTerrain(GetSystemEntity());
239  if (cmpTerrain)
240  {
241  float h = cmpTerrain->GetExactGroundLevel(projectile.pos.X, projectile.pos.Z);
242  if (projectile.pos.Y < h)
243  {
244  projectile.pos.Y = h; // stick precisely to the terrain
245  projectile.stopped = true;
246  }
247  }
248  }
249 
250  // Construct a rotation matrix so that (0,1,0) is in the direction of 'delta'
251 
252  CVector3D up(0, 1, 0);
253 
254  delta.Normalize();
255  CVector3D axis = up.Cross(delta);
256  if (axis.LengthSquared() < 0.0001f)
257  axis = CVector3D(1, 0, 0); // if up & delta are almost collinear, rotate around some other arbitrary axis
258  else
259  axis.Normalize();
260 
261  float angle = acosf(up.Dot(delta));
262 
263  CMatrix3D transform;
264  CQuaternion quat;
265  quat.FromAxisAngle(axis, angle);
266  quat.ToMatrix(transform);
267 
268  // Then apply the translation
269  transform.Translate(projectile.pos);
270 
271  // Move the model
272  projectile.unit->GetModel().SetTransform(transform);
273 }
274 
276 {
277  for (size_t i = 0; i < m_Projectiles.size(); ++i)
278  {
279  AdvanceProjectile(m_Projectiles[i], frameTime);
280  }
281 
282  // Remove the ones that have reached their target
283  for (size_t i = 0; i < m_Projectiles.size(); )
284  {
285  // Projectiles hitting targets get removed immediately.
286  // Those hitting the ground stay for a while, because it looks pretty.
287  if (m_Projectiles[i].stopped)
288  {
289  if (m_Projectiles[i].time - m_Projectiles[i].timeHit > PROJECTILE_DECAY_TIME)
290  {
291  // Delete in-place by swapping with the last in the list
294  m_Projectiles.pop_back();
295  continue; // don't increment i
296  }
297  }
298 
299  ++i;
300  }
301 }
302 
304 {
305  // Scan through the projectile list looking for one with the correct id to remove
306  for (size_t i = 0; i < m_Projectiles.size(); i++)
307  {
308  if (m_Projectiles[i].id == id)
309  {
310  // Delete in-place by swapping with the last in the list
313  m_Projectiles.pop_back();
314  return;
315  }
316  }
317 }
318 
319 void CCmpProjectileManager::RenderSubmit(SceneCollector& collector, const CFrustum& frustum, bool culling)
320 {
321  CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity());
322  int player = GetSimContext().GetCurrentDisplayedPlayer();
323  ICmpRangeManager::CLosQuerier los (cmpRangeManager->GetLosQuerier(player));
324  bool losRevealAll = cmpRangeManager->GetLosRevealAll(player);
325 
326  for (size_t i = 0; i < m_Projectiles.size(); ++i)
327  {
328  // Don't display projectiles outside the visible area
329  ssize_t posi = (ssize_t)(0.5f + m_Projectiles[i].pos.X / TERRAIN_TILE_SIZE);
330  ssize_t posj = (ssize_t)(0.5f + m_Projectiles[i].pos.Z / TERRAIN_TILE_SIZE);
331  if (!losRevealAll && !los.IsVisible(posi, posj))
332  continue;
333 
334  CModelAbstract& model = m_Projectiles[i].unit->GetModel();
335 
336  model.ValidatePosition();
337 
338  if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetWorldBoundsRec()))
339  continue;
340 
341  // TODO: do something about LOS (copy from CCmpVisualActor)
342 
343  collector.SubmitRecursive(&model);
344  }
345 }
An entity initialisation parameter node.
Definition: ParamNode.h:112
void SubscribeToMessageType(MessageTypeId mtid)
Subscribe the current component type to the given message type.
void Translate(float x, float y, float z)
Definition: Matrix3D.cpp:172
uint32_t LaunchProjectile(entity_id_t source, CFixedVector3D targetPoint, fixed speed, fixed gravity)
A simple fixed-point number class.
Definition: Fixed.h:115
static void ClassInit(CComponentManager &componentManager)
#define REGISTER_COMPONENT_TYPE(cname)
Definition: Component.h:30
const CFrustum & frustum
Definition: MessageTypes.h:156
virtual CVector3D GetProjectileLaunchPoint()=0
Return the exact position where a projectile should be launched from (based on the actor&#39;s ammo prop ...
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
virtual CLosQuerier GetLosQuerier(player_id_t player)=0
Returns a CLosQuerier for checking whether vertex positions are visible to the given player (or other...
void AdvanceProjectile(Projectile &projectile, float dt)
const ssize_t TERRAIN_TILE_SIZE
metres [world space units] per tile in x and z
Definition: Terrain.h:40
#define LOGERROR
Definition: CLogger.h:35
void DeleteUnit(CUnit *unit)
Definition: UnitManager.cpp:72
virtual bool GetLosRevealAll(player_id_t player)=0
Returns whether the whole map has been made visible to the given player.
Serialization interface; see serialization overview.
Definition: ISerializer.h:120
virtual float GetExactGroundLevel(float x, float z)=0
Add renderable objects to the scene collector.
Definition: MessageTypes.h:145
virtual uint32_t LaunchProjectileAtPoint(entity_id_t source, CFixedVector3D target, fixed speed, fixed gravity)
Launch a projectile from entity source to point target.
float Dot(const CVector3D &vector) const
Definition: Vector3D.cpp:48
static void swap(UniqueRange &p1, UniqueRange &p2)
Definition: unique_range.h:176
virtual void SetTransform(const CMatrix3D &transform)
CVector3D Cross(const CVector3D &vector) const
Definition: Vector3D.cpp:55
bool IsBoxVisible(const CVector3D &position, const CBoundingBoxAligned &bounds) const
Definition: Frustum.cpp:116
Projectile manager.
void Interpolate(float frameTime)
virtual void NumberU32_Unbounded(const char *name, uint32_t &out)
void Normalize()
Definition: Vector3D.cpp:77
Definition: Unit.h:35
CModelAbstract & GetModel() const
Definition: Unit.h:56
virtual std::wstring GetProjectileActor()=0
Return the filename of the actor to be used for projectiles from this unit, or the empty string if no...
virtual void SubmitRecursive(CModelAbstract *model)
Submit a model that is part of the scene, including attached sub-models.
Definition: Scene.cpp:37
float deltaSimTime
Elapsed simulation time since previous interpolate, in seconds.
Definition: MessageTypes.h:134
virtual void HandleMessage(const CMessage &msg, bool global)
This interface accepts renderable objects.
Definition: Scene.h:82
virtual int GetType() const =0
float X
Definition: Vector3D.h:31
CMatrix3D ToMatrix() const
Definition: Quaternion.cpp:152
float Y
Definition: Vector3D.h:31
void RenderSubmit(SceneCollector &collector, const CFrustum &frustum, bool culling)
static const float PROJECTILE_DECAY_TIME
#define DEFAULT_COMPONENT_ALLOCATOR(cname)
Definition: Component.h:44
const CSimContext & GetSimContext() const
Definition: IComponent.h:52
virtual void Serialize(ISerializer &serialize)
void NumberU32_Unbounded(const char *name, uint32_t value)
Serialize a number.
Definition: ISerializer.h:171
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
int GetCurrentDisplayedPlayer() const
Returns the player ID that the current display is being rendered for.
Definition: SimContext.cpp:68
intptr_t ssize_t
Definition: wposix_types.h:82
CEntityHandle GetSystemEntity() const
Definition: IComponent.h:50
static std::string GetSchema()
virtual CFixedVector3D GetPosition()=0
Returns the current x,y,z position (no interpolation).
Abstract base class for graphical objects that are used by units, or as props attached to other CMode...
Definition: ModelAbstract.h:36
CUnitManager & GetUnitManager() const
Definition: SimContext.cpp:45
SceneCollector & collector
Definition: MessageTypes.h:155
static size_t model
Definition: x86_x64.cpp:211
unsigned int uint32_t
Definition: wposix_types.h:53
std::vector< Projectile > m_Projectiles
Object providing efficient abstracted access to the LOS state.
float LengthSquared() const
Definition: Vector3D.cpp:67
float Z
Definition: Vector3D.h:31
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)
void FromAxisAngle(const CVector3D &axis, float angle)
Definition: Quaternion.cpp:260
virtual void RemoveProjectile(uint32_t)
Removes a projectile, used when the projectile has hit a target.
Prepare for rendering a new frame (set up model positions etc).
Definition: MessageTypes.h:122
virtual const CBoundingBoxAligned GetWorldBoundsRec()
Returns world space bounds of this object and all child objects.
Definition: ModelAbstract.h:92
virtual void Init(const CParamNode &paramNode)
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
virtual std::wstring GetActorShortName()=0
Return the short name of the actor that&#39;s being displayed, or the empty string on error...
virtual void ValidatePosition()=0
Ensure that both the transformation and the bone matrices are correct for this model and all its prop...
Deserialization interface; see serialization overview.
Definition: IDeserializer.h:34