Archive for the ‘mesh file format’ tag
X to OpenCTM Converter
The OpenCTM file format for compressed triangle meshes looks very promising. To celebrate my gigasecond birthday, I wrote a .x to .ctm converter that exports positions and normals. It currently discards texture coordinates, but it shouldn’t be hard to extend it. I can’t guarantee that this tool is super robust, but it worked well enough for the test cases that I ran across.
I simply used ID3DXMesh to load in the .x file and the OpenCTM API to write out the .ctm file. Unfortunately, the usage of ID3DXMesh makes this tool Windows-only.
It’s a command line tool that works like this:
Usage: x2ctm [sourcefile] [sourcefile] ...
To form a destination filename, it simply replaces the existing file extension with .ctm and preserves the folder path.
At a high level, here’s how it works: (if you want, skip to the downloads section)
void ExportCTM(ID3DXMesh* pMesh, const char* destFile) { // 1. Find where the positions, normals, and texture coordinates live. // 2. Check that we support the format of the data. // 3. Obtain vertex & index counts from the D3D mesh; allocate memory for the CTM mesh. // 4. Lock down the verts and pluck out the positions and normals. // 5. Lock down the indices and convert them to unsigned 32-bit integers. // 6. Instance the OpenCTM mesh and save it to disk. // 7. Free the OpenCTM buffers. }
Here’s the actual code for the above summary:
void ExportCTM(ID3DXMesh* pMesh, const char* destFile) { // Find where the positions, normals, and texture coordinates live. const WORD MISSING_ATTRIBUTE = 0xffff; WORD positionsOffset = MISSING_ATTRIBUTE; WORD normalsOffset = MISSING_ATTRIBUTE; WORD texCoordsOffset = MISSING_ATTRIBUTE; D3DVERTEXELEMENT9 vertexLayout[MAX_FVF_DECL_SIZE]; D3DVERTEXELEMENT9 endMarker = D3DDECL_END(); pMesh->GetDeclaration(vertexLayout); D3DVERTEXELEMENT9* pVertexLayout = &vertexLayout[0]; for (int attrib = 0; attrib < MAX_FVF_DECL_SIZE; ++attrib, pVertexLayout++) { if (0 == memcmp(&vertexLayout[attrib], &endMarker, sizeof(endMarker))) { break; } if (pVertexLayout->Stream != 0) { continue; } if (pVertexLayout->Usage == D3DDECLUSAGE_POSITION && pVertexLayout->Type == D3DDECLTYPE_FLOAT3) { cout << "Contains positions." << endl; positionsOffset = pVertexLayout->Offset; } if (pVertexLayout->Usage == D3DDECLUSAGE_NORMAL && pVertexLayout->Type == D3DDECLTYPE_FLOAT3) { cout << "Contains normals." << endl; normalsOffset = pVertexLayout->Offset; } if (pVertexLayout->Usage == D3DDECLUSAGE_TEXCOORD && pVertexLayout->Type == D3DDECLTYPE_FLOAT2) { cout << "Contains texture coordinates." << endl; texCoordsOffset = pVertexLayout->Offset; } } // Check that we support the format of the data. if (positionsOffset == MISSING_ATTRIBUTE) { cerr << "Unsupported Vertex Declaration in " << destFile << endl; exit(1); } // Obtain vertex & index counts from the D3D mesh; allocate memory for the CTM mesh. DWORD dwVertexCount = pMesh->GetNumVertices(); DWORD dwTriangleCount = pMesh->GetNumFaces(); DWORD dwIndexCount = dwTriangleCount * 3; CTMfloat* pCtmVertices = new CTMfloat[3* dwVertexCount]; CTMuint* pCtmIndices = new CTMuint[dwIndexCount]; CTMfloat* pCtmNormals = (normalsOffset == 0xffff) ? 0 : new CTMfloat[3 * dwVertexCount]; // Lock down the verts and pluck out the positions and normals. { void* pData = 0; pMesh->LockVertexBuffer(D3DLOCK_READONLY , &pData); if (positionsOffset != MISSING_ATTRIBUTE) { unsigned char* pSource = ((unsigned char*) pData) + positionsOffset; unsigned char* pDest = (unsigned char* ) pCtmVertices; DWORD dwSourceStride = pMesh->GetNumBytesPerVertex(); DWORD dwDestStride = sizeof(CTMfloat) * 3; for (DWORD dwVertex = 0; dwVertex < dwVertexCount; ++dwVertex) { memcpy(pDest, pSource, dwDestStride); pSource += dwSourceStride; pDest += dwDestStride; } } if (normalsOffset != MISSING_ATTRIBUTE) { unsigned char* pSource = ((unsigned char*) pData) + normalsOffset; unsigned char* pDest = (unsigned char* ) pCtmNormals; DWORD dwSourceStride = pMesh->GetNumBytesPerVertex(); DWORD dwDestStride = sizeof(CTMfloat) * 3; for (DWORD dwVertex = 0; dwVertex < dwVertexCount; ++dwVertex) { memcpy(pDest, pSource, dwDestStride); pSource += dwSourceStride; pDest += dwDestStride; } } pMesh->UnlockVertexBuffer(); } // Lock down the indices and convert them to unsigned 32-bit integers. { void* pData = 0; pMesh->LockIndexBuffer(D3DLOCK_READONLY , &pData); DWORD dwOptions = pMesh->GetOptions(); DWORD dwSourceStride = (dwOptions & D3DXMESH_32BIT) ? 4 : 2; unsigned char* pSource = (unsigned char*) pData; unsigned char* pDest = (unsigned char*) pCtmIndices; DWORD dwDestStride = sizeof(CTMuint); for (DWORD dwIndex = 0; dwIndex < dwIndexCount; ++dwIndex) { memset(pDest, 0, dwDestStride); memcpy(pDest, pSource, dwSourceStride); pSource += dwSourceStride; pDest += dwDestStride; } pMesh->UnlockIndexBuffer(); } // Instance the OpenCTM mesh and save it to disk. CTMcontext context = ctmNewContext(CTM_EXPORT); ctmDefineMesh(context, pCtmVertices, dwVertexCount, pCtmIndices, dwTriangleCount, pCtmNormals); ctmSave(context, destFile); ctmFreeContext(context); // Free the OpenCTM buffers. delete [] pCtmVertices; delete [] pCtmIndices; delete [] pCtmNormals; }
Downloads
I wrote this tool with Visual Studio 2010. The code is covered by the WTFPL license.