Jump to content

  • Log In with Google      Sign In   
  • Create Account

FREE SOFTWARE GIVEAWAY

We have 4 x Pro Licences (valued at $59 each) for 2d modular animation software Spriter to give away in this Thursday's GDNet Direct email newsletter.


Read more in this forum topic or make sure you're signed up (from the right-hand sidebar on the homepage) and read Thursday's newsletter to get in the running!


Combining multiple .x files into a single .x file


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
29 replies to this topic

#1 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 04 July 2013 - 12:37 PM

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.



Sponsor:

#2 belfegor   Crossbones+   -  Reputation: 2723

Like
3Likes
Like

Posted 04 July 2013 - 01:21 PM

If you can get it the easy way with MeshView, you (might) need to use: ID3DXAnimationController::CloneAnimationController then RegisterAnimationSet then D3DXSaveHierarchyToFile.

I done it once this way but deleted the project long time ago since i don't use dx animation system anymore, so i will guess few steps here:

Load meshes with animations.

Clone and create new animation controller :

DWORD numAllAnimations = 0;
for i = numMeshes
    numAllAnimatios += mesh[i]->getAnimController()->GetNumAnimationSets();
 
// animCtrl is animation controller from first mesh, it doesn't matter much i just need starting point
LPD3DXANIMATIONCONTROLLER newController;
animCtrl->CloneAnimationController(
        animCtrl->GetMaxNumAnimationOutputs(),
        numAllAnimations,
        animCtrl->GetMaxNumTracks(),
        animCtrl->GetMaxNumEvents(), &newController);

Register each of their animation sets to new controller:

for i = numMeshes
    for j = mesh[i].numAnimationSet
        LPD3DXANIMATIONSET set;
        mesh[i]->getAnimController()->GetAnimationSet(j, &set);
        newController->RegisterAnimationSet(set);

then save your hierarchy to file:

D3DXSaveMeshHierarchyToFile("character_with_all_animations.x", D3DXF_FILEFORMAT_TEXT, &rootFrame, newController, 0);

That should be it.

 

Note that you should check all functions for failure!

 

EDIT:

You might want to skip registering first mesh animation sets as it is probably already included into new animation controller since we cloned it from.


Edited by belfegor, 04 July 2013 - 01:32 PM.


#3 Steve_Segreto   Crossbones+   -  Reputation: 1552

Like
2Likes
Like

Posted 04 July 2013 - 01:55 PM

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.



#4 LancerSolurus   Members   -  Reputation: 617

Like
0Likes
Like

Posted 04 July 2013 - 02:40 PM

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.


******************************************************************************************
Youtube Channel


#5 Norman Barrows   Crossbones+   -  Reputation: 2349

Like
0Likes
Like

Posted 04 July 2013 - 02:50 PM

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?


Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

 

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

 

 


#6 belfegor   Crossbones+   -  Reputation: 2723

Like
0Likes
Like

Posted 04 July 2013 - 03:21 PM


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.



#7 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 05 July 2013 - 10:00 AM

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, 05 July 2013 - 10:12 AM.


#8 belfegor   Crossbones+   -  Reputation: 2723

Like
0Likes
Like

Posted 05 July 2013 - 10:26 AM

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, 05 July 2013 - 10:27 AM.


#9 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 05 July 2013 - 11:05 AM

@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));
}


#10 belfegor   Crossbones+   -  Reputation: 2723

Like
2Likes
Like

Posted 05 July 2013 - 11:13 AM

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



#11 Steve_Segreto   Crossbones+   -  Reputation: 1552

Like
0Likes
Like

Posted 05 July 2013 - 11:43 AM

D3DXSaveMeshHierarchToFile on MSDN says (http://msdn.microsoft.com/en-us/library/windows/desktop/bb205427(v=vs.85).aspx):

 

This function does not save compressed animation sets.

 

Does your CMeshHierarchy create compressed animation sets by any chance?



#12 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 05 July 2013 - 01:10 PM

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.



#13 belfegor   Crossbones+   -  Reputation: 2723

Like
1Likes
Like

Posted 05 July 2013 - 01:53 PM

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, 05 July 2013 - 01:58 PM.


#14 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 05 July 2013 - 03:03 PM

@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.



#15 Steve_Segreto   Crossbones+   -  Reputation: 1552

Like
0Likes
Like

Posted 05 July 2013 - 04:52 PM

Combine http://msdn.microsoft.com/en-us/library/windows/desktop/bb172776(v=vs.85).aspx and http://msdn.microsoft.com/en-us/library/windows/desktop/bb205397(v=vs.85).aspx and specify D3DXPLAY_ONCE



#16 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 06 July 2013 - 02:05 AM

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.



#17 belfegor   Crossbones+   -  Reputation: 2723

Like
0Likes
Like

Posted 06 July 2013 - 02:28 AM


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, 06 July 2013 - 02:29 AM.


#18 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 06 July 2013 - 03:14 AM

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);


#19 belfegor   Crossbones+   -  Reputation: 2723

Like
0Likes
Like

Posted 06 July 2013 - 03:56 AM


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();


#20 Medo3337   Members   -  Reputation: 680

Like
0Likes
Like

Posted 06 July 2013 - 05:11 AM

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.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS