asset handling library / engine?

Started by
13 comments, last by RobM 11 years, 12 months ago

thanks a massive amount,
I've fixed 4. at which point the vertices started moving, I fixed 3. and 2. silly mistakes from when I was panicking on why it wasn't working.
5. I'm not sure what you mean, I assume you mean ALL inverse bind matrices need to be passed to the shader (animated once being multiplied by the transform), either that or you mean I need to generate key frames for every single bone animated or not.


Say you have shoulder, elbow and wrist joint in your skeleton. Shoulder joint is animated. At the moment, your code is sending shoulder joint to vertex shader but not the elbow and wrist joints (because only shoulder joint has animation keyframes), so vertices that are weighted to elbow and wrist joints are going to be broken. I think the issue was somewhere around your CreateAnimationClass logic. Using your anon.dae model, only 31 "NodeClass"es are being passed into CreateAnimationClass, despite the fact that anon.dae model has ~40 joints in its joint hierarchy.


1. I think I've got... I'm not sure though because the whole thing is still a mess of vertices, this may be 5.s problem though

unsigned short _currentIndex = _nodeIndices[_i].first;
_tempMatrix = _currentBone->mOffsetMatrix.Inverse();
_inverseBindTransforms[_currentIndex] = D3DXMATRIX(_tempMatrix.a1, _tempMatrix.a2, _tempMatrix.a3, _tempMatrix.a4,
_tempMatrix.b1, _tempMatrix.b2, _tempMatrix.b3, _tempMatrix.b4,
_tempMatrix.c1, _tempMatrix.c2, _tempMatrix.c3, _tempMatrix.c4,
_tempMatrix.d1, _tempMatrix.d2, _tempMatrix.d3, _tempMatrix.d4);

Does this seem about right? this is in the for loop for bones, the transforms are sent to the animation class where this happnes:

D3DXMATRIX* ::AnimationClass::GetTransforms(double p_time)
{
D3DXMATRIX* _result = new D3DXMATRIX[64];
for (unsigned short i = 0; i < 64; i++)
{
if (i > m_owner->GetBoneCount())
{ break; }
else
{ _result = m_inverseBindTransforms; }
}
for (map<unsigned short, NodeClass>::iterator i = m_nodes->begin(); i != m_nodes->end(); i++)
{
(*i).second.CalculateTransform(p_time);
_result[(*i).first] = m_inverseBindTransforms[(*i).first] * (*i).second.GetCompleteTransform();
}
return _result;
}



Looks about right.
Advertisement
this is just hopeless in my case I am ONCE AGAIN STUCK!
I'm getting very very very very sick of this, all I want to do is write a freaking game engine (I know easier said than done, but I'd think by now I'd at least have it drawing the damn models)
and again I come back to needing to say I shouldn't be this annoyed at it, just the amount of time I've spent getting stuff wrong is severely outweighing the time I've got things right.

Here is the best I can get it at the moment.
scrnw.png

The Shader Code

float4x4 World;
float4x4 View;
float4x4 Proj;
float4x3 Bones[64];
struct VSI
{
float4 Position : POSITION;
float3 Normal : NORMAL;
float3 Weights : BLENDWEIGHT;
float4 Indices : BLENDINDICES;
float2 UV : TEXCOORD;
};
struct VSO
{
float4 Position : SV_POSITION;
float3 Normal : NORMAL;
};
void Skin(inout VSI input)
{
float4x3 skinning = 0;

skinning += Bones[input.Indices.x] * input.Weights.x;
skinning += Bones[input.Indices.y] * input.Weights.y;
skinning += Bones[input.Indices.z] * input.Weights.z;
skinning += Bones[input.Indices.w] * (1 - input.Weights.x - input.Weights.y - input.Weights.z);
input.Position.xyz = mul(input.Position, skinning);
input.Normal.xyz = mul(input.Normal, skinning);
}
VSO VS(VSI input)
{
VSO output;
Skin(input);
float4 Position = input.Position;
Position = mul(Position, World);
Position = mul(Position, View);
output.Position = mul(Position, Proj);
output.Normal = input.Normal;
return output;
}
float4 PS(VSO input) : SV_TARGET
{
return float4(input.Normal, 1);
}
technique Default
{
pass p0
{
VertexShader = compile vs_3_0 VS();
PixelShader = compile ps_3_0 PS();
}
}


