• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Medo Mex

Combining multiple .x files into a single .x file

29 posts in this topic

I have created multiple .x files, each one got different animation, example: "walk.x", "run.x", "die.x".

 

How do I combine them together to create "character.x"?

 

I tried using DirectX SDK tool called MeshView, but it's not working, I don't get to see the animations in the program.

 

I'm using Alin's DirectX Exporter for exporting .x file from 3Ds Max.

0

Share this post


Link to post
Share on other sites

You don't need to do this programmatically. You can do it from the command line or using a batch script.

 

Assuming all your X files are in text format, just trim all but one of the X Files on disk so that they only have "AnimationSets" in them. Leave one of the X files so it has the other skinned mesh pieces (like Frames and Meshes), now just use 'copy' from the command line to concatenate all the files into one.

 

e.g.

 

copy /A bugbear.x + bugbear_walk.x + bugbear_idle.x bugbear_all.x

 

This leaves a file bugbear_all.x that has the primary skinned mesh as well as the walk and idle animation sets. Unfortunately the copy command leaves a redundant EOF character at the end of the file, which you will have to remove somehow.

2

Share this post


Link to post
Share on other sites

If it's BIP  animations, then simply combine them in the 'Mixer' in Max (select root skeletal node and click on the Motion tab). I don't know if Alin's exporter supports multiple animations in one file, but Pandasoft's exporter most certainly does. Doing it this way means you don't have to post-combine them if you use the animations on another character.

0

Share this post


Link to post
Share on other sites

as i suspected, and as Steve's post confirms, you can put multiple animations in a single .x file. another way to do it is to use text format, and simply cut and paste them together using a text editor. double check your .xof file format description in the directx docs to make sure you get the syntax right. .x files tend to nest stuff using lots of "{ }'s " as i recall. 

 


since i don't use dx animation system anymore

 

out of curiosity, what do you use now?

0

Share this post


Link to post
Share on other sites

out of curiosity, what do you use now?

 

I am using my own mesh format and animation system, since i always wanted full control over things and i really hated dx a.s. guts.

It is working but not perfect yet (having some problems with transitions between animations... long story) but i am working really hard and i will resolve my issues.

Also i have created 3ds max utility plugin using their IGame interface, with which i was surprised how easy it was to use, to export animation (and other) data that i need.

0

Share this post


Link to post
Share on other sites
Okay, trying to open and save .x file and the following code fail, getting D3DERR_INVALIDCALL when I call D3DXSaveMeshHierarchyToFile():
LPD3DXANIMATIONCONTROLLER animController;
hr = D3DXLoadMeshHierarchyFromX(filePath, D3DXMESH_MANAGED, device, 
memoryAllocator, NULL, &frameRoot, &animController);
assert(SUCCEEDED(hr)); // OK

hr = D3DXSaveMeshHierarchyToFile("character_with_all_animations.x", D3DXF_FILEFORMAT_TEXT, frameRoot, animController, NULL);
assert(SUCCEEDED(hr)); // <---- ERROR: Getting D3DERR_INVALIDCALL here
Edited by Medo3337
0

Share this post


Link to post
Share on other sites

I have just tried the same thing, saving immediately after load function and it succeeded for me.

Enable dx debug runtime to examine any error logs you might have in output window. Do you know how?

Edited by belfegor
0

Share this post


Link to post
Share on other sites

@belfegor: Notice that "memoryAllocator" is associated with MeshHierarchy class.

 

I'm running debug mode and don't see any error message in the output window.

 

Here is main.cpp source code:

// main.cpp
#include "windows.h"
#include "d3d9.h"
#include "d3dx9.h"
#include "MeshHierarchy.h"
#include "assert.h"

#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

LPDIRECT3DDEVICE9 device;
LPDIRECT3D9 d3d;

