Jump to content

  • Log In with Google      Sign In   
  • Create Account


asset handling library / engine?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
14 replies to this topic

#1 bombshell93   Members   -  Reputation: 198

Like
0Likes
Like

Posted 21 April 2012 - 05:27 AM

Okay, I've been trying to handle the loading of models and animations myself but its just not working and I'm wasting my time on it when I want to get into graphics programming rather than screw around for weeks trying to get animations working!
All I want is something to load a model and then by simply passing a time delta will return an array of transforms (cubic (or similarly) interpolated), then I can pass the transforms to my shaders and render the model.

I've tried doing this with the assistance of Assimp and I've had so many problems its ridiculous. Model loading was simple, loading weights I had a bit of trouble but I THINK I have that done, but I can't be sure until animations load, which I've tried many different ways to no avail, in fact the closest I've got to it transforms the model into a mutilated mess.

I've (and I'm not joking) spent weeks trying to get it working but I just can't and its driven me nuts, all I want to do is handle logic and the rendering process.
I would go back to using XNA, but the power loss in using a managed language takes away too much of the freedom I want while building up the rendering process.

Any and all help would be greatly appreciated,
Thanks in advanced,
Bombshell

Sponsor:

#2 InvalidPointer   Members   -  Reputation: 1366

Like
0Likes
Like

Posted 21 April 2012 - 06:50 AM

transforms the model into a mutilated mess

Are you concatenating transforms up the hierarchy? If not, try that, and if you are, try not doing that.
clb: At the end of 2012, the positions of jupiter, saturn, mercury, and deimos are aligned so as to cause a denormalized flush-to-zero bug when computing earth's gravitational force, slinging it to the sun.

#3 bombshell93   Members   -  Reputation: 198

Like
0Likes
Like

Posted 21 April 2012 - 07:06 AM

tried them both, I've tried hierarchy, non-hierarchy, oddly with same results, so them I started looking at changing the shader, I used several examples and references to put the shader together in different ways to no avail.
I've copied the nodes directly from assimp no modifying, bar the name which was linked to an ID during loading and referenced when specifying bone indices and weights, and then again when processing the animations, so I'm certain their loaded right.
I've tried making matrices with linear and cubic interpolation as well as trying directly out of the key frames with no interpolation, its always the same mangled mess, I can't figure out why, but even best case scenario I'm willing to bet I'd need to tear everything down to the processing of per vertex bone indices and weights, of which I've done on 2 occasions before this.

So long story short, I'm fed up of doing the loading, its just a cluster---k and 4th times a charm just seems irritating to me.
I'd much rather get a library or engine made by people who know how to handle loading of game assets, then I can worry about what I've been trying to get to all along, logic and graphics.

#4 RobMaddison   Members   -  Reputation: 640

Like
0Likes
Like

Posted 23 April 2012 - 04:13 AM

Don't give up - it's hard work and determination like this that make us great programmers over great copy/pasters.

I considered using some 3rd party asset loading code but then I realised I'd really benefit from doing I myself and I'm glad I did - it took a while but it's worth it.

My approach was to use the COLLADA DOM and parse the XML myself, there are two great articles on this that cover animation and skeletons (google 'collada animation' and check out the top few links).

The process is fairly straight forward If you read the docs thoroughly and use your own code. I just wrote a converter that parses the collada document and builds mesh data, skeleton data and animation data, then spurts all three out into 3 separate binary files for quick loading into the engine. Works a treat and I've certainly learnt a heap about animation and skeletons, etc.

Keep going and good luck, Rome wasn't built in a day...

#5 teutonicus   Members   -  Reputation: 518

Like
0Likes
Like

Posted 23 April 2012 - 04:44 AM