Get Transforms Code in the animation class, the return value is sent directly to the shader:

D3DXMATRIX* ::AnimationClass::GetTransforms(double p_time)
{
D3DXMATRIX* _result = new D3DXMATRIX[64]; //64 the size of the matrix array for the bones in the shader
for (unsigned short i = 0; i < 64; i++)
{
if (i > m_owner->GetBoneCount())
{ break; }
else
{ _result = m_inverseBindTransforms; }
}
for (map<unsigned short, NodeClass>::iterator i = m_nodes->begin(); i != m_nodes->end(); i++) // calculate transforms before hand so parents and children are all up to date
{
(*i).second.CalculateTransform(p_time);
}
for (map<unsigned short, NodeClass>::iterator i = m_nodes->begin(); i != m_nodes->end(); i++) // the unsigned short is the bone ID mapped to the node
{
_result[(*i).first] = m_inverseBindTransforms[(*i).first] * (*i).second.GetCompleteTransform();
}
return _result;
}

Get Complete Transforms Code

D3DXMATRIX P2P::graphics::NodeClass::GetCompleteTransform()
{
if (m_owner->NodeExists(m_parent) && m_index != m_parent) //m_index only == m_parent when the node is the root node
{ return (m_transform * m_owner->GetNode(m_parent)->GetCompleteTransform()); }
return m_transform;
}


I'm just going in circles,
STUCK!
GET HELP!
HELPS A LOT!
STUCK!
GET HELP!
etc.
I'm really sick of this, there's little to no point in me asking for help if the moment I apply that I get another problem that I can not solve, its essentially getting someone else to do all my work which I have to be honest is making me feel guilty, last thing I'd want to do is waste someone elses time -.-''
[color=#282828][font=helvetica, arial, verdana, tahoma, sans-serif]

[background=rgb(250, 251, 252)]"write a freaking game engine" - Not gonna happen. Best you can hope for is a tech demo of some sorts. [/background]

[/font]

If you're starting out, don't program in C++. Do it in C#/SlimDX.

Make sure you understand the concepts before you start coding. Go into Wolfram Alpha/Mathematica and see how the formulas are related.

Check your code logic using simple cases. For the animation, you want to check a (1 bone / 1 triangle) scene and a (2 bone hierarchy / 2 triangles) scene. Make sure it matches what you see in Maya/Modo/...

In your animation thing, transform the verts on the CPU side and make sure it works before attempting GPU skinning.
thats the problem, I'm not starting out, I've been teaching myself graphics programming (writing engines with XNA mostly, which in fairness is probably why importing models and writing model and animation structures is posing such a problem)
I've been doing this for about 4 years (2 and a half of which I was in school, I've been job hunting since I'm not in a very good place for jobs right now) most spare time goes into learning more quirks of game development.
I've gone out of my way to learn C#, Java, C++ many rendering techniques (forward, slim deferred, fat deferred, voxel, even writing my own rasterizer at one point) how to handle game logic, writing physics engines, etc. etc. I've basicly touched on everything but concentrated more on graphics programming.
I figured my job search being a bit of a dud I might go into collage this year, during which I'd try to get something game development wise off the ground, it seems putting what I have learned into my own engine isn't doing me much good about now as I'm still stuck on the base of animation processing.

my life story aside,
my understanding of the concept of skinned animation so far is,
vertices in a mesh contain a bone index and weight tying it completely or partially (depending on the weight) to a set of bones
the transforms for these bones are represented by key frames holding position and or rotation and or scaling.
the matrix calculated by the interpolated key frame values is them multiplied by its parent bones transforms and then by the inverse bind matrix (representing the default position of the bone, Assimp has the offset transforms relative to the root bone so multiplying by the parents is impractical.)
once all bones transform matrices have been computed they are sent to the GPU in an array arranged via the bones index (the same as which was used to map bones to vertices)
The GPU per vertex merges the transforms based on the vertices weights and applies the transforms to the position before applying the view and projection matrices.

if I've got anything wrong please let me know, the intention of posting here was to learn my mistakes,
if this topic is any testament I'm terrible at spotting my mistakes alone.

this may just be a case of me looking in the wrong places, but I've also had a problem of finding documentation,
most links for Assimp are for loading static meshes, and most links for animations take me to making animations rather than playing them :/
So here is what you're trying to do put in simple terms - this definitely works because I've just done it (exporting a COLLADA dae file from 3ds max).

To get skinning working, you will need:

1) a model in some 3d package
2) a skeleton attached to that model with per-vertex bone indices and weights. Most modelling programs will give/support this
3) an export of that model along with its skeleton and any animations (in human readable form is best obviously)
4) some way of importing that exported file into your engine

