Combining multiple .x files into a single .x file

Started by
28 comments, last by Steve_Segreto 10 years, 9 months ago

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?

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

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.

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

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

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.


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.

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


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

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.

This topic is closed to new replies.

Advertisement