//----------------------------------------------------------------------------- // File: D3DFile.cpp // // Desc: Support code for loading DirectX .X files. // // Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #define STRICT #define D3D_OVERLOADS #include "StdAfx.h" #include "guids.h" #include "D3DUtil.h" #include "D3DMath.h" #include "D3DTextr.h" #include "dxfile.h" #include "rmxfguid.h" #include "rmxftmpl.h" #include "D3DFile.h" #include "sealife.h" //----------------------------------------------------------------------------- // Name: GetFace // Desc: Get the nth face //----------------------------------------------------------------------------- DWORD* GetFace(DWORD* pFaceData, DWORD dwFace) { for(DWORD i=0; i 0) { dwNumIndices += (*pFaceData-2)*3; pFaceData += *pFaceData + 1; } return dwNumIndices; } //----------------------------------------------------------------------------- // Name: CD3DFileBaseObject() // Desc: //----------------------------------------------------------------------------- CD3DFileObject::CD3DFileObject(TCHAR* strName) { m_pNext = NULL; m_pChild = NULL; m_strName[0] = 0; m_bHasMeshData = FALSE; if (strName) StrCpy(m_strName, strName); // Set a default matrix D3DUtil_SetIdentityMatrix(m_mat); // Set a default material D3DUtil_InitMaterial(m_Material[0].m_mtrl, 1.0f, 1.0f, 1.0f); ZeroMemory(m_Material, sizeof(m_Material)); m_dwNumMaterials = 0; m_bHasAlpha = FALSE; // Clear out vertex data m_dwNumVertices = 0L; m_pVertices = NULL; m_dwNumIndices = 0L; m_pIndices = NULL; } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- CD3DFileObject::~CD3DFileObject() { SAFE_DELETE(m_pChild); SAFE_DELETE(m_pNext); for(DWORD i=0; iAddNext(pChild); else m_pChild = pChild; } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- VOID CD3DFileObject::AddNext(CD3DFileObject* pNext) { if (m_pNext) m_pNext->AddNext(pNext); else m_pNext = pNext; } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- VOID CD3DFileObject::Render(LPDIRECT3DDEVICE7 pd3dDevice, BOOL bAlpha) { if (m_bHasMeshData) { // Render the mesh WORD* pIndices = m_pIndices; for(DWORD i=0; i <= m_dwNumMaterials; i++) { // Skip materials with no references if (0L == m_Material[i].m_dwNumIndices) continue; // Render opaque and transparent meshes during separate passes if (bAlpha == m_bHasAlpha) { TCHAR* strTexture = m_Material[i].m_strTexture; DWORD dwNumIndices = m_Material[i].m_dwNumIndices; if (strTexture[0]) pd3dDevice->SetTexture(0, D3DTextr_GetSurface(strTexture)); pd3dDevice->SetMaterial(&m_Material[i].m_mtrl); pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, D3DFVF_VERTEX, m_pVertices, m_dwNumVertices, pIndices, dwNumIndices, NULL); } pIndices += m_Material[i].m_dwNumIndices; } } else { if (m_pChild) { // Save the old matrix sate D3DMATRIX matWorldOld, matWorldNew; pd3dDevice->GetTransform(D3DTRANSFORMSTATE_WORLD, &matWorldOld); // Concat the frame matrix with the current world matrix matWorldNew = m_mat * matWorldOld; pd3dDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matWorldNew); // Render the child nodes m_pChild->Render(pd3dDevice, bAlpha); // Restore the old matrix state pd3dDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matWorldOld); } } // Render the remaining sibling nodes if (m_pNext) m_pNext->Render(pd3dDevice, bAlpha); } //----------------------------------------------------------------------------- // Name: SetMaterialData() // Desc: Sets the material structure for the mesh //----------------------------------------------------------------------------- VOID CD3DFileObject::SetMaterialData(DWORD dwMaterial, D3DMATERIAL7* pmtrl, TCHAR* strName) { if (dwMaterial < MAX_MATERIAL) { m_Material[dwMaterial].m_mtrl = *pmtrl; StrCpyN(m_Material[dwMaterial].m_strTexture, strName, MAX_TEXTURE_NAME); if (pmtrl->diffuse.a < 1.0f) m_bHasAlpha = TRUE; } } //----------------------------------------------------------------------------- // Name: AddFace() // Desc: Adds one or more faces to a material slot in a Mesh. Note: this must // be called in order (material 0 first, then 1, ...) //----------------------------------------------------------------------------- VOID CD3DFileObject::AddFace(DWORD dwMaterial, DWORD* pFaceData, DWORD dwNumFaces) { // Make sure dwMaterial is in range if (dwMaterial >= MAX_MATERIAL) return; // Update the material count if (m_dwNumMaterials < dwMaterial+1) m_dwNumMaterials = dwMaterial+1; // add indices to the end WORD* pIndices = m_pIndices; for(DWORD i=0; i<=dwMaterial; i++) pIndices += m_Material[i].m_dwNumIndices; // Assign the indices (build a triangle fan for high-order polygons) while(dwNumFaces--) { DWORD dwNumVerticesPerFace = *pFaceData++; for(DWORD i=2; iGetData(NULL, &dwSize, (VOID**)&pData))) return NULL; // Set the material properties for the mesh D3DMATERIAL7 mtrl; ZeroMemory(&mtrl, sizeof(mtrl)); memcpy(&mtrl.diffuse, (VOID*)(pData+0), sizeof(FLOAT)*4); memcpy(&mtrl.ambient, (VOID*)(pData+0), sizeof(FLOAT)*4); memcpy(&mtrl.power, (VOID*)(pData+16), sizeof(FLOAT)*1); memcpy(&mtrl.specular, (VOID*)(pData+20), sizeof(FLOAT)*3); memcpy(&mtrl.emissive, (VOID*)(pData+32), sizeof(FLOAT)*3); strTexture[0] = 0; LPDIRECTXFILEOBJECT pChildObj; if (SUCCEEDED(pFileData->GetNextObject(&pChildObj))) { LPDIRECTXFILEDATA pChildData; if (SUCCEEDED(pChildObj->QueryInterface(IID_IDirectXFileData, (VOID**)&pChildData))) { const GUID* pguid; pChildData->GetType(&pguid); if (TID_D3DRMTextureFilename == *pguid) { TCHAR** string; if (FAILED(pChildData->GetData(NULL, &dwSize, (VOID**)&string))) return NULL; D3DTextr_CreateTextureFromFile(*string); StrCpyN(strTexture, *string, 128); } pChildData->Release(); } pChildObj->Release(); } pMesh->SetMaterialData(dwMaterial, &mtrl, strTexture); return S_OK; } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- HRESULT ParseMeshMaterialList(LPDIRECTXFILEDATA pFileData, CD3DFileObject* pMesh) { LPDIRECTXFILEOBJECT pChildObj; LPDIRECTXFILEDATA pChildData; LPDIRECTXFILEDATAREFERENCE pChildDataRef; DWORD dwMaterial = 0; while(SUCCEEDED(pFileData->GetNextObject(&pChildObj))) { if (SUCCEEDED(pChildObj->QueryInterface(IID_IDirectXFileData, (VOID**)&pChildData))) { const GUID* pguid; pChildData->GetType(&pguid); if (TID_D3DRMMaterial == *pguid) { ParseMaterial(pChildData, pMesh, dwMaterial++); } pChildData->Release(); } if (SUCCEEDED(pChildObj->QueryInterface(IID_IDirectXFileDataReference, (VOID**)&pChildDataRef))) { if (SUCCEEDED(pChildDataRef->Resolve(&pChildData))) { const GUID* pguid; pChildData->GetType(&pguid); if (TID_D3DRMMaterial == *pguid) { ParseMaterial(pChildData, pMesh, dwMaterial++); } pChildData->Release(); } pChildDataRef->Release(); } pChildObj->Release(); } return S_OK; } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- HRESULT ParseMesh(LPDIRECTXFILEDATA pFileData, CD3DFileObject* pParentFrame) { DWORD dwNameLen=80; CHAR strName[80]; TCHAR szName[80]; if (FAILED(pFileData->GetName(strName, &dwNameLen))) return E_FAIL; // Read the Mesh data from the file LONG_PTR pData; DWORD dwSize; SHAnsiToUnicode(strName, szName, ARRAYSIZE(szName)); if (FAILED(pFileData->GetData(NULL, &dwSize, (VOID**)&pData))) return E_FAIL; DWORD dwNumVertices = *((DWORD*)pData); pData += 4; D3DVECTOR* pVertices = ((D3DVECTOR*)pData); pData += 12*dwNumVertices; DWORD dwNumFaces = *((DWORD*)pData); pData += 4; DWORD* pFaceData = (DWORD*)pData; // Create the Mesh object CD3DFileObject* pMesh = new CD3DFileObject(szName); pMesh->SetMeshGeometry(pVertices, dwNumVertices, pFaceData, dwNumFaces); BOOL bHasNormals = FALSE; BOOL bHasMaterials = FALSE; // Enumerate child objects. LPDIRECTXFILEOBJECT pChildObj; while(SUCCEEDED(pFileData->GetNextObject(&pChildObj))) { LPDIRECTXFILEDATA pChildData; if (SUCCEEDED(pChildObj->QueryInterface(IID_IDirectXFileData, (VOID**)&pChildData))) { const GUID* pGUID; LONG_PTR pData; DWORD dwSize; pChildData->GetType(&pGUID); if (FAILED(pChildData->GetData(NULL, &dwSize, (VOID**)&pData))) { delete pMesh; return NULL; } if (TID_D3DRMMeshMaterialList == *pGUID) { DWORD dwNumMaterials = *((DWORD*)pData); pData += 4; DWORD dwNumMatFaces = *((DWORD*)pData); pData += 4; DWORD* pMatFace = (DWORD*)pData; if (dwNumMaterials == 1 || dwNumMatFaces != dwNumFaces) { // Only one material add all faces at once pMesh->AddFace(0, pFaceData, dwNumFaces); } else { // Multiple materials, add in sorted order for(DWORD mat=0; matAddFace(mat, GetFace(pFaceData, face), 1); } } } ParseMeshMaterialList(pChildData, pMesh); bHasMaterials = TRUE; } if (TID_D3DRMMeshNormals == *pGUID) { DWORD dwNumNormals = *((DWORD*)pData); D3DVECTOR* pNormals = (D3DVECTOR*)(pData+4); if (dwNumNormals == dwNumVertices) { pMesh->SetNormals(pNormals); bHasNormals = TRUE; } } if (TID_D3DRMMeshTextureCoords == *pGUID) { // Copy the texture coords into the mesh's vertices DWORD dwNumTexCoords = *((DWORD*)pData); FLOAT* pTexCoords = (FLOAT*)(((FLOAT*)pData)+4); if (dwNumTexCoords == dwNumVertices) pMesh->SetTextureCoords(pTexCoords); } pChildData->Release(); } pChildObj->Release(); } if (FALSE == bHasMaterials) pMesh->AddFace(0, pFaceData, dwNumFaces); if (FALSE == bHasNormals) pMesh->ComputeNormals(); pParentFrame->AddChild(pMesh); return S_OK; } //----------------------------------------------------------------------------- // Name: // Desc: //----------------------------------------------------------------------------- HRESULT ParseFrame(LPDIRECTXFILEDATA pFileData, CD3DFileObject* pParentFrame) { DWORD dwNameLen=80; CHAR strName[80]; TCHAR szName[80]; if (FAILED(pFileData->GetName(strName, &dwNameLen))) return E_FAIL; SHAnsiToUnicode(strName, szName, ARRAYSIZE(szName)); CD3DFileObject* pFrame = new CD3DFileObject(szName); // Enumerate child objects. LPDIRECTXFILEOBJECT pChildObj; while(SUCCEEDED(pFileData->GetNextObject(&pChildObj))) { LPDIRECTXFILEDATA pChildData; if (SUCCEEDED(pChildObj->QueryInterface(IID_IDirectXFileData, (VOID**)&pChildData))) { const GUID* pGUID; pChildData->GetType(&pGUID); if (TID_D3DRMFrame == *pGUID) ParseFrame(pChildData, pFrame); if (TID_D3DRMMesh == *pGUID) ParseMesh(pChildData, pFrame); if (TID_D3DRMFrameTransformMatrix == *pGUID) { DWORD dwSize; VOID* pData; if (FAILED(pChildData->GetData(NULL, &dwSize, &pData))) { delete pFrame; return NULL; } if (dwSize == sizeof(D3DMATRIX)) { // Convert from a left- to a right-handed cordinate system D3DMATRIX* pmatFrame = (D3DMATRIX*)pData; pmatFrame->_13 *= -1.0f; pmatFrame->_31 *= -1.0f; pmatFrame->_23 *= -1.0f; pmatFrame->_32 *= -1.0f; pmatFrame->_43 *= -1.0f; pFrame->SetMatrix(pmatFrame); } } pChildData->Release(); } pChildObj->Release(); } pParentFrame->AddChild(pFrame); return S_OK; } //----------------------------------------------------------------------------- // Name: CD3DFile() // Desc: Class constructor //----------------------------------------------------------------------------- CD3DFile::CD3DFile() { m_pRoot = NULL; } //----------------------------------------------------------------------------- // Name: ~CD3DFile() // Desc: Class destructor //----------------------------------------------------------------------------- CD3DFile::~CD3DFile() { SAFE_DELETE(m_pRoot); } //----------------------------------------------------------------------------- // Name: Load() // Desc: Loads a .X geometry file, and creates a hierarchy of frames and meshes // to represent the geometry in that file. //----------------------------------------------------------------------------- HRESULT CD3DFile::Load(LPCTSTR pszFilename) { HRESULT hr; LPDIRECTXFILE pDXFile; LPDIRECTXFILEENUMOBJECT pEnumObj = NULL; LPDIRECTXFILEDATA pFileData; const GUID* pGUID; CD3DFileObject* pFrame = NULL; CHAR szFilename[MAX_PATH]; SHTCharToAnsi(pszFilename, szFilename, ARRAYSIZE(szFilename)); // Cleanup any existing object SAFE_DELETE(m_pRoot); // Create the file object, and register the D3DRM templates for .X files if (FAILED(DirectXFileCreate(&pDXFile))) return E_FAIL; if (FAILED(pDXFile->RegisterTemplates((VOID*)D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES))) { pDXFile->Release(); return E_FAIL; } // Create an enumerator object, to enumerate through the .X file objects. // This will open the file in the current directory. hr = pDXFile->CreateEnumObject(szFilename, DXFILELOAD_FROMFILE, &pEnumObj); if (FAILED(hr)) { CHAR szPath[MAX_PATH]; GetCurrentDirectoryA(ARRAYSIZE(szPath), szPath); PathAppendA(szPath, szFilename); hr = pDXFile->CreateEnumObject(szPath, DXFILELOAD_FROMFILE, &pEnumObj); if (FAILED(hr)) { pDXFile->Release(); return hr; } } // Create a root object for the X file object m_pRoot = new CD3DFileObject(TEXT("D3DFile_Root")); // Cycle through each object. Parse meshes and frames as appropriate while(SUCCEEDED(hr = pEnumObj->GetNextDataObject(&pFileData))) { pFileData->GetType(&pGUID); if (*pGUID == TID_D3DRMFrame) ParseFrame(pFileData, m_pRoot); if (*pGUID == TID_D3DRMMesh) ParseMesh(pFileData, m_pRoot); pFileData->Release(); } // Success will result in hr == DXFILEERR_NOMOREOBJECTS if (DXFILEERR_NOMOREOBJECTS == hr) hr = S_OK; else SAFE_DELETE(m_pRoot); pEnumObj->Release(); pDXFile->Release(); return hr; } //----------------------------------------------------------------------------- // Name: GetMeshVertices() // Desc: Traverse the hierarchy of frames and meshes that make up the file // object, and retrieves the vertices for the specified mesh. //----------------------------------------------------------------------------- HRESULT CD3DFile::GetMeshVertices(TCHAR* strName, D3DVERTEX** ppVertices, DWORD* pdwNumVertices) { CD3DFileObject* pObject = FindObject(strName); if (pObject) return pObject->GetMeshGeometry(ppVertices, pdwNumVertices, NULL, NULL); return E_FAIL; } //----------------------------------------------------------------------------- // Name: GetMeshVertices() // Desc: Traverse the hierarchy of frames and meshes that make up the file // object, and retrieves the vertices for the specified mesh. //----------------------------------------------------------------------------- HRESULT CD3DFile::GetMeshIndices(TCHAR* strName, WORD** ppIndices, DWORD* pdwNumIndices) { CD3DFileObject* pObject = FindObject(strName); if (pObject) return pObject->GetMeshGeometry(NULL, NULL, ppIndices, pdwNumIndices); return E_FAIL; } //----------------------------------------------------------------------------- // Name: EnumObjects() // Desc: Enumerates all objects in the file. //----------------------------------------------------------------------------- BOOL CD3DFileObject::EnumObjects(BOOL (*fnCallback)(CD3DFileObject*,D3DMATRIX*,VOID*), D3DMATRIX* pmat, VOID* pContext) { if (fnCallback(this, pmat, pContext) == TRUE) return TRUE; if (m_pChild) { // Concat matrix set D3DMATRIX matSave = (*pmat); (*pmat) = (*pmat) * m_mat; if (m_pChild->EnumObjects(fnCallback, pmat, pContext) == TRUE) return TRUE; // Restore matrix set (*pmat) = matSave; } if (m_pNext) if (m_pNext->EnumObjects(fnCallback, pmat, pContext) == TRUE) return TRUE; return FALSE; } //----------------------------------------------------------------------------- // Name: EnumObjects() // Desc: Enumerates all objects in the file. //----------------------------------------------------------------------------- VOID CD3DFile::EnumObjects(BOOL (*fnCallback)(CD3DFileObject*,D3DMATRIX*,VOID*), D3DMATRIX* pmat, VOID* pContext) { if (m_pRoot) { D3DMATRIX mat; if (pmat) mat = *pmat; else D3DUtil_SetIdentityMatrix(mat); m_pRoot->EnumObjects(fnCallback, &mat, pContext); } } //----------------------------------------------------------------------------- // Name: ScaleMeshCB() // Desc: Callback to scale a mesh //----------------------------------------------------------------------------- BOOL ScaleMeshCB(CD3DFileObject* pFileObject, D3DMATRIX*, VOID* pContext) { D3DVERTEX* pVertices; DWORD dwNumVertices; if (SUCCEEDED(pFileObject->GetMeshGeometry(&pVertices, &dwNumVertices, NULL, NULL))) { for(DWORD i=0; istrName, pFileObject->GetName())) { data->pObject = pFileObject; return TRUE; } // Keep enumerating return FALSE; } //----------------------------------------------------------------------------- // Name: Scale() // Desc: Scales all meshes in the file //----------------------------------------------------------------------------- VOID CD3DFile::Scale(FLOAT fScale) { EnumObjects(ScaleMeshCB, NULL, (VOID*)&fScale); } //----------------------------------------------------------------------------- // Name: FindObject() // Desc: Searches all meshes in file object and returns named mesh //----------------------------------------------------------------------------- CD3DFileObject* CD3DFile::FindObject(TCHAR* strName) { if (NULL == strName) return m_pRoot; struct FINDMESHRECORD { TCHAR* strName; CD3DFileObject* pObject; }; FINDMESHRECORD data = { strName, NULL }; EnumObjects(FindMeshCB, NULL, (VOID*)&data); return data.pObject; } //----------------------------------------------------------------------------- // Name: Render() // Desc: Renders the hierarchy of frames and meshes that make up the file // object //----------------------------------------------------------------------------- HRESULT CD3DFile::Render(LPDIRECT3DDEVICE7 pd3dDevice) { LPDIRECTDRAWSURFACE7 pddsSavedTexture; D3DMATRIX matSaved; D3DMATERIAL7 mtrlSaved; DWORD dwAlphaState, dwSrcBlendState, dwDestBlendState; if (m_pRoot) { // State render states that will be overwritten pd3dDevice->GetMaterial(&mtrlSaved); pd3dDevice->GetTexture(0, &pddsSavedTexture); pd3dDevice->GetTransform(D3DTRANSFORMSTATE_WORLD, &matSaved); pd3dDevice->GetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, &dwAlphaState); pd3dDevice->GetRenderState(D3DRENDERSTATE_SRCBLEND, &dwSrcBlendState); pd3dDevice->GetRenderState(D3DRENDERSTATE_DESTBLEND, &dwDestBlendState); // Render the opaque file object's hierarchy of frames and meshes m_pRoot->Render(pd3dDevice, FALSE); // Render the transparent file object's hierarchy of frames and meshes // pd3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE); // pd3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA); // pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA); // m_pRoot->Render(pd3dDevice, TRUE); // Restore the render states pd3dDevice->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, dwAlphaState); pd3dDevice->SetRenderState(D3DRENDERSTATE_SRCBLEND, dwSrcBlendState); pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND, dwDestBlendState); pd3dDevice->SetTransform(D3DTRANSFORMSTATE_WORLD, &matSaved); pd3dDevice->SetTexture(0, pddsSavedTexture); pd3dDevice->SetMaterial(&mtrlSaved); // Keep the ref count of the texture consistent if (pddsSavedTexture) pddsSavedTexture->Release(); } return S_OK; }