In this tutorial we will be expanding on the program in the Render loop tutorial to show the basics of moving a model's built-in skeleton. We will be using a 'Dwarf' model that has a relatively simple skeletal structure (with 46 bones). The individual bones inside the skeletal structure are usually referred to as 'joints'. The following topics are covered:
The tutorial's code files and media files are installed along with Ginjo-Builder and can be accessed from the 'Project' window that is shown when the editor starts. This tutorial is called '11_Skeletal_Animation' in the 'Tutorials' tab.
First we create a project and compiler options XML file. (See Render loop for a more detailed description of creating a project.)
Open "SkeletalAnim.xml", and just like in Render loop add the <exe> and <startup> elements. We will also import the GB namespace so we don't have to type it out every time. (For the full list of XML tags see Compiler options.)
<?xml version="1.0" encoding="utf-8"?> <root> <exe path=".\game.exe" /> <startup name="AnimateJoints.main" /> <ImportSymbols> <namespace name="GB" /> </ImportSymbols> </root>
In our XML file we specified the startup function as "AnimateJoints.main". Double-click on "AnimateJoints.gbc" in the project tree to open it, and add the following function, called "main". (When you run your program this is the function where execution starts.) Initialize the Irrlicht 3D engine and add the Dwarf model as in the code below. In this tutorial we do not have any lights, thus the Lighting material flag of the model must be turned to false. (Otherwise the model will be completely black.)
function main(var:string cmdArgs[]) returns Int32 { var:gb.IrrlichtCreationParameters options=new gb.IrrlichtCreationParameters() //Without anti-aliasing our model edges would be jagged options.AntiAliasing=255 //Tell Irrlicht to use OpenGL for graphics options.DriverType=gb.video.DriverType.OpenGL //Turn off logging, we won't be using it for now options.LoggingLevel=gb.LogLevel.None var:gb.IrrlichtDevice engine = gb.IrrlichtDevice.CreateDevice(options) //Also set our window's title engine.SetWindowCaption('Skeletal animation') var:video.VideoDriver driver=engine.VideoDriver var:Scene.SceneManager smgr=engine.SceneManager //load model from relative path ..\media\dwarf.x var:scene.AnimatedMesh DwarfMesh = smgr.GetMesh('..\media\dwarf.x') //add model to scene var:Scene.AnimatedMeshSceneNode Dwarf Dwarf = smgr.AddAnimatedMeshSceneNode(DwarfMesh) if(!isNull(Dwarf)){ Dwarf.Scale = new core.Vector3Df(0.8) Dwarf.Rotation=new core.Vector3Df(0,-90,0) Dwarf.SetMaterialFlag(video.MaterialFlag.Lighting, false) } }
Next add a Maya camera from the cameras tutorial and the render loop to the "main" function exactly as in the first tutorial.
var:gb.scene.CameraSceneNode Camera= smgr.AddCameraSceneNodeMaya() camera.Target=Dwarf.Position + new core.Vector3Df(0,30,0) var:video.Color background=new video.Color(160, 160, 160) while (engine.Run()){ driver.BeginScene(video.ClearBufferFlag.All, background) smgr.DrawAll() driver.EndScene() }
First, we must enable joint animation on the model and retrieve the bone we want to animate using either the bone's index or its name. There is now function for listing the joint names in a model. This information is either provided by the creator of the model or retrieved using a tool such as the Open 3D Model Viewer. Update the model's code as follows:
var:Scene.AnimatedMeshSceneNode Dwarf Dwarf = smgr.AddAnimatedMeshSceneNode(DwarfMesh) if(!isNull(Dwarf)){ Dwarf.Scale = new core.Vector3Df(0.8) Dwarf.Rotation=new core.Vector3Df(0,-90,0) Dwarf.SetMaterialFlag(video.MaterialFlag.Lighting, false) Dwarf.SetJointMode(scene.JointUpdateOnRender.Control) Bone=Dwarf.GetJointNode('rsholda') if(isNull(Bone)){alert('Bone not found'); return 1} }
In the movement/animators tutorial we saw how to update a model's position based on elapsed time. We will use the same technique here to change the retrieved bone's rotation. Update the render loop as follows:
var:core.Vector3Df BoneRotation var:boolean Reverse var:uint32 oldTime=engine.Timer.Time var:uint32 newTime var:single timeDiff var:video.Color background=new video.Color(160, 160, 160) Bone.Rotation=new core.Vector3Df(0) while (engine.Run()){ BoneRotation=Bone.Rotation newTime=engine.Timer.Time timeDiff=convert.ToSingle(newTime-oldTime)/1000 //time difference in seconds if(Reverse){BoneRotation.X-=timeDiff*50} else{BoneRotation.X+=timeDiff*50} if(BoneRotation.X>45){Reverse=true} elseif(BoneRotation.X<0){Reverse=false} Bone.Rotation=BoneRotation oldTime=newTime driver.BeginScene(video.ClearBufferFlag.All, background) smgr.DrawAll() driver.EndScene() }