int main()
{
d3d = Direct3DCreate9(D3D9b_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.BackBufferWidth = 800;
d3dpp.BackBufferHeight = 600;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.hDeviceWindow = GetConsoleWindow();
d3dpp.BackBufferCount = 1;

HRESULT hr;
hr = d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, GetConsoleWindow(),
                       D3DCREATE_HARDWARE_VERTEXPROCESSING,
                       &d3dpp,
                       &device);
assert(SUCCEEDED(hr));

CMeshHierarchy *memoryAllocator = new CMeshHierarchy;

LPD3DXFRAME frameRoot;
LPD3DXANIMATIONCONTROLLER animController;
hr = D3DXLoadMeshHierarchyFromX("character.x", D3DXMESH_MANAGED, device, 
memoryAllocator, NULL, &frameRoot, &animController);
assert(SUCCEEDED(hr));

hr = D3DXSaveMeshHierarchyToFile("character_with_all_animations.x", D3DXF_FILEFORMAT_TEXT, frameRoot, animController, NULL);
if (hr == D3DERR_INVALIDCALL)
   MessageBox(NULL, "Invalid D3D Call", "Error", MB_ICONERROR | MB_OK);
assert(SUCCEEDED(hr));
}
0

Share this post


Link to post
Share on other sites

You have not enabled DX debug runtime!

 

1. You need to link with debug dx lib: d3dx9d.lib (notice the d)

2. Set this at top of your cpp (before including any dx headers):

#define D3D_DEBUG_INFO          1

3. Set this options in dx control panel (dxcpl.exe), you can find it in DX SDK utilities folder:

dxcpl.jpg

4. Run your app with debugging and close it

5. Examine output window for DX error/warning logs

2

Share this post


Link to post
Share on other sites
I fixed this problem, now I have a slight another problem, I couldn't combine the animations together:
vector<LPCSTR> filePath;
filePath.push_back("walk.x");
filePath.push_back("die.x");

LPD3DXFRAME frameRoot;
LPD3DXANIMATIONCONTROLLER newController;

DWORD numAllAnimations = 0;
for(UINT i = 0; i < filePath.size(); i++)
{
LPD3DXANIMATIONCONTROLLER animController;
hr = D3DXLoadMeshHierarchyFromX(filePath[i], D3DXMESH_MANAGED, device, 
memoryAllocator, NULL, &frameRoot, &animController);
assert(SUCCEEDED(hr));

numAllAnimations += animController->GetNumAnimationSets();

hr = animController->CloneAnimationController(animController->GetMaxNumAnimationOutputs(),
                   numAllAnimations,
                   animController->GetMaxNumTracks(),
                   animController->GetMaxNumEvents(), 
                   &newController);
assert(SUCCEEDED(hr));

for(UINT j = 0; j < animController->GetNumAnimationSets(); j++)
{
    LPD3DXANIMATIONSET set;
    animController->GetAnimationSet(j, &set);
    newController->RegisterAnimationSet(set);
}
}

delete memoryAllocator;
memoryAllocator=0;

hr = D3DXSaveMeshHierarchyToFile("G:\\character_with_all_animations.x", D3DXF_FILEFORMAT_TEXT, frameRoot, newController, NULL);
if (hr == D3DERR_INVALIDCALL)
MessageBox(NULL, "Invalid D3D Call", NULL, MB_ICONERROR | MB_OK);
assert(SUCCEEDED(hr));

I can only get one of the animations to work, not both.

0

Share this post


Link to post
Share on other sites

You have to put CloneAnimationController before loop so it is cloned only once. To make it easy for me to explain with little code as possible i am gonna assume you have only one animation set in each x file, then you could improve it later for multiple animations per x file when you got this working:

vector<LPCSTR> filePath;
filePath.push_back("walk.x");
filePath.push_back("die.x");
filePath.push_back("idle.x");
filePath.push_back("crouch.x");
filePath.push_back("wave.x");
filePath.push_back("jump.x");
 
DWORD numAllAnimations = filePath.size();

LPD3DXFRAME frameRoot, othersFrameRoot;
LPD3DXANIMATIONCONTROLLER newController;
LPD3DXANIMATIONCONTROLLER animController;

D3DXLoadMeshHierarchyFromX(filePath[0], D3DXMESH_MANAGED, device, 
memoryAllocator, NULL, &frameRoot, &animController);
 
animController->CloneAnimationController(animController->GetMaxNumAnimationOutputs(),
  numAllAnimations,
  animController->GetMaxNumTracks(),
  animController->GetMaxNumEvents(), 
  &newController);

for(UINT i = 1; i < filePath.size(); i++) // note that we skip first
{

    D3DXLoadMeshHierarchyFromX(filePath[i], D3DXMESH_MANAGED, device, 
memoryAllocator, NULL, &othersFrameRoot, &animController);

LPD3DXANIMATIONSET set;
animController->GetAnimationSet(0, &set);
newController->RegisterAnimationSet(set);
}

delete memoryAllocator;
memoryAllocator=0;

hr = D3DXSaveMeshHierarchyToFile("G:\\character_with_all_animations.x", D3DXF_FILEFORMAT_TEXT, frameRoot, newController, NULL);

I am not sure if CloneAnimationCotroller will copy animation set in new controller from one that with cloned from, since MSDN docs are incomplete and obscure, so you need to test that.

 

As always check all functions for failure, i removed it for readability. Also there is some memory leaks here but it doesn't matter much you can fix that later until you got this working first.

Edited by belfegor
1

Share this post


Link to post
Share on other sites

@belfegor: Working now! some animations like "die" should not loop, how do I stop it from looping? I want to play "some" animations just once.

 

"walk", "run" animations working correctly since they should loop, however "die" is looping while it should not.

0

Share this post


Link to post
Share on other sites

Is it possible that I determine that the animation should play once or loop from 3Ds Max?

 

I never use D3DXCreateKeyframedAnimationSet() in my code, instead, I'm getting the existing animation set using GetAnimationSet() and assigning it to the track.

0

Share this post


Link to post
Share on other sites

Is it possible that I determine that the animation should play once or loop from 3Ds Max?

It doesn't work like that.

 

Just stop updating your bone matrices when "die" animation finishes, this way you also save yourself a dozen matrix multiplies and what not.

Edited by belfegor
0

Share this post


Link to post
Share on other sites

Okay, so how do I know that the animation finished so I can stop updating the bone matrices?

 

Or maybe there is a way to set D3DXPLAY_ONCE, how do I update the following code to set D3DXPLAY_ONCE?

UINT animationIndex = 0; // Animation that I want to play
LPD3DXANIMATIONSET set;
animController->GetAnimationSet(animationIndex, &set);
0

Share this post


Link to post
Share on other sites


Okay, so how do I know that the animation finished so I can stop updating the bone matrices?

 

Or maybe there is a way to set D3DXPLAY_ONCE, how do I update the following code to set D3DXPLAY_ONCE?

This is why i hate ("hate" is even light word for what i think about) dx animation system as there is no convenient way to do this, but instead you need to do stupid hoops and hacks to setup such a simple thing.

From what i gathered from Frank Luna animation book you need to set a callback and create compressed keyframed animation set, should look something like this:

...
ID3DXKeyframedAnimationSet* animSetTemp = 0;  _animCtrl->GetAnimationSet(
dieAnimationSetInx, (ID3DXAnimationSet**)&animSetTemp);
// Compress it.
 ID3DXBuffer* compressedInfo = 0; 
animSetTemp->Compress(D3DXCOMPRESS_DEFAULT,
0.5f, 0, &compressedInfo);
 
UINT numCallbacks = 1; 
D3DXKEY_CALLBACK key;
 
double ticks = animSetTemp->GetSourceTicksPerSecond();
keys.Time = animSetTemp->GetPeriod()*ticks;  
keys.pCallbackData = (void*)&MyCallbackData;
 
