Thinking about the BoneBox problem again, it seems to me that it's sometimes desirable to have the Boxes defined in BoneSpace, sometimes in ModelSpace, and sometimes in WorldSpace.. it depends what we want to do with it.

For the purpose of synchronizing the animation of the boxes to the bones and rendering them (to visually prove the code so far), it's best for us to get the boxes into modelspace.
The reason why is because the SkinMesh vertices are also defined in modelspace..which implies that we can 'deform' our boxes by treating them to the same process as the SkinMesh vertices, which means that the boxes are automatically transformed in synch with the bones, and it gets better - they can all be rendered in a single pass (no walking of hierarchies etc), and theres no messing around with matrices required.

All we need to do is add a line of code to the bonebox builder to transform the box points back from bonespace to modelspace :)

Once we switch to RagDoll mode, we'll want the Boxes in BoneSpace for the purpose of orienting them, and in ModelSpace for the purpose of collision detection between bodyparts..

Finally, when we want to perform collision detection against the rest of the universe, we'll need the boxes in WorldSpace.

Anyway, it seems to me to be a tidy solution to obtain the bindpose box points in modelspace as we generate our boxes, and thus we'll always have reference values for bonespace and modelspace, leaving only worldspace to worry about, which we'll worry about when we need to.
Posted on 2006-04-16 09:24:27 by Homer
I'd like to explain my understanding of the various Matrices being employed in the Animated SkinMesh, as much for my own benefit as anyone else's.

Firstly, we have the Frame Matrices.
Each frame in the hierarchy has its own unique 'Frame Transformation Matrix'.
This matrix can be thought of as "the difference" between a parent and a child frame.
We can use it to "move from parent space to child space", by combining the transformation of a given Parent with that of its Child. This gives us a second kind of Matrix, the "Combined Frame Matrix".. If we combine any given Frame's Transform matrix with the Combined matrices of all of its Parents (all the way back to Root), we have calculated (for the given Frame) a Combined Matrix that gets us all the way from ModelSpace to FrameSpace without requiring us to perform all those matrix multiplies ever again..
We "walk the entire frame hierarchy" and precalculate all the "Combined Frame Matrices" for all the Frames in the entire hierarchy, irrespective of whether the Frames represent Bones or not.

OK its important to mention at this point that our Combined Frame Matrices describe the skeleton in BIND POSE..

The next kind of Matrix to talk about is the "Bone Offset" matrix..
These are the matrices which are internally managed (animated) by the SkinMesh.
They represent "how much to move each Bone".
Sometimes they are called the "Skin Offset" matrices, because they deform the Skin vertices. Anyway, we combine each "difference matrix" with the Combined matrix of the Frame it represents, in order to "bend our skeleton". This results in a set of "Final Matrices" which we'll use to deform our BindPose mesh each time we render a frame.

So, these "final matrices" take us from ModelSpace into BoneSpace, and then further, to the animated space of the Bone.. that is to say, the final matrices are used to ANIMATE OUR BONES.

OK, now I'm going to leave the land of FACTS behind and talk about my implementation ideas.

We can't deform our BoneBoxes using the same method as the SkinMesh unless we want to go to the trouble of setting up SkinWeights etc, but we can achieve the same effect by hand, by transforming our ModelSpace boneboxes using the Final Matrices, and then getting them back into ModelSpace using the Inverse of the Combined Frame Matrix.
This should in my mind animate the boneboxes correctly, because it moves them from ModelSpace to "new, animated bonespace", and then it moves them from "old, bindpose bonespace" back to ModelSpace .. this implies that the "difference between the Old and New matrices" has been applied to the Bone.. That's like saying "We applied the BoneOffset Matrix directly to each BoneBox, in BoneSpace".. Well bloody hell, why don't we just do that instead? :P

Did that make the slightest bit of sense?

If our Boxes are defined in ModelSpace, we use the Final Matrices to animate them.
If our Boxes are defined in BoneSpace, we use the OffsetMatrices to animate them.
Either way, the transformed vertices are in BoneSpace, and need to be transformed back into ModelSpace.
Posted on 2006-04-17 03:37:14 by Homer
Hi Homer,

