1
1
forked from 0ad/0ad

Props remember who they're attached to and other mostly bone matrix related

things that should fix the heads-detached-from-bodies problem.

This was SVN commit r3025.
This commit is contained in:
prefect 2005-10-26 01:03:28 +00:00
parent 2842098a49
commit a357c7334e
3 changed files with 141 additions and 87 deletions

View File

@ -171,7 +171,11 @@ void CGameView::RenderModels(CUnitManager *pUnitMan, CProjectileManager *pProjec
for (uint i=0;i<units.size();++i)
{
int status = losMgr->GetUnitStatus(units[i], g_Game->GetLocalPlayer());
if (frustum.IsBoxVisible(CVector3D(0,0,0), units[i]->GetModel()->GetBounds())
CModel* model = units[i]->GetModel();
model->ValidatePosition();
if (frustum.IsBoxVisible(CVector3D(0,0,0), model->GetBounds())
&& status != UNIT_HIDDEN)
{
if(units[i] != g_BuildingPlacer.m_actor)
@ -185,20 +189,25 @@ void CGameView::RenderModels(CUnitManager *pUnitMan, CProjectileManager *pProjec
{
color = CColor(0.7f, 0.7f, 0.7f, 1.0f);
}
units[i]->GetModel()->SetShadingColor(color);
model->SetShadingColor(color);
}
PROFILE( "submit models" );
SubmitModelRecursive(units[i]->GetModel());
SubmitModelRecursive(model);
}
}
const std::vector<CProjectile*>& projectiles=pProjectileMan->GetProjectiles();
for (uint i=0;i<projectiles.size();++i)
{
const CBound& bound = projectiles[i]->GetModel()->GetBounds();
CModel* model = projectiles[i]->GetModel();
model->ValidatePosition();
const CBound& bound = model->GetBounds();
CVector3D centre;
bound.GetCentre(centre);
if (frustum.IsBoxVisible(CVector3D(0,0,0), bound)
&& losMgr->GetStatus(centre.X, centre.Z, g_Game->GetLocalPlayer()) == LOS_VISIBLE)
{
@ -259,6 +268,7 @@ void CGameView::CameraLock(float x, float y, float z, bool smooth)
void CGameView::SubmitModelRecursive(CModel* model)
{
model->ValidatePosition();
g_Renderer.Submit(model);
const std::vector<CModel::Prop>& props=model->GetProps();

View File

@ -26,9 +26,9 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor
CModel::CModel()
: m_Flags(0), m_Anim(0), m_AnimTime(0),
m_BoneMatricesValid(false), m_BoneMatrices(0), m_InvBoneMatrices(0),
m_ShadingColor(1,1,1,1)
: m_Parent(0), m_Flags(0), m_Anim(0), m_AnimTime(0),
m_BoneMatrices(0), m_InvBoneMatrices(0),
m_PositionValid(false), m_ShadingColor(1,1,1,1)
{
}
@ -36,6 +36,23 @@ CModel::CModel()
// Destructor
CModel::~CModel()
{
// Detach us from our parent
if (m_Parent)
{
for(std::vector<Prop>::iterator iter = m_Parent->m_Props.begin();
iter != m_Parent->m_Props.end();
++iter)
{
if (iter->m_Model == this)
{
m_Parent->m_Props.erase(iter);
break;
}
}
m_Parent = 0;
}
ReleaseData();
}
@ -79,33 +96,13 @@ bool CModel::InitModel(CModelDefPtr modeldef)
m.Translate(defpose[i].m_Translation);
m.GetInverse(m_InvBoneMatrices[i]);
}
m_BoneMatricesValid=true;
}
m_PositionValid = true;
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the given point using the given blend and bonestate data
/* JW: function is apparently currently unused
static CVector3D SkinPoint(const CVector3D& pos,const SVertexBlend& blend,
const CBoneState* bonestates)
{
CVector3D result(0,0,0);
for (int i=0;i<SVertexBlend::SIZE && blend.m_Bone[i]!=0xff;i++) {
CMatrix3D m;
m.SetIdentity();
m.Rotate(bonestates[blend.m_Bone[i]].m_Rotation);
m.Translate(bonestates[blend.m_Bone[i]].m_Translation);
CVector3D tmp=m.Transform(pos);
result+=tmp*blend.m_Weight[i];
}
return result;
}
*/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SkinPoint: skin the given point using the given blend and matrix data
@ -183,8 +180,11 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
// at the origin. The box is later re-transformed onto the object, without
// having to recalculate the size of the box.
CMatrix3D transform, oldtransform = GetTransform();
CModel* oldparent = m_Parent;
m_Parent = 0;
transform.SetIdentity();
SetTransform(transform);
CRenderableObject::SetTransform(transform);
// Following seems to stomp over the current animation time - which, unsurprisingly,
// introduces artefacts in the currently playing animation. Save it here and restore it
@ -192,17 +192,21 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
float AnimTime = m_AnimTime;
// iterate through every frame of the animation
for (size_t j=0;j<anim->GetNumFrames();j++) {
for (size_t j=0;j<anim->GetNumFrames();j++) {
m_PositionValid = false;
ValidatePosition();
// extend bounds by vertex positions at the frame
for (size_t i=0;i<numverts;i++) {
CVector3D tmp=SkinPoint(verts[i].m_Coords,verts[i].m_Blend,GetBoneMatrices());
CVector3D tmp = SkinPoint(verts[i].m_Coords,verts[i].m_Blend,GetBoneMatrices());
result+=tmp;
}
// advance to next frame
m_AnimTime+=anim->GetFrameTime();
m_BoneMatricesValid=false;
m_AnimTime += anim->GetFrameTime();
}
m_PositionValid = false;
m_Parent = oldparent;
SetTransform(oldtransform);
m_AnimTime = AnimTime;
}
@ -259,53 +263,81 @@ void CModel::Update(float time)
SetDirty(RENDERDATA_UPDATE_VERTICES);
// mark matrices as dirty
m_BoneMatricesValid = false;
InvalidatePosition();
}
// update props
for (uint i=0; i<m_Props.size(); i++) {
for (uint i=0; i<m_Props.size(); i++)
{
m_Props[i].m_Model->Update(time);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GenerateBoneMatrices: calculate necessary bone transformation matrices for skinning
void CModel::GenerateBoneMatrices()
// InvalidatePosition
void CModel::InvalidatePosition()
{
if (!m_Anim || !m_BoneMatrices) return;
m_PositionValid = false;
PROFILE( "generating bone matrices" );
for (uint i = 0; i < m_Props.size(); ++i)
m_Props[i].m_Model->InvalidatePosition();
}
debug_assert(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());
m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime,m_BoneMatrices);
const CMatrix3D& transform=GetTransform();
for (size_t i=0;i<m_pModelDef->GetNumBones();i++) {
m_BoneMatrices[i].Concatenate(transform);
m_BoneMatrices[i].GetInverse(m_InvBoneMatrices[i]);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ValidatePosition: ensure that current transform and bone matrices are both uptodate
void CModel::ValidatePosition()
{
if (m_PositionValid)
{
debug_assert(!m_Parent || m_Parent->m_PositionValid);
return;
}
if (m_Parent && !m_Parent->m_PositionValid)
{
// Make sure we don't base our calculations on
// a parent animation state that is out of date.
m_Parent->ValidatePosition();
// Parent will recursively call our validation.
debug_assert(m_PositionValid);
return;
}
// update transform of boned props
// TODO, RC - ugh, we'll be doing this twice (for boned props, at least) - once here,
// and once again in SetTransform; better to just do it in Update?
for (size_t j=0;j<m_Props.size();j++) {
if (m_Anim && m_BoneMatrices)
{
PROFILE( "generating bone matrices" );
debug_assert(m_pModelDef->GetNumBones() == m_Anim->m_AnimDef->GetNumKeys());
m_Anim->m_AnimDef->BuildBoneMatrices(m_AnimTime,m_BoneMatrices);
const CMatrix3D& transform=GetTransform();
for (size_t i=0;i<m_pModelDef->GetNumBones();i++) {
m_BoneMatrices[i].Concatenate(transform);
m_BoneMatrices[i].GetInverse(m_InvBoneMatrices[i]);
}
}
m_PositionValid = true;
// re-position and validate all props
for (size_t j = 0; j < m_Props.size(); ++j)
{
const Prop& prop=m_Props[j];
if (prop.m_Point->m_BoneIndex!=0xff) {
CMatrix3D proptransform=prop.m_Point->m_Transform;;
if (prop.m_Point->m_BoneIndex!=0xff) {
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
} else {
proptransform.Concatenate(transform);
}
prop.m_Model->SetTransform(proptransform);
}
CMatrix3D proptransform = prop.m_Point->m_Transform;;
if (prop.m_Point->m_BoneIndex != 0xff)
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
else
proptransform.Concatenate(m_Transform);
prop.m_Model->SetTransform(proptransform);
prop.m_Model->ValidatePosition();
}
m_BoneMatricesValid=true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SetAnimation: set the given animation as the current animation on this model;
// return false on error, else true
@ -344,7 +376,7 @@ bool CModel::SetAnimation(CSkeletonAnim* anim, bool once, float speed, CSkeleton
m_AnimSpeed = speed * anim->m_Speed;
}
m_Anim=anim;
m_Anim = anim;
return true;
}
@ -355,6 +387,7 @@ void CModel::AddProp(SPropPoint* point, CModel* model)
{
// position model according to prop point position
model->SetTransform(point->m_Transform);
model->m_Parent = this;
// check if we're already using this point, and replace
// model on it if so
@ -412,26 +445,12 @@ CModel* CModel::Clone() const
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SetTransform: set the transform on this object, and reorientate props accordingly
void CModel::SetTransform(const CMatrix3D& transform)
void CModel::SetTransform(const CMatrix3D& transform)
{
// call base class to set transform on this object
CRenderableObject::SetTransform(transform);
InvalidateBounds();
GenerateBoneMatrices();
// now set transforms on props
for (size_t i=0;i<m_Props.size();i++) {
const Prop& prop=m_Props[i];
CMatrix3D proptransform=prop.m_Point->m_Transform;;
if (prop.m_Point->m_BoneIndex!=0xff) {
proptransform.Concatenate(m_BoneMatrices[prop.m_Point->m_BoneIndex]);
} else {
proptransform.Concatenate(transform);
}
prop.m_Model->SetTransform(proptransform);
}
InvalidatePosition();
}
//////////////////////////////////////////////////////////////////////////

View File

@ -92,17 +92,22 @@ public:
// calculate bounds encompassing all vertex positions for given animation
void CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result);
// set transform of this object, and recurse down into props to update their world space transform
/**
* SetTransform: Set transform of this object.
*
* @note In order to ensure that all child props are updated properly,
* you must call ValidatePosition().
*/
void SetTransform(const CMatrix3D& transform);
// return the models bone matrices
const CMatrix3D* GetBoneMatrices() {
if (!m_BoneMatricesValid) GenerateBoneMatrices();
debug_assert(m_PositionValid);
return m_BoneMatrices;
}
// return the models inverted bone matrices
const CMatrix3D* GetInvBoneMatrices() {
if (!m_BoneMatricesValid) GenerateBoneMatrices();
debug_assert(m_PositionValid);
return m_InvBoneMatrices;
}
@ -121,11 +126,27 @@ public:
// return a clone of this model
CModel* Clone() const;
/**
* ValidatePosition: Ensure that both the transformation and the bone
* matrices are correct for this model and all its props.
*/
void ValidatePosition();
private:
// delete anything allocated by the model
void ReleaseData();
// calculate necessary bone transformation matrices for skinning
void GenerateBoneMatrices();
/**
* InvalidatePosition: Mark this model's position and bone matrices,
* and all props' positions as invalid.
*/
void InvalidatePosition();
/**
* m_Parent: If non-null, m_Parent points to the model that we
* are attached to.
*/
CModel* m_Parent;
// object flags
u32 m_Flags;
@ -147,8 +168,6 @@ private:
float m_AnimSpeed;
// time (in MS) into the current animation
float m_AnimTime;
// flag stating whether bone matrices are currently valid
bool m_BoneMatricesValid;
// current state of all bones on this model; null if associated modeldef isn't skeletal
CMatrix3D* m_BoneMatrices;
// inverse of the above world space transform of the above matrices
@ -156,6 +175,12 @@ private:
// list of current props on model
std::vector<Prop> m_Props;
/**
* m_PositionValid: true if both transform and and bone matrices
* are valid.
*/
bool m_PositionValid;
// modulating color
CColor m_ShadingColor;
};