Graduation Project

This is a college project
Skeletal animation
By
Ali Essmael
2007
Content
1 – Mathematics background
 Joint description
 Bind skin with joint
2 – Programming background
 NCore
 NInput
 NWindow
 NRenderer
 NGui
 NMdl
 NNet
 NPhysics
 NAudio
 NEngine
3 – The Project
Mathematics background
Joint Description
If we have a coordinate system A, we can locate any position relative to
A by
Position is not enough to represent object in space. If we know that a man
stands in position (x, y, z), we don’t know his direction, so we must find
way to describe his orientation. This way is to attach coordinate system B
on object and give description of B relative to A.
We merge position and rotation in one matrix called frame which is a set
of four vectors.
(Reference introduction to robotics chp2)
Rotation matrix is 9 values and these values are related. For example
length of every row or column is one. Dot product for any two raw or any
two columns is zero. These relations make ability to represents rotation in
3 values such as Euler or 4 values such as quaternion which is faster and
solves problem in previous one.
(Reference 3D Math primer for graphics and game development)
Bind Skin with joint
There are two way to bind skin. first is to bind every vertex with one joint
.
This is the first way .this way have problems, it show unwanted
appearance at joint and it show character move as robot.
By improvements done for display cards another way is suggested, this
way is to bind every vertex with more than joint.
(Reference 3D Game Engine Architecture / page 410)
Programming background
The Project is divided into many separated part. This is for
 Making programming simple.
 Making programming effective.
 If we need make project run on another operating system, it will be
possible and easer.
 We can use any part in another project.
 Understanding huge project will be easer.