If you have the money then I think something like Granny may do what you want (but I've never had a chance to use it myself). Otherwise you're left with existing engines, UDK/Source/etc. This side of things tends to be very game specific and studios that aren't licensing an engine generally roll their own.

If you'd like to post your code then maybe one of us can have a look at it. Getting skeletal animation going the first time can be tricky; there are quite a few things going on from model import to vertex shader and a single little mistake can totally fubar the end result.

A good way to start debugging this yourself is to get the simplest model that you can working. Export a cylinder skinned to three animated joints and get that working. Also add in some sort of way to visualise your skeletons (render lines from parent to all child joints), this may help you figure out where things are going wrong ie. root joint might look good but children get worse down the hierarchy; you're likely doing something wrong when concatenating matrices. Having some way to visualise your skeletons also takes the skinning code in your vertex shader out of the equation, which should help you hone in on the problem.

#6 bombshell93   Members   -  Reputation: 198

Like
0Likes
Like

Posted 23 April 2012 - 02:05 PM

I've tried all I can and this is all I have at the moment. I really, really wish I could do it myself, I figured it'd be important should I want to try some stuff out (not sure what but there's bound to be something come to mind)
But I keep failing, its very very irritating to waste so much time, all I have to say for it is, I've learned a lot of what NOT to do.

for those interested, the P2PLib.h has some naming and syntax conventions I use near the beginning, to help readability.

EDIT: forgot to hit the upload button, hang on a few minutes while I upload it.

EDITEDIT: here it is with the assimp library and linked directories done via macro so you shouldn't need to do much messing around to get it compiling.

Attached Files



#7 teutonicus   Members   -  Reputation: 518

Like
0Likes
Like

Posted 23 April 2012 - 05:20 PM

Your code's looking for assets in:
C:/Users/Bombshell/Documents/Visual Studio 2010/Projects/P2PENGINE/Debug/
and said assets don't exist in the .rar that you uploaded.

#8 bombshell93   Members   -  Reputation: 198

Like
0Likes
Like

Posted 23 April 2012 - 05:38 PM

*facepalm* sorry about that, completely forgot.

Attached Files



#9 teutonicus   Members   -  Reputation: 518

Like
1Likes
Like

Posted 24 April 2012 - 08:34 PM

Got a chance to give your code a quick look today and got skeletal animation working with my own test model. The issues I found:

1. You are concatenating the transforms in your joint hierarchy (ie. generating model space transforms for your joints) correctly. But you are not then transforming them into bind space (the pose your joint hierarchy/skeleton was in when mesh was bound to it). This is where skinning happens. To do this you want to save the inverse bind transform for each of your joints and use this to transform your joints into bind space. Another way to think of it is 'getting the difference between the bind pose and the current pose'. Applying this to the mesh transforms it from the bind pose to your current pose.

Using Assimp you can get the bind transforms for your joints from aiBone::mOffsetMatrix. Invert this transform to use it as below:
D3DXMATRIX* ::AnimationClass::GetTransforms(...)
{
...

D3DXMATRIX inverse_bind_transform = i->second.GetInverseBindTransform();
_result[(*i).first] = inverse_bind_transform * (*i).second.GetCompleteTransform();
..
}

2. Your skinning function is only ever using the first joint index for each weight:
skinning += Bones[input.Indices[0]] * input.Weights[0];
skinning += Bones[input.Indices[0]] * input.Weights[1];
skinning += Bones[input.Indices[0]] * input.Weights[2];
skinning += Bones[input.Indices[0]] * (1 - input.Weights[0] - input.Weights[1] - input.Weights[2]);

should probably be:
skinning += Bones[input.Indices[0]] * input.Weights[0];
skinning += Bones[input.Indices[1]] * input.Weights[1];
skinning += Bones[input.Indices[2]] * input.Weights[2];
skinning += Bones[input.Indices[3]] * (1 - input.Weights[0] - input.Weights[1] - input.Weights[2]);

3. In NodeClass::CalculateTransform, you're calling D3DXMatrixScaling rather than D3DXMatrixTranslation to apply translation (near the end of the function).

4. Your vertex declaration does not match the vertex structure being used. You're using a D3DXCOLOR for your joint indices, but declaring it as a D3DCOLOR in your vertex declaration.
D3DXCOLOR = float4
D3DCOLOR = DWORD

5. Only animated joints are being sent to your vertex shader. If there's an animated joint in your hierarchy that has a parent which isn't animated then it's going to be wrong. Any vertex that is weighted to a joint which is not animated is going to be wrong. I didn't fix this as it would take a bit of time, so I just used a test model with animation on every joint. I've attached it to this post in case you want to use it.

Also I think there's a divide by zero in your animation interpolation code, you'll notice things going a bit wacky when the animation loops around.

Attached Files

  • Attached File  test.zip   22.94KB   33 downloads


#10 bombshell93   Members   -  Reputation: 198

Like
0Likes
Like

Posted 25 April 2012 - 09:01 AM

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.
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[i] = m_inverseBindTransforms[i]; }
}
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;
}


#11 teutonicus   Members   -  Reputation: 518

Like
0Likes
Like

Posted 25 April 2012 - 06:13 PM

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[i] = m_inverseBindTransforms[i]; }
}
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.

#12 bombshell93   Members   -  Reputation: 198

Like
0Likes
Like

Posted 28 April 2012 - 11:29 AM

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.
Posted Image

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[i] = m_inverseBindTransforms[i]; }
}
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 -.-''

#13 jameszhao00   Members   -  Reputation: 267

Like
0Likes
Like

Posted 28 April 2012 - 01:53 PM

"write a freaking game engine" - Not gonna happen. Best you can hope for is a tech demo of some sorts.



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.

#14 bombshell93   Members   -  Reputation: 198

Like
0Likes
Like

Posted 28 April 2012 - 02:48 PM

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 :/

#15 RobMaddison   Members   -  Reputation: 640

Like
0Likes
Like

Posted 29 April 2012 - 08:33 AM

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...





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS