Sign in to follow this  
  • entries
    45
  • comments
    44
  • views
    50160

Skinned mesh code part 2 - Rockland skinned mesh API

Sign in to follow this  
Norman Barrows

1015 views

Skinned mesh code part 2

Rockland skinned mesh API
// ------------------------ Rockland skinned mesh API -------------------------- // -------------------------------- load skinned mesh -----------------------------------// loads a skinned mesh from a .x file, and creates an animation controller for it.// NOTE: load mesh hierarchy from X automatically makes the created controller use the loaded mesh.// returns: // 0 success// 1 invalid call// 2 out of memory// 3 setup bone matrix pointers errorint load_skinned_mesh(char *filename,ID3DXAnimationController** ani_controller_ptr_addr,LPD3DXFRAME* g_pFrameRoot_addr){HRESULT result; // resultCAllocateHierarchy Alloc; // allocator routineresult=D3DXLoadMeshHierarchyFromX(filename,D3DXMESH_MANAGED,Zd3d_device_ptr,&Alloc,NULL,g_pFrameRoot_addr,ani_controller_ptr_addr);switch (result) { case D3DERR_INVALIDCALL: return(1); break; case E_OUTOFMEMORY: return(2); break; }// result = D3D_OKresult=SetupBoneMatrixPointers(*g_pFrameRoot_addr,*g_pFrameRoot_addr);if (result != S_OK) { return(3); }return(0);} // ------------------- unload skinned mesh ---------------------------------void unload_skinned_mesh(ID3DXAnimationController* ani_controller_ptr,LPD3DXFRAME g_pFrameRoot){CAllocateHierarchy Alloc;D3DXFrameDestroy(g_pFrameRoot,&Alloc);SAFE_RELEASE(ani_controller_ptr);} // ------------------- set D3DINDEXEDVS projection matrix transpose ----------// only required by D3DINDEXEDVS skinning method// must be called before calling set_indexed_shader_proj_mat!void set_D3DINDEXEDVS_proj_mat_transpose(){D3DXMatrixTranspose(&g_matProjT,&Zprojection_matrix);} // --------------------- load effect file ------------------------------------// only required for shader based skinning methods// returns S_OK on successHRESULT load_effect_file(){HRESULT hr;DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE; // Read the D3DX effect file// WCHAR str[MAX_PATH];// V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, L"SkinnedMesh.fx" ) ); // If this fails, there should be debug output as to why the .fx file failed to compile V_RETURN( D3DXCreateEffectFromFile( Zd3d_device_ptr, "SkinnedMesh.fx", NULL, NULL, dwShaderFlags, NULL, &g_pEffect, NULL ) );return(S_OK);} // ----------------------- release effect file -------------------------------------// only required for shader based skinning methodsvoid unload_effect_file(){SAFE_RELEASE( g_pEffect );} // ------------------ set indexed shader proj mat -------------------------------------// Set the projection matrix for indexed skinning vertex shaders// only required for D3DINDEXEDVS and D3DINDEXEDHLSLVS skinning methods// must call set_D3DINDEXEDVS_proj_mat_transpose first!void set_indexed_shader_proj_mat(){HRESULT hr;if( g_SkinningMethod == D3DINDEXEDVS ) { V( Zd3d_device_ptr->SetVertexShaderConstantF( 2, ( float* )&g_matProjT, 4 ) ); }else if( g_SkinningMethod == D3DINDEXEDHLSLVS ) { V( g_pEffect->SetMatrix( "mViewProj", &Zprojection_matrix ) ); }} // --------------------------- set shader light -------------------------------// // Set Light for vertex shader// only required for shader based skinning methods// note you`ll want to modify this light to match your // fixed function one or your lighting won`t look right!void set_shader_light(){HRESULT hr; D3DXVECTOR4 vLightDir( 0.0f, 1.0f, -1.0f, 0.0f );D3DXVec4Normalize( &vLightDir, &vLightDir );V( Zd3d_device_ptr->SetVertexShaderConstantF( 1, ( float* )&vLightDir, 1 ) );V( g_pEffect->SetVector( "lhtDir", &vLightDir ) );} // ------------- update animation -----------------------------// advances the controller`s timer by "seconds", // tweens between the two closest keyframes, then sets the bone matrices// of the skeleton.void update_ani(ID3DXAnimationController* ani_controller_ptr,double seconds){ani_controller_ptr->AdvanceTime( seconds, NULL );} // --------------------- add world transform to skeleton ---------------------// call update_ani first!void add_world_transform_to_skeleton(LPD3DXFRAME g_pFrameRoot,LPD3DXMATRIX mWorld_ptr){UpdateFrameMatrices( g_pFrameRoot, mWorld_ptr );} // ------------------------ draw skinned mesh ----------------------// draws a skinned mesh// ZmView is the view matrix// to port to code not based on the z3d game library, // replace Zd3d_device ptr and ZmView with your own d3d device pointer and view matrix.void draw_skinned_mesh(LPD3DXFRAME g_pFrameRoot){DrawFrame( Zd3d_device_ptr, g_pFrameRoot, &ZmView );} // ------------------------------ get behavior flags ----------------------------// gets behavior flags (HW caps -> creation params -> behavoir flags) for indexed shading methods// only required for D3DINDEXED, D3DINDEXEDVS, and D3DINDEXEDHLSLVS skinning methodsvoid get_indexed_behavior_flags(){D3DDEVICE_CREATION_PARAMETERS cp;Zd3d_device_ptr->GetCreationParameters( &cp );g_dwBehaviorFlags = cp.BehaviorFlags;} // ---------------------------- load shaders -----------------------------// returns S_OK on success// loads the indexed vertex shaders// only required for shader based skinning methodsHRESULT load_shaders(){HRESULT hr;DWORD dwShaderFlags = 0;/*#if defined( DEBUG ) || defined( _DEBUG ) // Set the D3DXSHADER_DEBUG flag to embed debug information in the shaders. // Setting this flag improves the shader debugging experience, but still allows // the shaders to be optimized and to run exactly the way they will run in // the release configuration of this program. dwShaderFlags |= D3DXSHADER_DEBUG; #endif#if defined(DEBUG_VS) || defined(DEBUG_PS) dwShaderFlags |= D3DXSHADER_DEBUG|D3DXSHADER_SKIPVALIDATION; #endif*/ for( DWORD iInfl = 0; iInfl < 4; ++iInfl ) { LPD3DXBUFFER pCode; // Assemble the vertex shader file// WCHAR str[MAX_PATH];// DXUTFindDXSDKMediaFileCch( str, MAX_PATH, g_wszShaderSource[iInfl] ); if( FAILED( hr = D3DXAssembleShaderFromFile( g_wszShaderSource[iInfl], NULL, NULL, dwShaderFlags, &pCode, NULL ) ) ) return hr; // Create the vertex shader if( FAILED( hr = Zd3d_device_ptr->CreateVertexShader( ( DWORD* )pCode->GetBufferPointer(), &g_pIndexedVertexShader[iInfl] ) ) ) { return hr; } pCode->Release(); }return(S_OK);} // ------------------------------------ unload shaders -------------------------// only required for shader based skinning methodsvoid unload_shaders(){for( DWORD iInfl = 0; iInfl < 4; ++iInfl ) SAFE_RELEASE( g_pIndexedVertexShader[iInfl] );} // ----------------------------------- get bone (by name) -----------------------------------// returns a pointer to a bone (D3DXFRAME), given its name (as it appears in the .x file, not blender, etc!)// use this to get a pointer to a bone you want to attach a mesh to, // such as a hand bone you want to attach a weapon to.// example:// LPD3DXFRAME my_bone_pointer=get_bone(root_bone_pointer,"some_bone_name");// the bone`s world transform is then (D3DXFRAME_DERIVED*)my_bone_pointer->CombinedTransformMatrix// getting bone by name requires chasing pointers to travere the frame hierarchy tree, // and string comparisons at each node. so you want to just do it once for bones you`re // interested in when you load a skined mesh, and save the bone pointers for later use when// drawing meshes attached to those bones.LPD3DXFRAME get_bone(LPD3DXFRAME rootframe,char *s){return(D3DXFrameFind(rootframe,s));} // -------------------- get mesh container, given the mesh container name ---------------------// NOTE: blender does not export mesh container names! // it only exports them as comments in the .x file.// use get_mesh_container2 to get the first mesh container of a frame,// given the frame name, which blender does export. // recursively searches a linked list of meshcontainers and returns a pointer to a mesh container, given its name.// returns NULL if not found// NOTE: unfortunately, blender doesn`t seem to export mesh names except as comments. so mesh names are always "".// so this wont really work with files from blender.LPD3DXMESHCONTAINER get_mesh_container_part2(LPD3DXMESHCONTAINER p,char *s){msg3(p->Name);if (strcmp(p->Name,s) == 0) { return(p); }if (p->pNextMeshContainer == NULL) { return(NULL); }return(get_mesh_container_part2(p->pNextMeshContainer,s));} // returns a pointer to a meshcontainer in a skinned mesh, given its name.// returns NULL if not found// rootframe is the root frame of the skinned mesh// does a recursive traversal of the frame hierarchy. for each frame with a meshcontainer, // it does a recursive search of the linked list of meshcontainers, // looking for a meshcontainer with a matching name.// USAGE:// 1. add a texID variable to D3DXMESHCONTAINER_DERIVED// 2. after loading the skinned mesh, use get_mesh_container to get a pointer to a mesh container.// 3. use the pointer to the mesh container to set the texID// 4. use drawmeshcontainer5 to draw ( it uses the texID)// this gives you interchangeable poolable textures for each mesh of the skinned mesh.// NOTE: unfortunately, blender doesn`t seem to export mesh names except as comments. so mesh names are always "".// so this wont really work with files from blender.LPD3DXMESHCONTAINER get_mesh_container(LPD3DXFRAME rootframe,char *s){LPD3DXMESHCONTAINER p;p=NULL;if (rootframe->pMeshContainer != NULL) { p=get_mesh_container_part2(rootframe->pMeshContainer,s); if (p != NULL) { return(p); } }if (rootframe->pFrameSibling != NULL) { p=get_mesh_container(rootframe->pFrameSibling,s); if (p != NULL) { return(p); } }if (rootframe->pFrameFirstChild != NULL) { p=get_mesh_container(rootframe->pFrameFirstChild,s); if (p != NULL) { return(p); } }return(NULL);} // --------------------------- get first mesh container of a frame, given the frame name -------------------------- // returns a pointer to the first meshcontainer of a frame, given the name of the frame.// returns NULL if frame not found, or frame meshcontainer is null.// rootframe is the root frame of the skinned mesh.// uses get_bone to get the frame, then returns the frame`s mesh container pointer.// USAGE:// 1. add a texID variable to D3DXMESHCONTAINER_DERIVED// 2. after loading the skinned mesh, use get_mesh_container2 to get a pointer to a mesh container.// 3. if necessary, use meshcontainer->pNextContainer to traverse down the linked list of meshcontainers, // until you get to the one you want.// 4. just before drawing, use the pointer to the mesh container to set the texID// 5. use drawmeshcontainer5 to draw (it uses the texID)// this gives you interchangeable poolable textures for each mesh of the skinned mesh.// AND it works with .x files with no mesh container names, such as those exported from blender. LPD3DXMESHCONTAINER get_mesh_container2(LPD3DXFRAME rootframe,char *s){LPD3DXFRAME p;p=get_bone(rootframe,s);if (p == NULL) { return(NULL); }return(p->pMeshContainer);} // ---------------------------------- clone controller ---------------------------// clones an animation controller.// used when drawing multiple instances of the same skinned mesh, each with its own animation.// use this to clone a copy of the "master" controller you used to load the skinned mesh.// create a clone for each instance you want to draw. only use the master for cloning, not for animation.// when drawing just one instance, there is no need to clone, and its ok to use the master for animation.void clone_controller(ID3DXAnimationController* source,ID3DXAnimationController** target){source->CloneAnimationController( source->GetMaxNumAnimationOutputs(), source->GetMaxNumAnimationSets(), source->GetMaxNumTracks(), source->GetMaxNumEvents(), target);} // ------------------------------------ set animation --------------------------------------// sets a controller to the animation given by "index", and starts the animation from the beginning. // sorry - no animation blending - yet! .// use this to switch between multiple animationsvoid set_ani(ID3DXAnimationController* p,int index){LPD3DXANIMATIONSET a;p->GetAnimationSet((UINT)index,&a);p->SetTrackAnimationSet(0,a);p->SetTrackPosition(0,0);p->ResetTime();p->SetTrackSpeed(0,1.0f);} // -------------------------- update and draw skinned emsh -------------------------------------- // combines update_ani, add_world_transform_to_skeleton, and draw_skinned_meshvoid update_and_draw_skinned_mesh(ID3DXAnimationController* ani_controller_ptr,double seconds,LPD3DXFRAME g_pFrameRoot,LPD3DXMATRIX mWorld_ptr){update_ani(ani_controller_ptr,seconds);add_world_transform_to_skeleton(g_pFrameRoot,mWorld_ptr);draw_skinned_mesh(g_pFrameRoot);} // ========================== skinned mesh test routines 1 and 2 =============================== // toggles a controller to the next animation// only used with the 1st skinned mesh`s controller for the test routine// num_anis is the number of animation sets the controller hasvoid change_ani(ID3DXAnimationController* p,int num_anis){static int current_ani=0;current_ani++;if (current_ani >= num_anis) { current_ani=0; }set_ani(p,current_ani);} // draws a quad at the root of each frame in the hierarchy of the test .x file used.// this tested the basic ability to use combinedtransformmatrix for attaching meshes to bones.void draw_hat(LPD3DXFRAME g_pFrameRoot){Zdrawinfo a;Zcdi(&a); D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )g_pFrameRoot;a.mWorld=pFrame->CombinedTransformationMatrix;Zdraw2(&a); pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameFirstChild);a.mWorld=pFrame->CombinedTransformationMatrix;Zdraw2(&a); pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameFirstChild);a.mWorld=pFrame->CombinedTransformationMatrix;Zdraw2(&a); pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameFirstChild);a.mWorld=pFrame->CombinedTransformationMatrix;Zdraw2(&a); /*pFrame=(D3DXFRAME_DERIVED*)(pFrame->pFrameSibling);a.mWorld=pFrame->CombinedTransformationMatrix;Zdraw2(&a);*/} // draws a spinning textured quad w/ alphatest, no cull, no lighting.// rotation is based on ET of timer #7// used to test drawing skinned meshes with regular meshes in the same // beginscene - end scene block. this helped to determine what needs // to be reset after drawing a skinned mesh.void draw_hat2(){Zdrawinfo a;Zcdi(&a);a.texID=250;a.sx=10.0f;a.sy=10.0f;a.sz=10.0f;a.rx=pi/2.0f;a.ry=(float)Zelapsedtime(7)/1000.0f;a.y=5.0f;a.alphatest=1;Zdraw(&a);} // draws a quad at the root of a bone (frame).// to draw a static mesh relative to the root of a bone, // start with the transform matrix for the object relative to the root of the bone, // then concatenate on the bone`s CombinedTransformationMatrix// don`t forget to call update_ani and add_world_transform_to_skeleton first!// use get_bone to get a D3DXFRAME pointer to the bone by name, and save it for // later use when drawing meshes attached to that bone.void draw_hat3(LPD3DXFRAME bone){Zdrawinfo a;// clear the zdrawinfo struct. // this sets it to drawtype:staticmesh, scale 1, meshID 0 (unit quad), texID 0 (grass tile 1), materialID 0 (Z3D default material), // no cull, no clamp, no alphatest, and no translation or rotation...Zcdi(&a);D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )bone;// set the drawinfo`s world matrix to the combined transform matrix...a.mWorld=pFrame->CombinedTransformationMatrix;// add the drawinfo to the render queue...Zdraw2(&a);} // draws eyeballs attached to bone with offset.// to draw a static mesh relative to the root of a bone, // start with the transform matrix for the object relative to the root of the bone, // then concatenate on the bone`s CombinedTransformationMatrix// don`t forget to call update_ani and add_world_transform_to_skeleton first!// use get_bone to get a D3DXFRAME pointer to the bone by name, and save it for // later use when drawing meshes attached to that bone.void draw_eyeballs(LPD3DXFRAME bone,int texID){Zdrawinfo a;Zcdi(&a);a.meshID=8;a.texID=texID;a.cull=1;D3DXFRAME_DERIVED* pFrame = ( D3DXFRAME_DERIVED* )bone;// use matrix manipulator to create bone to right eyeball transform matrix...Mstart();Mscale(0.1f,0.1f,0.1f);Mrotate(0,90);Mrotate(1,320);Mmove(-0.096f,1.55f,0.244f);// cat combined matrix onto matrix manipulator mat to get world mat...D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);// add to render queue...Zdraw2(&a);// use matrix manipulator to create bone to left eyeball transform matrix...Mstart();Mscale(0.1f,0.1f,0.1f);Mrotate(0,90);Mrotate(1,320);Mmove(0.102f,1.555f,0.206f);// cat combined matrix onto matrix manipulator mat to get world mat...D3DXMatrixMultiply(&a.mWorld,&Mmat,&pFrame->CombinedTransformationMatrix);// add to render queue...Zdraw2(&a);} void skinned_mesh_test(){LPD3DXFRAME g_pFrameRoot = NULL; // ptr to root frame of the skeletonLPD3DXFRAME bone1 = NULL; // ptr to bone.001 of the skeletonID3DXAnimationController* ani_controller_ptr = NULL; // pointer to the animation cotrollerID3DXAnimationController* ani_controller_ptr2 = NULL; // pointer to the animation cotrollerID3DXAnimationController* ani_controller_ptr3 = NULL; // pointer to the animation cotroller// number of animations the mesh hasint num_anis;int result,quit;LPD3DXMESHCONTAINER p;// NOTE: have to put the * with the varname, NOT the type!// using: int* a,b,c; a is an int pointer, but b and c are just ints!// using: int *a,*b,*c; you get a,b,and c as int pointers, the desired result.D3DXMESHCONTAINER_DERIVED *pBody, *pBra, *pLoincloth;result=load_skinned_mesh("tiny.x",&ani_controller_ptr,&g_pFrameRoot);switch (result) { case 1: msg3("error: invalid call!"); break; case 2: msg3("error: out of memory!"); break; case 3: msg3("error: setup bone matrix pointers"); break; }// get number of anis for toggle ani...num_anis=(int)ani_controller_ptr->GetNumAnimationSets(); // get pointer to bone1 for demonstrating drawing a mesh attached to a bone...// note that blender calls it "bone.001" but exports it as "Armature_Bone_001".bone1=get_bone(g_pFrameRoot,"Armature_Bone_001");if (bone1 == NULL) { msg3("error: Armature_Bone_001 not found in g_pFrameRoot!"); } // get pointer to body mesh to demonstrate interchangeable pooled textures for each mesh of a skinned meshp=get_mesh_container2(g_pFrameRoot,"body");if (p == NULL) { msg3("error: body frame meshcontainer is null!"); }pBody=(D3DXMESHCONTAINER_DERIVED*)p;p=get_mesh_container2(g_pFrameRoot,"bra");if (p == NULL) { msg3("error: bra frame meshcontainer is null!"); }pBra=(D3DXMESHCONTAINER_DERIVED*)p;p=get_mesh_container2(g_pFrameRoot,"loincloth");if (p == NULL) { msg3("error: loincloth frame meshcontainer is null!"); }pLoincloth=(D3DXMESHCONTAINER_DERIVED*)p; // display animation set names, and number of animations in each set.// looks like an animation is the keyframes for one bone (IE: D3DXFRAME), // and an animationset is all the keyframes for all the bones in a skeleton (IE: a D3DXFRAME hierarchy).// the number of animations in an animationset should match the // number of animated bones (D3DXFRAMES) in the frame hierachy.// NOTE: looks like animation sets appear in the controller in reverse order of their order in the .x file!// apparently it does an "add at front" when it adds an animation set to the list of animation sets in the controller. /* int i;// i jLPD3DXANIMATIONSET a;const char *s=NULL;char s2[100]; for (i=0; iGetAnimationSet((UINT)i,&a);// NOTE: looks like getname returns a pointer to an exisiting string, use it in a readonly manner, and do NOT free it! s=a->GetName(); strcpy_s(s2,100,s); msg3(s2);// cr j (int)a->GetNumAnimations// c i2s j s2// c msg3 s2// NOTE: GetAnimationNameByIndex also appears to return a pointer to an exisiting string, // the docs are somewhat unclear.// use it in a readonly manner, and do NOT free it! } */ // clone ani controllers:// apparently, you want to load one skinned mesh with a "master" controller, // then clone the master controller for each instance you want to draw. // so each skinned mesh instance will have a controller, plus you`ll have the master,// which is just used for cloning, not animating.// never really found out why everyone does it this way, // maybe that way you don`t have to reset everything in the clone, such as track position.// never tested using the master for both animating and cloning.// so ani_controller_ptr is the master, and ani_controller_ptr2 and 3 are for the two instances the test draws.clone_controller(ani_controller_ptr,&ani_controller_ptr2);// init controller to play the 1st animationset_ani(ani_controller_ptr2,0);clone_controller(ani_controller_ptr,&ani_controller_ptr3);// init controller to play the 2nd animationset_ani(ani_controller_ptr3,1);// set a directional light...Zsetlite(0,0.0f,-1.0f,1.0f,1.0f);// turn it on...Zlite(0,1);// set view matrix....Zsetcam(0.0f,5.0f,-10.0f,0.0f,0.0f,0.0f);quit=0;// timer 7 ET is used for the rotation of the quad in drawhat2Zstarttimer(7);// set material to z3d default material....Zsetmaterial(0);AAturnon();while (!quit) {// timer 8 is used for framerate limiter.// like Caveman 3.0, the test runs at 15fps. i`ve also run it at 60 fps no problem. Zstarttimer(8); Zclearscreen(32,0,32); Zclearzbuf(); Zbeginscene();// clear render queue... Zcleardrawlist();// draw quad relative to bone1. this call adds the quad to the render queue... draw_hat3(bone1);// turn off ligting (makes the quad easier to see)... Zlighting(0);// render the render queue... Zdrawlist();// turn lighting back on Zlighting(1); /*// update ani controller for 1st skinned mesh... update_ani(ani_controller_ptr2,0.066);// init z3d matrix manipulator to identity matrix... Mstart();// concatenate on a traslation matrix... Mmove(-1.0f,0.0f,0.0f);// add 1st skinned mesh world transform to skeleton...// Mmat is the z3d matrix manipulator`s matrix add_world_transform_to_skeleton(g_pFrameRoot,&Mmat); */ // set textures for 1st skinned mesh...// c Zsettex 387 pBody->texID=387; pBra->texID=159; pLoincloth->texID=159; /*// draw 1st skinned mesh... draw_skinned_mesh(g_pFrameRoot); */ // init z3d matrix manipulator to identity matrix... Mstart();// concatenate on a traslation matrix... Mmove(-1.0f,0.0f,0.0f);// update and draw skinned mesh with one call... update_and_draw_skinned_mesh(ani_controller_ptr2,0.066,g_pFrameRoot,&Mmat); // drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...// set z3d state manager current mesh to none... Zcurrentmesh=-1;// reset FVF back to z3d FVF... Zd3d_device_ptr->SetFVF(ZFVF); // ok, draw some eyeballs attached to the head bone (now that its updated)... draw_eyeballs(bone1,266); /*// update ani controller for 2nd skinned mesh... update_ani(ani_controller_ptr3,0.066);// init z3d matrix manipulator to identity matrix... Mstart();// concatenate on a traslation matrix... Mmove(1.0f,0.0f,0.0f);// add 2nd skinned mesh world transform to skeleton...// Mmat is the z3d matrix manipulator`s matrix add_world_transform_to_skeleton(g_pFrameRoot,&Mmat); */ // set textures for 2nd skinned mesh...// c Zsettex 388 pBody->texID=388; pBra->texID=192; pLoincloth->texID=192; /*// draw 2nd skinned mesh... draw_skinned_mesh(g_pFrameRoot); */ // init z3d matrix manipulator to identity matrix... Mstart();// concatenate on a traslation matrix... Mmove(1.0f,0.0f,0.0f);// update and draw skinned mesh with one call... update_and_draw_skinned_mesh(ani_controller_ptr3,0.066,g_pFrameRoot,&Mmat); // drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...// set z3d state manager current mesh to none... Zcurrentmesh=-1;// reset FVF back to z3d FVF... Zd3d_device_ptr->SetFVF(ZFVF); tx(100,800,"press ESC to quit"); tx(100,850,"press SPACE to change animation"); AAdraw();// end scene and present... Zshowscene();// if they hit spacebar, toggle the 1st skinned mesh to the next animation... if (Zkeypressed(VK_SPACE)) { change_ani(ani_controller_ptr2,num_anis); while (Zkeypressed(VK_SPACE)) { } }// if they hit escape, quit the test... if (Zkeypressed(VK_ESCAPE)) { quit=1; } AAprocess_input();// framerate limiter: wait until 66 millisconds has passed before beginning next frame... while (Zelapsedtime(8)<66) { } }msg3("unloading stuff...");AAturnoff();// unload the mesh and master controller...unload_skinned_mesh(ani_controller_ptr,g_pFrameRoot);// release the clones...ani_controller_ptr2->Release();ani_controller_ptr3->Release();msg3("all done!");} // uses update_and_draw_skinned_mesh w/ drawmeshcontainer5// code to display animation set names has been removedvoid skinned_mesh_test2(){LPD3DXFRAME g_pFrameRoot = NULL; // ptr to root frame of the skeletonLPD3DXFRAME bone1 = NULL; // ptr to bone.001 of the skeletonID3DXAnimationController* ani_controller_ptr = NULL; // pointer to the animation cotrollerID3DXAnimationController* ani_controller_ptr2 = NULL; // pointer to the animation cotrollerID3DXAnimationController* ani_controller_ptr3 = NULL; // pointer to the animation cotroller// number of animations (animation sets) the mesh hasint num_anis;int result,quit;LPD3DXMESHCONTAINER p;// NOTE: have to put the * with the varname, NOT the type!// using: int* a,b,c; a is an int pointer, but b and c are just ints!// using: int *a,*b,*c; you get a,b,and c as int pointers, the desired result.D3DXMESHCONTAINER_DERIVED *pBody, *pBra, *pLoincloth;result=load_skinned_mesh("tiny.x",&ani_controller_ptr,&g_pFrameRoot);switch (result) { case 1: msg3("error: invalid call!"); break; case 2: msg3("error: out of memory!"); break; case 3: msg3("error: setup bone matrix pointers"); break; }// get number of anis for toggle ani...num_anis=(int)ani_controller_ptr->GetNumAnimationSets(); // get pointer to bone1 for demonstrating drawing a mesh attached to a bone...// note that blender calls it "bone.001" but exports it as "Armature_Bone_001".bone1=get_bone(g_pFrameRoot,"Armature_Bone_001");if (bone1 == NULL) { msg3("error: Armature_Bone_001 not found in g_pFrameRoot!"); } // get pointer to body mesh to demonstrate interchangeable pooled textures for each mesh of a skinned meshp=get_mesh_container2(g_pFrameRoot,"body");if (p == NULL) { msg3("error: body frame meshcontainer is null!"); }pBody=(D3DXMESHCONTAINER_DERIVED*)p;p=get_mesh_container2(g_pFrameRoot,"bra");if (p == NULL) { msg3("error: bra frame meshcontainer is null!"); }pBra=(D3DXMESHCONTAINER_DERIVED*)p;p=get_mesh_container2(g_pFrameRoot,"loincloth");if (p == NULL) { msg3("error: loincloth frame meshcontainer is null!"); }pLoincloth=(D3DXMESHCONTAINER_DERIVED*)p; // clone ani controllers:// apparently, you want to load one skinned mesh with a "master" controller, // then clone the master controller for each instance you want to draw. // so each skinned mesh instance will have a controller, plus you`ll have the master,// which is just used for cloning, not animating.// never really found out why everyone does it this way, // maybe that way you don`t have to reset everything in the clone, such as track position.// never tested using the master for both animating and cloning.// so ani_controller_ptr is the master, and ani_controller_ptr2 and 3 are for the two instances the test draws.clone_controller(ani_controller_ptr,&ani_controller_ptr2);// init controller to play the 1st animationset_ani(ani_controller_ptr2,0);clone_controller(ani_controller_ptr,&ani_controller_ptr3);// init controller to play the 2nd animationset_ani(ani_controller_ptr3,1);// set a directional light...Zsetlite(0,0.0f,-1.0f,1.0f,1.0f);// turn it on...Zlite(0,1);// set view matrix....Zsetcam(0.0f,5.0f,-10.0f,0.0f,0.0f,0.0f);quit=0;// timer 7 ET is used for the rotation of the quad in drawhat2Zstarttimer(7);// set material to z3d default material....Zsetmaterial(0);AAturnon();while (!quit) {// timer 8 is used for framerate limiter.// like Caveman 3.0, the test runs at 15fps. i`ve also run it at 60 fps no problem. Zstarttimer(8); Zclearscreen(32,0,32); Zclearzbuf(); Zbeginscene(); // set textures for 1st skinned mesh... pBody->texID=387; pBra->texID=159; pLoincloth->texID=159; // init z3d matrix manipulator to identity matrix... Mstart();// concatenate on a traslation matrix... Mmove(-1.0f,0.0f,0.0f);// update and draw skinned mesh with one call... update_and_draw_skinned_mesh(ani_controller_ptr2,0.066,g_pFrameRoot,&Mmat); // drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...// set z3d state manager current mesh to none... Zcurrentmesh=-1;// reset FVF back to z3d FVF... Zd3d_device_ptr->SetFVF(ZFVF); // clear render queue... Zcleardrawlist();// ok, draw some eyeballs attached to the head bone (now that its updated)...// this call adds them to the render queue. draw_eyeballs(bone1,266);// turn off ligting (makes the quad easier to see)... Zlighting(0);// render the render queue... Zdrawlist();// turn lighting back on Zlighting(1); // set textures for 2nd skinned mesh... pBody->texID=388; pBra->texID=192; pLoincloth->texID=192; // init z3d matrix manipulator to identity matrix... Mstart();// concatenate on a traslation matrix... Mmove(1.0f,0.0f,0.0f);// update and draw skinned mesh with one call... update_and_draw_skinned_mesh(ani_controller_ptr3,0.066,g_pFrameRoot,&Mmat); // drawmeshcontainer (all versions) changes the current mesh and fvf, must reset...// set z3d state manager current mesh to none... Zcurrentmesh=-1;// reset FVF back to z3d FVF... Zd3d_device_ptr->SetFVF(ZFVF); // clear render queue... Zcleardrawlist();// draw quad relative to bone1. this call adds the quad to the render queue... draw_hat3(bone1);// turn off ligting (makes the quad easier to see)... Zlighting(0);// render the render queue... Zdrawlist();// turn lighting back on Zlighting(1); tx(100,800,"press ESC to quit"); tx(100,850,"press SPACE to change animation"); AAdraw();// end scene and present... Zshowscene();// if they hit spacebar, toggle the 1st skinned mesh to the next animation... if (Zkeypressed(VK_SPACE)) { change_ani(ani_controller_ptr2,num_anis); while (Zkeypressed(VK_SPACE)) { } }// if they hit escape, quit the test... if (Zkeypressed(VK_ESCAPE)) { quit=1; } AAprocess_input();// framerate limiter: wait until 66 millisconds has passed before beginning next frame... while (Zelapsedtime(8)<66) { } }msg3("unloading stuff...");AAturnoff();// unload the mesh and master controller...unload_skinned_mesh(ani_controller_ptr,g_pFrameRoot);// release the clones...ani_controller_ptr2->Release();ani_controller_ptr3->Release();msg3("all done!");} /*skinned mesh info: exporting from blender: 1. link the 1st ani to the armature object. leave the rest unlinked with a fake user. or leave all unlinked with fake users. do not link animations to meshes - only link them to the skeleton! 2. select the armature and mesh objects, then export selected objects.3. delete the default_animation from the exported .x file with notepad. not doing so seems to interfere with changing animations. not true! xyz eulers arent exported!4. export fails if more than 1 armature in the scene.5. ALL animation sets are exported from startframe through endframe. for animationsets with different lengths, they must be exported to different files, then paseted together with notepad.6. only quaternion rotations are exported? xyz eulers dont get exported? apparently so!7. apply modifiers (armature modifier) is not required.8. export fps seems to be required. unable to determine default value of anim ticks per sec.9. export default pose seems to not matter - not required. possible optimizations:1. different skinning methods. test each. only nonindexed fixed function pure device hw vertex blending has been tested so far. but theres only about 6 fps difference on my pc with tiny sample between non-indexed fixed function hw vertex blending and indexed shader methods.2. AnimationSet resource pooling.3. body mesh resource pooling w/ interchangeable heads.4. convert the static meshmaster and controller pools to dynamic memory (vectors, etc)5. reorganize from PODs and procedural code into into oo classes. the meshmaster and controller pools would be classes. pretty much everything else would be methods in those classes or part of low level classes used by the meshmaster and controller pool classes.6. add ability to draw a mesh from a skinned mesh using the render queue separate static mesh attached to a bone:hair clothes - might need to be rigged to the skeletonwpns & equipment - some might need to be rigged to the skeleton different textures:skintoneseye colors different meshes:single mesh for head and body. separate mesh for eyes. faces (wide / narrow nose, eye shape, etc): separate heads can leave a seam. editing complete meshes is slightly less work (dont have to detach a head). therefore, caveman will use a single head and body mesh. this means: create a complete head and body mesh for each face. setting texture on a per mesh basis vs not at all makes no difference in fps when drawing 100 meshes. the bottleneck is the number of tris, not setting textures. sharing the same animation between two different skinned meshes:1. create, rig, and animate the first skinned mesh. save and export.2. delete the mesh, leaving the animated rig.3. append the second mesh (load it into the existing scene)4. rig the mesh. save and export. all done! skinned mesh LOD:1. create and export mesh as usual2. decimate mesh, then export again.3. load both meshes4. cm or npc now has two meshIDs, not one. one high LOD, one low LOD.5. when its time to draw, decide hi or low LOD based on distance. 6. if the controller`s mesh is not the LOD you want, get the current animation time from the controller, destroy the mesh instance, create a mesh instance with the correct LOD mesh, then set the new controllers time to the animation time you got from the old controller. this way the animation continues from where it was.7. update and draw as usual. adapting all this for use in caveman:every skinned mesh loaded will have a "master" animation controller to go with it.so you'll have a struct that holds a skinned mesh, and its master animation controller.and you'll have a second type of struct for an instance of a skinned mesh, with a cloned animation controller, and a pointer to the skinned mesh to use.then you'll have an array of each of these structs.and a load_mesh routine that loads a mesh into the array of skinned meshes & master controllers.and a activate_controller routine that activates a struct in the array of cloned controllers, clones the controller, and sets the skinned mesh pointer (g_pFrameRoot).then you'll have a draw_instance routine that draws the skinned mesh using a cloned controller and its skinned mesh pointer.and you'll need a deactivate_controller routineand an unload_mesh routinea skinned mesh struct should also hold a pointer to the head bone for drawing hair and eyesattached to the head bone, and pointers to the various meshcontainers (body, bra, loincloth, etc)for setting textures on the fly. */
Sign in to follow this