void MeshX::BuildSkinnedMesh() { ID3DXMesh* oldMesh = allocMeshHierarchy.GetMeshList().front()->MeshData.pMesh; //compute normal D3DVERTEXELEMENT9 elements[MAX_FVF_DECL_SIZE]; oldMesh->GetDeclaration(elements); ID3DXMesh* tempMesh = 0; ID3DXMesh* tempOpMesh = 0; oldMesh->CloneMesh(D3DXMESH_SYSTEMMEM, elements, pDevice, &tempMesh); if( !HasNormals(tempMesh) ) D3DXComputeNormals(tempMesh, 0); //optimize the mesh DWORD* adj = new DWORD[tempMesh->GetNumFaces()*3]; ID3DXBuffer* remap = 0; tempMesh->GenerateAdjacency(0.00001f, adj); tempMesh->Optimize(D3DXMESH_SYSTEMMEM | D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_ATTRSORT, adj, 0, 0, &remap, &tempOpMesh); SafeRelease(tempMesh); // In the .X file (specifically the array DWORD vertexIndices[nWeights] // data member of the SkinWeights template) each bone has an array of // indices which identify the vertices of the mesh that the bone influences. // Because we have just rearranged the vertices (from optimizing), the vertex // indices of a bone are obviously incorrect (i.e., they index to vertices the bone // does not influence since we moved vertices around). In order to update a bone's // vertex indices to the vertices the bone _does_ influence, we simply need to specify // where we remapped the vertices to, so that the vertex indices can be updated to // match. This is done with the ID3DXSkinInfo::Remap method. skinInfo->Remap(tempOpMesh->GetNumVertices(), (DWORD*)remap->GetBufferPointer()); SafeRelease(remap); // Done with remap info. DWORD numBoneComboEntries = 0; ID3DXBuffer* boneComboTable = 0; DWORD maxVertInfluences; skinInfo->ConvertToIndexedBlendedMesh(tempOpMesh, 0, 128, 0, 0, 0, 0, &maxVertInfluences, &numBoneComboEntries, &boneComboTable, &this->mesh); SafeRelease(tempOpMesh); SafeRelease(boneComboTable); delete[] adj; }
void LoadXFile(const std::string & filename, ID3DXMesh** meshOut, std::vector<Mtrl> & mtrls, std::vector<IDirect3DTexture9*> & texs) { ID3DXMesh* meshSys = 0; ID3DXBuffer * adjBuffer = 0; ID3DXBuffer * mtrlBuffer = 0; DWORD numMtrls = 0; //step 1. Load the x file into system memory HR(D3DXLoadMeshFromX(filename.c_str(), D3DXMESH_SYSTEMMEM, gd3dDevice, &adjBuffer, &mtrlBuffer, 0, &numMtrls, &meshSys)); //step 2. look into MAX_FVF_DECL_SIZE D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; HR(meshSys->GetDeclaration(elems)); bool hasNormals = false; D3DVERTEXELEMENT9 term = D3DDECL_END(); for(int i = 0; i < MAX_FVF_DECL_SIZE; ++i) { //did we reach end? if(elems[i].Stream == 0xff) break; if(elems[i].Type == D3DDECLTYPE_FLOAT3 && elems[i].Usage == D3DDECLUSAGE_NORMAL && elems[i].UsageIndex == 0) { hasNormals = true; break; } } //step 3. D3DVERTEXELEMENT9 elements[64]; UINT numElements = 0; VertexPNT::Decl->GetDeclaration(elements, &numElements); ID3DXMesh * temp = 0; //HR(meshSys->CloneMesh(D3DXMESH_SYSTEMMEM, elements, gd3dDevice, &temp)); HR(meshSys->CloneMesh(D3DXMESH_32BIT, elements, gd3dDevice, &temp)); ReleaseCOM(meshSys); meshSys = temp; //step 4 if( hasNormals == false ) HR(D3DXComputeNormals(meshSys, 0)); //step 5 HR(meshSys->Optimize(D3DXMESH_MANAGED | D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjBuffer->GetBufferPointer(), 0, 0, 0, meshOut)); ReleaseCOM(meshSys); //Done w/ system mesh ReleaseCOM(adjBuffer); //done with buffer //step 6: get the materials and load the textures if(mtrlBuffer != 0 && numMtrls != 0) { D3DXMATERIAL *d3dxMtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(DWORD i = 0; i < numMtrls; ++i) { //save the ith material. MatD3D ambient //doesnt have a default value, so I'm setting //it to be the same as the diffuse Mtrl m; m.ambient = d3dxMtrls[i].MatD3D.Diffuse; m.diffuse = d3dxMtrls[i].MatD3D.Diffuse; m.spec = d3dxMtrls[i].MatD3D.Specular; m.specPower = d3dxMtrls[i].MatD3D.Power; mtrls.push_back(m); //check if the ith material has an associative texture if(d3dxMtrls[i].pTextureFilename != 0) { //yes, load the texture for the ith subset IDirect3DTexture9* tex = 0; char *texFN = d3dxMtrls[i].pTextureFilename; HR(D3DXCreateTextureFromFile(gd3dDevice, texFN, &tex)); //save the loaded texure texs.push_back(tex); } else { //no texture texs.push_back( 0 ); } } } ReleaseCOM(mtrlBuffer); // done with the buffer }
void LoadXFile( const std::string& filename, ID3DXMesh** meshOut, std::vector<Mtrl>& mtrls, std::vector<IDirect3DTexture9*>& texs) { // Step 1: Load the .x file from file into a system memory mesh. ID3DXMesh* meshSys = 0; ID3DXBuffer* adjBuffer = 0; ID3DXBuffer* mtrlBuffer = 0; DWORD numMtrls = 0; HR(D3DXLoadMeshFromX(filename.c_str(), D3DXMESH_SYSTEMMEM, gd3dDevice, &adjBuffer, &mtrlBuffer, 0, &numMtrls, &meshSys)); // Step 2: Find out if the mesh already has normal info? D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; HR(meshSys->GetDeclaration(elems)); bool hasNormals = false; D3DVERTEXELEMENT9 term = D3DDECL_END(); for(int i = 0; i < MAX_FVF_DECL_SIZE; ++i) { // Did we reach D3DDECL_END() {0xFF,0,D3DDECLTYPE_UNUSED, 0,0,0}? if(elems[i].Stream == 0xff ) break; if( elems[i].Type == D3DDECLTYPE_FLOAT3 && elems[i].Usage == D3DDECLUSAGE_NORMAL && elems[i].UsageIndex == 0 ) { hasNormals = true; break; } } // Step 3: Change vertex format to VertexPNT. D3DVERTEXELEMENT9 elements[64]; UINT numElements = 0; VertexPNT::Decl->GetDeclaration(elements, &numElements); ID3DXMesh* temp = 0; HR(meshSys->CloneMesh(D3DXMESH_SYSTEMMEM, elements, gd3dDevice, &temp)); ReleaseCOM(meshSys); meshSys = temp; // Step 4: If the mesh did not have normals, generate them. if( hasNormals == false) HR(D3DXComputeNormals(meshSys, 0)); // Step 5: Optimize the mesh. HR(meshSys->Optimize(D3DXMESH_MANAGED | D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjBuffer->GetBufferPointer(), 0, 0, 0, meshOut)); ReleaseCOM(meshSys); // Done w/ system mesh. ReleaseCOM(adjBuffer); // Done with buffer. // Step 6: Extract the materials and load the textures. if( mtrlBuffer != 0 && numMtrls != 0 ) { D3DXMATERIAL* d3dxmtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(DWORD i = 0; i < numMtrls; ++i) { // Save the ith material. Note that the MatD3D property does not have an ambient // value set when its loaded, so just set it to the diffuse value. Mtrl m; m.ambient = d3dxmtrls[i].MatD3D.Diffuse; m.diffuse = d3dxmtrls[i].MatD3D.Diffuse; m.spec = d3dxmtrls[i].MatD3D.Specular; m.specPower = d3dxmtrls[i].MatD3D.Power; mtrls.push_back( m ); // Check if the ith material has an associative texture if( d3dxmtrls[i].pTextureFilename != 0 ) { // Yes, load the texture for the ith subset IDirect3DTexture9* tex = 0; char* texFN = d3dxmtrls[i].pTextureFilename; HR(D3DXCreateTextureFromFile(gd3dDevice, texFN, &tex)); // Save the loaded texture texs.push_back( tex ); } else { // No texture for the ith subset texs.push_back( 0 ); } } } ReleaseCOM(mtrlBuffer); // done w/ buffer }
void SkinnedMesh::buildSkinnedMesh(ID3DXMesh* mesh) { //==================================================================== // First add a normal component and 2D texture coordinates component. D3DVERTEXELEMENT9 elements[64]; UINT numElements = 0; VertexPNT::Decl->GetDeclaration(elements, &numElements); ID3DXMesh* tempMesh = 0; HR(mesh->CloneMesh(D3DXMESH_SYSTEMMEM, elements, gd3dDevice, &tempMesh)); if( !hasNormals(tempMesh) ) HR(D3DXComputeNormals(tempMesh, 0)); //==================================================================== // Optimize the mesh; in particular, the vertex cache. DWORD* adj = new DWORD[tempMesh->GetNumFaces()*3]; ID3DXBuffer* remap = 0; HR(tempMesh->GenerateAdjacency(EPSILON, adj)); ID3DXMesh* optimizedTempMesh = 0; HR(tempMesh->Optimize(D3DXMESH_SYSTEMMEM | D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_ATTRSORT, adj, 0, 0, &remap, &optimizedTempMesh)); ReleaseCOM(tempMesh); // Done w/ this mesh. delete[] adj; // Done with buffer. // In the .X file (specifically the array DWORD vertexIndices[nWeights] // data member of the SkinWeights template) each bone has an array of // indices which identify the vertices of the mesh that the bone influences. // Because we have just rearranged the vertices (from optimizing), the vertex // indices of a bone are obviously incorrect (i.e., they index to vertices the bone // does not influence since we moved vertices around). In order to update a bone's // vertex indices to the vertices the bone _does_ influence, we simply need to specify // where we remapped the vertices to, so that the vertex indices can be updated to // match. This is done with the ID3DXSkinInfo::Remap method. HR(mSkinInfo->Remap(optimizedTempMesh->GetNumVertices(), (DWORD*)remap->GetBufferPointer())); ReleaseCOM(remap); // Done with remap info. //==================================================================== // The vertex format of the source mesh does not include vertex weights // nor bone index data, which are both needed for vertex blending. // Therefore, we must convert the source mesh to an "indexed-blended-mesh," // which does have the necessary data. DWORD numBoneComboEntries = 0; ID3DXBuffer* boneComboTable = 0; HR(mSkinInfo->ConvertToIndexedBlendedMesh(optimizedTempMesh, D3DXMESH_MANAGED | D3DXMESH_WRITEONLY, MAX_NUM_BONES_SUPPORTED, 0, 0, 0, 0, &mMaxVertInfluences, &numBoneComboEntries, &boneComboTable, &mSkinnedMesh)); ReleaseCOM(optimizedTempMesh); // Done with tempMesh. ReleaseCOM(boneComboTable); // Don't need bone table. #if defined(DEBUG) | defined(_DEBUG) // Output to the debug output the vertex declaration of the mesh at this point. // This is for insight only to see what exactly ConvertToIndexedBlendedMesh // does to the vertex declaration. D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; HR(mSkinnedMesh->GetDeclaration(elems)); OutputDebugString("\nVertex Format After ConvertToIndexedBlendedMesh\n"); int i = 0; while( elems[i].Stream != 0xff ) // While not D3DDECL_END() { if( elems[i].Type == D3DDECLTYPE_FLOAT1) OutputDebugString("Type = D3DDECLTYPE_FLOAT1; "); if( elems[i].Type == D3DDECLTYPE_FLOAT2) OutputDebugString("Type = D3DDECLTYPE_FLOAT2; "); if( elems[i].Type == D3DDECLTYPE_FLOAT3) OutputDebugString("Type = D3DDECLTYPE_FLOAT3; "); if( elems[i].Type == D3DDECLTYPE_UBYTE4) OutputDebugString("Type = D3DDECLTYPE_UBYTE4; "); if( elems[i].Usage == D3DDECLUSAGE_POSITION) OutputDebugString("Usage = D3DDECLUSAGE_POSITION\n"); if( elems[i].Usage == D3DDECLUSAGE_BLENDWEIGHT) OutputDebugString("Usage = D3DDECLUSAGE_BLENDWEIGHT\n"); if( elems[i].Usage == D3DDECLUSAGE_BLENDINDICES) OutputDebugString("Usage = D3DDECLUSAGE_BLENDINDICES\n"); if( elems[i].Usage == D3DDECLUSAGE_NORMAL) OutputDebugString("Usage = D3DDECLUSAGE_NORMAL\n"); if( elems[i].Usage == D3DDECLUSAGE_TEXCOORD) OutputDebugString("Usage = D3DDECLUSAGE_TEXCOORD\n"); ++i; } #endif }
void XManager::loadXFile(IDirect3DDevice9* dev, const std::string& filename, ID3DXMesh** meshOut, std::vector<Material>* mtrls, std::vector<IDirect3DTexture9*>* texs) { // Step 1: Load the .x file from file into a system memory mesh. ID3DXMesh* meshSys = 0; ID3DXBuffer* adjBuffer = 0; ID3DXBuffer* mtrlBuffer = 0; DWORD numMtrls = 0; HR(D3DXLoadMeshFromX(filename.c_str(), D3DXMESH_SYSTEMMEM, dev, &adjBuffer, &mtrlBuffer, 0, &numMtrls, &meshSys)); // Step 2: Find out if the mesh already has normal info? D3DVERTEXELEMENT9 elems[MAX_FVF_DECL_SIZE]; HR(meshSys->GetDeclaration(elems)); bool hasNormals = false; D3DVERTEXELEMENT9 term = D3DDECL_END(); for(int i = 0; i < MAX_FVF_DECL_SIZE; ++i) { // Did we reach D3DDECL_END() {0xFF,0,D3DDECLTYPE_UNUSED, 0,0,0}? if(elems[i].Stream == 0xff ) break; if( elems[i].Type == D3DDECLTYPE_FLOAT3 && elems[i].Usage == D3DDECLUSAGE_NORMAL && elems[i].UsageIndex == 0 ) { hasNormals = true; break; } } // Step 3: Change vertex format to CustomVertex3NormalUV D3DVERTEXELEMENT9 elements[64]; UINT numElements = 0; CustomVertex3NormalUV::decl->GetDeclaration(elements, &numElements); ID3DXMesh* temp = 0; HR(meshSys->CloneMesh(D3DXMESH_SYSTEMMEM, elements, dev, &temp)); SAFERELEASECOM(meshSys); meshSys = temp; // Step 4: If the mesh did not have normals, generate them. if( hasNormals == false) HR(D3DXComputeNormals(meshSys, 0)); // Step 5: Optimize the mesh. HR(meshSys->Optimize(D3DXMESH_MANAGED | D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjBuffer->GetBufferPointer(), 0, 0, 0, meshOut)); SAFERELEASECOM(meshSys); // Done w/ system mesh. SAFERELEASECOM(adjBuffer); // Done with buffer. // Step 6: Extract the materials and load the textures. if( mtrlBuffer != 0 && numMtrls != 0 ) { D3DXMATERIAL* d3dxmtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(DWORD i = 0; i < numMtrls; ++i) { // Save the ith material. Note that the MatD3D property does not have an ambient // value set when its loaded, so just set it to the diffuse value. Material m; m.ambient = d3dxmtrls[i].MatD3D.Diffuse; m.diffuse = d3dxmtrls[i].MatD3D.Diffuse; m.spec = d3dxmtrls[i].MatD3D.Specular; m.specPower = d3dxmtrls[i].MatD3D.Power; mtrls->push_back( m ); // Check if the ith material has an associative texture if( d3dxmtrls[i].pTextureFilename != 0 ) { IDirect3DTexture9* tex = 0; std::string s(d3dxmtrls[i].pTextureFilename); const char folder[] = "./texture/"; unsigned int pos = s.find_last_of('\/'); std::string newFileName(folder); // std::string::npos gets returned if no / was found if(pos != std::string::npos) { //std::string sub = s.substr(pos,s.size()); if(strcmp(s.substr(pos+1,s.size()).c_str(),"Watcher.tga") == 0) { newFileName.append("Watcher_Black.tga"); } else { newFileName.append(s.substr(pos+1,s.size())); } } else { newFileName.append(s); } // Save the loaded texture HR(D3DXCreateTextureFromFile(dev, newFileName.c_str(), &tex)); texs->push_back( tex ); } else {
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC) MainWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "xtocmod"; if (RegisterClass(&wc) == 0) { MessageBox(NULL, "Failed to register the window class.", "Fatal Error", MB_OK | MB_ICONERROR); return NULL; } DWORD windowStyle = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); g_mainWindow = CreateWindow("xtocmod", "xtocmod", windowStyle, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL); if (g_mainWindow == NULL) { MessageBox(NULL, "Error creating application window.", "Fatal Error", MB_OK | MB_ICONERROR); } //ShowWindow(g_mainWindow, SW_SHOW); SetForegroundWindow(g_mainWindow); SetFocus(g_mainWindow); // Initialize D3D g_d3d = Direct3DCreate9(D3D_SDK_VERSION); if (g_d3d == NULL) { ShowD3DErrorMessage("Initializing D3D", 0); return 1; } D3DPRESENT_PARAMETERS presentParams; ZeroMemory(&presentParams, sizeof(presentParams)); presentParams.Windowed = TRUE; presentParams.SwapEffect = D3DSWAPEFFECT_COPY; #if 0 presentParams.BackBufferWidth = 300; presentParams.BackBufferHeight = 300; presentParams.BackBufferCount = 1; presentParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; presentParams.Windowed = TRUE; #endif HRESULT hr = g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_mainWindow, D3DCREATE_HARDWARE_VERTEXPROCESSING, &presentParams, &g_d3dDev); if (FAILED(hr)) { ShowD3DErrorMessage("Creating D3D device", hr); //return 1; } string inputFilename(lpCmdLine); string outputFilename(inputFilename, 0, inputFilename.rfind('.')); outputFilename += ".cmod"; ID3DXMesh* mesh = NULL; ID3DXBuffer* adjacency = NULL; ID3DXBuffer* materialBuf = NULL; ID3DXBuffer* effects = NULL; DWORD numMaterials; hr = D3DXLoadMeshFromX(inputFilename.c_str(), 0, g_d3dDev, &adjacency, &materialBuf, &effects, &numMaterials, &mesh); if (FAILED(hr)) { ShowD3DErrorMessage("Loading mesh from X file", hr); return 1; } DWORD numVertices = mesh->GetNumVertices(); DWORD numFaces = mesh->GetNumFaces(); cout << "vertices: " << numVertices << '\n'; cout << "faces: " << numFaces << '\n'; cout << "adjacency buffer size: " << adjacency->GetBufferSize() << '\n'; ofstream meshfile(outputFilename.c_str()); // Output the header meshfile << "#celmodel__ascii\n\n"; cout << "numMaterials=" << numMaterials << '\n'; D3DXMATERIAL* materials = reinterpret_cast<D3DXMATERIAL*>(materialBuf->GetBufferPointer()); for (DWORD mat = 0; mat < numMaterials; mat++) { meshfile << "material\n"; meshfile << "diffuse " << materials[mat].MatD3D.Diffuse << '\n'; //meshfile << "emissive " << materials[mat].MatD3D.Emissive << '\n'; meshfile << "specular " << materials[mat].MatD3D.Specular << '\n'; meshfile << "specpower " << materials[mat].MatD3D.Power << '\n'; meshfile << "opacity " << materials[mat].MatD3D.Diffuse.a << '\n'; meshfile << "end_material\n\n"; } // Vertex format D3DVERTEXELEMENT9 declElements[MAX_FVF_DECL_SIZE]; hr = mesh->GetDeclaration(declElements); if (FAILED(hr)) { ShowD3DErrorMessage("Checking vertex declaration", hr); return 1; } DWORD stride = D3DXGetDeclVertexSize(declElements, 0); VertexAttribute vertexMap[VertexAttribute::MaxAttribute]; CreateVertexAttributeMap(declElements, vertexMap); meshfile << "mesh\n\n"; DumpVertexDescription(vertexMap, meshfile); ID3DXMesh* optMesh = NULL; ID3DXBuffer* vertexRemap = NULL; DWORD* faceRemap = new DWORD[numFaces]; DWORD* optAdjacency = new DWORD[numFaces * 3]; hr = mesh->Optimize(D3DXMESHOPT_COMPACT | D3DXMESHOPT_STRIPREORDER, //D3DXMESHOPT_VERTEXCACHE | reinterpret_cast<DWORD*>(adjacency->GetBufferPointer()), optAdjacency, faceRemap, &vertexRemap, &optMesh); if (FAILED(hr)) { ShowD3DErrorMessage("Optimize failed: ", hr); return 1; } // Attribute table DWORD attribTableSize = 0; hr = optMesh->GetAttributeTable(NULL, &attribTableSize); if (FAILED(hr)) { ShowD3DErrorMessage("Querying attribute table size", hr); return 1; } D3DXATTRIBUTERANGE* attribTable = NULL; if (attribTableSize > 0) { attribTable = new D3DXATTRIBUTERANGE[attribTableSize]; hr = optMesh->GetAttributeTable(attribTable, &attribTableSize); if (FAILED(hr)) { ShowD3DErrorMessage("Getting attribute table", hr); return 1; } } cout << "Attribute table size: " << attribTableSize << '\n'; if (attribTableSize == 1) { cout << "Attribute id: " << attribTable[0].AttribId << '\n'; } if (!DumpMeshVertices(optMesh, vertexMap, stride, meshfile)) return 1; // output the indices for (DWORD attr = 0; attr < attribTableSize; attr++) { StripifyMeshSubset(optMesh, attr, meshfile); } meshfile << "\nend_mesh\n"; #if 0 IDirect3DIndexBuffer9* indices = NULL; hr = mesh->GetIndexBuffer(&indices); #endif #if 0 // No message loop required for this app MSG msg; GetMessage(&msg, NULL, 0u, 0u); while (msg.message != WM_QUIT) { GetMessage(&msg, NULL, 0u, 0u); TranslateMessage(&msg); DispatchMessage(&msg); } #endif return 0; }