We can't deform our BoneBoxes using the same method as the SkinMesh unless we want to go to the trouble of setting up SkinWeights etc, but we can achieve the same effect by hand, by transforming our ModelSpace boneboxes using the Final Matrices, and then getting them back into ModelSpace using the Inverse of the Combined Frame Matrix.


Do you mean by this SkinWeights are useless.
Or more correctly: There is always a better way than using SkinWeights.

Friendly regards,
mdevries.
Posted on 2006-04-17 05:29:31 by mdevries
We currently apply our Final matrices to the Mesh vertices (ie bend the mesh) using a call to ID3DXSkinInfo.UpdateSkinnedMesh.. we can't use this to transform the BoneBoxes, but we can do what it does by hand, we simply ignore the SkinWeight, since the vertices of a BoneBox are only affected by the Bone that owns them.

It might help to see the formula being applied by the ID3DXSkinInfo.UpdateSkinnedMesh interface method:


We need to weight each vertex to define the influence of each bone Final Matrix. Thus the final vertex position in character space is defined by:

Vertexdest = SUM ( Vertexsource * Underlying_Bone_Final_Matrix * Underlying_Bone_Weight)


The only thing not shown there is that once the Vertices have been manipulated using the above formula, they are then transformed back into ModelSpace..

If we ignore the Weight part of it, and assuming that we begin with BoneBoxes defined in BoneSpace, we can vastly simplify this formula into this:
New BoneSpace Vertex = Old BoneSpace Vertex * BoneOffsetMatrix

and assuming that the BoneBoxes are defined in ModelSpace,
New ModelSpace Vertex = Old ModelSpace Vertex * BoneFinalMatrix

and in either case, performing this as a secondary step:
New ModelSpace Vertex = New BoneSpace Vertex * InvBoneMatrix
in order to bring the BoneSpace Vertex back into ModelSpace.

Does that help explain what I meant? (gee, I'm confusing myself now, lol)

Anyway, I realized later that we can cheat a little here, if we do the following:
During rendering, combine the current world transform with each final matrix so that we are in BoneSpace (and the animated bonespace at that), then draw the bonebox as a simple axially-aligned box.
What we just did is draw the BoneBoxes in the context of each animated Bone.
We don't have to tranform ANYTHING this way, which will be problematic later, but it's a quick and easy way to get the boxes on the screen so we can see them.
As I said, we're cheating..
Posted on 2006-04-17 09:25:54 by Homer

Here's another update of the Binary.
I PROMISE I'll post the source again soon, I just want to clean it up a little.
It took several attempts to get the BoneBoxes working.
Eventually I got it working using the first method I described, the shortcut method 'should' work too, maybe I made some small mistake, I'll try that again later.

You can see the BoneBoxes now, here I'm transforming the BoneBoxes using the very same matrices as the skinmesh, except applied 'rigidly' (skinweights ignored). I had to get the BoneBox definitions transformed into ModelSpace first, but that's where collision detection with other bodyparts will happen anyway, and that's coming soon..
Attachments:
Posted on 2006-04-18 10:56:00 by Homer
Hi Homer,

I tested the demo: It's working. This is one step ahead again.

Friendly regards,
mdevries.
Posted on 2006-04-18 14:36:05 by mdevries

Cool, thanks for the feedback :)

Being able to see the BoneBoxes should make it painfully clear what this is all about.
We'll be using the BoneBoxes for two quite separate purposes, although these purposes are in many ways related.

Firstly, we'll be using them to implement RAGDOLL PHYSICS.
Basically we'll apply some physics to the boxes, such that they collide with and respond to each other, and use the resulting orientations of the boxes to control the bones, and thus the mesh. This is kinda the opposite of what we are doing at the moment, right now we're constraining the boxes to follow the bones, rather than constraining the bones to follow the boxes...

The second thing we'll be using the boxes for is for collision detection between the skinmesh and 'the rest of the universe'. We can begin to think of the entire set of bone-oriented boundingboxes as comprising "a convex hull which totally encloses the model regardless of the orientation of its bones", ie, a COLLISION HULL.

