Home Up Articles FAQs Downloads Web Links Newsgroups About Me Search
 

Tutorial

How to fix and recompile the 3ds max DX 8.1 SDK Exporter

Written By Microsoft MVP: Eric DeBrosse
Content may not be duplicated without permission.
Created: 5-22-2002 Updated: 8-6-2002

Related Documents:
Exporting an .X File With 3ds Max 3 or 4 Using the DX SDK Exporter

In this short tutorial, I will try to point out the necessary steps involved in compiling the DX SDK 3ds max 4 exporter, XSkinExp.dle.  I will also describe important fixes that need to be made to the exporter source code, in order for it to export meshes properly.

For this tutorial we will be using Microsoft Visual C++ version 6.  You must also have the correct 3ds max SDK installed, as well as the SDK for Character Studio.  These may not have been installed by default when you installed the software.  If you don't have the SDK disks or they have become damaged, contact your authorized 3ds max distributor for replacements.  You might also try searching the support web site for 3ds max at http://www.discreet.com/support/max/.  Keep in mind that you must have the SDK for the exact version of max you are using.  For example, if you have 3ds max R4.2 you need the R4.2 SDK.  (If you did an upgrade from R4 to R4.2, you might still have the R4 SDK installed.)

A big problem with the exporter lies in the fact that it sometimes fails to export the MeshTextureCoords template to the exported .x file.  This could prevent an .x file from correctly loading and displaying the texture(s) on a mesh.

Start VC++ and open up the project for the exporter (xskinexp.dsp) that comes with the DX SDK.  You might wish to make a copy of the source, before you proceed to break it.  Open the Tools menu and select Options...  Select the Directories tab so we can specify the path to the include files and library files for the 3ds max SDK and Character Studio SDK.  The following screen shots show the settings I used.  Note the max paths that were added to the bottom of the lists:


 

Next we need to fix some lines of code in the source.  Open up the meshdata.cpp file and scroll down to line 197.  The if statement on this line needs some editing, change the following:

Note:  All line numbers given in this tutorial are based on the original source code, not the modified source code.

if ((pMesh->faces[iFace].flags & HAS_TVERTS)
    && (NULL != pMesh->tvFace)
    && ((int)pMesh->tvFace[iFace].t[iPoint] < pMesh->numTVerts) )

...to read something like this:

if ((pMesh->numTVerts > 0)
    && (NULL != pMesh->tvFace)
    && ((int)pMesh->tvFace[iFace].t[iPoint] < pMesh->numTVerts) )

The flag HAS_TVERTS appears to not be set correctly, or used at all for that matter.  Upon closer examination of the source code, a comment was found stating "DO NOT USE:  This flag is obsolete".  So it appears to me this flag should be removed from the line of code in question.  We will check for numTVerts being greater than 0 instead.

There also could be a problem with the cropping information.  In the exportxfile.cpp file at line 965, edit the code to disallow crops to (0, 0, 1, 1) and just do a pass through in that case.  (Thanks goes to Craig Peeper for making this suggestion.)

Something like this might work for you:

if ((rgCropInfo[iMaterial].fClipW != 1.0f) ||
    (rgCropInfo[iMaterial].fClipH != 1.0f) ||
    (rgCropInfo[iMaterial].fClipU != 0.0f) ||
    (rgCropInfo[iMaterial].fClipV != 0.0f))
{
    fX = (fX > 1.0f) ? 1.0f : ((fX < 0.0f) ? 0.0f : fX);
    fY = (fY > 1.0f) ? 1.0f : ((fY < 0.0f) ? 0.0f : fY);

    fX = (fX * rgCropInfo[iMaterial].fClipW) + rgCropInfo[iMaterial].fClipU;
    fY = (fY * rgCropInfo[iMaterial].fClipH) + rgCropInfo[iMaterial].fClipV;
}

There also seems to be a problem with normal generation.  You could have your application generate new normals after loading the .x file using the D3DXComputeNormals function.  Or you could try the following code modification to the exporter.