Project Parts
NCore Part
It contains any data type that programmer may need such NPoint3F
NString and NObject. It provides log file and schedule and another
services function, the way to use this part is
#include <NCore.h>
And link NCore.lib or
library, then we call
NCore_d.lib
if this part is converted to static
NString log[2];
Log[0] = "log1.log";
Log[1] = "log2.log";
new NCore(log,2);
First parameter is log files names array, second parameter is the number
of log file.
So we have global variable gCore we can use its function in any place in
code such
gCore->Error("error in function ggg",0);
Print message "error
in function ggg"
in red on the first log file
gCore->WriteLn("ok",NCCGreen,1);
Print "ok" in green on second log file and jump to next line
gCore->GetTime();
Return time since NCore creation
gCore->Schedule(3000,aaa);
Call function aaa after 3 seconds from previous call
gCore->Cancel(aaa);
Stop function aaa if it will be called after period
To explain two functions above, we will create second counter appears on
title bar of window and count from 1 to 10
int c = 0;
function count()
{
c++;
gWindow->Caption = NString(c);
gCore->Schedule(1000,count);
if( c >= 10)
{
gCore->Cancel(count);
}
}
At last we call count(); in main function.
gCore->Sleep(20);
Stop processor for 20 ms, this function must be called in main loop to
improve processor work.
We must call gCore->Update(); at while loop of project and gCore must be
deleted at the end of project.
Many data types are taken from 3D Game Engine Architecture and there
are data Types for math and another we see them
NObject Data Type
It is base class for any important class .it can store pointer to any variable,
then I can retrieve pointer in any place in code .for example if I have
variable v of NObject or derived from NObject I can register this variable
with name "aaa" using v->Register("aaa"); then I can get this variable
NObject v = NObject::Find("aaa"); so there is no need to include header
that contain variable or define variable in header.
NEvent Data Type
Contains list of pointer to function, we can use it as this example
function a1()
{
.
.
}
function a2()
{
.
.
}
NEvent e;
e += a1;
e += a2;
To call function a1 and a2 we call e();
This way add more dynamically and help us to make library run on more
operating system, we can note that afterward.
NString Data Type
It makes dealing with string simpler. It can convert from number to text
and from text to number – search text – split text to text array by split
character – compare operation, sum, sub – convert text to capital or small
letter – return text length – if text represent path I can get only path or
only file name or only extension.
NArray Data Type
NHashTable
NPoint2I
NPoint2F
NPoint2D
NPoint3I
NPoint3F
NPoint3D
NPoint4I
NPoint4F
NPoint4D
NMatrix2x2
NMatrix3x3
NMatrix4x4
NQuaternion
NEulerAngles
NSegment
NRay
NLine
NRect
NPolygon
NTriangle
NPlane
NAABB
NOBB
NCone
NSphere
dynamic array
NCircle
NColor
NInput part
Deals with input devices such as keyboard mouse (note: all projects don't
use msgProc to deal with input which is for windows). We can use it as
below
#include <NInput.h>
Call at the beginning
new NInput;
Call the following at main loop
gInput->Update();
So we have global variable we can use it in any place in code such as
gInput->KeyDown( NKEY_ESCAPE )
Return true at pressing escape
gInput->KeyUp( NKEY_ESCAPE )
Return true at leaving escape
gInput->Key( NKEY_ESCAPE )
Return true while we are pressing escape
We must delete
gInput
at the end of project
NWindow Part
Provide functionality to deal with window, to use it
#include <NWindow.h>
Call the following at the beginning of project
new NWindow("NoorEngine",800,600,false);
It creates window with name noorEngine, its size is 800x600 and it is not
fullscreen.
So we have global variable gWindow that we can use it such
gWindow->GetCursorPos();
Return mouse pos of cursor
We can use
gWindow->OnClose += NEngine_Close ;
Where OnClose is of NEvent Type, it will be called at closing window
NEngine_Close is function we need to call at closing window
Note that we don’t need to call NEngine_Close from inside Library and I
don’t interest what is the type of operating system, and the operation
become easer for end programmers.
We must delete gWindow at the end of project.
NRenderer Part
It provide functions to draw 2D and 3D object such as lines
#include <NRenderer.h>
new NRenderer(type)
Where type is the type of Renderer, OpenGl or DirectX or software (I
implement directX only).
So we have global variable
gRenderer
we can use it such
gRenderer->Color = NColor( 255,255,0,255);
gRenderer->Draw( box );
It draw obb box in orange
To draw line
gRenderer->DrawLine( NPoint3F( 0, 0 , -5 ) , NPoint3F( 0 , 0 , 5 )) ;
We must delete
gRenderer
at the end of project.
NGui Part
To create graphic user interface, it contains most controls that
programmer may need.
#include <NGui.h>
new NGui
So we have global variable gGui, we can use it. We must call
gGui->Render(RenderElapsed);
For example to create button which has half width and length of window
and call go function if we click it, we do the following
NButton* b = new NButton;
b->Pos.Set( 0, 0);
b->Width = 0.5;
b->Height = 0.5;
b->OnLButtonDown += Go;
gGui->Push( b );
The coordinate system which is used is
This coordinate cover all window ,using this way will solve problems that
appear when changing window size ,and facilitate programming .for
example if I want to put button at middle of window ,I set pos to (0,0)
and I don’t care what is window size .
Each control can add control as child, here we have tow way for
coordinate system either we consider the coordinate is relative to parent
or is relative to gWindow. For example
NCtrl* messageBox = new NCtrl;
messageBox->Pos.Set( 0.5 , 0.5 );
messageBox->Width = 0.5;
messageBox->Height = 0.5;
NButton* okButton = new NButton;
okButton->Pos.Set( 0,0);
okButton->Width = 0.5;
okButton->Height = 0.5 ;
okButton->Add( b ) ;
Code above create okButton and make it child to messageBox. Position of
okButton at middle of messageBox. Moving messageBox will move okButton,
changing messageBox size will change okButton size. To make the position
of child relative to gWindow and its size from gWindow Size we do the
following
NCtrl* messageBox = new NCtrl;
messageBox->Pos.Set( 0.5 , 0.5 );
messageBox->Width = 0.5;
messageBox->Height = 0.5;
NButton* okButton = new NButton;
okButton->PosInWorld = true ;
okButton->WorldPos.Set( 0,0);
okButton->SizeInWorld = true ;
okButton->WorldWidth = 0.5;
okButton->WorldHeight = 0.5 ;
okButton->Add( b ) ;
The code above will create okButton at middle of window, and its size is
half the size of window.
If position is relative to parent we use Pos and we can get its position
relative to window from WorldPos.
If position is relative to window we use WorldPos and we can get its
position relative to parent from Pos.
Size is the same.
There are many controls we see some of them
NCtrl
Base class for any control.
NButton
NPushButton
NVSlider
NHSlider
NVScroll
NHScroll
NListBox
NComboBox
NEditBox
NMenuBtn
NMenuList
NMenu
NRightMenu
NMdl Part
To import models designed by external 3d program such as maya, max. I
have written plug-In on Maya to export joints and its animation, and
export skin.
#include <NMdl.h>
To show skin "skn.skn" which its animation is taken from "skl.skl" we
write
Skn = NSkin::Get("Models\\skn.skn");
Skl = new NSkeleton ;
Skl->Pos.Set(0 ,0 , 0);
Skl->Visible = true ;
Skl->Load("Models\\skl.skl");
Skl->Skin = Skn ;
Where "skn.skn", "skl.skl" are two files exported from Maya.
We must call skl->Render( RenderElapsed); at main loop
We must call NSkin::Create(); at beginning
We must call NSkin::Destroy(); at the end.
NNet Part
To deal with local area network or internet
#include <NNet.h>
new NNet( true,"www.NoorEngine.com") ;
Where first parameter determine if PC is client, and if it is we must give
server address on second parameter.
So we have global variable gNet .to use it we see example
At client
gNet->WriteString("ok");
gNet->WriteFloat( 1.55f );
gNet->SendPacket(server);
At server
if( gNet->GetPacket(&from) > 0 )
{
NString str = gNet->ReadString();
Float f = gNet->ReadFloat();
}
We must call gNet->Update(); at main loop and we must delete
the end of project.
(Reference Programming Multiplayer Games)
NPhysics Part
Under construction
(Reference Physics Based animation)
NAudio Part
Under construction
NEngine Part
It includes all part above and makes it ready
#include <NEngine.h>
We call the following at main program
new NEngine ;
NInterface* i = new NInterface;
i->Pos.Set( 0.0f , 0.0f ) ;
i->Width = 1.0f ;
i->Height = 1.0f ;
i->Enabled = true ;
i->Visible = true;
gGui->Push( i );
gEngine->Run();
delete gEngine;
Where NInterface is class contains graphic user interface.
gNet
at
The Project
If we start project we can see
Cubes represent a set of joint .you can change camera direction by
pressing left mouse button and move it. You move left, right, forward,
backward using a, d, w, and s.
You can select any joint and see DH parameter of the joint at right bar
You can change value by right clicking over value and moving mouse top
or down.
Or you can click over value and write it directly.
Below DH parameter there is location of the last joint
I have class of NChain contains array of NFJoint which represents yellow
cubes. class NFJoint contains DH parameter
F32
F32
F32
F32
LinkLength
LinkTwist
LinkOffset
JointAngle
;//0 for first and last joint
; //0 for first and last joint
;//var if joint is prismatic -- 0 for first joint
;//var if joint is revolute -- 0 for first joint
Through these parameters we can get local transformation, and from local
transformation we can get global transformation.
Tool bar contain many buttons
Visible
Wire
Play
Joints
Coord
to make killa (the name of character) visible
to draw killa in wireframe
to play walk animation
to show joints
to show coordinate systems of every joint
You can control animation by animate joints, this is done by selecting
joint (its color converted to red and coordinate system appear on it), then
selecting axis on it then move or rotate joint using left bar
To move joint about selected axis right click on move icon and drag
mouse top or down .at the same way you can rotate.
Note figure below which rotate character about lower spin about y axis
Character skeleton is of class NSkeleton, this class contains array of
NJoint, and every joint contain id to parent joint and
NPoint3F
Pos;
NQuaternion
Rot;
Instead of DH parameters, from pos and rot we can calculate local
transformation and from it we can calculate global transformation. After
we have all global transformations of joints we pass them to skin shader
to compute new skin shape and draw it.
The shader is skin.fx
float4x4
float3x3
float3
float4
float
worldViewProj
world
eyePos
lightColor
shininess
:worldViewProjection;
:world;
:eyePos;
:lightColor;
:shininess;
#define numBones 80
float4x3 bonesMatrixes[ numBones ] : MatrixesArray;
texture diffuseTexture : DiffuseTexture;
texture specularTexture : SpecularTexture;
------------------------------------ //
struct vertexInput {
float3 p
:POSITION;
float3 normal
:NORMAL;
float4 bonesId
:BLENDINDICES;
float4 weights
:BLENDWEIGHT;
float2 uv
:TEXCOORD0;
};
struct vertexOutput{
float4 hPosition
float2 uv1
float2 uv2
float4 spec
};
:POSITION;
:TEXCOORD0;
:TEXCOORD1;
:COLOR0;
------------------------------------ //
vertexOutput VS_TransformAndTexture(vertexInput IN)
{
vertexOutput OUT;
float3 worldPos =
mul( float4(IN.p , 1.0) ,
(float4x3)bonesMatrixes[IN.bonesId.x])
mul( float4(IN.p , 1.0) ,
(float4x3)bonesMatrixes[IN.bonesId.y])
mul( float4(IN.p , 1.0) ,
(float4x3)bonesMatrixes[IN.bonesId.z])
mul( float4(IN.p , 1.0) ,
(float4x3)bonesMatrixes[IN.bonesId.w])
float3 normal =
mul( float4(IN.normal , 1.0) ,
(float3x3)bonesMatrixes[IN.bonesId.x])
mul( float4(IN.normal , 1.0) ,
(float3x3)bonesMatrixes[IN.bonesId.y])
mul( float4(IN.normal , 1.0) ,
(float3x3)bonesMatrixes[IN.bonesId.z])
mul( float4(IN.normal , 1.0) ,
(float3x3)bonesMatrixes[IN.bonesId.w])
normal = normalize( normal );
OUT.hPosition = mul( float4(worldPos,1) ,
OUT.uv1 = IN.uv;
* IN.weights.x +
* IN.weights.y+
* IN.weights.z+
* IN.weights.w;
* IN.weights.x+
* IN.weights.y+
* IN.weights.z+
* IN.weights.w;
worldViewProj);
OUT.uv2 = IN.uv;
float3 E = normalize( eyePos - worldPos); //eye vector
//output specular
OUT.spec = lightColor * pow(max(0,dot(normal,E)),shininess);
return OUT;
}
------------------------------------ //
sampler diffuseSampler = sampler_state
{
texture = <diffuseTexture>;
MIPFILTER = LINEAR;
MINFILTER = LINEAR;
MAGFILTER = LINEAR;
};
sampler specularSampler = sampler_state
{
texture = <specularTexture>;
MIPFILTER = LINEAR;
MINFILTER = LINEAR;
MAGFILTER = LINEAR;
};
----------------------------------- //
float4 PS_Textured( vertexOutput IN): COLOR
{
float4 diffuseTexture = tex2D( diffuseSampler, IN.uv1 );
float4 specularTexture = tex2D( specularSampler, IN.uv2 );
return diffuseTexture + IN.spec * specularTexture.r;
}
---------- ------------------------- //
technique Skin
{
pass p0
{
VertexShader = compile vs_1_1 VS_TransformAndTexture;)(
PixelShader = compile ps_1_1 PS_Textured;)(
}
}
I learn shader programming from
DirectX SDK Doc
www.ati.com
www.nvidia.com
Joints are moved by user and we can make them move by physics so we
can simulate drop of character from high place or simulate collision of
character with car …
We can also move joints by external device (motion capture)