It's a lot cheaper to test against the BoneBoxes than it is to test against the mesh.
Since (as you can see from the demo)  the Hull is quite a good fit, it's acceptable for gaming purposes to use this as our only collision test.. the alternative, if we wanna be really accurate and still faster than testing against the mesh is to precalculate the subset of mesh faces which share the subset of bone-affected Vertices (during our box generation phase) and to perform a secondary collision test against the Faces, should the primary test against the Box succeed..

As a matter of fact, the more Joints the model has, the more BoneBoxes are required, the tighter the Hull fits, and the more pronounced is the efficiency of using the Hull rather than the Mesh for collisions.

For skeletons with low numbers of Joints, it might not seem so cool.
But for skeletons with realistic numbers of Joints, it almost seems crazy not to do it, particularly if we want a few Instances of our beasties onscreen ;)

Next post, I shall attach the updated source, and begin discussion of exactly how we go about switching from Animated mode to RagDoll mode (activating the bone physics), in particular how to initialize the State of the physics system (by extracting some physics variables implied by the animation of the skeleton) :)

Have a nice day.



Posted on 2006-04-19 01:03:38 by Homer
Hi Homer,

My machine is a Pentium 2.66 GHz running on WinXP SP2.
It takes about 4 seconds on my machine, to load and render the animated model, including the BoneBoxes.
How much time does it take on your machine?

Friendly regards,
mdevries.
Posted on 2006-04-19 10:39:21 by mdevries
So, umm, where do I get the .X file?
Posted on 2006-04-19 10:48:07 by f0dder
It takes about the same time on my 500mhz machine (ge4 video), so its likely that m$'s xfile parsing code is not very efficient, ie, I am blaming disk accesses and cache misses.
It's possible to render stuff while loading..

I promised to send updated source, here she is :)
Let me know if it doesnt compile (I forgot to send something etc)

Now I wanna start talking about physics :P
Most people would look away, so you must be weird.. let's continue :)
( Don't worry if you're totally new to physics, just accept that theres a bunch of formula that have to be coded up, then we just apply them and pray;) )

'RagDoll Mode' requires that we initialize some more of those quirky and hitherto unmentioned fields of the RagDollBone structure.
When we switch from Animated mode to RagDoll mode, we'll calculate some values which describe the physics system at the Time that we switched over from Animated to RagDoll mode.

The first thing that needs to be added to get the Physics going is some code to update the contents of a new field in CRagDoll called m_PrevState.
It's a copy of the previous content of CRagDoll.m_State.. each time we update m_State, we also update m_PrevState.
This gives us two snapshots of the Bone's dynamic properties, at two linear moments in time..which is all we need to deduce some values to initialize the kinematic properties of the bone :)

To give you an obvious example, we can now deduce the change in rotation over time, and extract it as a Rotation Matrix directly from the Prev and Current matrices.. this is how we calculate the Angular Velocity of each Bone :)

The physics code is actually a "numerical integrator", which basically is a mathematical tool for solving an equation whose components are interdependant.. ie a "differential equation".

In my next post I'll start describing Euler Integration, which is the most simple kind of 'numerical integrator' .. theres others, and they are really just variations on the basic Euler Integrator. Euler's solution is the fastest and the least accurate, but should be "good enough" for our purposes :)

Attachments:
Posted on 2006-04-19 11:11:49 by Homer
f0dder - get the XFiles from the DirectX SDK 'media' folder  (130 odd meg installer for a few pissy files)

The demo requires that any texture files referenced by an xfile be in the relative path specified within the xfile..

Otherwise just google for them, I'm sure you'll find them, or if not, just find another one.. Since the loader isn't incredibly robust, there's likely a few xfiles that will crash it.
For example, if they contain progressive meshes or other unhandled data types.

Everyone - Tiny's name is a joke.
The model is 600-ish units in height :)
What's not so funny is that its primary axis is Z rather than Y, so it needs to have some axes switched in order to display it correctly (I rotate it 90 degrees to view it in the current source..)
Thanks again Microsoft, for releasing something that seems ok until you try to USE it.
Posted on 2006-04-19 11:17:21 by Homer
Hi Homer,

