Allow attaching an object's mesh instance, add SetAttach{Bones,Transform}

stable-5.1
Armin Burgmeier 2010-01-29 22:18:18 +01:00
parent fde2143f8e
commit bb28160406
5 changed files with 341 additions and 158 deletions

View File

@ -1101,8 +1101,8 @@ void C4Object::Execute()
ExecLife();
// Base
ExecBase();
// Animation
if(pMeshInstance) pMeshInstance->ExecuteAnimation();
// Animation. If the mesh is attached, then don't execute animation here but let the parent object do it to make sure it is only executed once a frame.
if(pMeshInstance && !pMeshInstance->GetAttachParent()) pMeshInstance->ExecuteAnimation();
// Timer
Timer++;
if (Timer>=Def->Timer)

View File

@ -5583,7 +5583,8 @@ static Nillable<int> FnPlayAnimation(C4AulObjectContext *ctx, C4String *szAnimat
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return C4VNull;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return C4VNull;
Instance = Attached->Child;
}
@ -5618,7 +5619,8 @@ static bool FnStopAnimation(C4AulObjectContext *ctx, int iAnimationNumber, Nilla
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return false;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return false;
Instance = Attached->Child;
}
@ -5638,7 +5640,8 @@ static Nillable<int> FnGetRootAnimation(C4AulObjectContext *ctx, int iSlot, Nill
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return C4VNull;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return C4VNull;
Instance = Attached->Child;
}
@ -5656,7 +5659,8 @@ static Nillable<int> FnGetAnimationLength(C4AulObjectContext *ctx, C4String *szA
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return C4VNull;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return C4VNull;
Instance = Attached->Child;
}
@ -5674,7 +5678,8 @@ static Nillable<C4String*> FnGetAnimationName(C4AulObjectContext *ctx, int iAnim
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return C4VNull;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return C4VNull;
Instance = Attached->Child;
}
@ -5692,7 +5697,8 @@ static Nillable<int> FnGetAnimationPosition(C4AulObjectContext *ctx, int iAnimat
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return C4VNull;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return C4VNull;
Instance = Attached->Child;
}
@ -5710,7 +5716,8 @@ static Nillable<int> FnGetAnimationWeight(C4AulObjectContext *ctx, int iAnimatio
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return C4VNull;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return C4VNull;
Instance = Attached->Child;
}
@ -5728,7 +5735,8 @@ static bool FnSetAnimationPosition(C4AulObjectContext *ctx, int iAnimationNumber
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return false;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return false;
Instance = Attached->Child;
}
@ -5750,7 +5758,8 @@ static bool FnSetAnimationWeight(C4AulObjectContext *ctx, int iAnimationNumber,
if(!iAttachNumber.IsNil())
{
const StdMeshInstance::AttachedMesh* Attached = Instance->GetAttachedMeshByNumber(iAttachNumber);
if(!Attached) return false;
// OwnChild is set if an object's instance is attached. In that case the animation should be set directly on that object.
if(!Attached || !Attached->OwnChild) return false;
Instance = Attached->Child;
}
@ -5763,11 +5772,15 @@ static bool FnSetAnimationWeight(C4AulObjectContext *ctx, int iAnimationNumber,
return true;
}
static Nillable<long> FnAttachMesh(C4AulContext *ctx, C4ID idMesh, C4String* szParentBone, C4String* szChildBone, C4ValueArray* Transformation)
{
static C4Value FnAttachMesh(C4AulContext *ctx, C4Value* pPars)
{
if(!ctx->Obj || !ctx->Obj->pMeshInstance) return C4VNull;
C4Def* pDef = C4Id2Def(idMesh);
if(!pDef || pDef->Graphics.Type != C4DefGraphics::TYPE_Mesh) return C4VNull;
//if(!Mesh) return C4VNull;
PAR(any, Mesh);
PAR(string, szParentBone);
PAR(string, szChildBone);
PAR(array, Transformation);
StdMeshMatrix trans = StdMeshMatrix::Identity();
if(Transformation)
@ -5790,17 +5803,83 @@ static Nillable<long> FnAttachMesh(C4AulContext *ctx, C4ID idMesh, C4String* szP
trans(2,3) = arr[11].getInt()/1000.0f;
}
const StdMeshInstance::AttachedMesh* attach = ctx->Obj->pMeshInstance->AttachMesh(*pDef->Graphics.Mesh, szParentBone->GetData(), szChildBone->GetData(), trans);
if(!attach) return C4VNull;
return attach->Number;
StdMeshInstance::AttachedMesh* attach;
C4Object* pObj = Mesh.getObj();
if(pObj)
{
if(!pObj->pMeshInstance) return C4VNull;
attach = ctx->Obj->pMeshInstance->AttachMesh(*pObj->pMeshInstance, szParentBone->GetData(), szChildBone->GetData(), trans);
}
else
{
C4ID id = Mesh.getC4ID();
if(id == C4ID::None) return C4VNull;
C4Def* pDef = C4Id2Def(id);
if(pDef->Graphics.Type != C4DefGraphics::TYPE_Mesh) return C4VNull;
attach = ctx->Obj->pMeshInstance->AttachMesh(*pDef->Graphics.Mesh, szParentBone->GetData(), szChildBone->GetData(), trans);
}
static bool FnDetachMesh(C4AulContext *ctx, long iAttachNumber)
{
if(!attach) return C4VNull;
return C4VInt(attach->Number);
}
static bool FnDetachMesh(C4AulObjectContext *ctx, long iAttachNumber)
{
if(!ctx->Obj || !ctx->Obj->pMeshInstance) return false;
return ctx->Obj->pMeshInstance->DetachMesh(iAttachNumber);
}
static bool FnSetAttachBones(C4AulObjectContext* ctx, long iAttachNumber, Nillable<C4String*> szParentBone, Nillable<C4String*> szChildBone)
{
if(!ctx->Obj || !ctx->Obj->pMeshInstance) return false;
StdMeshInstance::AttachedMesh* attach = ctx->Obj->pMeshInstance->GetAttachedMeshByNumber(iAttachNumber);
if(!attach) return false;
if(!szParentBone.IsNil())
{
C4String* ParentBone = szParentBone;
if(!attach->SetParentBone(ParentBone->GetData())) return false;
}
if(!szChildBone.IsNil())
{
C4String* ChildBone = szChildBone;
if(!attach->SetChildBone(ChildBone->GetData())) return false;
}
return true;
}
static bool FnSetAttachTransform(C4AulObjectContext* ctx, long iAttachNumber, C4ValueArray* Transformation)
{
if(!ctx->Obj || !ctx->Obj->pMeshInstance) return false;
if(!Transformation) return false;
StdMeshInstance::AttachedMesh* attach = ctx->Obj->pMeshInstance->GetAttachedMeshByNumber(iAttachNumber);
if(!attach) return false;
if(Transformation->GetSize() != 12)
throw new C4AulExecError(ctx->Obj, "AttachMesh: Transformation is not a valid 3x4 matrix");
StdMeshMatrix trans;
const C4ValueArray& arr = *Transformation;
trans(0,0) = arr[0].getInt()/1000.0f;
trans(0,1) = arr[1].getInt()/1000.0f;
trans(0,2) = arr[2].getInt()/1000.0f;
trans(0,3) = arr[3].getInt()/1000.0f;
trans(1,0) = arr[4].getInt()/1000.0f;
trans(1,1) = arr[5].getInt()/1000.0f;
trans(1,2) = arr[6].getInt()/1000.0f;
trans(1,3) = arr[7].getInt()/1000.0f;
trans(2,0) = arr[8].getInt()/1000.0f;
trans(2,1) = arr[9].getInt()/1000.0f;
trans(2,2) = arr[10].getInt()/1000.0f;
trans(2,3) = arr[11].getInt()/1000.0f;
attach->SetAttachTransformation(trans);
return true;
}
//=========================== C4Script Function Map ===================================
// defined function class
@ -6286,8 +6365,10 @@ void InitFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "GetAnimationWeight", FnGetAnimationWeight);
AddFunc(pEngine, "SetAnimationPosition", FnSetAnimationPosition);
AddFunc(pEngine, "SetAnimationWeight", FnSetAnimationWeight);
AddFunc(pEngine, "AttachMesh", FnAttachMesh);
//AddFunc(pEngine, "AttachMesh", FnAttachMesh); defined in C4ScriptFnMap
AddFunc(pEngine, "DetachMesh", FnDetachMesh);
AddFunc(pEngine, "SetAttachBones", FnSetAttachBones);
AddFunc(pEngine, "SetAttachTransform", FnSetAttachTransform);
AddFunc(pEngine, "goto", Fn_goto);
AddFunc(pEngine, "this", Fn_this);
@ -6721,6 +6802,8 @@ C4ScriptFnDef C4ScriptFnMap[]={
{ "EffectCall", 1 ,C4V_Any ,{ C4V_C4Object,C4V_Int ,C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnEffectCall_C4V, 0 },
{ "EffectVar", 1 ,C4V_pC4Value ,{ C4V_Int ,C4V_C4Object,C4V_Int ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnEffectVar_C4V, 0 },
{ "AttachMesh", 1 ,C4V_Int ,{ C4V_Any ,C4V_String ,C4V_String ,C4V_Array ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,0 , FnAttachMesh },
{ "eval", 1 ,C4V_Any ,{ C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnEval, 0 },
{ "VarN", 1 ,C4V_Any ,{ C4V_String ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any ,C4V_Any} ,MkFnC4V FnVarN, 0 },

View File

@ -902,23 +902,26 @@ namespace
// Render attached meshes
for(StdMeshInstance::AttachedMeshIter iter = instance.AttachedMeshesBegin(); iter != instance.AttachedMeshesEnd(); ++iter)
{
const StdMeshInstance::AttachedMesh* attach = *iter;
const StdMeshMatrix& FinalTrans = attach->GetFinalTransformation();
// Convert matrix to column-major order, add fourth row
const float attach_trans_gl[16] = {
iter->FinalTrans(0,0), iter->FinalTrans(1,0), iter->FinalTrans(2,0), 0,
iter->FinalTrans(0,1), iter->FinalTrans(1,1), iter->FinalTrans(2,1), 0,
iter->FinalTrans(0,2), iter->FinalTrans(1,2), iter->FinalTrans(2,2), 0,
iter->FinalTrans(0,3), iter->FinalTrans(1,3), iter->FinalTrans(2,3), 1
FinalTrans(0,0), FinalTrans(1,0), FinalTrans(2,0), 0,
FinalTrans(0,1), FinalTrans(1,1), FinalTrans(2,1), 0,
FinalTrans(0,2), FinalTrans(1,2), FinalTrans(2,2), 0,
FinalTrans(0,3), FinalTrans(1,3), FinalTrans(2,3), 1
};
// TODO: Take attach transform's parity into account
glPushMatrix();
glMultMatrixf(attach_trans_gl);
RenderMeshImpl(*iter->Child, dwModClr, dwPlayerColor, parity);
RenderMeshImpl(*attach->Child, dwModClr, dwPlayerColor, parity);
glPopMatrix();
#if 0
const StdMeshMatrix& own_trans = instance.GetBoneTransform(iter->ParentBone)
* StdMeshMatrix::Transform(instance.Mesh.GetBone(iter->ParentBone).Transformation);
const StdMeshMatrix& own_trans = instance.GetBoneTransform(attach->ParentBone)
* StdMeshMatrix::Transform(instance.Mesh.GetBone(attach->ParentBone).Transformation);
// Draw attached bone
glDisable(GL_DEPTH_TEST);

View File

@ -1145,6 +1145,16 @@ void StdMesh::AddMasterBone(StdMeshBone* bone)
AddMasterBone(bone->Children[i]);
}
const StdMeshBone* StdMesh::GetBoneByName(const StdStrBuf& name) const
{
// Lookup parent bone
for(unsigned int i = 0; i < Bones.size(); ++i)
if(Bones[i]->Name == name)
return Bones[i];
return NULL;
}
const StdMeshAnimation* StdMesh::GetAnimationByName(const StdStrBuf& name) const
{
StdCopyStrBuf name2(name);
@ -1210,6 +1220,46 @@ bool StdMeshInstance::AnimationNode::GetBoneTransform(unsigned int bone, StdMesh
}
}
StdMeshInstance::AttachedMesh::AttachedMesh(unsigned int number, StdMeshInstance* parent, StdMeshInstance* child, bool own_child,
unsigned int parent_bone, unsigned int child_bone, const StdMeshMatrix& transform):
Number(number), Parent(parent), Child(child), OwnChild(own_child),
ParentBone(parent_bone), ChildBone(child_bone), AttachTrans(transform),
FinalTransformDirty(true)
{
}
StdMeshInstance::AttachedMesh::~AttachedMesh()
{
if(OwnChild)
delete Child;
}
bool StdMeshInstance::AttachedMesh::SetParentBone(const StdStrBuf& bone)
{
const StdMeshBone* bone_obj = Parent->Mesh.GetBoneByName(bone);
if(!bone_obj) return false;
ParentBone = bone_obj->Index;
FinalTransformDirty = true;
return true;
}
bool StdMeshInstance::AttachedMesh::SetChildBone(const StdStrBuf& bone)
{
const StdMeshBone* bone_obj = Child->Mesh.GetBoneByName(bone);
if(!bone_obj) return false;
ChildBone = bone_obj->Index;
FinalTransformDirty = true;
return true;
}
void StdMeshInstance::AttachedMesh::SetAttachTransformation(const StdMeshMatrix& transformation)
{
AttachTrans = transformation;
FinalTransformDirty = true;
}
StdMeshInstance::StdMeshInstance(const StdMesh& mesh):
Mesh(mesh), CurrentFaceOrdering(FO_Fixed),
BoneTransforms(Mesh.GetNumBones(), StdMeshMatrix::Identity()),
@ -1231,8 +1281,14 @@ StdMeshInstance::StdMeshInstance(const StdMesh& mesh):
StdMeshInstance::~StdMeshInstance()
{
for(AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
delete iter->Child;
// If we are attached then detach from parent
if(AttachParent)
AttachParent->Parent->DetachMesh(AttachParent->Number);
// Remove all attach children
while(!AttachChildren.empty())
DetachMesh(AttachChildren.back()->Number);
while(!AnimationStack.empty())
StopAnimation(AnimationStack.front());
assert(AnimationNodes.empty());
@ -1256,9 +1312,11 @@ void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering)
BoneTransformsDirty = true;
// Update attachments
// Update attachments (only own meshes for now... others might be displayed both attached and non-attached...)
// still not optimal.
for(AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
iter->Child->SetFaceOrdering(ordering);
if((*iter)->OwnChild)
(*iter)->Child->SetFaceOrdering(ordering);
}
}
@ -1287,6 +1345,9 @@ StdMeshInstance::AnimationNode* StdMeshInstance::PlayAnimation(const StdMeshAnim
break;*/
Number2 = Number1 + 1;
position->Value = BoundBy(position->Value, 0.0f, animation.Length);
weight->Value = BoundBy(weight->Value, 0.0f, 1.0f);
if(Number1 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) NULL);
if(sibling && Number2 == AnimationNodes.size()) AnimationNodes.push_back( (StdMeshInstance::AnimationNode*) NULL);
@ -1431,50 +1492,59 @@ void StdMeshInstance::ExecuteAnimation()
// Update animation for attached meshes
for(AttachedMeshList::iterator iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
iter->Child->ExecuteAnimation();
(*iter)->Child->ExecuteAnimation();
}
const StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMesh(const StdMesh& mesh, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation)
StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMesh(const StdMesh& mesh, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation)
{
AttachedMesh attach = { 0 };
unsigned int i;
StdMeshInstance* instance = new StdMeshInstance(mesh);
instance->SetFaceOrdering(CurrentFaceOrdering);
AttachedMesh* attach = AttachMesh(*instance, parent_bone, child_bone, transformation, true);
if(!attach) { delete instance; return NULL; }
return attach;
}
StdMeshInstance::AttachedMesh* StdMeshInstance::AttachMesh(StdMeshInstance& instance, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation, bool own_child)
{
// We don't allow an instance to be attached to multiple parent instances for now
if(instance.AttachParent) return NULL;
// Make sure there are no cyclic attachments
for(StdMeshInstance* Parent = this; Parent->AttachParent != NULL; Parent = Parent->AttachParent->Parent)
if(Parent == &instance)
return NULL;
AttachedMesh* attach = NULL;
unsigned int number = 1;
// Find free index.
attach.Number = 1;
for(AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
if(iter->Number >= attach.Number)
attach.Number = iter->Number + 1;
if((*iter)->Number >= number)
number = (*iter)->Number + 1;
// Lookup parent bone
for(i = 0; i < Mesh.GetNumBones(); ++i)
if(Mesh.GetBone(i).Name == parent_bone)
{ attach.ParentBone = i; break; }
if(i == Mesh.GetNumBones()) return NULL;
// Lookup child bone
for(i = 0; i < mesh.GetNumBones(); ++i)
if(mesh.GetBone(i).Name == child_bone)
{ attach.ChildBone = i; break; }
if(i == mesh.GetNumBones()) return NULL;
attach.AttachTrans = transformation;
attach.Parent = this;
attach.Child = new StdMeshInstance(mesh);
attach.Child->SetFaceOrdering(CurrentFaceOrdering);
const StdMeshBone* parent_bone_obj = Mesh.GetBoneByName(parent_bone);
const StdMeshBone* child_bone_obj = instance.Mesh.GetBoneByName(child_bone);
if(!parent_bone_obj || !child_bone_obj) return NULL;
// TODO: Face Ordering is not lined up... can't do that properly here
attach = new AttachedMesh(number, this, &instance, own_child, parent_bone_obj->Index, child_bone_obj->Index, transformation);
instance.AttachParent = attach;
AttachChildren.push_back(attach);
attach.Child->AttachParent = &AttachChildren.back();
BoneTransformsDirty = true; // so that FinalTrans is computed before rendering
return &AttachChildren.back();
return attach;
}
bool StdMeshInstance::DetachMesh(unsigned int number)
{
for(AttachedMeshList::iterator iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
{
if(iter->Number == number)
if((*iter)->Number == number)
{
delete iter->Child;
// Reset attach parent of child so it does not try
// to detach itself on destruction.
(*iter)->Child->AttachParent = NULL;
delete *iter;
AttachChildren.erase(iter);
return true;
}
@ -1483,114 +1553,124 @@ bool StdMeshInstance::DetachMesh(unsigned int number)
return false;
}
const StdMeshInstance::AttachedMesh* StdMeshInstance::GetAttachedMeshByNumber(unsigned int number) const
StdMeshInstance::AttachedMesh* StdMeshInstance::GetAttachedMeshByNumber(unsigned int number) const
{
for(AttachedMeshIter iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
if(iter->Number == number)
return &*iter;
if((*iter)->Number == number)
return *iter;
return NULL;
}
void StdMeshInstance::UpdateBoneTransforms()
{
// Nothing changed since last time
if(!BoneTransformsDirty) return;
// Compute transformation matrix for each bone.
for(unsigned int i = 0; i < BoneTransforms.size(); ++i)
if(BoneTransformsDirty)
{
StdMeshTransformation Transformation;
const StdMeshBone& bone = Mesh.GetBone(i);
const StdMeshBone* parent = bone.GetParent();
assert(!parent || parent->Index < i);
bool have_transform = false;
for(unsigned int j = 0; j < AnimationStack.size(); ++j)
// Compute transformation matrix for each bone.
for(unsigned int i = 0; i < BoneTransforms.size(); ++i)
{
if(have_transform)
StdMeshTransformation Transformation;
const StdMeshBone& bone = Mesh.GetBone(i);
const StdMeshBone* parent = bone.GetParent();
assert(!parent || parent->Index < i);
bool have_transform = false;
for(unsigned int j = 0; j < AnimationStack.size(); ++j)
{
StdMeshTransformation other;
if(AnimationStack[j]->GetBoneTransform(i, other))
Transformation = StdMeshTransformation::Nlerp(Transformation, other, 1.0f); // TODO: Allow custom weighing for slot combination
}
else
{
have_transform = AnimationStack[j]->GetBoneTransform(i, Transformation);
}
}
if(!have_transform)
{
if(parent)
BoneTransforms[i] = BoneTransforms[parent->Index];
else
BoneTransforms[i] = StdMeshMatrix::Identity();
}
else
{
BoneTransforms[i] = StdMeshMatrix::Transform(bone.Transformation * Transformation * bone.InverseTransformation);
if(parent) BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
}
}
// Compute transformation for each vertex. We could later think about
// doing this on the GPU using a vertex shader. This would then probably
// need to go to CStdGL::PerformMesh and CStdD3D::PerformMesh.
// (can only work for fixed face ordering though)
for(unsigned int i = 0; i < Vertices.size(); ++i)
{
const StdSubMesh& submesh = Mesh.GetSubMesh(i);
std::vector<StdMeshVertex>& instance_vertices = Vertices[i];
assert(submesh.GetNumVertices() == instance_vertices.size());
for(unsigned int j = 0; j < instance_vertices.size(); ++j)
{
const StdSubMesh::Vertex& vertex = submesh.GetVertex(j);
StdMeshVertex& instance_vertex = instance_vertices[j];
if(!vertex.BoneAssignments.empty())
{
instance_vertex.x = instance_vertex.y = instance_vertex.z = 0.0f;
instance_vertex.nx = instance_vertex.ny = instance_vertex.nz = 0.0f;
instance_vertex.u = vertex.u; instance_vertex.v = vertex.v;
for(unsigned int k = 0; k < vertex.BoneAssignments.size(); ++k)
if(have_transform)
{
const StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments[k];
instance_vertex += assignment.Weight * (BoneTransforms[assignment.BoneIndex] * vertex);
StdMeshTransformation other;
if(AnimationStack[j]->GetBoneTransform(i, other))
Transformation = StdMeshTransformation::Nlerp(Transformation, other, 1.0f); // TODO: Allow custom weighing for slot combination
}
else
{
have_transform = AnimationStack[j]->GetBoneTransform(i, Transformation);
}
}
if(!have_transform)
{
if(parent)
BoneTransforms[i] = BoneTransforms[parent->Index];
else
BoneTransforms[i] = StdMeshMatrix::Identity();
}
else
{
instance_vertex = vertex;
BoneTransforms[i] = StdMeshMatrix::Transform(bone.Transformation * Transformation * bone.InverseTransformation);
if(parent) BoneTransforms[i] = BoneTransforms[parent->Index] * BoneTransforms[i];
}
}
// Compute transformation for each vertex. We could later think about
// doing this on the GPU using a vertex shader. This would then probably
// need to go to CStdGL::PerformMesh and CStdD3D::PerformMesh.
// (can only work for fixed face ordering though)
for(unsigned int i = 0; i < Vertices.size(); ++i)
{
const StdSubMesh& submesh = Mesh.GetSubMesh(i);
std::vector<StdMeshVertex>& instance_vertices = Vertices[i];
assert(submesh.GetNumVertices() == instance_vertices.size());
for(unsigned int j = 0; j < instance_vertices.size(); ++j)
{
const StdSubMesh::Vertex& vertex = submesh.GetVertex(j);
StdMeshVertex& instance_vertex = instance_vertices[j];
if(!vertex.BoneAssignments.empty())
{
instance_vertex.x = instance_vertex.y = instance_vertex.z = 0.0f;
instance_vertex.nx = instance_vertex.ny = instance_vertex.nz = 0.0f;
instance_vertex.u = vertex.u; instance_vertex.v = vertex.v;
for(unsigned int k = 0; k < vertex.BoneAssignments.size(); ++k)
{
const StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments[k];
instance_vertex += assignment.Weight * (BoneTransforms[assignment.BoneIndex] * vertex);
}
}
else
{
instance_vertex = vertex;
}
}
}
if(CurrentFaceOrdering != FO_Fixed)
ReorderFaces();
}
// Update attachment's attach transformations. Note this is done recursively.
for(AttachedMeshList::iterator iter = AttachChildren.begin(); iter != AttachChildren.end(); ++iter)
{
iter->Child->UpdateBoneTransforms();
AttachedMesh* attach = *iter;
const bool ChildBoneTransformsDirty = attach->Child->BoneTransformsDirty;
attach->Child->UpdateBoneTransforms();
// Compute matrix to change the coordinate system to the one of the attached bone:
// The idea is that a vertex at the child bone's position transforms to the parent bone's position.
// Therefore (read from right to left) we first apply the inverse of the child bone transformation,
// then an optional scaling matrix, and finally the parent bone transformation
if(BoneTransformsDirty || ChildBoneTransformsDirty || attach->FinalTransformDirty)
{
// Compute matrix to change the coordinate system to the one of the attached bone:
// The idea is that a vertex at the child bone's position transforms to the parent bone's position.
// Therefore (read from right to left) we first apply the inverse of the child bone transformation,
// then an optional scaling matrix, and finally the parent bone transformation
// TODO: we can cache the three matrices in the middle since they don't change over time,
// reducing this to two matrix multiplications instead of four each frame.
// Might even be worth to compute the complete transformation directly when rendering then
// (saves per-instance memory, but requires recomputation if the animation does not change).
iter->FinalTrans = BoneTransforms[iter->ParentBone]
* StdMeshMatrix::Transform(Mesh.GetBone(iter->ParentBone).Transformation)
* iter->AttachTrans
* StdMeshMatrix::Transform(iter->Child->Mesh.GetBone(iter->ChildBone).InverseTransformation)
* StdMeshMatrix::Inverse(iter->Child->BoneTransforms[iter->ChildBone]);
// TODO: we can cache the three matrices in the middle since they don't change over time,
// reducing this to two matrix multiplications instead of four each frame.
// Might even be worth to compute the complete transformation directly when rendering then
// (saves per-instance memory, but requires recomputation if the animation does not change).
// TODO: We might also be able to cache child inverse, and only recomupte it if
// child bone transforms are dirty (saves matrix inversion for unanimated attach children).
attach->FinalTrans = BoneTransforms[attach->ParentBone]
* StdMeshMatrix::Transform(Mesh.GetBone(attach->ParentBone).Transformation)
* attach->AttachTrans
* StdMeshMatrix::Transform(attach->Child->Mesh.GetBone(attach->ChildBone).InverseTransformation)
* StdMeshMatrix::Inverse(attach->Child->BoneTransforms[attach->ChildBone]);
attach->FinalTransformDirty = false;
}
}
if(CurrentFaceOrdering != FO_Fixed)
ReorderFaces();
BoneTransformsDirty = false;
}

View File

@ -288,6 +288,7 @@ public:
const StdMeshBone& GetBone(unsigned int i) const { return *Bones[i]; }
unsigned int GetNumBones() const { return Bones.size(); }
const StdMeshBone* GetBoneByName(const StdStrBuf& name) const;
const StdMeshAnimation* GetAnimationByName(const StdStrBuf& name) const;
@ -402,32 +403,50 @@ public:
// Update animations' value providers; call once a frame
void ExecuteAnimation();
struct AttachedMesh
class AttachedMesh
{
unsigned int Number;
StdMeshInstance* Parent;
StdMeshInstance* Child;
StdMeshMatrix AttachTrans;
friend class StdMeshInstance;
public:
AttachedMesh(unsigned int number, StdMeshInstance* parent, StdMeshInstance* child, bool own_child,
unsigned int parent_bone, unsigned int child_bone, const StdMeshMatrix& transform);
~AttachedMesh();
const unsigned int Number;
StdMeshInstance* const Parent;
StdMeshInstance* const Child;
const bool OwnChild; // Whether to delete child on destruction
bool SetParentBone(const StdStrBuf& bone);
bool SetChildBone(const StdStrBuf& bone);
void SetAttachTransformation(const StdMeshMatrix& transformation);
const StdMeshMatrix& GetFinalTransformation() const { return FinalTrans; }
private:
unsigned int ParentBone;
unsigned int ChildBone;
// Cache final transformation, updated in UpdateBoneTransforms()
StdMeshMatrix AttachTrans;
// Cache final attach transformation, updated in UpdateBoneTransform
StdMeshMatrix FinalTrans;
bool FinalTransformDirty; // Whether FinalTrans is up to date or not
};
typedef std::list<AttachedMesh> AttachedMeshList;
typedef std::vector<AttachedMesh*> AttachedMeshList;
typedef AttachedMeshList::const_iterator AttachedMeshIter;
// Returns number of added attachment, or 0 on failure
const AttachedMesh* AttachMesh(const StdMesh& mesh, const StdStrBuf& own_bone, const StdStrBuf& other_bone, const StdMeshMatrix& transformation = StdMeshMatrix::Identity());
// Create a new instance and attach it to this mesh.
AttachedMesh* AttachMesh(const StdMesh& mesh, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation = StdMeshMatrix::Identity());
// Attach an instance to this instance. If own_child is true then take ownership of instance, deleting it when the mesh is detached.
AttachedMesh* AttachMesh(StdMeshInstance& instance, const StdStrBuf& parent_bone, const StdStrBuf& child_bone, const StdMeshMatrix& transformation = StdMeshMatrix::Identity(), bool own_child = false);
// Removes attachment with given number
bool DetachMesh(unsigned int number);
// Returns attached mesh with given number
const AttachedMesh* GetAttachedMeshByNumber(unsigned int number) const;
AttachedMesh* GetAttachedMeshByNumber(unsigned int number) const;
// To iterate through attachments
AttachedMeshIter AttachedMeshesBegin() const { return AttachChildren.begin(); }
AttachedMeshIter AttachedMeshesEnd() const { return AttachChildren.end(); }
const AttachedMesh* GetAttachParent() const { return AttachParent; }
AttachedMesh* GetAttachParent() const { return AttachParent; }
// Get vertex of instance, with current animation applied. This needs to
// go elsewhere if/when we want to calculate this on the hardware.
@ -444,10 +463,9 @@ public:
const StdMeshMatrix& GetBoneTransform(unsigned int i) const { return BoneTransforms[i]; }
// Update bone transformation matrices, and vertex positions. Call this
// before rendering. This is called recursively for attached children. Do not
// call this on attached children only, since it will not update the
// attachment transform otherwise.
// Update bone transformation matrices, vertex positions and final attach transformations of attached children.
// This is called recursively for attached children, so there is no need to call it on attached children only
// which would also not update its attach transformation. Call this once before rendering.
void UpdateBoneTransforms();
const StdMesh& Mesh;
@ -475,8 +493,7 @@ protected:
// Not asymptotically efficient, but we do not expect many attached meshes anyway.
// In theory map would fit better, but it's probably not worth the extra overhead.
// Don't use vector though so that pointers to AttachedMesh (AttachParent) stay valid.
std::list<AttachedMesh> AttachChildren;
std::vector<AttachedMesh*> AttachChildren;
AttachedMesh* AttachParent;
bool BoneTransformsDirty;