In meshdata.cpp find line 260.

FindNormal(pMeshData, iRawVertexIndex, iSmoothingGroupIndex,
    pMeshData->m_rgVertices[iNewVertex].vNormal);

...and move it to before line 249.  Like this:

FindNormal(pMeshData, iRawVertexIndex, iSmoothingGroupIndex,
    pMeshData->m_rgVertices[iNewVertex].vNormal);

// link into wedge list of point rep
pMeshData->m_rgVertices[iNewVertex].iWedgeList =
    pMeshData->m_rgVertices[iRawVertexIndex].iWedgeList;
pMeshData->m_rgVertices[iRawVertexIndex].iWedgeList = iNewVertex;

Apparently FindNormal should be called before linking into the wedge list and setting up the vertex info.

There is one other fix for the normals.  We need to make an adjustment to Line 274 of meshdata.cpp.

pMeshData->m_rgVertices[iVertex].vNormal.Normalize();

The above line needs to be changed to:

pMeshData->m_rgVertices[iVertex].vNormal =
    pMeshData->m_rgVertices[iVertex].vNormal.Normalize();

This ensures the vertices will be normalized.  (Thanks goes to Craig Peeper for submitting the above suggestions with regards to the normals.)

There is also a bug that could cause problems with your materials.  Basically, the exporter will crash 3ds max if you have two materials that have the same diffuse/emissive etc. and at least one of them does not use a bitmap in the diffuse channel.  In Line 1915 of ExportXFile.cpp, there is a strcmp that could be called  with NULL pointers.  Changing the code to something like the following should help:

// we must first check to see if one of the filenames is NULL.
if ((rgMaterials[iCurMaterial].pTextureFilename == NULL) ||
    (rgMaterials[iSearchMaterial].pTextureFilename == NULL))
{
    // at least one of the filenames is NULL, see if we have a match
    if ((memcmp(&(rgMaterials[iCurMaterial].MatD3D),
        &(rgMaterials[iSearchMaterial].MatD3D), sizeof(D3DMATERIAL8)) == 0)
        && (rgMaterials[iCurMaterial].pTextureFilename == NULL)
        && (rgMaterials[iSearchMaterial].pTextureFilename == NULL))
    {
        // source and destination textures are NULL and have the same
        // materials, so it's a redundant material.
        bFound = TRUE;
        break;
    }
}
else
{
    // if both the material and texture name are the same,
    //   then it is a redundant material.
    if ((memcmp(&(rgMaterials[iCurMaterial].MatD3D),
        &(rgMaterials[iSearchMaterial].MatD3D), sizeof(D3DMATERIAL8)) == 0)
        && (strcmp(rgMaterials[iCurMaterial].pTextureFilename,
        rgMaterials[iSearchMaterial].pTextureFilename) == 0))
    {
        bFound = TRUE;
        break;
    }
}

Now all that remains is rebuilding the XSkinExp.dle file (F7 key) and copying the compiled .dle file into your max plugins folder.  Hopefully after a quick test of the exporter you will find that it now works correctly with your textures.

One other note, you might want to change the build options from Win32 Debug to Win32 Release for a smaller plug-in footprint.

In the future, I may add descriptions on how to convert the mesh data from the right-handed coordinate system that 3ds max uses to a left-handed coordinate system (the default for Direct3D).  Until then, you might visit the web page at the discreet sparks knowledgebase called How to convert a 3ds max camera to a Direct3D camera.

If I find more fixes for the exporter code, I will try to post them here.  If you have any fixes for the exporter (or this tutorial in general) that you would like to share, please let me know.  Keep checking back for updates to this page as I am constantly discovering new information.

Note: Please do not e-mail me asking for a compiled version of the exporter.  For one thing, I didn't compile it for all the versions and revisions of max.  Furthermore, the exporter bears a Microsoft copyright - or I might consider having downloads of it from my web site.  Sorry.

 
edebrosse@mvps.org
2009 Eric DeBrosse

www.orionengine.com

Hit Counter
Visitors since 5/21/2002