Sign in to follow this  

Replacing a mesh container in a model

This topic is 4559 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, does anybody know a way to replace a mesh container in a model with another mesh or mesh container? I'm trying make a program that allows the user to change body parts of a mesh model. this is what I have done so far: 1. Loaded a mesh hierarchy of a model person 2. Loaded a mesh hierarchy of a mesh of a head 3. traversed through the model hierarchy, until it finds the frame named "Head" 4. Replace the Frame->pMeshContainer->MeshData.pMesh with the mesh model of the head, by using the CloneMeshFVF function. So far, the mesh is replaced successfully, but only the mesh, not the textures. The textures remain as the old one. I have tried copying the pMaterial data, but nothing happens... I also tried copying the whole pMeshContainer pointer, by doing something like: pFrame->pMeshContainer = NewPart->pFrameRoot->pMeshContainer, but that only results in the head disappeared. I'm not sure what I am trying to do is correct... Is there a better way to approach this? Should I just load the mesh of the new head using the standard LoadMeshFromX or should I load it as a hierarchy? Please... help needed..! I'm on this project for quite awhile now, and it hasn't made much progress since this problem... I will try to elaborate the problem if I sound confusing here... thanks very much in advance. -fuchiefck

Share this post


Link to post
Share on other sites
Hello,

The proper was is the way you were doing it first. Clone the pMesh and set it to the pointer of the original mesh container.

Then, you need to replace both pMaterial and pTexture. You only mention copying pMaterial in your post. If you only copy the one then you would still render with the old image file.

If you did change the pTexture already and just forgot to mention it, or if you do it and it still doesn't work, then I can find you an example.

Good Luck

Share this post


Link to post
Share on other sites
Thanks for pointing that out, Geek#.

I neglected the pTextures because I thought the texture info was contained in the pMaterials. The problem now is, the pFrame->pMeshContainer is a standard LPD3DXMESHCONTAINER, not a derived one, so I can't have direct access to the pTextures pointer... or am I just being stupid and missed something?! Anyway you have isolated the problem for me, I will look into it now... thanks again.

BTW you mentioned about an example on how to do this... it would be great if you can post it up here or PM it to me. Thanks heaps

-fuchiefck

Share this post


Link to post
Share on other sites
I think I can field this one.

I seem to have lost my meshcontainer swapping demo. I think it turned into my skinned mesh blending one. However, I think I remember most of it. I don't really have time to test it so bare with my source here.

First of all you really should make a derived meshcontainer, and a derived frame as well. The frame needs an extra matrix to hold the combination of it's offset matrix and all it's parent offset matrices. It will make things much easier on you.

This frame class has served me well all the way into blended meshes. When you create the frame in the AllocHierarchy you should set the combined transformation matrix to DirectX.Matrix.Identity.

public class ExtendedFrame : Direct3D.Frame
{
private DirectX.Matrix _combinedTransformationMatrix;

public DirectX.Matrix CombinedTransformationMatrix
{
get { return _combinedTransformationMatrix; }
set { _combinedTransformationMatrix = value; }
}
}



This is all you'll really need for a mesh container I believe.

public class ExtendedMeshContainer : Direct3D.MeshContainer
{
private Direct3D.Material[] _materials;
private Direct3D.Texture[] _textures;

// Note: Make get/set property for your material and texture arrays

public ExtendedMeshContainer()
{
}

public void LoadTextures()
{
Direct3D.ExtendedMaterial[] materials = GetMaterials();

if(materials != null)
{
_materials = new Direct3D.Material[materials.Length];
_textures = new Direct3D.Texture[materials.Length];

for(int counter = 0; counter < materials.Length; ++counter)
{
_materials[counter] = materials[counter].Material3D;
_materials[counter].Ambient = _materials[counter].Diffuse;

if(materials[counter].TextureFilename != null)
{
// Load the texture...
}
else
{
_textures[counter] = null;
}
}
}
}
}