ID3DXCompressedAnimationSet* compressedAnimSet = 0; 
D3DXCreateCompressedAnimationSet(animSetTemp->GetName(),
animSetTemp->GetSourceTicksPerSecond(),
animSetTemp->GetPlaybackType(), compressedInfo,
numCallbacks, &key, &compressedAnimSet);
 
compressedInfo->Release();
// Remove the old (non compressed) animation set.
_animCtrl->UnregisterAnimationSet(animSetTemp);
animSetTemp->Release();
// Add the new (compressed) animation set.
_animCtrl->RegisterAnimationSet(compressedAnimSet);
// Hook up the animation set to the first track.
_animCtrl->SetTrackAnimationSet(0, compressedAnimSet);
compressedAnimSet->Release();
0

Share this post


Link to post
Share on other sites

Hmm, the code is throwing "Access violation" exception, but the purpose in the code is to set a callback for the animations.

 

Why can't I simply set the flag D3DXPLAY_ONCE? I believe it can be done by simply setting D3DXPLAY_ONCE somewhere instead of doing callbacks.

0

Share this post


Link to post
Share on other sites

Here is what I tried to do to set D3DXPLAY_ONCE flag (not working though):

ID3DXKeyframedAnimationSet* set = 0;
animController->GetAnimationSet(animationIndex, (ID3DXAnimationSet**)&set);
D3DXCreateKeyframedAnimationSet(set->GetName(), set->GetSourceTicksPerSecond(), D3DXPLAY_ONCE, set->GetNumAnimations(), set->GetNumCallbackKeys(), NULL, &set);

// Code here to set the track and play the animation...
0

Share this post


Link to post
Share on other sites

From what i can tell D3DXCreateKeyframedAnimationSet only outputs ID3DXKeyframedAnimationSet it does not copy SRT keys from existing animation set, you need to that yourself.
Note that i have not done this before so i am going to guess with help from MSDN documentation:
 

ID3DXAnimationSet* dieAnimationSet;
ID3DXKeyframedAnimationSet* dieKeyFramedAnimationSet;
 
animController->GetAnimationSet(animationIndex, &dieAnimationSet);
 
// creates "empty" keyframed-animation-set
D3DXCreateKeyframedAnimationSet(
    dieAnimationSet->GetName(), 
    dieAnimationSet->GetSourceTicksPerSecond(), 
    D3DXPLAY_ONCE,
    dieAnimationSet->GetNumAnimations(), 
    dieAnimationSet->GetNumCallbackKeys(), 
    NULL, 
    &dieKeyFramedAnimationSet);
 
// now you need to copy SRT keys data from dieAnimationSet to dieKeyFramedAnimationSet
// some thing here i am not sure how to obtain as documentation is not clear enough
LPD3DXKEY_VECTOR3 scaleKeys[ numKeys ];
LPD3DXKEY_QUATERNION rotKeys[ numKeys ];
LPD3DXKEY_VECTOR3 translKeys[ numKeys ];

for each numKeys and "periodic/local/whatever_time_they_mean_by_this"
{
    D3DXVECTOR3 scale, transl;
    D3DXQUATERNION rot;
    dieAnimationSet->GetSRT( timePos, 0, &scale, &rot, &transl );
    scaleKeys[ i ].Time = timePos;
    scaleKeys[ i ].Value = scale;
    rotKeys[ i ].Time = timePos;
    rotKeys[ i ].Value = rot;
    translKeys[ i ].Time = timePos;
    translKeys[ i ].Value = transl;
}

DWORD newAnimIndex;
dieKeyFramedAnimationSet->RegisterAnimationSRTKeys(
    "animation_name",
    numKeys, numKeys, numKeys,
    scaleKeys, rotKeys, translKeys, &newAnimIndex);

