Sunday, July 31, 2016

Confusion Of Assimp Sdk, Animation System Design Need Advice

Confusion Of Assimp Sdk, Animation System Design Need Advice

Hellow !

I 'm going to make an animation system for game......

I will export models to custom format model using the assimp sdk.  I will only export joint nodes and mesh nodes, and animation data.  

The  output scene file format will look like these:

<ModelNode>

  <JointNode>  lcltransform  .....

     <meshNode>  lcltransform....  <\meshNode>

  <\JointNode>

  ......many joints to omit.

<\ModelNode>

 

The JointNode holds inverse bind pose matrix .

As for the MeshNode, I know , I know, the static mesh is not completely static, the "static" means it can not be deformed,

it can also have motions, so mesh will be implemented as MeshNode. It will inherit transform from  ancestors, so it will move!!  

As for the ModelNode, why it inherit from SceneNode is that  it act as the root node of all joint nodes and model nodes,   somewhat look like  assimp root node.

All these node will hold local transform.

 

 

The question is, assimp scene nodes are not just joint nodes and mesh nodes, and "pure node", which is used to pass transform, the root is the most apparent one.

I 'm confusing that  whether it will go wrong, if I don't export these nodes, because I'm afraid that some "pure nodes" would have a non-identity transform !!

 

In the animation export part , I will use these code:

std::map<std::string, int> name2Joint;

struct animation 

{
  void export() 
   {
     export(duration, tickPersecond);
     for(node in influenced nodes) 
     {
        jointID = name2Joint[node.name];
        export(jointID, frame.pos, frame.rot, frame.scale);
     }
    }  
};
 
 
My animation system is the one of the most naivest animation system in the world , it can only import, and display .
I have not begin to code, and I don't know if it will work, so
I will show my code,  and if someone find any mistake or have a better implementation, please tell. 
 
 
 
Here's are my data structure regarding animation in the engine side:
struct Frame 
{
   _pos, _rot, _scale; // all are  reference to parent
};
 
struct JointFrame 
{
  _jointID;                                        // will be used to find joint node at update time
  vector<Frame> _frames;
};
 
struct Animation 
{
  vector<JointFrame> _jointFrames;
  void update();
};
 
 
And here's the data structure of Model in engine side:
 
struct SceneNode 
  SceneNode  *_parent;
    pos, rot, scale;             // all are  reference to parent
  _globalTransform;
  virtual  void update();
  virtual   void postUpdate()=0;
};
 
struct Joint : public SceneNode 
{
  Matrix4f _invBindMat;  // inverse bind matrix
};
 
 
struct Model  : public SceneNode  
//why it inherit from SceneNode is that 
// it act as the root node of all joint nodes and model nodes,   somewhat look like  assimp root node
{
   vector<Mesh> _meshes;  
   vector<Joint*> _joints;
   vector<Matrix4f> _JointFinalMats
   void PostUpdate();       // use to update _JointFinalMats after all Joints' GlobalTransform have updated, will be called by Scene
};
 
struct Scene
{
  vector<SceneNode*> _nodes;
  void update();
};
 

 

The flowchart of big big updates :

scene::update()

{

   animation.update();  //  update joint's local transform by keyframes

    foreach(node in _nodes) node.update();    // update joint's global transform

    foreach(node in _nodes) node.postUpdate();  // update jointFinalMat array

     

}

 

void Animation ::update() 
{
   for(jointFrame in   _jointFrames) 

   {

      frame = jointFrame.findFrame(gametime);

      joint =   this->parentModel.findJoint(jointFrame._jointID);

 

      // all are reference to parent

      joint->setPos(frame._pos);

      joint->setRot(frame._rot); 

      joint->setScale(frame._scale);  

   }

}

 

void SceneNode::update() 
{
   _globalTransform = localTransform(_pos, _rot, _scale)  *    _parent->_globalTransform ;

}

 

void Model::postUpdate() 
{

   
    foreach(joint in _joints) 

    {

       _JointFinalMats[joint.ID] =  joint._invBindMat * joint._globalTransform;

   }
}

 

 

After I have attached joints to scene graph,  and Scene::update will call joint::update, where  jointFinalMat = inverseBindposeMat *  jointGlobalTransform

will take place. Since the jointGlobalTransform has contain  ToRootMat,  I will pass a identity matrix to shader as a world matrix.

The code will look like :

if(mesh.hasBone) 
{

    worldMat = Identity;

else 
{
  worldMat =  mesh.globalTransfom

 

the shader code:

cbuffer cbPerObject : register(b0)
{
row_major float4x4 worldMat;
row_major float4x4 worldInvTransposeMat;
row_major float4x4 worldviewprojMat;
};
 
cbuffer cbSkinned : register(b1)
{
row_major float4x4 boneMat[96];
};

 

 
struct SkinnedVertexIn
{
float3 PosL       : POSITION;
float3 NormalL    : NORMAL;
float3 TangentL   : TANGENT;
float2 Tex        : TEXCOORD;
uint4 BoneIndices : BONEINDICES;
float4 Weights    : WEIGHTS;
};

 

 
VertexOut vsmain(SkinnedVertexIn vin)
{
VertexOut vout;
float weights[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
weights[0] = vin.Weights.x;
weights[1] = vin.Weights.y;
weights[2] = vin.Weights.z;
weights[3] = 1.0f - weights[0] - weights[1] - weights[2];
 
float3 posL = float3(0.0f, 0.0f, 0.0f);
float3 normalL = float3(0.0f, 0.0f, 0.0f);
float3 tangentL = float3(0.0f, 0.0f, 0.0f);
for (int i = 0; i < 4; ++i)
{
posL += weights[i] * mul(float4(vin.PosL, 1.0f), boneMat[vin.BoneIndices[i]]).xyz;
normalL += weights[i] * mul(vin.NormalL, (float3x3)boneMat[vin.BoneIndices[i]]);
tangentL += weights[i] * mul(vin.TangentL.xyz, (float3x3)boneMat[vin.BoneIndices[i]]);
}
 
vout.PosW = mul(float4(posL, 1.0f), worldMat).xyz;
vout.NormalW = mul(normalL, (float3x3) worldInvTransposeMat);
vout.TangentW = mul(tangentL, (float3x3)worldMat);
vout.PosH = mul(float4(posL, 1.0f), worldviewprojMat);
vout.Tex = vin.Tex;
return vout;
}

No comments:

Post a Comment