Now, when you create your mesh container in the AllocHierarchy you can setup all the data as you probably did before but call LoadTextures() and it'll load them for you and copy the Material3D data, which is all you need for rendering.

Swapping is then pretty easy...

CurrentMeshContainer.MeshInfo = newMeshContainer.MeshInfo;
CurrentMeshContainer.Materials = newMeshContainer.Materials;
CurrentMeshContainer.Textures = newMeshContainer.Textures;



And you can render your meshContainer by looping from zero to CurrentMeshContainer.Materials.Count - 1.

I think the hardeset part of meshcontainer animation and swapping is probably making sure your frames update properly.

Hope some of this helps anyone.
Ask away if you have anymore questions.

Share this post


Link to post
Share on other sites
Thanks for your reply Kahsm...

Seems you program in C#... I'm using unmanaged C++... but anyhow I can get the jist of the logic in the code. Thanks again.

One thing tho, You swapped the meshes by just copying the MeshInfo (which I assume is equivalent to MeshData in C++)? I did that before but the mesh just disappears, and thats why I resorted to CloneMeshFVF to clone the MeshData.pMesh, which works ok...

I'm also getting confused on the AllocHierarchy part... When it creates the Frames and MeshContainers, it creates them using the default structs right? (the LPD3DXFRAME and LPD3DXMESHCONTAINER) Can I change those standard functions to work with the derived structs, or is the format strictly as it is?

eg can I change:

STDMETHOD( CreateMeshContainer )( THIS_ LPCSTR Name, CONST D3DXMESHDATA * pMeshData,
CONST D3DXMATERIAL * pMaterials,
CONST D3DXEFFECTINSTANCE * pEffectInstances,
DWORD NumMaterials,
CONST DWORD * pAdjacency, LPD3DXSKININFO pSkinInfo,
LPD3DXMESHCONTAINER * ppNewMeshContainer );

to this:

STDMETHOD( CreateMeshContainer )( THIS_ LPCSTR Name, CONST D3DXMESHDATA * pMeshData,
CONST D3DXMATERIAL * pMaterials,
CONST D3DXEFFECTINSTANCE * pEffectInstances,
DWORD NumMaterials,
CONST DWORD * pAdjacency, LPD3DXSKININFO pSkinInfo,
LPMESHCONTAINER_DERIVED * ppNewMeshContainer );

?

I just dunno why the ppTextures pointer is not a standard variable declared in LPD3DXMESHCONTAINER... and when I do the swapping, I have to type-convert the pMeshContainer I'm working with into the MESHCONTAINER_DERIVED type... which gets quite messy...

thanks

-fuchiefck

Share this post


Link to post
Share on other sites
Oh, sorry, I must have confused you with someone who asked about C# earlier.

That's no problem I'm flexible, though I can't really help you with clearing resources ;)

First of all, you're right it's MeshData (got it mixed up with SkinInfo).

In C++ you do want to clone the mesh as you are doing. MView and SkinnedMesh actually clone it to a mesh pointer in their extended mesh container and render from that instead of the mesh in MeshData.

It's not the function declarations you're going to change to use your own structures, you have to "new" them inside those functions, then when you return it at the end it'll be cast (or boxed) down to a normal MeshContainer. Later on you can cast (or box) it back to your extended version to get your extra info.

Here's a C++ example as best as I can give it.

STDMETHOD( CreateMeshContainer )( THIS_ LPCSTR Name, CONST D3DXMESHDATA *pMeshData, CONST D3DXMATERIAL *pMaterials, CONST D3DXEFFECTINSTANCE *pEffectInstances, DWORD NumMaterials, CONST DWORD *pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER *ppNewMeshContainer);
{
LPMESHCONTAINER_DERIVED pYourMeshContainer = new D3DXMESHCONTAINER_DERIVED;

AllocateName(Name, &pYourMeshContainer->Name);

// lol, I can't even do this off the top of my head.
// Anyway... you set the name and the materials and textures similarly to my C# example above. here
// Then...

// It was made as a derived mesh container, but since derived inherits form meshcontainer you can set it to the pointer.
// The same thing happens with the frame.

*ppNewMeshContainer = pMeshContainer;
pMeshContainer = NULL;
}