A few files were missing, but were included in a previous post of yours.
- d3d9.inc
- SuperTimer.inc

Anyway, at assembly time I get the following complaint from the linker:

LINK : error LNK2001: unresolved external symbol _WinMainCRTStartup
DXSkinMesh.exe : fatal error LNK1120: 1 unresolved externals


Any idea what is missing?

Friendly regards,
mdevries.
Posted on 2006-04-19 17:20:01 by mdevries
That's an interesting error you got !!
It indicates that the last line of the ASM file is missing !!!

The Linker is complaining about the Program EntryPoint, whose label is identified by the "END label" directive at the end of the source.
The "END start" directive is missing, or there is no matching "start:" label.
Please check the end of the ASM file and verify this.

Posted on 2006-04-20 00:41:57 by Homer

I'm going to leap ahead a little here and describe the steps involved in "integrating the physics state".

In mathematics, integrating means "solving a non-linear differential equation".
In the context of our GameDev physics, integrating means "updating the physics state over time".

We'll be coding up a Method whose name is Integrate, and whose purpose is to update the physics state of a rigid body, given the elapsed time and previous physics state.
This implies that the previous physics state is already known to us, which in turn implies that we need to initialize the physics state.

Before talking more about the initial physics state, I'd like to cover the steps involved in advancing the physics state with time, ie, Integrating it.
Once we see what the Integrate method requires from us, we'll be in a better position to code the physics init method.

Posted on 2006-04-20 02:50:42 by Homer
Hi Homer,

I have been looking for a missing "end label". But it is right there. So that should not be the problem.
But the source contains the following line:

include D:\masm32\m32lib\atofp.asm


This file contains an "end". But it has no label it is referring to.
Could this file be the problem? Did you leave the source unchanged?
(What version of atofp.asm do you use?)


I have one more question:
Is your version of ID3DXAllocateHierarchy.inc the same as in the previously published source? Because it was not included in the last source either, so I am using the original version.

Friendly regards,
mdevries.
Posted on 2006-04-20 14:00:42 by mdevries
Files that will be packed in a .lib don't need a label after "end".
Only the file that contains the startpoint of execution needs "end start".
Posted on 2006-04-21 06:53:06 by Ultrano
...and files that are INCLUDE'd must not have END.
Posted on 2006-04-21 08:27:52 by f0dder
Thanks Ultrano and Fodder for your comments.

I copied the atofp.asm file from the m32lib directory to the project directory. Then I removed the "END" directive from that file, and tried to build the project again.
This time one of the complaints was about pRagDollBone.

Homer,

I remember you added pRagDollBone to the FRAME struct.
So I did that manually in ID3DXAllocateHierarchy too.
But I think you have changed more in ID3DXAllocateHierarchy.
Could you post the updated file, because it was missing in the previously attached project source?

Friendly regards,
mdevries.
Posted on 2006-04-21 17:46:15 by mdevries
Attached is my current ID3DXAllocateHierarchy, and yes, some small changes made in here.
Sorry about that oversight.
My atofp is the standard one from masm32 lib folder.
My FloatToString is modified to append a ".0"  when displaying floats that happen to be perfect integers, such as 10.0f
I've just managed to break a couple of fingers, and am typing like a chicken.
Maybe I'm gone a few days while I patch myself up.
l8r
Posted on 2006-04-22 02:58:22 by Homer
Time to show the heart of the physics code, the Integrate method.
I choose to begin with pseudocode:

Position += Elapsed*LinearVelocity
AngularMomentum += Elapsed * Torque
LinearVelocity += Elapsed * Force / Mass
qVelocity = Elapsed *AngularVelocity
(and then we recalculate the orientation matrix using instantaneous qVelocity)

We can see from this that we need to already have :
-Elapsed Time
-Linear Velocity
-Torque
-Force
-Mass
-Angular Velocity
-Angular Momentum

So, at the moment we switch from animated to ragdoll mode, we need to extract all these values from the current and previous animation frames.
We'll start with the easier ones, and work our way through them.

Once we've managed to calculate the physical state of each bone at the magic switching moment in time, we can then modify the values over time using the pseudocode above.

Posted on 2006-04-22 08:35:39 by Homer