mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-27 12:32:30 +00:00
# Unit animation improvements.
Animate props much more sensibly. Move ammo code out of CUnit. Move animation logic out of CModel. Launch projectiles from the correct location. Use entity's speeds and sounds in actor viewer. Add -nosound option to disable audio, and allow audio by default in Atlas. Remove some obsolete options. This was SVN commit r7609.
This commit is contained in:
@@ -9,5 +9,5 @@
|
|||||||
<Anchor>upright</Anchor>
|
<Anchor>upright</Anchor>
|
||||||
<Floating>false</Floating>
|
<Floating>false</Floating>
|
||||||
</Position>
|
</Position>
|
||||||
<UnitMotion/>
|
<UnitAI/>
|
||||||
</Entity>
|
</Entity>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<Anchor>upright</Anchor>
|
<Anchor>upright</Anchor>
|
||||||
<Floating>false</Floating>
|
<Floating>false</Floating>
|
||||||
</Position>
|
</Position>
|
||||||
<UnitMotion/>
|
<UnitAI/>
|
||||||
<Footprint>
|
<Footprint>
|
||||||
<Circle radius="4"/>
|
<Circle radius="4"/>
|
||||||
<Height>1.0</Height>
|
<Height>1.0</Height>
|
||||||
|
|||||||
@@ -244,8 +244,8 @@ function init(window, bottomWindow)
|
|||||||
actorViewer.controls.push(animationBoxBox);
|
actorViewer.controls.push(animationBoxBox);
|
||||||
var animationBox = new wxStaticBoxSizer(animationBoxBox, wxOrientation.VERTICAL);
|
var animationBox = new wxStaticBoxSizer(animationBoxBox, wxOrientation.VERTICAL);
|
||||||
var animationSelector = new wxChoice(bottomWindow, -1, wxDefaultPosition, wxDefaultSize,
|
var animationSelector = new wxChoice(bottomWindow, -1, wxDefaultPosition, wxDefaultSize,
|
||||||
[ "attack1", "attack2", "build", "corpse", "death",
|
[ "build", "corpse", "death",
|
||||||
"gather_fruit", "gather_grain", "gather_wood", "gather_stone", "gather_metal",
|
"gather_fruit", "gather_grain", "gather_meat", "gather_metal", "gather_stone", "gather_wood",
|
||||||
"idle", "melee", "run", "walk" ] // TODO: this list should come from the actor
|
"idle", "melee", "run", "walk" ] // TODO: this list should come from the actor
|
||||||
);
|
);
|
||||||
animationSelector.stringSelection = "idle";
|
animationSelector.stringSelection = "idle";
|
||||||
|
|||||||
+84
-105
@@ -35,13 +35,13 @@
|
|||||||
#include "ps/Profile.h"
|
#include "ps/Profile.h"
|
||||||
|
|
||||||
#include "ps/CLogger.h"
|
#include "ps/CLogger.h"
|
||||||
#define LOG_CATEGORY L"graphics"
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
CModel::CModel(CSkeletonAnimManager& skeletonAnimManager)
|
CModel::CModel(CSkeletonAnimManager& skeletonAnimManager)
|
||||||
: m_Parent(NULL), m_Flags(0), m_Anim(NULL), m_AnimTime(0),
|
: m_Parent(NULL), m_Flags(0), m_Anim(NULL), m_AnimTime(0),
|
||||||
m_BoneMatrices(NULL), m_InverseBindBoneMatrices(NULL),
|
m_BoneMatrices(NULL), m_InverseBindBoneMatrices(NULL),
|
||||||
|
m_AmmoPropPoint(NULL), m_AmmoLoadedProp(0),
|
||||||
m_PositionValid(false), m_ShadingColor(1,1,1,1), m_PlayerID((size_t)-1),
|
m_PositionValid(false), m_ShadingColor(1,1,1,1), m_PlayerID((size_t)-1),
|
||||||
m_SkeletonAnimManager(skeletonAnimManager)
|
m_SkeletonAnimManager(skeletonAnimManager)
|
||||||
{
|
{
|
||||||
@@ -223,8 +223,16 @@ CSkeletonAnim* CModel::BuildAnimation(const VfsPath& pathname, const char* name,
|
|||||||
anim->m_Name = name;
|
anim->m_Name = name;
|
||||||
anim->m_AnimDef = def;
|
anim->m_AnimDef = def;
|
||||||
anim->m_Speed = speed;
|
anim->m_Speed = speed;
|
||||||
anim->m_ActionPos = actionpos * anim->m_AnimDef->GetDuration();
|
|
||||||
anim->m_ActionPos2 = actionpos2 * anim->m_AnimDef->GetDuration();
|
if (actionpos == -1.f)
|
||||||
|
anim->m_ActionPos = -1.f;
|
||||||
|
else
|
||||||
|
anim->m_ActionPos = actionpos * anim->m_AnimDef->GetDuration();
|
||||||
|
|
||||||
|
if (actionpos2 == -1.f)
|
||||||
|
anim->m_ActionPos2 = -1.f;
|
||||||
|
else
|
||||||
|
anim->m_ActionPos2 = actionpos2 * anim->m_AnimDef->GetDuration();
|
||||||
|
|
||||||
anim->m_ObjectBounds.SetEmpty();
|
anim->m_ObjectBounds.SetEmpty();
|
||||||
InvalidateBounds();
|
InvalidateBounds();
|
||||||
@@ -233,86 +241,20 @@ CSkeletonAnim* CModel::BuildAnimation(const VfsPath& pathname, const char* name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Update: update this model by the given time, in msec
|
// Update: update this model to the given time, in msec
|
||||||
void CModel::Update(float time)
|
void CModel::UpdateTo(float time)
|
||||||
{
|
{
|
||||||
if (m_Anim && m_Anim->m_AnimDef && m_BoneMatrices)
|
// update animation time, but don't calculate bone matrices - do that (lazily) when
|
||||||
{
|
// something requests them; that saves some calculation work for offscreen models,
|
||||||
time *= m_Anim->m_Speed;
|
// and also assures the world space, inverted bone matrices (required for normal
|
||||||
|
// skinning) are up to date with respect to m_Transform
|
||||||
|
m_AnimTime = time;
|
||||||
|
|
||||||
float oldAnimTime = m_AnimTime;
|
// mark vertices as dirty
|
||||||
|
SetDirty(RENDERDATA_UPDATE_VERTICES);
|
||||||
|
|
||||||
// update animation time, but don't calculate bone matrices - do that (lazily) when
|
// mark matrices as dirty
|
||||||
// something requests them; that saves some calculation work for offscreen models,
|
InvalidatePosition();
|
||||||
// and also assures the world space, inverted bone matrices (required for normal
|
|
||||||
// skinning) are up to date with respect to m_Transform
|
|
||||||
m_AnimTime += time;
|
|
||||||
|
|
||||||
float duration = m_Anim->m_AnimDef->GetDuration();
|
|
||||||
if (m_AnimTime > duration)
|
|
||||||
{
|
|
||||||
if (m_Flags & MODELFLAG_NOLOOPANIMATION)
|
|
||||||
{
|
|
||||||
if (m_NextAnim)
|
|
||||||
SetAnimation(m_NextAnim);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Changing to no animation - probably becoming a corpse.
|
|
||||||
// Make sure the last displayed frame is the final frame
|
|
||||||
// of the animation.
|
|
||||||
float nearlyEnd = duration - 1.f; // 1 msec
|
|
||||||
if (fabs(oldAnimTime - nearlyEnd) < 1.f)
|
|
||||||
SetAnimation(NULL);
|
|
||||||
else
|
|
||||||
m_AnimTime = nearlyEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_AnimTime = fmod(m_AnimTime, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark vertices as dirty
|
|
||||||
SetDirty(RENDERDATA_UPDATE_VERTICES);
|
|
||||||
|
|
||||||
// mark matrices as dirty
|
|
||||||
InvalidatePosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
// update props
|
|
||||||
for (size_t i = 0; i < m_Props.size(); ++i)
|
|
||||||
m_Props[i].m_Model->Update(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool CModel::NeedsNewAnim(float time) const
|
|
||||||
{
|
|
||||||
// TODO: fix UnitAnimation so it correctly loops animated props
|
|
||||||
|
|
||||||
if (m_Anim && m_Anim->m_AnimDef && m_BoneMatrices)
|
|
||||||
{
|
|
||||||
time *= m_Anim->m_Speed;
|
|
||||||
|
|
||||||
float duration = m_Anim->m_AnimDef->GetDuration();
|
|
||||||
|
|
||||||
if (m_AnimTime + time > duration)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CModel::CheckActionTriggers(float time, bool& action, bool& action2) const
|
|
||||||
{
|
|
||||||
if (m_Anim && m_Anim->m_AnimDef && m_BoneMatrices)
|
|
||||||
{
|
|
||||||
time *= m_Anim->m_Speed;
|
|
||||||
|
|
||||||
if (m_AnimTime <= m_Anim->m_ActionPos && m_AnimTime + time > m_Anim->m_ActionPos)
|
|
||||||
action = true;
|
|
||||||
|
|
||||||
if (m_AnimTime <= m_Anim->m_ActionPos2 && m_AnimTime + time > m_Anim->m_ActionPos2)
|
|
||||||
action2 = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -382,34 +324,35 @@ void CModel::ValidatePosition()
|
|||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// SetAnimation: set the given animation as the current animation on this model;
|
// SetAnimation: set the given animation as the current animation on this model;
|
||||||
// return false on error, else true
|
// return false on error, else true
|
||||||
bool CModel::SetAnimation(CSkeletonAnim* anim, bool once, CSkeletonAnim* next)
|
bool CModel::SetAnimation(CSkeletonAnim* anim, bool once)
|
||||||
{
|
{
|
||||||
m_Anim=NULL; // in case something fails
|
m_Anim = NULL; // in case something fails
|
||||||
|
|
||||||
if (anim) {
|
if (anim)
|
||||||
|
{
|
||||||
m_Flags &= ~MODELFLAG_NOLOOPANIMATION;
|
m_Flags &= ~MODELFLAG_NOLOOPANIMATION;
|
||||||
|
|
||||||
if (once)
|
if (once)
|
||||||
{
|
|
||||||
m_Flags |= MODELFLAG_NOLOOPANIMATION;
|
m_Flags |= MODELFLAG_NOLOOPANIMATION;
|
||||||
m_NextAnim = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_BoneMatrices && anim->m_AnimDef) {
|
if (!m_BoneMatrices && anim->m_AnimDef)
|
||||||
|
{
|
||||||
// not boned, can't animate
|
// not boned, can't animate
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_BoneMatrices && !anim->m_AnimDef) {
|
if (m_BoneMatrices && !anim->m_AnimDef)
|
||||||
|
{
|
||||||
// boned, but animation isn't valid
|
// boned, but animation isn't valid
|
||||||
// (e.g. the default (static) idle animation on an animated unit)
|
// (e.g. the default (static) idle animation on an animated unit)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anim->m_AnimDef && anim->m_AnimDef->GetNumKeys() != m_pModelDef->GetNumBones()) {
|
if (anim->m_AnimDef && anim->m_AnimDef->GetNumKeys() != m_pModelDef->GetNumBones())
|
||||||
|
{
|
||||||
// mismatch between model's skeleton and animation's skeleton
|
// mismatch between model's skeleton and animation's skeleton
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"Mismatch between model's skeleton and animation's skeleton (%lu model bones != %lu animation keys)",
|
LOGERROR(L"Mismatch between model's skeleton and animation's skeleton (%lu model bones != %lu animation keys)",
|
||||||
(unsigned long)m_pModelDef->GetNumBones(), (unsigned long)anim->m_AnimDef->GetNumKeys());
|
(unsigned long)m_pModelDef->GetNumBones(), (unsigned long)anim->m_AnimDef->GetNumKeys());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,7 +374,6 @@ bool CModel::SetAnimation(CSkeletonAnim* anim, bool once, CSkeletonAnim* next)
|
|||||||
void CModel::CopyAnimationFrom(CModel* source)
|
void CModel::CopyAnimationFrom(CModel* source)
|
||||||
{
|
{
|
||||||
m_Anim = source->m_Anim;
|
m_Anim = source->m_Anim;
|
||||||
m_NextAnim = source->m_NextAnim;
|
|
||||||
m_AnimTime = source->m_AnimTime;
|
m_AnimTime = source->m_AnimTime;
|
||||||
|
|
||||||
m_Flags &= ~MODELFLAG_CASTSHADOWS;
|
m_Flags &= ~MODELFLAG_CASTSHADOWS;
|
||||||
@@ -457,16 +399,49 @@ void CModel::AddProp(SPropPoint* point, CModel* model, CObjectEntry* objectentry
|
|||||||
m_Props.push_back(prop);
|
m_Props.push_back(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
void CModel::AddAmmoProp(SPropPoint* point, CModel* model, CObjectEntry* objectentry)
|
||||||
// RemoveProp: remove any props from the given point
|
|
||||||
void CModel::RemoveProp(SPropPoint* point)
|
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_Props.size(); i++) {
|
AddProp(point, model, objectentry);
|
||||||
if (m_Props[i].m_Point == point) {
|
m_AmmoPropPoint = point;
|
||||||
delete m_Props[i].m_Model;
|
m_AmmoLoadedProp = m_Props.size() - 1;
|
||||||
// (when a prop is removed it will automatically remove its prop point)
|
m_Props[m_AmmoLoadedProp].m_Hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CModel::ShowAmmoProp()
|
||||||
|
{
|
||||||
|
if (m_AmmoPropPoint == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Show the ammo prop, hide all others on the same prop point
|
||||||
|
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||||
|
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
||||||
|
m_Props[i].m_Hidden = (i != m_AmmoLoadedProp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CModel::HideAmmoProp()
|
||||||
|
{
|
||||||
|
if (m_AmmoPropPoint == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Hide the ammo prop, show all others on the same prop point
|
||||||
|
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||||
|
if (m_Props[i].m_Point == m_AmmoPropPoint)
|
||||||
|
m_Props[i].m_Hidden = (i == m_AmmoLoadedProp);
|
||||||
|
}
|
||||||
|
|
||||||
|
CModel* CModel::FindFirstAmmoProp()
|
||||||
|
{
|
||||||
|
if (m_AmmoPropPoint)
|
||||||
|
return m_Props[m_AmmoLoadedProp].m_Model;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_Props.size(); ++i)
|
||||||
|
{
|
||||||
|
CModel* model = m_Props[i].m_Model->FindFirstAmmoProp();
|
||||||
|
if (model)
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -482,10 +457,16 @@ CModel* CModel::Clone() const
|
|||||||
clone->SetMaterial(m_Material);
|
clone->SetMaterial(m_Material);
|
||||||
clone->SetAnimation(m_Anim);
|
clone->SetAnimation(m_Anim);
|
||||||
clone->SetFlags(m_Flags);
|
clone->SetFlags(m_Flags);
|
||||||
for (size_t i=0;i<m_Props.size();i++) {
|
|
||||||
|
for (size_t i = 0; i < m_Props.size(); i++)
|
||||||
|
{
|
||||||
// eek! TODO, RC - need to investigate shallow clone here
|
// eek! TODO, RC - need to investigate shallow clone here
|
||||||
clone->AddProp(m_Props[i].m_Point, m_Props[i].m_Model->Clone(), m_Props[i].m_ObjectEntry);
|
if (m_AmmoPropPoint && i == m_AmmoLoadedProp)
|
||||||
|
clone->AddAmmoProp(m_Props[i].m_Point, m_Props[i].m_Model->Clone(), m_Props[i].m_ObjectEntry);
|
||||||
|
else
|
||||||
|
clone->AddProp(m_Props[i].m_Point, m_Props[i].m_Model->Clone(), m_Props[i].m_ObjectEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,8 +512,6 @@ void CModel::SetPlayerColor(const CColor& colour)
|
|||||||
void CModel::SetShadingColor(const CColor& colour)
|
void CModel::SetShadingColor(const CColor& colour)
|
||||||
{
|
{
|
||||||
m_ShadingColor = colour;
|
m_ShadingColor = colour;
|
||||||
for( std::vector<Prop>::iterator it = m_Props.begin(); it != m_Props.end(); ++it )
|
for (std::vector<Prop>::iterator it = m_Props.begin(); it != m_Props.end(); ++it)
|
||||||
{
|
|
||||||
it->m_Model->SetShadingColor(colour);
|
it->m_Model->SetShadingColor(colour);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-20
@@ -29,6 +29,7 @@
|
|||||||
#include "RenderableObject.h"
|
#include "RenderableObject.h"
|
||||||
#include "Material.h"
|
#include "Material.h"
|
||||||
#include "ps/Overlay.h"
|
#include "ps/Overlay.h"
|
||||||
|
|
||||||
struct SPropPoint;
|
struct SPropPoint;
|
||||||
class CObjectEntry;
|
class CObjectEntry;
|
||||||
class CSkeletonAnim;
|
class CSkeletonAnim;
|
||||||
@@ -45,17 +46,16 @@ class CModel : public CRenderableObject
|
|||||||
{
|
{
|
||||||
NONCOPYABLE(CModel);
|
NONCOPYABLE(CModel);
|
||||||
|
|
||||||
friend class CUnitAnimation;
|
|
||||||
// HACK - we should probably move the rest of this class's animation state
|
|
||||||
// into the animation class, then it wouldn't need friend access
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct Prop {
|
struct Prop
|
||||||
Prop() : m_Point(0), m_Model(0), m_ObjectEntry(0) {}
|
{
|
||||||
|
Prop() : m_Point(0), m_Model(0), m_ObjectEntry(0), m_Hidden(false) {}
|
||||||
|
|
||||||
SPropPoint* m_Point;
|
SPropPoint* m_Point;
|
||||||
CModel* m_Model;
|
CModel* m_Model;
|
||||||
CObjectEntry* m_ObjectEntry;
|
CObjectEntry* m_ObjectEntry;
|
||||||
|
|
||||||
|
bool m_Hidden; // temporarily removed from rendering
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -68,14 +68,8 @@ public:
|
|||||||
bool InitModel(const CModelDefPtr& modeldef);
|
bool InitModel(const CModelDefPtr& modeldef);
|
||||||
// calculate the world space bounds of this model
|
// calculate the world space bounds of this model
|
||||||
void CalcBounds();
|
void CalcBounds();
|
||||||
// update this model's state; 'time' is the time since the last update, in MS
|
// update this model's state; 'time' is the absolute time since the start of the animation, in MS
|
||||||
void Update(float time);
|
void UpdateTo(float time);
|
||||||
// returns true if Update(time) will require a new animation (due to the
|
|
||||||
// current one ending)
|
|
||||||
bool NeedsNewAnim(float time) const;
|
|
||||||
// sets the bool& arguments if the given time update will cross the trigger points for those actions;
|
|
||||||
// otherwise leaves the arguments unchanged
|
|
||||||
void CheckActionTriggers(float time, bool& action, bool& action2) const;
|
|
||||||
|
|
||||||
// get the model's geometry data
|
// get the model's geometry data
|
||||||
CModelDefPtr GetModelDef() { return m_pModelDef; }
|
CModelDefPtr GetModelDef() { return m_pModelDef; }
|
||||||
@@ -100,7 +94,7 @@ public:
|
|||||||
CColor GetShadingColor() { return m_ShadingColor; }
|
CColor GetShadingColor() { return m_ShadingColor; }
|
||||||
|
|
||||||
// set the given animation as the current animation on this model
|
// set the given animation as the current animation on this model
|
||||||
bool SetAnimation(CSkeletonAnim* anim, bool once = false, CSkeletonAnim* next = NULL);
|
bool SetAnimation(CSkeletonAnim* anim, bool once = false);
|
||||||
|
|
||||||
// get the currently playing animation, if any
|
// get the currently playing animation, if any
|
||||||
CSkeletonAnim* GetAnimation() const { return m_Anim; }
|
CSkeletonAnim* GetAnimation() const { return m_Anim; }
|
||||||
@@ -162,10 +156,32 @@ public:
|
|||||||
*/
|
*/
|
||||||
CSkeletonAnim* BuildAnimation(const VfsPath& pathname, const char* name, float speed, float actionpos, float actionpos2);
|
CSkeletonAnim* BuildAnimation(const VfsPath& pathname, const char* name, float speed, float actionpos, float actionpos2);
|
||||||
|
|
||||||
// add a prop to the model on the given point
|
/**
|
||||||
|
* Add a prop to the model on the given point.
|
||||||
|
*/
|
||||||
void AddProp(SPropPoint* point, CModel* model, CObjectEntry* objectentry);
|
void AddProp(SPropPoint* point, CModel* model, CObjectEntry* objectentry);
|
||||||
// remove any props from the given point
|
|
||||||
void RemoveProp(SPropPoint* point);
|
/**
|
||||||
|
* Add a prop to the model on the given point, and treat it as the ammo prop.
|
||||||
|
* The prop will be hidden by default.
|
||||||
|
*/
|
||||||
|
void AddAmmoProp(SPropPoint* point, CModel* model, CObjectEntry* objectentry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the ammo prop (if any), and hide any other props on that prop point.
|
||||||
|
*/
|
||||||
|
void ShowAmmoProp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the ammo prop (if any), and show any other props on that prop point.
|
||||||
|
*/
|
||||||
|
void HideAmmoProp();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the first prop used for ammo, by this model or its own props.
|
||||||
|
*/
|
||||||
|
CModel* FindFirstAmmoProp();
|
||||||
|
|
||||||
// return prop list
|
// return prop list
|
||||||
std::vector<Prop>& GetProps() { return m_Props; }
|
std::vector<Prop>& GetProps() { return m_Props; }
|
||||||
const std::vector<Prop>& GetProps() const { return m_Props; }
|
const std::vector<Prop>& GetProps() const { return m_Props; }
|
||||||
@@ -209,8 +225,6 @@ private:
|
|||||||
CBound m_ObjectBounds;
|
CBound m_ObjectBounds;
|
||||||
// animation currently playing on this model, if any
|
// animation currently playing on this model, if any
|
||||||
CSkeletonAnim* m_Anim;
|
CSkeletonAnim* m_Anim;
|
||||||
// animation to switch back to when the current one finishes
|
|
||||||
CSkeletonAnim* m_NextAnim;
|
|
||||||
// time (in MS) into the current animation
|
// time (in MS) into the current animation
|
||||||
float m_AnimTime;
|
float m_AnimTime;
|
||||||
// current state of all bones on this model; null if associated modeldef isn't skeletal
|
// current state of all bones on this model; null if associated modeldef isn't skeletal
|
||||||
@@ -220,6 +234,16 @@ private:
|
|||||||
// list of current props on model
|
// list of current props on model
|
||||||
std::vector<Prop> m_Props;
|
std::vector<Prop> m_Props;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The prop point to which the ammo prop is attached, or NULL if none
|
||||||
|
*/
|
||||||
|
SPropPoint* m_AmmoPropPoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If m_AmmoPropPoint is not NULL, then the index in m_Props of the ammo prop
|
||||||
|
*/
|
||||||
|
size_t m_AmmoLoadedProp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* true if both transform and and bone matrices are valid.
|
* true if both transform and and bone matrices are valid.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public:
|
|||||||
|
|
||||||
struct Anim {
|
struct Anim {
|
||||||
// constructor
|
// constructor
|
||||||
Anim() : m_Speed(1), m_ActionPos(0.5), m_ActionPos2(0.0) {}
|
Anim() : m_Speed(1.f), m_ActionPos(-1.f), m_ActionPos2(-1.f) {}
|
||||||
|
|
||||||
// name of the animation - "Idle", "Run", etc
|
// name of the animation - "Idle", "Run", etc
|
||||||
CStr m_AnimName;
|
CStr m_AnimName;
|
||||||
@@ -43,8 +43,8 @@ public:
|
|||||||
VfsPath m_FileName;
|
VfsPath m_FileName;
|
||||||
// animation speed, as specified in XML actor file
|
// animation speed, as specified in XML actor file
|
||||||
float m_Speed;
|
float m_Speed;
|
||||||
// fraction of the way through the animation that the interesting bit(s)
|
// fraction [0.0, 1.0] of the way through the animation that the interesting bit(s)
|
||||||
// happens
|
// happens, or -1.0 if unspecified
|
||||||
float m_ActionPos;
|
float m_ActionPos;
|
||||||
float m_ActionPos2;
|
float m_ActionPos2;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
/* Copyright (C) 2010 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -37,11 +37,8 @@
|
|||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#define LOG_CATEGORY L"graphics"
|
CObjectEntry::CObjectEntry(CObjectBase* base) :
|
||||||
|
m_Base(base), m_Color(1.0f, 1.0f, 1.0f, 1.0f), m_Model(NULL)
|
||||||
CObjectEntry::CObjectEntry(CObjectBase* base)
|
|
||||||
: m_Base(base), m_Color(1.0f, 1.0f, 1.0f, 1.0f),
|
|
||||||
m_AmmunitionModel(NULL), m_AmmunitionPoint(NULL), m_Model(NULL)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +69,7 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
|||||||
str << variation.color;
|
str << variation.color;
|
||||||
int r, g, b;
|
int r, g, b;
|
||||||
if (! (str >> r >> g >> b)) // Any trailing data is ignored
|
if (! (str >> r >> g >> b)) // Any trailing data is ignored
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"Invalid RGB colour '%hs'", variation.color.c_str());
|
LOGERROR(L"Actor '%ls' has invalid RGB colour '%hs'", m_Base->m_ShortName.c_str(), variation.color.c_str());
|
||||||
else
|
else
|
||||||
m_Color = CColor(r/255.0f, g/255.0f, b/255.0f, 1.0f);
|
m_Color = CColor(r/255.0f, g/255.0f, b/255.0f, 1.0f);
|
||||||
}
|
}
|
||||||
@@ -93,7 +90,7 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
|||||||
CModelDefPtr modeldef (objectManager.GetMeshManager().GetMesh(m_ModelName));
|
CModelDefPtr modeldef (objectManager.GetMeshManager().GetMesh(m_ModelName));
|
||||||
if (!modeldef)
|
if (!modeldef)
|
||||||
{
|
{
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"CObjectEntry::BuildModel(): Model %ls failed to load", m_ModelName.string().c_str());
|
LOGERROR(L"CObjectEntry::BuildVariation(): Model %ls failed to load", m_ModelName.string().c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,8 +141,8 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// start up idling
|
// start up idling
|
||||||
if (! m_Model->SetAnimation(GetRandomAnimation("idle")))
|
if (!m_Model->SetAnimation(GetRandomAnimation("idle")))
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to set idle animation in model \"%ls\"", m_ModelName.string().c_str());
|
LOGERROR(L"Failed to set idle animation in model \"%ls\"", m_ModelName.string().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// build props - TODO, RC - need to fix up bounds here
|
// build props - TODO, RC - need to fix up bounds here
|
||||||
@@ -164,31 +161,37 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
|||||||
CObjectEntry* oe = objectManager.FindObjectVariation(prop.m_ModelName.string().c_str(), selections);
|
CObjectEntry* oe = objectManager.FindObjectVariation(prop.m_ModelName.string().c_str(), selections);
|
||||||
if (!oe)
|
if (!oe)
|
||||||
{
|
{
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to build prop model \"%ls\" on actor \"%ls\"", prop.m_ModelName.string().c_str(), m_Base->m_ShortName.c_str());
|
LOGERROR(L"Failed to build prop model \"%ls\" on actor \"%ls\"", prop.m_ModelName.string().c_str(), m_Base->m_ShortName.c_str());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also pluck out the other special attachpoint 'loaded-<proppoint>'
|
// If we don't have a projectile but this prop does (e.g. it's our rider), then
|
||||||
if (prop.m_PropPointName.length() > 7 && prop.m_PropPointName.Left(7) == "loaded-")
|
// use that as our projectile too
|
||||||
|
if (m_ProjectileModelName.empty() && !oe->m_ProjectileModelName.empty())
|
||||||
|
m_ProjectileModelName = oe->m_ProjectileModelName;
|
||||||
|
|
||||||
|
CStr ppn = prop.m_PropPointName;
|
||||||
|
bool isAmmo = false;
|
||||||
|
|
||||||
|
// Handle the special attachpoint 'loaded-<proppoint>'
|
||||||
|
if (ppn.Find("loaded-") == 0)
|
||||||
{
|
{
|
||||||
CStr ppn = prop.m_PropPointName.substr(7);
|
ppn = prop.m_PropPointName.substr(7);
|
||||||
m_AmmunitionModel = oe->m_Model;
|
isAmmo = true;
|
||||||
m_AmmunitionPoint = modeldef->FindPropPoint(ppn);
|
}
|
||||||
if (! m_AmmunitionPoint)
|
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to find matching prop point called \"%hs\" in model \"%ls\" for actor \"%ls\"", ppn.c_str(), m_ModelName.string().c_str(), m_Base->m_ShortName.c_str());
|
SPropPoint* proppoint = modeldef->FindPropPoint(ppn.c_str());
|
||||||
|
if (proppoint)
|
||||||
|
{
|
||||||
|
CModel* propmodel = oe->m_Model->Clone();
|
||||||
|
if (isAmmo)
|
||||||
|
m_Model->AddAmmoProp(proppoint, propmodel, oe);
|
||||||
|
else
|
||||||
|
m_Model->AddProp(proppoint, propmodel, oe);
|
||||||
|
propmodel->SetAnimation(oe->GetRandomAnimation("idle"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
LOGERROR(L"Failed to find matching prop point called \"%hs\" in model \"%ls\" for actor \"%ls\"", ppn.c_str(), m_ModelName.string().c_str(), m_Base->m_ShortName.c_str());
|
||||||
SPropPoint* proppoint = modeldef->FindPropPoint(prop.m_PropPointName.c_str());
|
|
||||||
if (proppoint)
|
|
||||||
{
|
|
||||||
CModel* propmodel = oe->m_Model->Clone();
|
|
||||||
m_Model->AddProp(proppoint, propmodel, oe);
|
|
||||||
propmodel->SetAnimation(oe->GetRandomAnimation("idle"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LOG(CLogger::Error, LOG_CATEGORY, L"Failed to find matching prop point called \"%hs\" in model \"%ls\" for actor \"%ls\"", prop.m_PropPointName.c_str(), m_ModelName.string().c_str(), m_Base->m_ShortName.c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup flags
|
// setup flags
|
||||||
@@ -235,21 +238,26 @@ bool CObjectEntry::BuildVariation(const std::vector<std::set<CStr> >& selections
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName)
|
CSkeletonAnim* CObjectEntry::GetRandomAnimation(const CStr& animationName) const
|
||||||
{
|
{
|
||||||
SkeletonAnimMap::iterator lower = m_Animations.lower_bound(animationName);
|
SkeletonAnimMap::const_iterator lower = m_Animations.lower_bound(animationName);
|
||||||
SkeletonAnimMap::iterator upper = m_Animations.upper_bound(animationName);
|
SkeletonAnimMap::const_iterator upper = m_Animations.upper_bound(animationName);
|
||||||
size_t count = std::distance(lower, upper);
|
size_t count = std::distance(lower, upper);
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
{
|
|
||||||
// LOG(CLogger::Warning, LOG_CATEGORY, L"Failed to find animation '%hs' for actor '%ls'", animationName.c_str(), m_ModelName.c_str());
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
else
|
size_t id = rand(0, count);
|
||||||
{
|
std::advance(lower, id);
|
||||||
// TODO: Do we care about network synchronisation of random animations?
|
return lower->second;
|
||||||
size_t id = rand(0, count);
|
}
|
||||||
std::advance(lower, id);
|
|
||||||
return lower->second;
|
std::vector<CSkeletonAnim*> CObjectEntry::GetAnimations(const CStr& animationName) const
|
||||||
}
|
{
|
||||||
|
std::vector<CSkeletonAnim*> anims;
|
||||||
|
|
||||||
|
SkeletonAnimMap::const_iterator lower = m_Animations.lower_bound(animationName);
|
||||||
|
SkeletonAnimMap::const_iterator upper = m_Animations.upper_bound(animationName);
|
||||||
|
for (SkeletonAnimMap::const_iterator it = lower; it != upper; ++it)
|
||||||
|
anims.push_back(it->second);
|
||||||
|
return anims;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
/* Copyright (C) 2010 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -57,12 +57,12 @@ public:
|
|||||||
|
|
||||||
CStrW m_ProjectileModelName;
|
CStrW m_ProjectileModelName;
|
||||||
|
|
||||||
CModel* m_AmmunitionModel;
|
|
||||||
SPropPoint* m_AmmunitionPoint;
|
|
||||||
|
|
||||||
// Returns a randomly-chosen animation matching the given name.
|
// Returns a randomly-chosen animation matching the given name.
|
||||||
// If none is found, returns NULL.
|
// If none is found, returns NULL.
|
||||||
CSkeletonAnim* GetRandomAnimation(const CStr& animationName);
|
CSkeletonAnim* GetRandomAnimation(const CStr& animationName) const;
|
||||||
|
|
||||||
|
// Returns all the animations matching the given name.
|
||||||
|
std::vector<CSkeletonAnim*> GetAnimations(const CStr& animationName) const;
|
||||||
|
|
||||||
// corresponding model
|
// corresponding model
|
||||||
CModel* m_Model;
|
CModel* m_Model;
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ public:
|
|||||||
// (treated as 0 if m_AnimDef == NULL)
|
// (treated as 0 if m_AnimDef == NULL)
|
||||||
float m_Speed;
|
float m_Speed;
|
||||||
// Times during the animation at which the interesting bits happen,
|
// Times during the animation at which the interesting bits happen,
|
||||||
// as msec times in the range [0, AnimDef->GetDuration].
|
// as msec times in the range [0, AnimDef->GetDuration],
|
||||||
|
// or special value -1 if unspecified.
|
||||||
// ActionPos is used for melee hits, projectile launches, etc.
|
// ActionPos is used for melee hits, projectile launches, etc.
|
||||||
// ActionPos2 is used for loading projectile ammunition.
|
// ActionPos2 is used for loading projectile ammunition.
|
||||||
float m_ActionPos;
|
float m_ActionPos;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
#include "ps/Game.h"
|
#include "ps/Game.h"
|
||||||
#include "ps/Player.h"
|
#include "ps/Player.h"
|
||||||
|
|
||||||
CUnit::CUnit(CObjectEntry* object, CEntity* entity, CObjectManager& objectManager,
|
CUnit::CUnit(CObjectEntry* object, CObjectManager& objectManager,
|
||||||
const std::set<CStr>& actorSelections)
|
const std::set<CStr>& actorSelections)
|
||||||
: m_Object(object), m_Model(object->m_Model->Clone()),
|
: m_Object(object), m_Model(object->m_Model->Clone()),
|
||||||
m_ID(invalidUnitId), m_ActorSelections(actorSelections),
|
m_ID(invalidUnitId), m_ActorSelections(actorSelections),
|
||||||
@@ -44,8 +44,7 @@ CUnit::~CUnit()
|
|||||||
delete m_Model;
|
delete m_Model;
|
||||||
}
|
}
|
||||||
|
|
||||||
CUnit* CUnit::Create(const CStrW& actorName, CEntity* entity,
|
CUnit* CUnit::Create(const CStrW& actorName, const std::set<CStr>& selections, CObjectManager& objectManager)
|
||||||
const std::set<CStr>& selections, CObjectManager& objectManager)
|
|
||||||
{
|
{
|
||||||
CObjectBase* base = objectManager.FindObjectBase(actorName);
|
CObjectBase* base = objectManager.FindObjectBase(actorName);
|
||||||
|
|
||||||
@@ -62,101 +61,7 @@ CUnit* CUnit::Create(const CStrW& actorName, CEntity* entity,
|
|||||||
if (! obj)
|
if (! obj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return new CUnit(obj, entity, objectManager, actorSelections);
|
return new CUnit(obj, objectManager, actorSelections);
|
||||||
}
|
|
||||||
|
|
||||||
void CUnit::ShowAmmunition()
|
|
||||||
{
|
|
||||||
if (!m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint)
|
|
||||||
return;
|
|
||||||
// Remove any previous ammunition prop, in case there's still one left on there
|
|
||||||
m_Model->RemoveProp(m_Object->m_AmmunitionPoint);
|
|
||||||
// Then add the new prop
|
|
||||||
m_Model->AddProp(m_Object->m_AmmunitionPoint, m_Object->m_AmmunitionModel->Clone(), m_Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUnit::HideAmmunition()
|
|
||||||
{
|
|
||||||
if (!m_Object->m_AmmunitionModel || !m_Object->m_AmmunitionPoint)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Remove the ammunition prop
|
|
||||||
m_Model->RemoveProp(m_Object->m_AmmunitionPoint);
|
|
||||||
// Restore the original props that were on the ammo attachpoint, by copying them from
|
|
||||||
// the base model
|
|
||||||
std::vector<CModel::Prop>& props = m_Object->m_Model->GetProps();
|
|
||||||
std::vector<CModel::Prop>::iterator it;
|
|
||||||
for (it = props.begin(); it != props.end(); ++it)
|
|
||||||
{
|
|
||||||
if (it->m_Point == m_Object->m_AmmunitionPoint)
|
|
||||||
{
|
|
||||||
m_Model->AddProp(m_Object->m_AmmunitionPoint, it->m_Model->Clone(), m_Object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static CSkeletonAnim* GetRandomAnimation(const CStr& name, CObjectEntry* object)
|
|
||||||
{
|
|
||||||
CSkeletonAnim* anim = object->GetRandomAnimation(name);
|
|
||||||
|
|
||||||
// Fall back to 'idle', if no matching animation is found
|
|
||||||
if (anim == NULL && name != "idle")
|
|
||||||
anim = object->GetRandomAnimation("idle");
|
|
||||||
|
|
||||||
// Every object should have an idle animation (even if it's a dummy static one)
|
|
||||||
debug_assert(anim != NULL);
|
|
||||||
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool SetRandomAnimation(const CStr& name, bool once,
|
|
||||||
CModel* model, CObjectEntry* object)
|
|
||||||
{
|
|
||||||
CSkeletonAnim* anim = GetRandomAnimation(name, object);
|
|
||||||
if (anim)
|
|
||||||
{
|
|
||||||
model->SetAnimation(anim, once);
|
|
||||||
|
|
||||||
// Recursively apply the animation name to props
|
|
||||||
const std::vector<CModel::Prop>& props = model->GetProps();
|
|
||||||
for (std::vector<CModel::Prop>::const_iterator it = props.begin(); it != props.end(); ++it)
|
|
||||||
{
|
|
||||||
bool ok = SetRandomAnimation(name, once, it->m_Model, it->m_ObjectEntry);
|
|
||||||
if (! ok)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This shouldn't happen, since GetRandomAnimation tries to always
|
|
||||||
// return something valid
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool CUnit::SetRandomAnimation(const CStr& name, bool once)
|
|
||||||
{
|
|
||||||
return ::SetRandomAnimation(name, once, m_Model, m_Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
CSkeletonAnim* CUnit::GetRandomAnimation(const CStr& name)
|
|
||||||
{
|
|
||||||
return ::GetRandomAnimation(name, m_Object);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUnit::HasAnimation(const CStr& name)
|
|
||||||
{
|
|
||||||
return (m_Object->GetRandomAnimation(name) != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUnit::IsPlayingAnimation(const CStr& name)
|
|
||||||
{
|
|
||||||
return (m_Model->GetAnimation() && m_Model->GetAnimation()->m_Name == name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUnit::SetAnimationState(const CStr& name, bool once, float speed, float desync, bool keepSelection, const CStrW& soundgroup)
|
void CUnit::SetAnimationState(const CStr& name, bool once, float speed, float desync, bool keepSelection, const CStrW& soundgroup)
|
||||||
@@ -217,5 +122,7 @@ void CUnit::ReloadObject()
|
|||||||
delete m_Model;
|
delete m_Model;
|
||||||
m_Model = newModel;
|
m_Model = newModel;
|
||||||
m_Object = newObject;
|
m_Object = newObject;
|
||||||
|
|
||||||
|
m_Animation->ReloadUnit(); // TODO: maybe this should try to preserve animation state?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-27
@@ -25,7 +25,6 @@
|
|||||||
class CModel;
|
class CModel;
|
||||||
class CObjectEntry;
|
class CObjectEntry;
|
||||||
class CObjectManager;
|
class CObjectManager;
|
||||||
class CEntity;
|
|
||||||
class CSkeletonAnim;
|
class CSkeletonAnim;
|
||||||
class CUnitAnimation;
|
class CUnitAnimation;
|
||||||
|
|
||||||
@@ -44,15 +43,14 @@ class CUnit
|
|||||||
NONCOPYABLE(CUnit);
|
NONCOPYABLE(CUnit);
|
||||||
private:
|
private:
|
||||||
// Private constructor. Needs complete list of selections for the variation.
|
// Private constructor. Needs complete list of selections for the variation.
|
||||||
CUnit(CObjectEntry* object, CEntity* entity, CObjectManager& objectManager,
|
CUnit(CObjectEntry* object, CObjectManager& objectManager,
|
||||||
const std::set<CStr>& actorSelections);
|
const std::set<CStr>& actorSelections);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Attempt to create a unit with the given actor, attached to an entity
|
// Attempt to create a unit with the given actor, with a set of
|
||||||
// (or NULL), with a set of suggested selections (with the rest being randomised).
|
// suggested selections (with the rest being randomised).
|
||||||
// Returns NULL on failure.
|
// Returns NULL on failure.
|
||||||
static CUnit* Create(const CStrW& actorName, CEntity* entity,
|
static CUnit* Create(const CStrW& actorName, const std::set<CStr>& selections, CObjectManager& objectManager);
|
||||||
const std::set<CStr>& selections, CObjectManager& objectManager);
|
|
||||||
|
|
||||||
// destructor
|
// destructor
|
||||||
~CUnit();
|
~CUnit();
|
||||||
@@ -61,12 +59,6 @@ public:
|
|||||||
const CObjectEntry& GetObject() const { return *m_Object; }
|
const CObjectEntry& GetObject() const { return *m_Object; }
|
||||||
// get unit's model data
|
// get unit's model data
|
||||||
CModel& GetModel() const { return *m_Model; }
|
CModel& GetModel() const { return *m_Model; }
|
||||||
// get actor's entity; can be NULL
|
|
||||||
CEntity* GetEntity() const { return NULL; }
|
|
||||||
|
|
||||||
// Put here as it conveniently references both the model and the ObjectEntry
|
|
||||||
void ShowAmmunition();
|
|
||||||
void HideAmmunition();
|
|
||||||
|
|
||||||
/// See CUnitAnimation::SetAnimationState
|
/// See CUnitAnimation::SetAnimationState
|
||||||
void SetAnimationState(const CStr& name, bool once, float speed, float desync, bool keepSelection, const CStrW& soundgroup);
|
void SetAnimationState(const CStr& name, bool once, float speed, float desync, bool keepSelection, const CStrW& soundgroup);
|
||||||
@@ -80,16 +72,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
void UpdateModel(float frameTime);
|
void UpdateModel(float frameTime);
|
||||||
|
|
||||||
bool HasAnimation(const CStr& name);
|
|
||||||
|
|
||||||
// Sets the entity-selection, and updates the unit to use the new
|
// Sets the entity-selection, and updates the unit to use the new
|
||||||
// actor variation.
|
// actor variation.
|
||||||
void SetEntitySelection(const CStr& selection);
|
void SetEntitySelection(const CStr& selection);
|
||||||
|
|
||||||
// Returns whether the currently active animation is one of the ones
|
|
||||||
// matching 'name'.
|
|
||||||
bool IsPlayingAnimation(const CStr& name);
|
|
||||||
|
|
||||||
// Most units have a hopefully-unique ID number, so they can be referred to
|
// Most units have a hopefully-unique ID number, so they can be referred to
|
||||||
// persistently despite saving/loading maps. Default for new units is -1; should
|
// persistently despite saving/loading maps. Default for new units is -1; should
|
||||||
// usually be set to CUnitManager::GetNewID() after creation.
|
// usually be set to CUnitManager::GetNewID() after creation.
|
||||||
@@ -120,15 +106,6 @@ private:
|
|||||||
// object manager which looks after this unit's objectentry
|
// object manager which looks after this unit's objectentry
|
||||||
CObjectManager& m_ObjectManager;
|
CObjectManager& m_ObjectManager;
|
||||||
|
|
||||||
// Sets the animation a random one matching 'name'. If none is found,
|
|
||||||
// sets to idle instead. Applies recursively to props.
|
|
||||||
// SetEntitySelection(name) should typically be used before this.
|
|
||||||
bool SetRandomAnimation(const CStr& name, bool once = false);
|
|
||||||
|
|
||||||
// Returns a random animation matching 'name'. If none is found,
|
|
||||||
// returns idle instead.
|
|
||||||
CSkeletonAnim* GetRandomAnimation(const CStr& name);
|
|
||||||
|
|
||||||
void ReloadObject();
|
void ReloadObject();
|
||||||
|
|
||||||
friend class CUnitAnimation;
|
friend class CUnitAnimation;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "UnitAnimation.h"
|
#include "UnitAnimation.h"
|
||||||
|
|
||||||
#include "graphics/Model.h"
|
#include "graphics/Model.h"
|
||||||
|
#include "graphics/ObjectEntry.h"
|
||||||
#include "graphics/SkeletonAnim.h"
|
#include "graphics/SkeletonAnim.h"
|
||||||
#include "graphics/SkeletonAnimDef.h"
|
#include "graphics/SkeletonAnimDef.h"
|
||||||
#include "graphics/Unit.h"
|
#include "graphics/Unit.h"
|
||||||
@@ -40,8 +41,43 @@ static float DesyncSpeed(float speed, float desync)
|
|||||||
}
|
}
|
||||||
|
|
||||||
CUnitAnimation::CUnitAnimation(CUnit& unit)
|
CUnitAnimation::CUnitAnimation(CUnit& unit)
|
||||||
: m_Unit(unit), m_State("idle"), m_Looping(true), m_Speed(1.f), m_OriginalSpeed(1.f), m_Desync(0.f)
|
: m_Unit(unit), m_State("idle"), m_Looping(true), m_Speed(1.f), m_SyncRepeatTime(0.f), m_OriginalSpeed(1.f), m_Desync(0.f)
|
||||||
{
|
{
|
||||||
|
ReloadUnit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUnitAnimation::AddModel(CModel* model, const CObjectEntry* object)
|
||||||
|
{
|
||||||
|
SModelAnimState state;
|
||||||
|
|
||||||
|
state.anims = object->GetAnimations(m_State);
|
||||||
|
|
||||||
|
if (state.anims.empty())
|
||||||
|
state.anims = object->GetAnimations("idle");
|
||||||
|
debug_assert(!state.anims.empty()); // there must always be an idle animation
|
||||||
|
|
||||||
|
state.model = model;
|
||||||
|
state.animIdx = rand(0, state.anims.size());
|
||||||
|
state.time = 0.f;
|
||||||
|
state.pastLoadPos = false;
|
||||||
|
state.pastActionPos = false;
|
||||||
|
|
||||||
|
m_AnimStates.push_back(state);
|
||||||
|
|
||||||
|
model->SetAnimation(state.anims[state.animIdx], !m_Looping);
|
||||||
|
|
||||||
|
// Recursively add all props
|
||||||
|
const std::vector<CModel::Prop>& props = model->GetProps();
|
||||||
|
for (std::vector<CModel::Prop>::const_iterator it = props.begin(); it != props.end(); ++it)
|
||||||
|
{
|
||||||
|
AddModel(it->m_Model, it->m_ObjectEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CUnitAnimation::ReloadUnit()
|
||||||
|
{
|
||||||
|
m_AnimStates.clear();
|
||||||
|
AddModel(&m_Unit.GetModel(), &m_Unit.GetObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUnitAnimation::SetAnimationState(const CStr& name, bool once, float speed, float desync, bool keepSelection, const CStrW& actionSound)
|
void CUnitAnimation::SetAnimationState(const CStr& name, bool once, float speed, float desync, bool keepSelection, const CStrW& actionSound)
|
||||||
@@ -56,96 +92,135 @@ void CUnitAnimation::SetAnimationState(const CStr& name, bool once, float speed,
|
|||||||
m_ActionSound = actionSound;
|
m_ActionSound = actionSound;
|
||||||
|
|
||||||
m_Speed = DesyncSpeed(m_OriginalSpeed, m_Desync);
|
m_Speed = DesyncSpeed(m_OriginalSpeed, m_Desync);
|
||||||
|
m_SyncRepeatTime = 0.f;
|
||||||
|
|
||||||
if (! keepSelection)
|
if (! keepSelection)
|
||||||
m_Unit.SetEntitySelection(name);
|
m_Unit.SetEntitySelection(name);
|
||||||
|
|
||||||
m_Unit.SetRandomAnimation(m_State, !m_Looping);
|
ReloadUnit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUnitAnimation::SetAnimationSync(float actionTime, float repeatTime)
|
void CUnitAnimation::SetAnimationSync(float actionTime, float repeatTime)
|
||||||
{
|
{
|
||||||
CModel& model = m_Unit.GetModel();
|
m_SyncRepeatTime = repeatTime;
|
||||||
|
|
||||||
if (!model.m_Anim || !model.m_Anim->m_AnimDef)
|
// Update all the synced prop models to each coincide with actionTime
|
||||||
return;
|
for (std::vector<SModelAnimState>::iterator it = m_AnimStates.begin(); it != m_AnimStates.end(); ++it)
|
||||||
|
{
|
||||||
|
CSkeletonAnimDef* animDef = it->anims[it->animIdx]->m_AnimDef;
|
||||||
|
if (animDef == NULL)
|
||||||
|
continue; // ignore static animations
|
||||||
|
|
||||||
float duration = model.m_Anim->m_AnimDef->GetDuration();
|
float duration = animDef->GetDuration();
|
||||||
|
|
||||||
// Set the speed so it loops once in repeatTime
|
float actionPos = it->anims[it->animIdx]->m_ActionPos;
|
||||||
float speed = duration / repeatTime;
|
bool hasActionPos = (actionPos != -1.f);
|
||||||
|
|
||||||
// Compensate for the animation's scale factor
|
if (!hasActionPos)
|
||||||
if (model.m_Anim->m_Speed)
|
continue;
|
||||||
speed /= model.m_Anim->m_Speed;
|
|
||||||
|
|
||||||
// Need to offset so that start+actionTime*speed = ActionPos
|
float speed = duration / repeatTime;
|
||||||
float start = model.m_Anim->m_ActionPos - actionTime*speed;
|
|
||||||
// Wrap it so that it's within the animation
|
|
||||||
start = fmodf(start, duration);
|
|
||||||
if (start < 0)
|
|
||||||
start += duration;
|
|
||||||
|
|
||||||
// Make the animation run at the computed timings
|
// Need to offset so that start+actionTime*speed = actionPos
|
||||||
model.m_AnimTime = start;
|
float start = actionPos - actionTime*speed;
|
||||||
m_Speed = m_OriginalSpeed = speed;
|
// Wrap it so that it's within the animation
|
||||||
m_Desync = 0.f;
|
start = fmodf(start, duration);
|
||||||
|
if (start < 0)
|
||||||
|
start += duration;
|
||||||
|
|
||||||
|
it->time = start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CUnitAnimation::Update(float time)
|
void CUnitAnimation::Update(float time)
|
||||||
{
|
{
|
||||||
CModel& model = m_Unit.GetModel();
|
// Advance all of the prop models independently
|
||||||
|
for (std::vector<SModelAnimState>::iterator it = m_AnimStates.begin(); it != m_AnimStates.end(); ++it)
|
||||||
// Convert from real time to scaled animation time
|
|
||||||
float advance = time*m_Speed;
|
|
||||||
|
|
||||||
// Check if the current animation will trigger the action events
|
|
||||||
bool action = false;
|
|
||||||
bool action2 = false;
|
|
||||||
model.CheckActionTriggers(advance, action, action2);
|
|
||||||
|
|
||||||
// Choose a new random animation if we're going to loop
|
|
||||||
if (m_Looping && model.NeedsNewAnim(time))
|
|
||||||
{
|
{
|
||||||
// Re-desynchronise the animation, to keep the irregular drifting between separate units
|
CSkeletonAnimDef* animDef = it->anims[it->animIdx]->m_AnimDef;
|
||||||
m_Speed = DesyncSpeed(m_OriginalSpeed, m_Desync);
|
if (animDef == NULL)
|
||||||
advance = time*m_Speed;
|
continue; // ignore static animations
|
||||||
// TODO: should subtract time remaining in previous animation from advance, to be a bit more precise
|
|
||||||
|
|
||||||
m_Unit.SetRandomAnimation(m_State, false);
|
float duration = animDef->GetDuration();
|
||||||
// TODO: this really ought to interpolate smoothly into the new animation,
|
|
||||||
// instead of just cutting off the end of the previous one and jumping
|
|
||||||
// straight into the new.
|
|
||||||
|
|
||||||
// Check if the start of the new animation will trigger the action events
|
float actionPos = it->anims[it->animIdx]->m_ActionPos;
|
||||||
// (This is additive with the previous CheckActionTriggers)
|
float loadPos = it->anims[it->animIdx]->m_ActionPos2;
|
||||||
model.CheckActionTriggers(advance, action, action2);
|
bool hasActionPos = (actionPos != -1.f);
|
||||||
}
|
bool hasLoadPos = (loadPos != -1.f);
|
||||||
|
|
||||||
// TODO: props should get a new random animation once they loop, independent
|
// Find the current animation speed
|
||||||
// of the object they're propped onto, if we ever bother with animated props
|
float speed;
|
||||||
|
if (m_SyncRepeatTime && hasActionPos)
|
||||||
|
speed = duration / m_SyncRepeatTime;
|
||||||
|
else
|
||||||
|
speed = m_Speed * it->anims[it->animIdx]->m_Speed;
|
||||||
|
|
||||||
// If we're going to advance past the action point in this update, then perform the action
|
// Convert from real time to scaled animation time
|
||||||
if (action)
|
float advance = time * speed;
|
||||||
{
|
|
||||||
m_Unit.HideAmmunition();
|
|
||||||
|
|
||||||
if (!m_ActionSound.empty())
|
// If we're going to advance past the load point in this update, then load the ammo
|
||||||
|
if (hasLoadPos && !it->pastLoadPos && it->time + advance >= loadPos)
|
||||||
{
|
{
|
||||||
CmpPtr<ICmpSoundManager> cmpSoundManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
it->model->ShowAmmoProp();
|
||||||
if (!cmpSoundManager.null())
|
it->pastLoadPos = true;
|
||||||
cmpSoundManager->PlaySoundGroup(m_ActionSound, m_Unit.GetID());
|
}
|
||||||
|
|
||||||
|
// If we're going to advance past the action point in this update, then perform the action
|
||||||
|
if (hasActionPos && !it->pastActionPos && it->time + advance >= actionPos)
|
||||||
|
{
|
||||||
|
if (hasLoadPos)
|
||||||
|
it->model->HideAmmoProp();
|
||||||
|
|
||||||
|
if (!m_ActionSound.empty())
|
||||||
|
{
|
||||||
|
CmpPtr<ICmpSoundManager> cmpSoundManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
|
||||||
|
if (!cmpSoundManager.null())
|
||||||
|
cmpSoundManager->PlaySoundGroup(m_ActionSound, m_Unit.GetID());
|
||||||
|
}
|
||||||
|
|
||||||
|
it->pastActionPos = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->time + advance < duration)
|
||||||
|
{
|
||||||
|
// If we're still within the current animation, then simply update it
|
||||||
|
it->time += advance;
|
||||||
|
it->model->UpdateTo(it->time);
|
||||||
|
}
|
||||||
|
else if (m_Looping)
|
||||||
|
{
|
||||||
|
// If we've finished the current animation and want to loop...
|
||||||
|
|
||||||
|
// Wrap the timer around
|
||||||
|
it->time = fmod(it->time + advance, duration);
|
||||||
|
|
||||||
|
// If there's a choice of multiple animations, pick a new random one
|
||||||
|
if (it->anims.size() > 1)
|
||||||
|
{
|
||||||
|
size_t newAnimIdx = rand(0, it->anims.size());
|
||||||
|
if (newAnimIdx != it->animIdx)
|
||||||
|
{
|
||||||
|
it->animIdx = newAnimIdx;
|
||||||
|
it->model->SetAnimation(it->anims[it->animIdx], !m_Looping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it->pastActionPos = false;
|
||||||
|
it->pastLoadPos = false;
|
||||||
|
|
||||||
|
it->model->UpdateTo(it->time);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we've finished the current animation and don't want to loop...
|
||||||
|
|
||||||
|
// Update to very nearly the end of the last frame (but not quite the end else we'll wrap around when skinning)
|
||||||
|
float nearlyEnd = duration - 1.f;
|
||||||
|
if (fabs(it->time - nearlyEnd) > 1.f)
|
||||||
|
{
|
||||||
|
it->time = nearlyEnd;
|
||||||
|
it->model->UpdateTo(it->time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ditto for the second action point
|
|
||||||
if (action2)
|
|
||||||
{
|
|
||||||
m_Unit.ShowAmmunition();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: some animations (e.g. wood chopping) have two action points that should trigger sounds,
|
|
||||||
// so we ought to support that somehow
|
|
||||||
|
|
||||||
model.Update(advance);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
#include "ps/CStr.h"
|
#include "ps/CStr.h"
|
||||||
|
|
||||||
class CUnit;
|
class CUnit;
|
||||||
|
class CModel;
|
||||||
|
class CSkeletonAnim;
|
||||||
|
class CObjectEntry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deals with synchronisation issues between raw animation data (CModel, CSkeletonAnim)
|
* Deals with synchronisation issues between raw animation data (CModel, CSkeletonAnim)
|
||||||
@@ -69,12 +72,33 @@ public:
|
|||||||
*/
|
*/
|
||||||
void Update(float time);
|
void Update(float time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerate internal animation state from the models in the current unit.
|
||||||
|
* This should be called whenever the unit is changed externally, to keep this in sync.
|
||||||
|
*/
|
||||||
|
void ReloadUnit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct SModelAnimState
|
||||||
|
{
|
||||||
|
CModel* model;
|
||||||
|
std::vector<CSkeletonAnim*> anims;
|
||||||
|
size_t animIdx;
|
||||||
|
float time;
|
||||||
|
bool pastLoadPos;
|
||||||
|
bool pastActionPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<SModelAnimState> m_AnimStates;
|
||||||
|
|
||||||
|
void AddModel(CModel* model, const CObjectEntry* object);
|
||||||
|
|
||||||
CUnit& m_Unit;
|
CUnit& m_Unit;
|
||||||
CStr m_State;
|
CStr m_State;
|
||||||
bool m_Looping;
|
bool m_Looping;
|
||||||
float m_OriginalSpeed;
|
float m_OriginalSpeed;
|
||||||
float m_Speed;
|
float m_Speed;
|
||||||
|
float m_SyncRepeatTime;
|
||||||
float m_Desync;
|
float m_Desync;
|
||||||
CStrW m_ActionSound;
|
CStrW m_ActionSound;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -124,12 +124,12 @@ CUnit* CUnitManager::PickUnit(const CVector3D& origin, const CVector3D& dir, boo
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// CreateUnit: create a new unit and add it to the world
|
// CreateUnit: create a new unit and add it to the world
|
||||||
CUnit* CUnitManager::CreateUnit(const CStrW& actorName, CEntity* entity, const std::set<CStr>& selections)
|
CUnit* CUnitManager::CreateUnit(const CStrW& actorName, const std::set<CStr>& selections)
|
||||||
{
|
{
|
||||||
if (! m_ObjectManager)
|
if (! m_ObjectManager)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
CUnit* unit = CUnit::Create(actorName, entity, selections, *m_ObjectManager);
|
CUnit* unit = CUnit::Create(actorName, selections, *m_ObjectManager);
|
||||||
if (unit)
|
if (unit)
|
||||||
AddUnit(unit);
|
AddUnit(unit);
|
||||||
return unit;
|
return unit;
|
||||||
|
|||||||
@@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
class CUnit;
|
class CUnit;
|
||||||
class CVector3D;
|
class CVector3D;
|
||||||
class CEntity;
|
|
||||||
class CObjectManager;
|
class CObjectManager;
|
||||||
class CStr8;
|
class CStr8;
|
||||||
class CStrW;
|
class CStrW;
|
||||||
@@ -51,7 +50,7 @@ public:
|
|||||||
void DeleteAll();
|
void DeleteAll();
|
||||||
|
|
||||||
// creates a new unit and adds it to the world
|
// creates a new unit and adds it to the world
|
||||||
CUnit* CreateUnit(const CStrW& actorName, CEntity* entity, const std::set<CStr8>& selections);
|
CUnit* CreateUnit(const CStrW& actorName, const std::set<CStr8>& selections);
|
||||||
|
|
||||||
// return the units
|
// return the units
|
||||||
const std::vector<CUnit*>& GetUnits() const { return m_Units; }
|
const std::vector<CUnit*>& GetUnits() const { return m_Units; }
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ int g_xres, g_yres;
|
|||||||
bool g_VSync = false;
|
bool g_VSync = false;
|
||||||
|
|
||||||
bool g_Quickstart = false;
|
bool g_Quickstart = false;
|
||||||
|
bool g_DisableAudio = false;
|
||||||
|
|
||||||
// flag to switch on drawing terrain overlays
|
// flag to switch on drawing terrain overlays
|
||||||
bool g_ShowPathfindingOverlay = false;
|
bool g_ShowPathfindingOverlay = false;
|
||||||
@@ -150,7 +151,13 @@ static void ProcessCommandLineArgs(const CmdLineArgs& args)
|
|||||||
g_ConfigDB.CreateValue(CFG_COMMAND, "profile")->m_String = args.Get("profile");
|
g_ConfigDB.CreateValue(CFG_COMMAND, "profile")->m_String = args.Get("profile");
|
||||||
|
|
||||||
if (args.Has("quickstart"))
|
if (args.Has("quickstart"))
|
||||||
|
{
|
||||||
g_Quickstart = true;
|
g_Quickstart = true;
|
||||||
|
g_DisableAudio = true; // do this for backward-compatibility with user expectations
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Has("nosound"))
|
||||||
|
g_DisableAudio = true;
|
||||||
|
|
||||||
if (args.Has("shadows"))
|
if (args.Has("shadows"))
|
||||||
g_ConfigDB.CreateValue(CFG_COMMAND, "shadows")->m_String = "true";
|
g_ConfigDB.CreateValue(CFG_COMMAND, "shadows")->m_String = "true";
|
||||||
@@ -163,12 +170,6 @@ static void ProcessCommandLineArgs(const CmdLineArgs& args)
|
|||||||
|
|
||||||
if (args.Has("vsync"))
|
if (args.Has("vsync"))
|
||||||
g_ConfigDB.CreateValue(CFG_COMMAND, "vsync")->m_String = "true";
|
g_ConfigDB.CreateValue(CFG_COMMAND, "vsync")->m_String = "true";
|
||||||
|
|
||||||
if (args.Has("showOverlay"))
|
|
||||||
g_ShowPathfindingOverlay = true;
|
|
||||||
|
|
||||||
if (args.Has("triPathfind"))
|
|
||||||
g_TriPathfind = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,14 +60,11 @@ extern int g_xres, g_yres;
|
|||||||
extern bool g_VSync;
|
extern bool g_VSync;
|
||||||
|
|
||||||
extern bool g_Quickstart;
|
extern bool g_Quickstart;
|
||||||
|
extern bool g_DisableAudio;
|
||||||
|
|
||||||
extern CStrW g_CursorName;
|
extern CStrW g_CursorName;
|
||||||
|
|
||||||
class CmdLineArgs;
|
class CmdLineArgs;
|
||||||
extern void CONFIG_Init(const CmdLineArgs& args);
|
extern void CONFIG_Init(const CmdLineArgs& args);
|
||||||
|
|
||||||
extern bool g_ShowPathfindingOverlay;
|
|
||||||
|
|
||||||
extern bool g_TriPathfind;
|
|
||||||
|
|
||||||
#endif // INCLUDED_PS_GAMESETUP_CONFIG
|
#endif // INCLUDED_PS_GAMESETUP_CONFIG
|
||||||
|
|||||||
@@ -765,7 +765,8 @@ void Init(const CmdLineArgs& args, int flags)
|
|||||||
// note: no longer vfs_display here. it's dog-slow due to unbuffered
|
// note: no longer vfs_display here. it's dog-slow due to unbuffered
|
||||||
// file output and very rarely needed.
|
// file output and very rarely needed.
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(g_DisableAudio)
|
||||||
{
|
{
|
||||||
// speed up startup by disabling all sound
|
// speed up startup by disabling all sound
|
||||||
// (OpenAL init will be skipped).
|
// (OpenAL init will be skipped).
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
/* Copyright (C) 2009 Wildfire Games.
|
/* Copyright (C) 2010 Wildfire Games.
|
||||||
* This file is part of 0 A.D.
|
* This file is part of 0 A.D.
|
||||||
*
|
*
|
||||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* File : Scene.cpp
|
* File : Scene.cpp
|
||||||
* Project : graphics
|
* Project : graphics
|
||||||
* Description : This file contains default implementations and utilities
|
* Description : This file contains default implementations and utilities
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
* : classes.
|
* : classes.
|
||||||
*
|
*
|
||||||
* @note This file would fit just as well into the graphics/ subdirectory.
|
* @note This file would fit just as well into the graphics/ subdirectory.
|
||||||
**/
|
*/
|
||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
@@ -31,8 +31,6 @@
|
|||||||
|
|
||||||
#include "renderer/Scene.h"
|
#include "renderer/Scene.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////
|
||||||
// Default implementation traverses the model recursively and uses
|
// Default implementation traverses the model recursively and uses
|
||||||
// SubmitNonRecursive for the actual work.
|
// SubmitNonRecursive for the actual work.
|
||||||
@@ -41,9 +39,9 @@ void SceneCollector::SubmitRecursive(CModel* model)
|
|||||||
SubmitNonRecursive(model);
|
SubmitNonRecursive(model);
|
||||||
|
|
||||||
const std::vector<CModel::Prop>& props = model->GetProps();
|
const std::vector<CModel::Prop>& props = model->GetProps();
|
||||||
for (size_t i=0;i<props.size();i++) {
|
for (size_t i = 0; i < props.size(); i++)
|
||||||
SubmitRecursive(props[i].m_Model);
|
{
|
||||||
|
if (!props[i].m_Hidden)
|
||||||
|
SubmitRecursive(props[i].m_Model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -143,19 +143,25 @@ void CCmpProjectileManager::LaunchProjectile(entity_id_t source, CFixedVector3D
|
|||||||
std::set<CStr> selections;
|
std::set<CStr> selections;
|
||||||
|
|
||||||
Projectile projectile;
|
Projectile projectile;
|
||||||
projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, NULL, selections);
|
projectile.unit = GetSimContext().GetUnitManager().CreateUnit(name, selections);
|
||||||
if (!projectile.unit)
|
if (!projectile.unit)
|
||||||
{
|
{
|
||||||
// The error will have already been logged
|
// The error will have already been logged
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CmpPtr<ICmpPosition> sourcePos(GetSimContext(), source);
|
CVector3D sourceVec(sourceVisual->GetProjectileLaunchPoint());
|
||||||
if (sourcePos.null())
|
if (!sourceVec)
|
||||||
return;
|
{
|
||||||
|
// If there's no explicit launch point, take a guess based on the entity position
|
||||||
|
|
||||||
CVector3D sourceVec(sourcePos->GetPosition());
|
CmpPtr<ICmpPosition> sourcePos(GetSimContext(), source);
|
||||||
sourceVec.Y += 3.f; // TODO: ought to exactly match the appropriate prop point
|
if (sourcePos.null())
|
||||||
|
return;
|
||||||
|
|
||||||
|
CVector3D sourceVec(sourcePos->GetPosition());
|
||||||
|
sourceVec.Y += 3.f;
|
||||||
|
}
|
||||||
|
|
||||||
CVector3D targetVec;
|
CVector3D targetVec;
|
||||||
|
|
||||||
|
|||||||
@@ -389,7 +389,13 @@ void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& i
|
|||||||
permittedComponentTypes.insert("VisualActor");
|
permittedComponentTypes.insert("VisualActor");
|
||||||
permittedComponentTypes.insert("Footprint");
|
permittedComponentTypes.insert("Footprint");
|
||||||
permittedComponentTypes.insert("Obstruction");
|
permittedComponentTypes.insert("Obstruction");
|
||||||
// (This could be initialised once and reused, but it's not worth the effort)
|
|
||||||
|
// Need these for the Actor Viewer:
|
||||||
|
permittedComponentTypes.insert("Attack");
|
||||||
|
permittedComponentTypes.insert("UnitMotion");
|
||||||
|
permittedComponentTypes.insert("Sound");
|
||||||
|
|
||||||
|
// (This set could be initialised once and reused, but it's not worth the effort)
|
||||||
|
|
||||||
CParamNode::LoadXMLString(out, "<Entity/>");
|
CParamNode::LoadXMLString(out, "<Entity/>");
|
||||||
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ public:
|
|||||||
|
|
||||||
// Template state:
|
// Template state:
|
||||||
|
|
||||||
fixed m_Speed; // in units per second
|
fixed m_Speed; // in metres per second
|
||||||
|
fixed m_RunSpeed;
|
||||||
entity_pos_t m_Radius;
|
entity_pos_t m_Radius;
|
||||||
u8 m_PassClass;
|
u8 m_PassClass;
|
||||||
u8 m_CostClass;
|
u8 m_CostClass;
|
||||||
@@ -122,6 +123,15 @@ public:
|
|||||||
|
|
||||||
m_Speed = paramNode.GetChild("WalkSpeed").ToFixed();
|
m_Speed = paramNode.GetChild("WalkSpeed").ToFixed();
|
||||||
|
|
||||||
|
if (paramNode.GetChild("Run").IsOk())
|
||||||
|
{
|
||||||
|
m_RunSpeed = paramNode.GetChild("Run").GetChild("Speed").ToFixed();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_RunSpeed = m_Speed;
|
||||||
|
}
|
||||||
|
|
||||||
CmpPtr<ICmpObstruction> cmpObstruction(context, GetEntityId());
|
CmpPtr<ICmpObstruction> cmpObstruction(context, GetEntityId());
|
||||||
if (!cmpObstruction.null())
|
if (!cmpObstruction.null())
|
||||||
m_Radius = cmpObstruction->GetUnitRadius();
|
m_Radius = cmpObstruction->GetUnitRadius();
|
||||||
@@ -197,6 +207,11 @@ public:
|
|||||||
return m_Speed;
|
return m_Speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual fixed GetRunSpeed()
|
||||||
|
{
|
||||||
|
return m_RunSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
virtual void SetDebugOverlay(bool enabled)
|
virtual void SetDebugOverlay(bool enabled)
|
||||||
{
|
{
|
||||||
m_DebugOverlayEnabled = enabled;
|
m_DebugOverlayEnabled = enabled;
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public:
|
|||||||
m_ActorName = paramNode.GetChild("Actor").ToString();
|
m_ActorName = paramNode.GetChild("Actor").ToString();
|
||||||
|
|
||||||
std::set<CStr> selections;
|
std::set<CStr> selections;
|
||||||
m_Unit = context.GetUnitManager().CreateUnit(m_ActorName, NULL, selections);
|
m_Unit = context.GetUnitManager().CreateUnit(m_ActorName, selections);
|
||||||
if (!m_Unit)
|
if (!m_Unit)
|
||||||
{
|
{
|
||||||
// The error will have already been logged
|
// The error will have already been logged
|
||||||
@@ -204,6 +204,16 @@ public:
|
|||||||
return m_Unit->GetObject().m_ProjectileModelName;
|
return m_Unit->GetObject().m_ProjectileModelName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CVector3D GetProjectileLaunchPoint()
|
||||||
|
{
|
||||||
|
if (!m_Unit)
|
||||||
|
return CVector3D();
|
||||||
|
CModel* ammo = m_Unit->GetModel().FindFirstAmmoProp();
|
||||||
|
if (!ammo)
|
||||||
|
return CVector3D();
|
||||||
|
return ammo->GetTransform().GetTranslation();
|
||||||
|
}
|
||||||
|
|
||||||
virtual void SelectAnimation(std::string name, bool once, float speed, std::wstring soundgroup)
|
virtual void SelectAnimation(std::string name, bool once, float speed, std::wstring soundgroup)
|
||||||
{
|
{
|
||||||
if (!m_Unit)
|
if (!m_Unit)
|
||||||
@@ -249,7 +259,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
std::set<CStr> selections;
|
std::set<CStr> selections;
|
||||||
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, NULL, selections);
|
CUnit* newUnit = GetSimContext().GetUnitManager().CreateUnit(m_ActorName, selections);
|
||||||
|
|
||||||
if (!newUnit)
|
if (!newUnit)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual fixed GetSpeed() = 0;
|
virtual fixed GetSpeed() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the default speed that this unit will have when running, in metres per second.
|
||||||
|
*/
|
||||||
|
virtual fixed GetRunSpeed() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the rendering of debug info.
|
* Toggle the rendering of debug info.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -53,6 +53,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual std::wstring GetProjectileActor() = 0;
|
virtual std::wstring GetProjectileActor() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the exact position where a projectile should be launched from (based on the actor's
|
||||||
|
* ammo prop points).
|
||||||
|
* Returns (0,0,0) if no point can be found.
|
||||||
|
*/
|
||||||
|
virtual CVector3D GetProjectileLaunchPoint() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start playing the given animation. If there are multiple possible animations then it will
|
* Start playing the given animation. If there are multiple possible animations then it will
|
||||||
* pick one at random (not network-synchronised).
|
* pick one at random (not network-synchronised).
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#define LOG_CATEGORY L"audio"
|
#define LOG_CATEGORY L"audio"
|
||||||
|
|
||||||
|
static const bool DISABLE_INTENSITY = true; // disable for now since it's broken
|
||||||
|
|
||||||
void CSoundGroup::SetGain(float gain)
|
void CSoundGroup::SetGain(float gain)
|
||||||
{
|
{
|
||||||
@@ -124,7 +125,7 @@ static void HandleError(const std::wstring& message, const VfsPath& pathname, Li
|
|||||||
|
|
||||||
void CSoundGroup::PlayNext(const CVector3D& position)
|
void CSoundGroup::PlayNext(const CVector3D& position)
|
||||||
{
|
{
|
||||||
if(m_Intensity >= m_IntensityThreshold)
|
if(m_Intensity >= m_IntensityThreshold && !DISABLE_INTENSITY)
|
||||||
{
|
{
|
||||||
if(!is_playing(m_hReplacement))
|
if(!is_playing(m_hReplacement))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,8 +37,10 @@
|
|||||||
#include "renderer/Renderer.h"
|
#include "renderer/Renderer.h"
|
||||||
#include "renderer/Scene.h"
|
#include "renderer/Scene.h"
|
||||||
#include "renderer/SkyManager.h"
|
#include "renderer/SkyManager.h"
|
||||||
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
#include "simulation2/Simulation2.h"
|
#include "simulation2/Simulation2.h"
|
||||||
#include "simulation2/components/ICmpPosition.h"
|
#include "simulation2/components/ICmpPosition.h"
|
||||||
|
#include "simulation2/components/ICmpUnitMotion.h"
|
||||||
#include "simulation2/components/ICmpVisual.h"
|
#include "simulation2/components/ICmpVisual.h"
|
||||||
|
|
||||||
struct ActorViewerImpl : public Scene
|
struct ActorViewerImpl : public Scene
|
||||||
@@ -178,20 +180,37 @@ void ActorViewer::SetActor(const CStrW& name, const CStrW& animation)
|
|||||||
{
|
{
|
||||||
CStr anim = CStr(animation).LowerCase();
|
CStr anim = CStr(animation).LowerCase();
|
||||||
|
|
||||||
|
// Emulate the typical simulation animation behaviour
|
||||||
float speed;
|
float speed;
|
||||||
// TODO: this is just copied from template_unit.xml and isn't the
|
float repeattime = 0.f;
|
||||||
// same for all units. We ought to get it from the entity definition
|
|
||||||
// (if there is one)
|
|
||||||
if (anim == "walk")
|
if (anim == "walk")
|
||||||
{
|
{
|
||||||
speed = 7.f;
|
CmpPtr<ICmpUnitMotion> cmpUnitMotion(m.Simulation2, m.Entity);
|
||||||
|
if (!cmpUnitMotion.null())
|
||||||
|
speed = cmpUnitMotion->GetSpeed().ToFloat();
|
||||||
|
else
|
||||||
|
speed = 7.f; // typical unit speed
|
||||||
|
|
||||||
m.CurrentSpeed = speed;
|
m.CurrentSpeed = speed;
|
||||||
}
|
}
|
||||||
else if (anim == "run")
|
else if (anim == "run")
|
||||||
{
|
{
|
||||||
speed = 12.f;
|
CmpPtr<ICmpUnitMotion> cmpUnitMotion(m.Simulation2, m.Entity);
|
||||||
|
if (!cmpUnitMotion.null())
|
||||||
|
speed = cmpUnitMotion->GetRunSpeed().ToFloat();
|
||||||
|
else
|
||||||
|
speed = 12.f; // typical unit speed
|
||||||
|
|
||||||
m.CurrentSpeed = speed;
|
m.CurrentSpeed = speed;
|
||||||
}
|
}
|
||||||
|
else if (anim == "melee")
|
||||||
|
{
|
||||||
|
speed = 1.f; // speed will be ignored if we have a repeattime
|
||||||
|
m.CurrentSpeed = 0.f;
|
||||||
|
|
||||||
|
CStr code = "var cmp = Engine.QueryInterface("+CStr(m.Entity)+", IID_Attack); if (cmp) cmp.GetTimers(cmp.GetBestAttack()).repeat; else 0;";
|
||||||
|
m.Simulation2.GetScriptInterface().Eval(code.c_str(), repeattime);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Play the animation at normal speed, but movement speed is zero
|
// Play the animation at normal speed, but movement speed is zero
|
||||||
@@ -199,12 +218,28 @@ void ActorViewer::SetActor(const CStrW& name, const CStrW& animation)
|
|||||||
m.CurrentSpeed = 0.f;
|
m.CurrentSpeed = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CStr sound;
|
||||||
|
if (anim == "melee")
|
||||||
|
sound = "attack";
|
||||||
|
else if (anim == "build")
|
||||||
|
sound = "build";
|
||||||
|
else if (anim.Find("gather_") == 0)
|
||||||
|
sound = anim;
|
||||||
|
|
||||||
|
std::wstring soundgroup;
|
||||||
|
if (!sound.empty())
|
||||||
|
{
|
||||||
|
CStr code = "var cmp = Engine.QueryInterface("+CStr(m.Entity)+", IID_Sound); if (cmp) cmp.GetSoundGroup('"+sound+"'); else '';";
|
||||||
|
m.Simulation2.GetScriptInterface().Eval(code.c_str(), soundgroup);
|
||||||
|
}
|
||||||
|
|
||||||
CmpPtr<ICmpVisual> cmpVisual(m.Simulation2, m.Entity);
|
CmpPtr<ICmpVisual> cmpVisual(m.Simulation2, m.Entity);
|
||||||
if (!cmpVisual.null())
|
if (!cmpVisual.null())
|
||||||
{
|
{
|
||||||
// TODO: SetEntitySelection(anim)
|
// TODO: SetEntitySelection(anim)
|
||||||
// TODO: maybe we could play soundgroups from entities in here, to help test timings?
|
cmpVisual->SelectAnimation(anim, false, speed, soundgroup);
|
||||||
cmpVisual->SelectAnimation(anim, false, speed, L"");
|
if (repeattime)
|
||||||
|
cmpVisual->SetAnimationSync(0.f, repeattime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user