Oh, here's an example from:
http://www.toymaker.info/Games/html/x_file_animation.html

But I commented it a little more. Most of it is just testing for things you don't really care about when starting out.


HRESULT CMeshHeirarchy::CreateMeshContainer( THIS_
LPCSTR Name,
CONST D3DXMESHDATA *pMeshData,
CONST D3DXMATERIAL *pMaterials,
CONST D3DXEFFECTINSTANCE *, // Not used
DWORD NumMaterials,
CONST DWORD *pAdjacency,
LPD3DXSKININFO, // Not used
LPD3DXMESHCONTAINER* ppNewMeshContainer)

{
HRESULT hr;
D3DXMESHCONTAINER_DERIVED *pMeshContainer = NULL;
UINT NumFaces;
UINT iMaterial;
// UINT iBone, cBones;
LPDIRECT3DDEVICE9 pd3dDevice = NULL;

LPD3DXMESH pMesh = NULL;

*ppNewMeshContainer = NULL;

// Kahsm says "Some standard sanity checks here...
// not needed if you're mesh is fine."

// this sample does not handle patch meshes, so fail when one is found
if (pMeshData->Type != D3DXMESHTYPE_MESH)
{
hr = E_FAIL;
goto e_Exit;
}

// get the pMesh interface pointer out of the mesh data structure
pMesh = pMeshData->pMesh;

// this sample does not FVF compatible meshes, so fail when one is found
if (pMesh->GetFVF() == 0)
{
hr = E_FAIL;
goto e_Exit;
}

// Kahsm says "Here they create the derived mesh and beyond start setting the members."

// allocate the overloaded structure to return as a D3DXMESHCONTAINER
pMeshContainer = new D3DXMESHCONTAINER_DERIVED;
if (pMeshContainer == NULL)
{
hr = E_OUTOFMEMORY;
goto e_Exit;
}
memset(pMeshContainer, 0, sizeof(D3DXMESHCONTAINER_DERIVED));

// Kahsm says "Copy the name over"

// make sure and copy the name. All memory as input belongs to caller, interfaces can be addref'd though
hr = AllocateName(Name, &pMeshContainer->Name);
if (FAILED(hr))
goto e_Exit;

// Kahsm says "Here they build normals if there are none,
// you can actually ignore this if you know your mesh has it's normals."

pMesh->GetDevice(&pd3dDevice);
NumFaces = pMesh->GetNumFaces();

// if no normals are in the mesh, add them
if (!(pMesh->GetFVF() & D3DFVF_NORMAL))
{
pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

// clone the mesh to make room for the normals
hr = pMesh->CloneMeshFVF( pMesh->GetOptions(),
pMesh->GetFVF() | D3DFVF_NORMAL,
pd3dDevice, &pMeshContainer->MeshData.pMesh );
if (FAILED(hr))
goto e_Exit;

// get the new pMesh pointer back out of the mesh container to use
// NOTE: we do not release pMesh because we do not have a reference to it yet
pMesh = pMeshContainer->MeshData.pMesh;

// now generate the normals for the pmesh
D3DXComputeNormals( pMesh, NULL );
}
else // if no normals, just add a reference to the mesh for the mesh container
{
// Kahsm says "Remember to do this if you remove the normal building. This actually attaches the mesh."

pMeshContainer->MeshData.pMesh = pMesh;
pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;

pMesh->AddRef();
}


// Kahsm says "Copy over the materials, textures, and adjacency data...
// Now you might start to see why I like C# ;) All this was like 5 lines of code."

// allocate memory to contain the material information. This sample uses
// the D3D9 materials and texture names instead of the EffectInstance style materials
pMeshContainer->NumMaterials = max(1, NumMaterials);
pMeshContainer->pMaterials = new D3DXMATERIAL[pMeshContainer->NumMaterials];
pMeshContainer->ppTextures = new LPDIRECT3DTEXTURE9[pMeshContainer->NumMaterials];
pMeshContainer->pAdjacency = new DWORD[NumFaces*3];
if ((pMeshContainer->pAdjacency == NULL) || (pMeshContainer->pMaterials == NULL))
{
hr = E_OUTOFMEMORY;
goto e_Exit;
}

memcpy(pMeshContainer->pAdjacency, pAdjacency, sizeof(DWORD) * NumFaces*3);
memset(pMeshContainer->ppTextures, 0, sizeof(LPDIRECT3DTEXTURE9) * pMeshContainer->NumMaterials);

// if materials provided, copy them
if (NumMaterials > 0)
{
memcpy(pMeshContainer->pMaterials, pMaterials, sizeof(D3DXMATERIAL) * NumMaterials);

for (iMaterial = 0; iMaterial < NumMaterials; iMaterial++)
{
if (pMeshContainer->pMaterials[iMaterial].pTextureFilename != NULL)
{
HRESULT hr=D3DXCreateTextureFromFile( pd3dDevice, pMeshContainer->pMaterials[iMaterial].pTextureFilename,
&pMeshContainer->ppTextures[iMaterial] ) ;

if( FAILED(hr))
{
pMeshContainer->ppTextures[iMaterial] = NULL;

// Texture must not be available or some other error - output warning to viz output window
char buf[2048];

sprintf(buf,"\nCould not load %s",pMeshContainer->pMaterials[iMaterial].pTextureFilename);
OutputDebugString(buf);

sprintf(buf, " (DX Error: %s : %s) continuing with NULL texture\n\n",DXGetErrorString9(hr),DXGetErrorDescription9(hr));
OutputDebugString(buf);
}


// don't remember a pointer into the dynamic memory, just forget the name after loading
pMeshContainer->pMaterials[iMaterial].pTextureFilename = NULL;
}
}
}

// Kahsm says "You can ignore this too, it'll just render white if there's no material...
// you don't actually need a default one."

else // if no materials provided, use a default one
{
pMeshContainer->pMaterials[0].pTextureFilename = NULL;
memset(&pMeshContainer->pMaterials[0].MatD3D, 0, sizeof(D3DMATERIAL9));
pMeshContainer->pMaterials[0].MatD3D.Diffuse.r = 0.5f;
pMeshContainer->pMaterials[0].MatD3D.Diffuse.g = 0.5f;
pMeshContainer->pMaterials[0].MatD3D.Diffuse.b = 0.5f;
pMeshContainer->pMaterials[0].MatD3D.Specular = pMeshContainer->pMaterials[0].MatD3D.Diffuse;
}

// Kahsm says "And this is like the return statement. Now this mesh container can be child to it's frame.
//Remember to get your extended version you'll have to cast (or box) it to your derived verison."

*ppNewMeshContainer = pMeshContainer;
pMeshContainer = NULL;

e_Exit:
SAFE_RELEASE(pd3dDevice);

// call Destroy function to properly clean up the memory allocated
if (pMeshContainer != NULL)
{
DestroyMeshContainer(pMeshContainer);
}

return hr;
}



