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; }
VCNNode* D3DConverter::ConvertMesh(const std::wstring& name, LPD3DXMESHCONTAINER baseMeshContainer, D3DXFRAME* frameRoot, ID3DXAnimationController* animController, LPDIRECT3DDEVICE9 device) { MultiAnimMC* meshContainer = static_cast<MultiAnimMC*>(baseMeshContainer); ID3DXMesh* systemMesh = meshContainer->MeshData.pMesh; // Load vertex caches // DWORD meshFVF = systemMesh->GetFVF(); size_t vertexCount = systemMesh->GetNumVertices(); const DWORD stride = D3DXGetFVFVertexSize( meshFVF ); const DWORD normalStride = D3DXGetFVFVertexSize( D3DFVF_NORMAL ); const DWORD diffuseStride = D3DXGetFVFVertexSize( D3DFVF_DIFFUSE ); const DWORD textureStride = D3DXGetFVFVertexSize( D3DFVF_TEX1 ); std::vector<VCNFloat> vtPositionBuffer( vertexCount * kCacheStrides[VT_POSITION] ); std::vector<VCNFloat> vtBlendWeights( vertexCount * kCacheStrides[VT_BLENDWEIGHTS] ); //TODO Verify the size of this shit std::vector<DWORD> vtBlendIndices( vertexCount * kCacheStrides[VT_BLENDINDICES] ); //TODO Verify the size of this shit std::vector<VCNFloat> vtNormalBuffer( vertexCount * kCacheStrides[VT_LIGHTING] ); std::vector<VCNFloat> vtTextureBuffer( vertexCount * kCacheStrides[VT_DIFFUSE_TEX_COORDS] ); VCNFloat* vtPositionBuf = &vtPositionBuffer[0]; VCNFloat* vtBlendWeightBuf = &vtBlendWeights[0]; DWORD* vtBlendIndicesBuf = &vtBlendIndices[0]; VCNFloat* vtNormalBuf = &vtNormalBuffer[0]; VCNFloat* vtTextureBuf = &vtTextureBuffer[0]; BYTE* vbptr = NULL; BYTE* vblineptr = NULL; systemMesh->LockVertexBuffer(D3DLOCK_READONLY, (LPVOID*)&vblineptr); DWORD positionBlendAndIndicesStride = GetPositionStride(meshFVF); for(VCNUInt i = 0; i < vertexCount; ++i) { vbptr = vblineptr; if ( ContainsPositionInformation(meshFVF) ) { // Read position float* posData = (float*)vbptr; *vtPositionBuf = posData[0]; vtPositionBuf++; *vtPositionBuf = posData[1]; vtPositionBuf++; *vtPositionBuf = posData[2]; vtPositionBuf++; if (ContainsBlending(meshFVF)) { // Get blend weights size_t blendCount = (positionBlendAndIndicesStride / 4) - 3 - 1; // -3 to remove xyz, -1 to remove indices which come after for(size_t i = 0; i < blendCount; ++i) { *vtBlendWeightBuf = posData[3 + i]; vtBlendWeightBuf++; } vtBlendWeightBuf += 4 - blendCount; //each item is an array of 4 floats // Get blend indices // TODO SKIN Check the format we have to send this data as. if ( ContainsFlag(meshFVF, D3DFVF_LASTBETA_UBYTE4) ) { *vtBlendIndicesBuf = ((DWORD*)vbptr)[3 + blendCount]; vtBlendIndicesBuf++; } } vbptr += positionBlendAndIndicesStride; } else { VCN_ASSERT_FAIL( VCNTXT("Mesh FVF not supported (no vertex position) [FVF = %d, stride = %d]"), meshFVF, stride ); } // Read normal if ( ContainsFlag(meshFVF, D3DFVF_NORMAL) ) { D3DXVECTOR3* normal = (D3DXVECTOR3*)(vbptr); *vtNormalBuf = normal->x; vtNormalBuf++; *vtNormalBuf = normal->y; vtNormalBuf++; *vtNormalBuf = normal->z; vtNormalBuf++; // Set default diffuse color std::fill(vtNormalBuf, vtNormalBuf+3, 1.0f); vtNormalBuf += 3; vbptr += normalStride; } else { VCN_ASSERT_FAIL( VCNTXT("Mesh FVF not supported (no normals) [FVF = %d, stride = %d]"), meshFVF, stride ); } if ( ContainsFlag(meshFVF, D3DFVF_DIFFUSE) ) vbptr += diffuseStride; // Read texcoords // the check with D3DFVF_TEX0 is pretty useless as it's always true... the flag value is 0... if ( ContainsFlag(meshFVF, D3DFVF_TEX0) || ContainsFlag(meshFVF, D3DFVF_TEX1) ) { float* texCoords = (float*)(vbptr); *vtTextureBuf = texCoords[0]; vtTextureBuf++; *vtTextureBuf = texCoords[1]; vtTextureBuf++; vbptr += textureStride; } else { VCN_ASSERT_FAIL( VCNTXT("Mesh FVF not supported (no texture coordinates) [FVF = %d, stride = %d]"), meshFVF, stride ); } vblineptr += stride; } systemMesh->UnlockVertexBuffer(); VCND3D9* renderer = VCNRenderCore::GetInstance()->Cast<VCND3D9>(); // Generate cache resources that will be bind to Vicuna's meshes VCNResID positionCache = renderer->CreateCache(VT_POSITION, &vtPositionBuffer[0], vertexCount * kCacheStrides[VT_POSITION]); VCNResID lightingCache = renderer->CreateCache(VT_LIGHTING, &vtNormalBuffer[0], vertexCount * kCacheStrides[VT_LIGHTING]); VCNResID textureCache = renderer->CreateCache(VT_DIFFUSE_TEX_COORDS, &vtTextureBuffer[0], vertexCount * kCacheStrides[VT_DIFFUSE_TEX_COORDS]); VCNResID blendWeightCache = renderer->CreateCache(VT_BLENDWEIGHTS, &vtBlendWeights[0], vertexCount * kCacheStrides[VT_BLENDWEIGHTS]); VCNResID blendIndiceCache = renderer->CreateCache(VT_BLENDINDICES, &vtBlendIndices[0], vertexCount * kCacheStrides[VT_BLENDINDICES]); // Get model faces // VCNUShort* ibptr = 0; std::vector<VCNUShort> indices( systemMesh->GetNumFaces() * 3 ); systemMesh->LockIndexBuffer(D3DLOCK_READONLY, (LPVOID*)&ibptr); for(VCNUInt i = 0; i < systemMesh->GetNumFaces(); i++) { indices[(i * 3) + 0] = *(ibptr++); indices[(i * 3) + 1] = *(ibptr++); indices[(i * 3) + 2] = *(ibptr++); } systemMesh->UnlockIndexBuffer(); // Load materials // std::vector<VCNResID> materialIDS; D3DXMATERIAL* d3dxMaterials = meshContainer->pMaterials; for (DWORD i = 0; i < meshContainer->NumMaterials; ++i) { VCNResID materialID = kInvalidResID; // Create the texture if it exists - it may not if ( d3dxMaterials[i].pTextureFilename ) { VCNResID textureID = kInvalidResID; VCNString texturePath = VCNTXT("Textures/"); texturePath += VCN_A2W(d3dxMaterials[i].pTextureFilename); // Check if the texture is already loaded VCND3D9Texture* resTexture = VCNResourceCore::GetInstance()->GetResource<VCND3D9Texture>(texturePath); if ( !resTexture ) { textureID = VCNMaterialCore::GetInstance()->CreateTexture(texturePath); VCN_ASSERT_MSG( textureID != kInvalidResID, VCNTXT("Can't load texture %s"), texturePath.c_str() ); } else { textureID = resTexture->GetResourceID(); } VCNMaterial* material = new VCNMaterial(); const VCNString materialName = StringBuilder() << name << VCNTXT("_material_") << i; material->SetName( materialName ); VCNColor ambient = VCNColor((const VCNFloat*)&d3dxMaterials[i].MatD3D.Ambient); ambient.a = 1.0f; ambient += VCNColor(0.5f, 0.5f, 0.5f, 0); material->SetAmbientColor( ambient ); material->SetDiffuseColor( VCNColor((const VCNFloat*)&d3dxMaterials[i].MatD3D.Diffuse) ); material->SetSpecularColor( VCNColor((const VCNFloat*)&d3dxMaterials[i].MatD3D.Specular) ); material->SetSpecularPower( d3dxMaterials[i].MatD3D.Power ); VCNEffectParamSet& params = material->GetEffectParamSet(); params.SetEffectID( eidSkinned ); params.AddResource( VCNTXT("DiffuseTexture"), textureID ); // Add material as a resource. materialID = VCNResourceCore::GetInstance()->AddResource( material->GetName(), material ); } materialIDS.push_back( materialID ); } // Get the model attribute table with which we will instantiate has many mesh. // DWORD attribTableSize; std::vector<D3DXATTRIBUTERANGE> attribTable; HRESULT hr = systemMesh->GetAttributeTable( 0, &attribTableSize ); if ( FAILED(hr) ) return 0; attribTable.resize( attribTableSize ); hr = systemMesh->GetAttributeTable( &attribTable[0], &attribTableSize ); if ( FAILED(hr) ) return 0; // Set the root node VCNNode* rootNode = attribTableSize > 1 ? VCNNodeCore::GetInstance()->CreateNode<VCNNode>() : VCNNodeCore::GetInstance()->CreateNode<VCNRenderNode>(); rootNode->SetTag( StringBuilder() << name << VCNTXT("_Root") ); // For each attribute, we get the material texture for (DWORD i = 0; i < attribTableSize; ++i) { VCNRenderNode* partNode = attribTableSize == 1 ? safe_pointer_cast<VCNRenderNode*>( rootNode ) : VCNNodeCore::GetInstance()->CreateNode<VCNRenderNode>(); const VCNString partNodeName = StringBuilder() << name << VCNTXT("_Part_") << i; partNode->SetTag( partNodeName ); VCNMesh* partMesh = new VCNMesh(); partMesh->SetCacheID(VT_POSITION, positionCache); //SKIN do this for blend weights and blend indices partMesh->SetCacheID(VT_LIGHTING, lightingCache); partMesh->SetCacheID(VT_DIFFUSE_TEX_COORDS, textureCache); partMesh->SetCacheID(VT_BLENDWEIGHTS, blendWeightCache); partMesh->SetCacheID(VT_BLENDINDICES, blendIndiceCache); partMesh->SetPrimitiveType(PT_TRIANGLELIST); partMesh->SetBoneInfluenceCount( meshContainer->m_dwMaxNumFaceInfls ); size_t numBones = meshContainer->pSkinInfo == nullptr ? 0 : meshContainer->pSkinInfo->GetNumBones(); if (numBones > 0) { auto offsets = std::vector<Matrix4>(numBones); std::transform( std::begin(meshContainer->m_amxBoneOffsets), std::end(meshContainer->m_amxBoneOffsets), std::begin(offsets), [](const D3DXMATRIX& mat) { return Matrix4( (VCNFloat*)mat.m ); }); partMesh->SetBoneOffsets( std::move(offsets) ); LPD3DXBONECOMBINATION boneCombination = reinterpret_cast<LPD3DXBONECOMBINATION>( meshContainer->m_pBufBoneCombos->GetBufferPointer() ); size_t numPaletteEntries = meshContainer->m_dwNumPaletteEntries; std::vector<size_t> matriceIndexes; for(size_t paletteIndex = 0; paletteIndex < numPaletteEntries; ++paletteIndex) { size_t matIndex = boneCombination[i].BoneId[paletteIndex]; if ( matIndex == std::numeric_limits<size_t>::max()) continue; matriceIndexes.push_back(matIndex); } partMesh->SetMatrixPaletteIndexes(matriceIndexes); } const DWORD partFaceCount = attribTable[i].FaceCount; const void* partFaceBufferStart = &indices[attribTable[i].FaceStart * 3]; const VCNResID indexCacheID = renderer->CreateCache(VT_INDEX, partFaceBufferStart, partFaceCount * 3 * kCacheStrides[VT_INDEX]); partMesh->SetFaceCount( attribTable[i].FaceCount ); partMesh->SetFaceCache( indexCacheID ); // Compute bounding sphere float radius; D3DXVECTOR3 center; D3DXComputeBoundingSphere( (D3DXVECTOR3*)(&vtPositionBuffer[0] + attribTable[i].VertexStart * 3), attribTable[i].VertexCount, stride, ¢er, &radius ); VCNSphere modelBoundSphere( radius, V2V<Vector3>(center) ); partMesh->SetBoundingSphere( modelBoundSphere ); // Add mesh resource const VCNString partMeshName = StringBuilder() << name << VCNTXT("_part_") << i; const VCNResID partMeshID = VCNResourceCore::GetInstance()->AddResource( partMeshName, partMesh ); // Set model part node attributes partNode->SetMeshID( partMeshID ); if (animController && numBones > 0) { partNode->AddComponent( new VCND3DAnimator(partMeshID, animController, frameRoot, meshContainer->m_apmxBonePointers) ); } size_t index = attribTable[i].AttribId; index = index >= materialIDS.size() ? materialIDS.size() - 1 : index; partNode->SetMaterialID( materialIDS[index] ); // Add children to root if ( attribTableSize > 1 ) { rootNode->AttachChild( partNode->GetNodeID() ); } } return rootNode; }
bool CMeshBundle::loadMesh( const CResourceId& id, const CResourceId& fullName, CMesh& mesh ) const { // try to load with D3DX // obsolete case: .X files if( CStringHelper::endsWith( fullName.getUniqueName(), ".x" ) || CStringHelper::endsWith( fullName.getUniqueName(), ".X" ) ) { ID3DXBuffer* adjancency = NULL; ID3DXBuffer* material = NULL; ID3DXBuffer* effects = NULL; DWORD matCount; ID3DXMesh* dxmesh = NULL; HRESULT hres = D3DXLoadMeshFromX( fullName.getUniqueName().c_str(), D3DXMESH_SYSTEMMEM, &CD3DDevice::getInstance().getDevice(), &adjancency, &material, &effects, &matCount, &dxmesh ); if( !SUCCEEDED( hres ) ) return false; assert( dxmesh ); if( adjancency ) adjancency->Release(); if( material ) material->Release(); if( effects ) effects->Release(); // // init our mesh assert( !mesh.isCreated() ); // HACK - very limited int formatFlags = 0; DWORD dxFormat = dxmesh->GetFVF(); if( dxFormat & D3DFVF_XYZ ) formatFlags |= CVertexFormat::V_POSITION; if( dxFormat & D3DFVF_NORMAL ) formatFlags |= CVertexFormat::V_NORMAL; if( dxFormat & D3DFVF_TEX1 ) formatFlags |= CVertexFormat::V_UV0_2D; CVertexFormat vertFormat( formatFlags ); // HACK int indexStride = 2; CD3DVertexDecl* vertDecl = RGET_VDECL( CVertexDesc( vertFormat ) ); mesh.createResource( dxmesh->GetNumVertices(), dxmesh->GetNumFaces()*3, vertFormat, indexStride, *vertDecl, CMesh::BUF_STATIC ); // // now, copy data into our mesh void *dxvb, *dxib; dxmesh->LockVertexBuffer( 0, &dxvb ); dxmesh->LockIndexBuffer( 0, &dxib ); void* myvb = mesh.lockVBWrite(); void* myib = mesh.lockIBWrite(); memcpy( myvb, dxvb, mesh.getVertexCount() * mesh.getVertexStride() ); memcpy( myib, dxib, mesh.getIndexCount() * mesh.getIndexStride() ); dxmesh->UnlockVertexBuffer(); dxmesh->UnlockIndexBuffer(); mesh.unlockVBWrite(); mesh.unlockIBWrite(); // // create groups int ngroups; dxmesh->GetAttributeTable( 0, (DWORD*)&ngroups ); D3DXATTRIBUTERANGE *attrs = new D3DXATTRIBUTERANGE[ngroups]; dxmesh->GetAttributeTable( attrs, (DWORD*)&ngroups ); for( int i = 0; i < ngroups; ++i ) { const D3DXATTRIBUTERANGE& a = attrs[i]; mesh.addGroup( CMesh::CGroup( a.VertexStart, a.VertexCount, a.FaceStart, a.FaceCount ) ); } delete[] attrs; // release d3dx mesh dxmesh->Release(); } else { // our own format assert( !mesh.isCreated() ); bool ok = CMeshSerializer::loadMeshFromFile( fullName.getUniqueName().c_str(), mesh ); if( !ok ) return false; } mesh.computeAABBs(); CONSOLE.write( "mesh loaded '" + id.getUniqueName() + "'" ); return true; }