// then here unregister old dieAnimationSet and register new dieKeyFramedAnimationSet to animation controler

Now look how stupid this is for setting such a small and trivial thing.

Edited by belfegor
0

Share this post


Link to post
Share on other sites

@belfegor: I'm new to D3DX animation, so I will need a little more help
 

and "periodic/local/whatever_time_they_mean_by_this"

 

 

I don't understand this part

 

How do I get numKeys?

 

The following functions doesn't exists:

dieAnimationSet->GetSourceTicksPerSecond()
dieAnimationSet->GetNumCallbackKeys()
0

Share this post


Link to post
Share on other sites

I am sorry, i just copied your code from above for that function and didn't look if it is correct.

I commented in code that i don't know for some things how to obtain them.

You could put it like this for test:

D3DXCreateKeyframedAnimationSet(
    dieAnimationSet->GetName(),
    30.0, // for this check in 3ds max and/or exporter options on which value it was set
    D3DXPLAY_ONCE, 
    1, // assume 1 animation in set
    0, // assume no callback keys, otherwise i don't know how to obtain them
    NULL,
    &dieKeyFramedAnimationSet);

Then for numKeys you could try (my guess is that GetPeriod() is in seconds?):

double timeStep = dieAnimationSet->GetPeriod() / 30.0;
DWORD numKeys = std::floor(dieAnimationSet->GetPeriod() * 30);
double timePos = 0.0;
for(DWORD i =0; i < numKeys; ++i, timePos += timeStep)
{
    D3DXVECTOR3 scale, transl;
    D3DXQUATERNION rot;
    dieAnimationSet->GetSRT( timePos, 0, &scale, &rot, &transl );
    ...
}

Maybie there is an easier way but i don't see one.

Edited by belfegor
0

Share this post


Link to post
Share on other sites

Now, the animation is not playing anymore :/

 

Here is the code:

LPD3DXANIMATIONSET tempSet = 0;
LPD3DXKEYFRAMEDANIMATIONSET set;
m_animController->GetAnimationSet(index, &tempSet);


D3DXCreateKeyframedAnimationSet(
    tempSet->GetName(),
    30.0, // for this check in 3ds max and/or exporter options on which value it was set
    D3DXPLAY_ONCE, 
    1, // assume 1 animation in set
    0, // assume no callback keys, otherwise i don't know how to obtain them
    NULL,
    &set);


double timeStep = tempSet->GetPeriod() / 30.0;
DWORD numKeys = std::floor(tempSet->GetPeriod() * 30);


// now you need to copy SRT keys data from dieAnimationSet to dieKeyFramedAnimationSet
// some thing here i am not sure how to obtain as documentation is not clear enough
LPD3DXKEY_VECTOR3 scaleKeys = new D3DXKEY_VECTOR3[numKeys]();
LPD3DXKEY_QUATERNION rotKeys = new D3DXKEY_QUATERNION[numKeys]();
LPD3DXKEY_VECTOR3 translKeys = new D3DXKEY_VECTOR3[numKeys]();


double timePos = 0.0;
for(DWORD i =0; i < numKeys; ++i, timePos += timeStep)
{
    D3DXVECTOR3 scale, transl;
    D3DXQUATERNION rot;
tempSet->GetSRT( timePos, 0, &scale, &rot, &transl );


scaleKeys[ i ].Time = timePos;
    scaleKeys[ i ].Value = scale;
    rotKeys[ i ].Time = timePos;
    rotKeys[ i ].Value = rot;
    translKeys[ i ].Time = timePos;
    translKeys[ i ].Value = transl;
}


DWORD newAnimIndex;
set->RegisterAnimationSRTKeys(
    tempSet->GetName(),
    numKeys, numKeys, numKeys,
    scaleKeys, rotKeys, translKeys, &newAnimIndex);


animController->UnregisterAnimationSet(tempSet);
tempSet->Release();
animController->RegisterAnimationSet(set);

 

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0