Hope that helps a little more. See the link for the full source.

Share this post


Link to post
Share on other sites
thanks Kahsm, I was aware of the toymaker tute before, but thanks anyway, esp for your extra comments... they cleared quite a bit which i didn't understand before.

I've managed to do something to the texture as well, but its not getting replaced properly. Umm... this is a bit hard to explain, but i will try...

- The original MeshContainer in the Frame "Hair" is actually a hat and a bit of hair underneath it (you can try picturing that in your head.. :)).
- The hat uses a texture, say texHat.bmp, and the hair bit uses another texture, texHair.bmp.

Heres a bit of code that does the swapping:


//this recursive function traverses the frame hierarchy and replaces the
//mesh with a new mesh (part) when the specific frame is found (parttype)
//(CDevice is a class created to hold the LPDIRECT3DDEVICE9 pointer)
void CCharacterRenderer::ChangePart(LPCSTR parttype, CMeshEntity part,
LPD3DXFRAME pFrame, CDevice *lpDevice)
{
//type casting to convert the pMeshContainers to the Derived type
LPMESHCONTAINER pMC = (LPMESHCONTAINER)pFrame->pMeshContainer;
LPMESHCONTAINER pPartMC = (LPMESHCONTAINER)part.m_pFrameRoot->pMeshContainer;

//traverse through the frame hierarchy, when the part is found, replace the
//mesh data
if (pFrame->Name != NULL && strcmp(pFrame->Name, parttype) == 0)
{
//change part
pMC->pMaterials = pPartMC->pMaterials;
pMC->NumMaterials = pPartMC->NumMaterials ;
pMC->ppTextures = pPartMC->ppTextures;
pPartMC->MeshData.pMesh->CloneMeshFVF(pPartMC->MeshData.pMesh->GetOptions(), pPartMC->MeshData.pMesh->GetFVF(), lpDevice->GetObject(), &pMC->MeshData.pMesh);
return;
}

if (pFrame->pFrameSibling != NULL)
{
ChangePart(parttype, part, pFrame->pFrameSibling, lpDevice);
}

if (pFrame->pFrameFirstChild != NULL)
{
ChangePart(parttype, part, pFrame->pFrameFirstChild, lpDevice);
}
}