So in the exported file, you will need the find following things (I'm using COLLADA as an example but I'm sure your format is similar):

Overall bind shape matrix - this is a matrix which is applied to the model to take it from world space to the space in which you bound the skeleton to the mesh. I don't know where this is in your format but it should be fairly easy to find because there will be just one of them. This matrix is key. A lot of examples will tell you to combine this matrix with your skinning but in order to get things working on my engine and to wrap my head around it, I simply apply this matrix to all my model's vertices when the model is loaded and forget about it - this step, however you do it, is key.

A skeleton structure - the skeleton will have a root bone - maybe signified by it being at the root of the XML or having some other property relevant to your format (parent=null, etc). Each bone in the skeleton (including the root) will always have exactly one inverse bind matrix. In COLLADA, this isn't actually in the skeleton structure which is slightly confusing but it will be there somewhere. Each bone will also have a local bind pose matrix. This, in COLLADA, is in the skeleton structure.

This is, in fact, all you need to get skinning working - I haven't covered animation yet but we'll get to that.

So import your skeleton into a structure that has a root bone and in the bone class, allow any number of child bones. This can be very simple.

In your bone class, add two matrices, one is used for computing the local bone transforms and one is used for skinning. This can be combined at a later stage, but to understand skinning more easily I think it's better to separate them. So call one 'combinedLocalTransforms' an the other 'skinningMatrix'.

So each frame, you need to iterate through your skeleton hierarchy recursively starting at the root doing this:

this->combinedLocalTransforms = parentBone->combinedLocalTransforms * this->localBindPoseMatrix

and..

this->skinningMatrix = this->combinedLocalTransforms * this->inverseBindMatrix

Once the skeleton is recursed fully, you can go through it again adding each bone's skinningMatrix to a matrix array which is what you'll send to the shader.

There are things you need to be careful of though:

1) make sure the root bone is correct, ie it's the only bone without a parent
2) when you send the matrices to the shader, ensure the order matches up with your bone indices in each vertex.
3) ensure your row/col mode is correct according to which graphics library you're using (dx/OpenGL, etc)
4) once you've written everything, replacing the local bind pose matrix of each bone at runtime with the identity matrix should draw your model in its bind pose
5) in your vertex shader, try putting an if statement in when you compute the new vertex positions to only draw one bone affected by the skinning matrices... E.g.

If boneIndex=0
Do skinning
Else
Don't

That might help to see where things are going wrong on a per-bone basis.

Have a check through this and when you've got your bind pose being displayed correctly with skinning, we can show you how to apply the animations.

Let us know how you get on...

This topic is closed to new replies.

Advertisement