1
0
forked from 0ad/0ad

Fix animation syncronisation between actor props. Fixes #2324 one more time. Refs [18568] and [18265]. Reported by

wowgetoffyourcellphone

This was SVN commit r19031.
This commit is contained in:
wraitii 2016-12-08 23:16:54 +00:00
parent d6d45fc53d
commit 06333708f8
11 changed files with 52 additions and 35 deletions

View File

@ -38,6 +38,7 @@ element actor {
element animations { element animations {
element animation { element animation {
attribute name { text } & attribute name { text } &
attribute id { text }? &
attribute frequency { xsd:nonNegativeInteger }? & attribute frequency { xsd:nonNegativeInteger }? &
attribute file { text }? & attribute file { text }? &
attribute speed { xsd:nonNegativeInteger } & attribute speed { xsd:nonNegativeInteger } &

View File

@ -93,6 +93,9 @@
<element name="animation"> <element name="animation">
<interleave> <interleave>
<attribute name="name"/> <attribute name="name"/>
<optional>
<attribute name="id"/>
</optional>
<optional> <optional>
<attribute name="frequency"> <attribute name="frequency">
<data type="nonNegativeInteger"/> <data type="nonNegativeInteger"/>

View File

@ -255,7 +255,7 @@ const CBoundingBoxAligned CModel::GetObjectSelectionBoundsRec()
///////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////
// BuildAnimation: load raw animation frame animation from given file, and build a // BuildAnimation: load raw animation frame animation from given file, and build a
// animation specific to this model // animation specific to this model
CSkeletonAnim* CModel::BuildAnimation(const VfsPath& pathname, const CStr& name, int frequency, float speed, float actionpos, float actionpos2, float soundpos) CSkeletonAnim* CModel::BuildAnimation(const VfsPath& pathname, const CStr& name, const CStr& ID, int frequency, float speed, float actionpos, float actionpos2, float soundpos)
{ {
CSkeletonAnimDef* def = m_SkeletonAnimManager.GetAnimation(pathname); CSkeletonAnimDef* def = m_SkeletonAnimManager.GetAnimation(pathname);
if (!def) if (!def)
@ -263,6 +263,7 @@ CSkeletonAnim* CModel::BuildAnimation(const VfsPath& pathname, const CStr& name,
CSkeletonAnim* anim = new CSkeletonAnim(); CSkeletonAnim* anim = new CSkeletonAnim();
anim->m_Name = name; anim->m_Name = name;
anim->m_ID = ID;
anim->m_Frequency = frequency; anim->m_Frequency = frequency;
anim->m_AnimDef = def; anim->m_AnimDef = def;
anim->m_Speed = speed; anim->m_Speed = speed;

View File

@ -201,6 +201,7 @@ public:
* animation specific to this model. * animation specific to this model.
* @param pathname animation file to load * @param pathname animation file to load
* @param name animation name (e.g. "idle") * @param name animation name (e.g. "idle")
* @param ID specific ID of the animation, to sync with props
* @param frequency influences the random choices * @param frequency influences the random choices
* @param speed animation speed as a factor of the default animation speed * @param speed animation speed as a factor of the default animation speed
* @param actionpos offset of 'action' event, in range [0, 1] * @param actionpos offset of 'action' event, in range [0, 1]
@ -208,7 +209,7 @@ public:
* @param sound offset of 'sound' event, in range [0, 1] * @param sound offset of 'sound' event, in range [0, 1]
* @return new animation, or NULL on error * @return new animation, or NULL on error
*/ */
CSkeletonAnim* BuildAnimation(const VfsPath& pathname, const CStr& name, int frequency, float speed, float actionpos, float actionpos2, float soundpos); CSkeletonAnim* BuildAnimation(const VfsPath& pathname, const CStr& name, const CStr& ID, int frequency, float speed, float actionpos, float actionpos2, float soundpos);
/** /**
* Add a prop to the model on the given point. * Add a prop to the model on the given point.

View File

@ -60,6 +60,7 @@ void CObjectBase::LoadVariant(const CXeromyces& XeroFile, const XMBElement& vari
AT(event); AT(event);
AT(file); AT(file);
AT(frequency); AT(frequency);
AT(id);
AT(load); AT(load);
AT(maxheight); AT(maxheight);
AT(minheight); AT(minheight);
@ -163,6 +164,8 @@ void CObjectBase::LoadVariant(const CXeromyces& XeroFile, const XMBElement& vari
{ {
if (ae.Name == at_name) if (ae.Name == at_name)
anim.m_AnimName = ae.Value; anim.m_AnimName = ae.Value;
else if (ae.Name == at_id)
anim.m_ID = ae.Value;
else if (ae.Name == at_frequency) else if (ae.Name == at_frequency)
anim.m_Frequency = ae.Value.ToInt(); anim.m_Frequency = ae.Value.ToInt();
else if (ae.Name == at_file) else if (ae.Name == at_file)

View File

@ -45,6 +45,8 @@ public:
Anim() : m_Frequency(0), m_Speed(1.f), m_ActionPos(-1.f), m_ActionPos2(-1.f), m_SoundPos(-1.f) {} Anim() : m_Frequency(0), m_Speed(1.f), m_ActionPos(-1.f), m_ActionPos2(-1.f), m_SoundPos(-1.f) {}
// name of the animation - "Idle", "Run", etc // name of the animation - "Idle", "Run", etc
CStr m_AnimName; CStr m_AnimName;
// ID of the animation: if not empty, something specific to sync with props.
CStr m_ID = "";
int m_Frequency; int m_Frequency;
// filename of the animation - manidle.psa, manrun.psa, etc // filename of the animation - manidle.psa, manrun.psa, etc
VfsPath m_FileName; VfsPath m_FileName;

View File

@ -164,6 +164,7 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
CSkeletonAnim* anim = model->BuildAnimation( CSkeletonAnim* anim = model->BuildAnimation(
it->second.m_FileName, it->second.m_FileName,
name, name,
it->second.m_ID,
it->second.m_Frequency, it->second.m_Frequency,
it->second.m_Speed, it->second.m_Speed,
it->second.m_ActionPos, it->second.m_ActionPos,
@ -178,6 +179,7 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
{ {
CSkeletonAnim* anim = new CSkeletonAnim(); CSkeletonAnim* anim = new CSkeletonAnim();
anim->m_Name = "idle"; anim->m_Name = "idle";
anim->m_ID = "";
anim->m_AnimDef = NULL; anim->m_AnimDef = NULL;
anim->m_Frequency = 0; anim->m_Frequency = 0;
anim->m_Speed = 0.f; anim->m_Speed = 0.f;
@ -253,9 +255,9 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
return true; return true;
} }
CSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName) const CSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName, const CStr& ID) const
{ {
std::vector<CSkeletonAnim*> anims = GetAnimations(animationName); std::vector<CSkeletonAnim*> anims = GetAnimations(animationName, ID);
int totalFreq = 0; int totalFreq = 0;
for (CSkeletonAnim* anim : anims) for (CSkeletonAnim* anim : anims)
@ -271,11 +273,10 @@ CSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName) const
if (r < 0) if (r < 0)
return anim; return anim;
} }
LOGERROR("No animation found for name %s", animationName);
return NULL; return NULL;
} }
std::vector<CSkeletonAnim*> CObjectEntry::GetAnimations(const CStr& animationName) const std::vector<CSkeletonAnim*> CObjectEntry::GetAnimations(const CStr& animationName, const CStr& ID) const
{ {
std::vector<CSkeletonAnim*> anims; std::vector<CSkeletonAnim*> anims;
@ -283,12 +284,10 @@ std::vector<CSkeletonAnim*> CObjectEntry::GetAnimations(const CStr& animationNam
SkeletonAnimMap::const_iterator upper = m_Animations.upper_bound(animationName); SkeletonAnimMap::const_iterator upper = m_Animations.upper_bound(animationName);
for (SkeletonAnimMap::const_iterator it = lower; it != upper; ++it) for (SkeletonAnimMap::const_iterator it = lower; it != upper; ++it)
anims.push_back(it->second); {
if (ID.empty() || it->second->m_ID == ID)
if (anims.empty()) anims.push_back(it->second);
for (const std::pair<CStr, CSkeletonAnim*>& anim : m_Animations) }
if (anim.second->m_Frequency > 0)
anims.push_back(anim.second);
if (anims.empty()) if (anims.empty())
{ {

View File

@ -64,20 +64,18 @@ public:
std::wstring m_ProjectileModelName; std::wstring m_ProjectileModelName;
/** /**
* Returns a randomly-chosen animation matching the given name. * Returns a randomly-chosen animation matching the given ID, or animationName if ID is empty.
* The chosen animation is picked randomly from the GetAnimations list * The chosen animation is picked randomly from the GetAnimations list
* with the frequencies as weights (if there are any defined). * with the frequencies as weights (if there are any defined).
* This method should always return an animation * This method should always return an animation
*/ */
CSkeletonAnim* GetRandomAnimation(const CStr& animationName) const; CSkeletonAnim* GetRandomAnimation(const CStr& animationName, const CStr& ID = "") const;
/** /**
* Returns all the animations matching the given name. * Returns all the animations matching the given ID or animationName if ID is empty.
* - Prefers the animations names like the animationName * If none found returns Idle animations (which are always added)
* - Second choice are animations with a frequency
* - Last choice are the Idle animations (which are always added)
*/ */
std::vector<CSkeletonAnim*> GetAnimations(const CStr& animationName) const; std::vector<CSkeletonAnim*> GetAnimations(const CStr& animationName, const CStr& ID = "") const;
// corresponding model // corresponding model
CModelAbstract* m_Model; CModelAbstract* m_Model;
@ -87,6 +85,7 @@ public:
bool m_Outdated; bool m_Outdated;
private: private:
CSimulation2& m_Simulation; CSimulation2& m_Simulation;
typedef std::multimap<CStr, CSkeletonAnim*> SkeletonAnimMap; typedef std::multimap<CStr, CSkeletonAnim*> SkeletonAnimMap;

View File

@ -34,6 +34,8 @@ class CSkeletonAnim
public: public:
// the name of the action which uses this animation (e.g. "idle") // the name of the action which uses this animation (e.g. "idle")
CStr m_Name; CStr m_Name;
// the ID of this animation, to sync between props.
CStr m_ID = "";
// frequency of the animation // frequency of the animation
int m_Frequency; int m_Frequency;
// the raw animation frame data; may be NULL if this is a static 'animation' // the raw animation frame data; may be NULL if this is a static 'animation'

View File

@ -41,7 +41,7 @@ static float DesyncSpeed(float speed, float desync)
} }
CUnitAnimation::CUnitAnimation(entity_id_t ent, CModel* model, CObjectEntry* object) CUnitAnimation::CUnitAnimation(entity_id_t ent, CModel* model, CObjectEntry* object)
: m_Entity(ent), m_State("idle"), m_AnimationName("idle"), m_Looping(true), : m_Entity(ent), m_State("idle"), m_Looping(true),
m_Speed(1.f), m_SyncRepeatTime(0.f), m_OriginalSpeed(1.f), m_Desync(0.f) m_Speed(1.f), m_SyncRepeatTime(0.f), m_OriginalSpeed(1.f), m_Desync(0.f)
{ {
ReloadUnit(model, object); ReloadUnit(model, object);
@ -58,7 +58,7 @@ void CUnitAnimation::AddModel(CModel* model, const CObjectEntry* object)
state.model = model; state.model = model;
state.object = object; state.object = object;
state.anim = object->GetRandomAnimation(m_State); state.anim = object->GetRandomAnimation(m_State, m_AnimationID);
state.time = 0.f; state.time = 0.f;
state.pastLoadPos = false; state.pastLoadPos = false;
state.pastActionPos = false; state.pastActionPos = false;
@ -108,7 +108,7 @@ void CUnitAnimation::SetAnimationState(const CStr& name, bool once, float speed,
if (name != m_State) if (name != m_State)
{ {
m_State = name; m_State = name;
m_AnimationName = name; UpdateAnimationID();
ReloadUnit(m_Model, m_Object); ReloadUnit(m_Model, m_Object);
} }
@ -236,23 +236,17 @@ void CUnitAnimation::Update(float time)
{ {
// we're handling the root model // we're handling the root model
// choose animations from the complete state // choose animations from the complete state
anim = it->object->GetRandomAnimation(m_State); CStr oldID = m_AnimationID;
// if we use a new animation name, UpdateAnimationID();
// update the animations of all non-root models anim = it->object->GetRandomAnimation(m_State, m_AnimationID);
// sync with the root model, unless the root model could if (oldID != m_AnimationID)
// only resort to the "idle" state.
if (anim->m_Name != "idle")
m_AnimationName = anim->m_Name;
else
m_AnimationName = m_State;
if (it->anim->m_Name != m_AnimationName)
for (SModelAnimState animState : m_AnimStates) for (SModelAnimState animState : m_AnimStates)
if (animState.model != m_Model) if (animState.model != m_Model)
animState.model->SetAnimation(animState.object->GetRandomAnimation(m_AnimationName)); animState.model->SetAnimation(animState.object->GetRandomAnimation(m_State, m_AnimationID));
} }
else else
// choose animations that match the root // choose animations that match the root
anim = it->object->GetRandomAnimation(m_AnimationName); anim = it->object->GetRandomAnimation(m_State, m_AnimationID);
if (anim != it->anim) if (anim != it->anim)
{ {
@ -280,3 +274,9 @@ void CUnitAnimation::Update(float time)
} }
} }
} }
void CUnitAnimation::UpdateAnimationID()
{
CStr& ID = m_Object->GetRandomAnimation(m_State)->m_ID;
m_AnimationID = ID;
}

View File

@ -92,6 +92,12 @@ public:
void ReloadUnit(CModel* model, const CObjectEntry* object); void ReloadUnit(CModel* model, const CObjectEntry* object);
private: private:
/**
* Picks a new animation ID from our current state
*/
void UpdateAnimationID();
struct SModelAnimState struct SModelAnimState
{ {
CModel* model; CModel* model;
@ -117,7 +123,7 @@ private:
CModel* m_Model; CModel* m_Model;
const CObjectEntry* m_Object; const CObjectEntry* m_Object;
CStr m_State; CStr m_State;
CStr m_AnimationName; CStr m_AnimationID = "";
bool m_Looping; bool m_Looping;
float m_OriginalSpeed; float m_OriginalSpeed;
float m_Speed; float m_Speed;