CMeshEntity part is a mesh of a hat and hair of some different shape, with different textures.

After it changes the part, the mesh shape has changed to the new one, but the new texture does not appear, instead, the texture displayed is texHair.bmp of the original Hat/hair, and only that texture.

I know I am getting somewhere, I don't know why the texture isn't right...

thanks again...

-fuchiefck

Share this post


Link to post
Share on other sites
My C++ skills are very rusty and I think we're getting into memory problems here.

If you just set the pointers like that you're going to have horrible memory leaks. All the textures and materials are newed. You should really swap them elsewhere so you can switch them back, or delete[] them before you exit.

However, as far as I know it should still work. That brings me to the renderer. How do you setup your texture before you render? Have you set it up to use these arrays and not the data from the mesh?

Should be something like this...


for(int counter = 0; counter < pMC->NumMaterials; ++counter)
{

lpDevice->SetMaterial(&pMC->pMaterials[counter].MatD3D));
lpDevice->SetTexture(0,pMC->ppTextures[counter]));

pMC->MeshData.pMesh->DrawSubset(counter);
}




Not sure I can help you much form here, getting too C++ specific. If it still doesn't work I'd probably try something like this is your swap function:

delete[] pMC->pMaterials;
detete[] pMC->ppTextures;
pMC->NumMaterials = pPartMC->NumMaterials;
pMC->pMaterials = new D3DXMATERIAL[pMC->NumMaterials];
pMC->ppTextures = new LPDIRECT3DTEXTURE9[pMC->NumMaterials]

if(pMC->NumMaterials > 0)
{
memcpy(pMC->pMaterials, pPartMC->pMaterials, sizeof(D3DXMATERIAL) * NumMaterials);
memcpy(pMC->ppTextures, pPartMC->ppTextures, sizeof(D3DXMATERIAL) * NumMaterials);
}



I think that's how you'd make new copies of them. Either way, if you delete the materials and the textures, I can garentee you shouldn't be getting that old texture again, if you do, you're not using this array in your render.

Good Luck,

Share this post


Link to post
Share on other sites
Thanks again and again Kahsm, you have been really helpful...!

My rendering code is taken from the SkinnedMesh sample in the SDK, and its like how its done in your post... I believe the problem doesn't lie there...

How stupid of me to think that it should work just by setting the pointers..! I should revisit the introduction to Memory and Pointers! Anyway I think You have set me onto the right track. I just have to think of it as setting the materials and textures again for the replaced part, instead of just "copying" it. I should have this up and running soon... I will let you know.

thanks for your help again..!

-fuchiefck

Share this post


Link to post
Share on other sites

This topic is 4559 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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