Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
UnitAnimation.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 
20 #include "UnitAnimation.h"
21 
22 #include "graphics/Model.h"
23 #include "graphics/ObjectEntry.h"
24 #include "graphics/SkeletonAnim.h"
26 #include "graphics/Unit.h"
27 #include "lib/rand.h"
28 #include "ps/CStr.h"
29 #include "ps/Game.h"
32 
33 // Randomly modify the speed, so that units won't stay perfectly
34 // synchronised if they're playing animations of the same length
35 static float DesyncSpeed(float speed, float desync)
36 {
37  if (desync == 0.0f)
38  return speed;
39 
40  return speed * (1.f - desync + 2.f*desync*(rand(0, 256)/255.f));
41 }
42 
44  : m_Entity(ent), m_State("idle"), m_Looping(true),
45  m_Speed(1.f), m_SyncRepeatTime(0.f), m_OriginalSpeed(1.f), m_Desync(0.f)
46 {
47  ReloadUnit(model, object);
48 }
49 
51 {
52  m_Entity = ent;
53 }
54 
56 {
58 
59  state.anims = object->GetAnimations(m_State);
60 
61  if (state.anims.empty())
62  state.anims = object->GetAnimations("idle");
63  ENSURE(!state.anims.empty()); // there must always be an idle animation
64 
65  state.model = model;
66  state.animIdx = rand(0, state.anims.size());
67  state.time = 0.f;
68  state.pastLoadPos = false;
69  state.pastActionPos = false;
70  state.pastSoundPos = false;
71 
72  m_AnimStates.push_back(state);
73 
74  model->SetAnimation(state.anims[state.animIdx], !m_Looping);
75 
76  // Recursively add all props
77  const std::vector<CModel::Prop>& props = model->GetProps();
78  for (std::vector<CModel::Prop>::const_iterator it = props.begin(); it != props.end(); ++it)
79  {
80  CModel* propModel = it->m_Model->ToCModel();
81  if (propModel)
82  AddModel(propModel, it->m_ObjectEntry);
83  }
84 }
85 
87 {
88  m_Model = model;
89  m_Object = object;
90 
91  m_AnimStates.clear();
93 }
94 
95 void CUnitAnimation::SetAnimationState(const CStr& name, bool once, float speed, float desync, const CStrW& actionSound)
96 {
97  m_Looping = !once;
98  m_OriginalSpeed = speed;
99  m_Desync = desync;
100  m_ActionSound = actionSound;
101 
103  m_SyncRepeatTime = 0.f;
104 
105  if (name != m_State)
106  {
107  m_State = name;
108 
110  }
111 }
112 
114 {
115  m_SyncRepeatTime = repeatTime;
116 }
117 
119 {
120  // Update all the synced prop models to each coincide with actionTime
121  for (std::vector<SModelAnimState>::iterator it = m_AnimStates.begin(); it != m_AnimStates.end(); ++it)
122  {
123  CSkeletonAnimDef* animDef = it->anims[it->animIdx]->m_AnimDef;
124  if (animDef == NULL)
125  continue; // ignore static animations
126 
127  float duration = animDef->GetDuration();
128 
129  float actionPos = it->anims[it->animIdx]->m_ActionPos;
130  bool hasActionPos = (actionPos != -1.f);
131 
132  if (!hasActionPos)
133  continue;
134 
135  float speed = duration / m_SyncRepeatTime;
136 
137  // Need to offset so that start+actionTime*speed = actionPos
138  float start = actionPos - actionTime*speed;
139  // Wrap it so that it's within the animation
140  start = fmodf(start, duration);
141  if (start < 0)
142  start += duration;
143 
144  it->time = start;
145  }
146 }
147 
148 void CUnitAnimation::Update(float time)
149 {
150  // Advance all of the prop models independently
151  for (std::vector<SModelAnimState>::iterator it = m_AnimStates.begin(); it != m_AnimStates.end(); ++it)
152  {
153  CSkeletonAnimDef* animDef = it->anims[it->animIdx]->m_AnimDef;
154  if (animDef == NULL)
155  continue; // ignore static animations
156 
157  float duration = animDef->GetDuration();
158 
159  float actionPos = it->anims[it->animIdx]->m_ActionPos;
160  float loadPos = it->anims[it->animIdx]->m_ActionPos2;
161  float soundPos = it->anims[it->animIdx]->m_SoundPos;
162  bool hasActionPos = (actionPos != -1.f);
163  bool hasLoadPos = (loadPos != -1.f);
164  bool hasSoundPos = (soundPos != -1.f);
165 
166  // Find the current animation speed
167  float speed;
168  if (m_SyncRepeatTime && hasActionPos)
169  speed = duration / m_SyncRepeatTime;
170  else
171  speed = m_Speed * it->anims[it->animIdx]->m_Speed;
172 
173  // Convert from real time to scaled animation time
174  float advance = time * speed;
175 
176  // If we're going to advance past the load point in this update, then load the ammo
177  if (hasLoadPos && !it->pastLoadPos && it->time + advance >= loadPos)
178  {
179  it->model->ShowAmmoProp();
180  it->pastLoadPos = true;
181  }
182 
183  // If we're going to advance past the action point in this update, then perform the action
184  if (hasActionPos && !it->pastActionPos && it->time + advance >= actionPos)
185  {
186  if (hasLoadPos)
187  it->model->HideAmmoProp();
188 
189  if ( !hasSoundPos && !m_ActionSound.empty() )
190  {
192  if (cmpSoundManager)
193  cmpSoundManager->PlaySoundGroup(m_ActionSound, m_Entity);
194  }
195 
196  it->pastActionPos = true;
197  }
198  if (hasSoundPos && !it->pastSoundPos && it->time + advance >= soundPos)
199  {
200  if (!m_ActionSound.empty() )
201  {
203  if (cmpSoundManager)
204  cmpSoundManager->PlaySoundGroup(m_ActionSound, m_Entity);
205  }
206 
207  it->pastSoundPos = true;
208  }
209 
210  if (it->time + advance < duration)
211  {
212  // If we're still within the current animation, then simply update it
213  it->time += advance;
214  it->model->UpdateTo(it->time);
215  }
216  else if (m_Looping)
217  {
218  // If we've finished the current animation and want to loop...
219 
220  // Wrap the timer around
221  it->time = fmod(it->time + advance, duration);
222 
223  // If there's a choice of multiple animations, pick a new random one
224  if (it->anims.size() > 1)
225  {
226  size_t newAnimIdx = rand(0, it->anims.size());
227  if (newAnimIdx != it->animIdx)
228  {
229  it->animIdx = newAnimIdx;
230  it->model->SetAnimation(it->anims[it->animIdx], !m_Looping);
231  }
232  }
233 
234  it->pastActionPos = false;
235  it->pastLoadPos = false;
236  it->pastSoundPos = false;
237 
238  it->model->UpdateTo(it->time);
239  }
240  else
241  {
242  // If we've finished the current animation and don't want to loop...
243 
244  // Update to very nearly the end of the last frame (but not quite the end else we'll wrap around when skinning)
245  float nearlyEnd = duration - 1.f;
246  if (fabs(it->time - nearlyEnd) > 1.f)
247  {
248  it->time = nearlyEnd;
249  it->model->UpdateTo(it->time);
250  }
251  }
252  }
253 }
entity_id_t m_Entity
void AddModel(CModel *model, const CObjectEntry *object)
std::vector< CSkeletonAnim * > anims
Definition: UnitAnimation.h:98
void SetEntityID(entity_id_t ent)
Change the entity ID associated with this animation (currently used for playing locational sound effe...
std::vector< Prop > & GetProps()
Definition: Model.h:240
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton &#39;system&#39; components.
Definition: Entity.h:44
bool SetAnimation(CSkeletonAnim *anim, bool once=false)
Definition: Model.cpp:449
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
void Update(float time)
Advance the animation state.
void ReloadUnit(CModel *model, const CObjectEntry *object)
Regenerate internal animation state from the models in the current unit.
CGame * g_Game
Globally accessible pointer to the CGame object.
Definition: Game.cpp:56
void SetAnimationState(const CStr &name, bool once, float speed, float desync, const CStrW &actionSound)
Start playing an animation.
virtual CModel * ToCModel()
Dynamic cast.
Definition: Model.h:88
A simplified syntax for accessing entity components.
Definition: CmpPtr.h:55
CSimulation2 * GetSimulation2()
Get the pointer to the simulation2 object.
Definition: Game.h:136
float m_SyncRepeatTime
void SetAnimationSyncRepeat(float repeatTime)
Adjust the speed of the current animation, so that Update(repeatTime) will do a complete animation lo...
static size_t model
Definition: x86_x64.cpp:211
std::vector< CSkeletonAnim * > GetAnimations(const CStr &animationName) const
CModel * m_Model
float GetDuration() const
std::vector< SModelAnimState > m_AnimStates
Definition: Model.h:50
CUnitAnimation(entity_id_t ent, CModel *model, CObjectEntry *object)
Construct for a given unit, defaulting to the &quot;idle&quot; animation.
size_t rand(size_t min_inclusive, size_t max_exclusive)
return random integer in [min, max).
Definition: rand.cpp:53
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
void SetAnimationSyncOffset(float actionTime)
Adjust the offset of the current animation, so that Update(actionTime) will advance it to the &#39;action...
static float DesyncSpeed(float speed, float desync)
static enum @41 state
const CObjectEntry * m_Object