forked from Mirrors/openclonk
Allow attaching an object's mesh instance, add SetAttach{Bones,Transform}
parent
fde2143f8e
commit
bb28160406
|
@ -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)
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue