#include "stdafx.h" #include "Utility.h" #ifndef CODE_INGAME #include "NiLightMapMaterialHelper.h" #include "NiLightMapMaterial.h" #include #include //------------------------------------------------------------------------------ NiNode* NiLightMapMaterialHelper::ConvertToLightMapMaterial(NiNode* pkNode, int& iNumGeomConv, int& iNumGeomFail, std::vector& arrInfo) { if (pkNode == NULL) return NULL; pkNode->Update(0.0f); pkNode->UpdateProperties(); pkNode->UpdateEffects(); pkNode->UpdateControllers(0.0f); pkNode->UpdateNodeBound(); pkNode->CompactChildArray(); for (int i=0; iGetChildCount(); i++) { NiAVObject* pkAVObj = pkNode->GetAt(i); if (pkAVObj == NULL || pkAVObj->GetAppCulled()) continue;// if (NiIsKindOf(NiGeometry, pkAVObj)) { NiGeometry* pkGeom = (NiGeometry*)pkAVObj; bool bIsSFX = false; NiString kGeomName = pkGeom->GetName(); if (kGeomName == NULL) { kGeomName = "unnamed"; } if (kGeomName.Contains("sfx")) { bIsSFX = true; } try { char szInfo[256]; if (VerifyToLightMapMaterial(pkGeom, szInfo)) { // 通过验证 NiTriShape* pkNewGeom = NULL; // back up all property, extra data, controllers NiGeometryData::Consistency eBackupConsist = pkGeom->GetModelData()->GetConsistency(); pkGeom->GetModelData()->SetConsistency(NiGeometryData::MUTABLE); // 设置 material, 添加 uvset, 展平 UV //NiMaterial* pkMat = NiLightMapMaterial::GetMaterial(LIGHT_MAP_MATERIAL_NAME); int iNewUV = AddUVSet(pkGeom); if (iNewUV >= 0) { pkNewGeom = FlatUV(pkGeom, iNewUV); } // 将 pkGeom 的所有 property, extra data, controllers 拷贝到 new geom if (pkNewGeom != NULL) { // 拷贝位置 pkNewGeom->SetLocalTransform(pkGeom->GetLocalTransform()); // 拷贝 selected update 属性 pkNewGeom->SetSelectiveUpdate(pkGeom->GetSelectiveUpdate()); pkNewGeom->SetSelectiveUpdatePropertyControllers(pkGeom->GetSelectiveUpdatePropertyControllers()); pkNewGeom->SetSelectiveUpdateRigid(pkGeom->GetSelectiveUpdateRigid()); pkNewGeom->SetSelectiveUpdateTransforms(pkGeom->GetSelectiveUpdateTransforms()); // 拷贝 property for (int j=0; jGetProperty(j); if (pkProperty != NULL) { NiProperty* pkNewProp = (NiProperty*)pkProperty->Clone(); // 清除 Dark Map if (NiIsKindOf(NiTexturingProperty, pkNewProp)) { NiTexturingProperty* pkTexProp = (NiTexturingProperty*)pkNewProp; if (pkTexProp->GetDarkMap()) { pkTexProp->SetDarkMap(NULL); } } pkNewGeom->AttachProperty(pkNewProp); } } // 拷贝 TimeController NiTimeController* pkTimeControllers = pkGeom->GetControllers(); while (pkTimeControllers != NULL) { if (pkTimeControllers->GetTarget() == pkGeom) { pkTimeControllers->SetTarget(pkNewGeom); } pkTimeControllers = pkTimeControllers->GetNext(); } // 拷贝 ExtraData for (int j=0; jGetExtraDataSize(); j++) { pkNewGeom->AddExtraData(pkGeom->GetExtraDataAt(j)); } AddShaderMap(pkNewGeom, iNewUV); NiMaterial* pkMat = NiMaterial::GetMaterial(LIGHT_MAP_MATERIAL_NAME); if (pkMat == NULL) { pkMat = NiNew NiLightMapMaterial(); } pkNewGeom->ApplyAndSetActiveMaterial(pkMat); pkNewGeom->UpdateEffects(); pkNewGeom->UpdateProperties(); pkNewGeom->UpdateControllers(0.0f); pkNewGeom->Update(0.0f); // 检查 material NiMaterialProperty* pkMatProp = NiDynamicCast(NiMaterialProperty, pkNewGeom->GetProperty(NiProperty::MATERIAL)); if (pkMatProp != NULL) { //pkMatProp->SetShineness(0.0f); if(!bIsSFX) { //pkMatProp->SetEmittance(NiColor::BLACK); } else { //NiColor kEmitColor = pkMatProp->GetEmittance(); //kEmitColor.r = kEmitColor.r * 0.2f; //kEmitColor.g = kEmitColor.g * 0.2f; //kEmitColor.b = kEmitColor.b * 0.2f; //pkMatProp->SetEmittance(kEmitColor); } } pkNewGeom->GetModelData()->SetConsistency(eBackupConsist); // 验证新 geometry 包围盒是否异常 pkNewGeom->UpdateNodeBound(); if (pkNewGeom->GetWorldBound().GetRadius() > pkGeom->GetWorldBound().GetRadius()*2.0f) { //pkTimeControllers = pkNewGeom->GetControllers(); //while (pkTimeControllers != NULL) //{ // if (pkTimeControllers->GetTarget() == pkNewGeom) // { // pkTimeControllers->SetTarget(pkGeom); // } // pkTimeControllers = pkTimeControllers->GetNext(); //} NiDelete pkNewGeom; throw "Convert error. Abnormal bounding. "; } NiNode* pkParent = pkGeom->GetParent(); pkParent->AttachChild(pkNewGeom); pkParent->DetachChild(pkGeom); pkNewGeom->SetName(pkGeom->GetName()); if (pkNewGeom->GetName() == NULL) { pkNewGeom->SetName("unnamed"); } pkParent->Update(0.0f); pkParent->UpdateNodeBound(); } // 转换成功 iNumGeomConv++; } else { // 未通过验证 std::string strInfo = kGeomName; strInfo = strInfo + "," + szInfo; arrInfo.push_back(strInfo); // 验证错误信息 iNumGeomFail++; } } catch(char* pszException) { std::string strInfo = kGeomName; strInfo = strInfo + "," + pszException; arrInfo.push_back(strInfo); iNumGeomFail++; } catch(...) { std::string strInfo = kGeomName; strInfo = strInfo + "," + " Exception"; arrInfo.push_back(strInfo); iNumGeomFail++; } } else if (NiIsKindOf(NiNode, pkAVObj)) { ConvertToLightMapMaterial((NiNode*)pkAVObj, iNumGeomConv, iNumGeomFail, arrInfo); } } return pkNode; } //------------------------------------------------------------------------------ /// 判断一个 geometry 是否可以转换为 light map material bool NiLightMapMaterialHelper::VerifyToLightMapMaterial(NiGeometry* pkGeom, char szInfo[256]) { if (pkGeom == NULL) return false; // 0. pkGeom 是 NiTriBasedGeom // 1. pkGeom 原来使用的是 NiStandardMaterial // 2. pkGeom ShaderMap0 没有使用 // 3. 有 GeometryMorphController if (!NiIsKindOf(NiTriBasedGeom, pkGeom)) { sprintf(szInfo, "The Geometry %s is not NiTriBasedGeom. Skipped \n", pkGeom->GetName()); return false; } //if (pkGeom->GetName().Contains("sfx")) //{ // sprintf(szInfo, "The Geometry %s is SFX. Skipped \n", pkGeom->GetName()); // return false; //} NiString kName = pkGeom->GetActiveMaterial()->GetName(); if (pkGeom->GetActiveMaterial()->GetName() != STANDARD_MATERIAL_NAME) { // 原来使用的不是 standard material if (pkGeom->GetActiveMaterial()->GetName() == LIGHT_MAP_MATERIAL_NAME) { sprintf(szInfo, "Already Light Map Material. Skipped. \n", kName); } else { sprintf(szInfo, "The Geometry %s does not use standard material. Skipped. \n", kName); } return false; } NiTexturingProperty* pkTexProp = (NiTexturingProperty*)pkGeom->GetProperty(NiProperty::TEXTURING); if (pkTexProp == NULL) return true; // 原来没有使用 texture // 验证没有丢失纹理 const NiTexturingProperty::NiMapArray& arrMaps = pkTexProp->GetMaps(); for (int i=0; iGetTexture()); if (pkTex != NULL) { pkTex->LoadPixelDataFromFile(); if (pkTex->GetSourcePixelData() == NULL && pkTex->GetRendererData() == NULL) { const NiFixedString& kTexName = pkTex->GetName(); sprintf(szInfo, "Error: Missing Texture %s.\n", kTexName); return false; } } } if (pkTexProp->GetShaderMap(0) != NULL) { sprintf(szInfo, "ShaderMap0 already used."); return false; } if (pkGeom->GetController(&(NiGeomMorpherController::ms_RTTI))) { sprintf(szInfo, "Find NiGeomMorpherController."); return false; } return true; } //------------------------------------------------------------------------------ /// 为 pkGeom 添加一套新 uv, 返回新 uv id int NiLightMapMaterialHelper::AddUVSet(NiGeometry* pkGeom) { if (pkGeom == NULL) return -1; unsigned short usNumTexSetsBefore = pkGeom->GetTextureSets(); unsigned short usNumVert = pkGeom->GetVertexCount(); NiPoint2* arrTexSets = NiNew NiPoint2[usNumVert]; ZeroMemory(arrTexSets, usNumVert * sizeof(NiPoint2)); pkGeom->AppendTextureSet(arrTexSets); pkGeom->GetModelData()->MarkAsChanged(NiGeometryData::TEXTURE_MASK); pkGeom->Update(0.0f); unsigned short usNumVertAfter = pkGeom->GetTextureSets(); return usNumTexSetsBefore; } //-------------------------------------------------------------------------------------- BOOL CheckMeshValidation(LPD3DXMESH pMesh, LPD3DXMESH *pMeshOut, DWORD **ppAdjacency, BOOL bTopologicalAdjacency, BOOL bGeometricAdjacency, LPD3DXBUFFER pOrigAdj) { // 从 DX UV Atlas 例子考来的函数 HRESULT hr = S_OK; BOOL bResult = TRUE; DWORD *pAdjacencyIn = NULL, *pAdjacencyAlloc = NULL; LPD3DXBUFFER pErrorsAndWarnings = NULL; if (!(pMesh && ppAdjacency)) { bResult = FALSE; goto FAIL; } int iNumFaces = pMesh->GetNumFaces(); if ( bTopologicalAdjacency || bGeometricAdjacency ) { pAdjacencyAlloc = (DWORD*)NiMalloc(pMesh->GetNumFaces() * sizeof(DWORD)*3); if (!pAdjacencyAlloc) { LOG("Out of memory\n"); bResult = FALSE; goto FAIL; } pAdjacencyIn = pAdjacencyAlloc; } if ( bTopologicalAdjacency ) { hr = pMesh->ConvertPointRepsToAdjacency( NULL, pAdjacencyIn ); if (FAILED(hr)) { //printf("ConvertPointRepsToAdjacency() failed: %s\n", DXGetErrorString(hr)); bResult = FALSE; goto FAIL; } } else if ( bGeometricAdjacency ) { hr = pMesh->GenerateAdjacency( EPSILON, pAdjacencyIn ); if (FAILED(hr)) { //wprintf(L"GenerateAdjacency() failed: %s\n", DXGetErrorString(hr)); bResult = FALSE; goto FAIL; } } else if( pOrigAdj ) { pAdjacencyIn = (DWORD*)pOrigAdj->GetBufferPointer(); } hr = D3DXValidMesh(pMesh, pAdjacencyIn, &pErrorsAndWarnings); if (NULL != pErrorsAndWarnings) { char* s = (char*)pErrorsAndWarnings->GetBufferPointer(); wprintf(L"%S", s); SAFE_RELEASE(pErrorsAndWarnings); } if (FAILED(hr)) { wprintf(L"D3DXValidMesh() failed: %s. Attempting D3DXCleanMesh()\n", DXGetErrorString(hr) ); hr = D3DXCleanMesh(D3DXCLEAN_SIMPLIFICATION, pMesh, pAdjacencyIn, pMeshOut, pAdjacencyIn, &pErrorsAndWarnings); if (NULL != pErrorsAndWarnings) { char* s = (char*)pErrorsAndWarnings->GetBufferPointer(); wprintf(L"%S", s); } if (FAILED(hr)) { wprintf(L"D3DXCleanMesh() failed: %s\n", DXGetErrorString(hr) ); bResult = FALSE; goto FAIL; } else { wprintf(L"D3DXCleanMesh() succeeded: %s\n", DXGetErrorString(hr) ); } } else { *pMeshOut = pMesh; (*pMeshOut)->AddRef(); } FAIL: SAFE_RELEASE(pErrorsAndWarnings); if( bResult == false ) { SAFE_DELETE(pAdjacencyAlloc); SAFE_DELETE(*pMeshOut); } *ppAdjacency = pAdjacencyIn; return bResult; } //------------------------------------------------------------------------------ NiTriShape* NiLightMapMaterialHelper::FlatUV(NiGeometry* pkGeom, unsigned int uiID) { // 将 pkGeom 转换为 D3DXMESH, 调用 DX 的展 UV 函数.再将其转回 NiGeometry HRESULT hr = S_OK; bool bResult = true; NiTriShape* pkNewGeom = NULL; if (pkGeom == NULL) return NULL; LPD3DXMESH pD3DMesh = ConvertToD3DMesh(pkGeom); if (pD3DMesh == NULL) return NULL; LPD3DXMESH pOutputMesh = NULL, pMeshValid = NULL; DWORD* pAdjacency = NULL; //if (S_FALSE == D3DXUVAtlasCreate(pD3DMesh, 0, 1, 256, 256, 4, uiID, // NULL, NULL, NULL, NULL, 0.0f, NULL, ) // 这里有问题, pMeshValid 可能会比 pD3DMesh 的顶点多 if( false == CheckMeshValidation(pD3DMesh, &pMeshValid, &pAdjacency, false, true, NULL) ) { wprintf(L"Unable to clean mesh\n"); bResult = false; goto FAIL; } LPD3DXMESH pMeshUsed = pD3DMesh; //if (pD3DMesh->GetNumVertices() == pMeshValid->GetNumVertices()) //{ // pMeshUsed = pMeshValid; //} LPD3DXBUFFER pVertexRemapArray = NULL; // DX 的展 UV 函数 hr = D3DXUVAtlasCreate( pMeshUsed,// pMeshValid 0, 1, 512, 512, 4, // fGutter The minimum distance between two charts 0, // 目标 UV pAdjacency, NULL, NULL, NULL, NULL, NULL, D3DXUVATLAS_DEFAULT, &pOutputMesh, NULL, &pVertexRemapArray, NULL, NULL); if (FAILED(hr)) { goto FAIL; } //////////////////////////////////////// // 测试 //if (FAILED(D3DXSaveMeshToX( // "e:\\_pD3DMesh.x", // pD3DMesh, // NULL, // NULL, // NULL, // NULL, // D3DXF_FILEFORMAT_TEXT // ))) //{ // return NULL; //} //if (FAILED(D3DXSaveMeshToX( // "e:\\_pMeshValid.x", // pMeshValid, // NULL, // NULL, // NULL, // NULL, // D3DXF_FILEFORMAT_TEXT // ))) //{ // return NULL; //} //if (FAILED(D3DXSaveMeshToX( // "e:\\_pOutputMesh.x", // pOutputMesh, // NULL, // NULL, // NULL, // NULL, // D3DXF_FILEFORMAT_TEXT // ))) //{ // return NULL; //} /////////////////////////////////////// DWORD* pRemapArray = (DWORD*)(pVertexRemapArray->GetBufferPointer()); // 顶点重映射表 // 将 D3DMesh 的 uv0 拷贝回 pkGeometry if (NULL == (pkNewGeom = ApplyUVToGeometry(pOutputMesh, pkGeom, pRemapArray, uiID))) { goto FAIL; } FAIL: SAFE_RELEASE(pD3DMesh); SAFE_RELEASE(pOutputMesh); SAFE_RELEASE(pVertexRemapArray); return pkNewGeom; } //------------------------------------------------------------------------------ NiTriShape* NiLightMapMaterialHelper::ApplyUVToGeometry(LPD3DXMESH pD3DMesh, NiGeometry* pkGeom, DWORD* pRemapArray, unsigned int uiID) { // 展开 UV 后顶点会增加,需要重新创建 pkGeom 的 GeometryData. if (pD3DMesh == NULL || pkGeom == NULL) return false; DWORD dwNumVertOrig = pkGeom->GetVertexCount(); // 原顶点数量 DWORD dwNumVert = pD3DMesh->GetNumVertices(); // 新顶点数量 DWORD dwNumTris = pD3DMesh->GetNumFaces(); // 三角形数量 NiPoint2* arrUV = NiNew NiPoint2[dwNumVert]; // 展开的 UV, 对应 uiID WORD* wConnections = (WORD*)NiMalloc(dwNumTris*3*sizeof(WORD)); // 索引列表 ///////////////////////////////////// // 1. 从 pD3DMesh 中取出展开的 UV, 取出索引数据 DWORD dwBytesPerVert = sizeof(D3DXVECTOR3) + sizeof(D3DXVECTOR2); // position, uv0 DWORD dwBytesOfPoint3 = sizeof(D3DXVECTOR3); DWORD dwBytesOfPoint2 = sizeof(NiPoint2); sMeshVert* pVertBuff = NULL; pD3DMesh->LockVertexBuffer(0, (void**)&pVertBuff); for (int i=0; iUnlockVertexBuffer(); void* pIB = NULL; pD3DMesh->LockIndexBuffer(0, &pIB); NiMemcpy(wConnections, pIB, dwNumTris*3*sizeof(WORD)); pD3DMesh->UnlockIndexBuffer(); /////////////////////////////////////// // 2. 取出原顶点数据 NiPoint3* arrOrigVertices = pkGeom->GetVertices(); // 原顶点列表 NiPoint3* arrOrigNormals = pkGeom->GetNormals(); // 原法线 NiColorA* arrOrigColors = pkGeom->GetColors(); // 原顶点色列表 WORD wNumTexSets = pkGeom->GetTextureSets(); // uv sets 数量 NiPoint2* arrOrigUV = pkGeom->GetTextures(); // 原顶点UV ////////////////////////////////////// // 3. 新 GeometryData 数据 NiPoint3* arrNewVertices = NiNew NiPoint3[dwNumVert]; NiPoint3* arrNewNormals = NULL; if (pkGeom->GetNormalBinormalTangentMethod() == NiGeometryData::NBT_METHOD_NONE) { arrNewNormals = NiNew NiPoint3[dwNumVert]; } else { arrNewNormals = NiNew NiPoint3[dwNumVert*3]; } NiNew NiPoint3[dwNumVert]; NiColorA* arrNewColors = NiNew NiColorA[dwNumVert]; NiPoint2* arrNewUV = NiNew NiPoint2[dwNumVert*wNumTexSets]; // 数据拷贝 for (int i=0; iGetNormalBinormalTangentMethod(), dwNumTris, wConnections); //pkGeomData->CalculateNormals(); NiTriShape* pkNewGeom = NiNew NiTriShape(pkGeomData); //pkGeom->GetModelData()->MarkAsChanged(NiGeometryData::DIRTY_MASK); pkNewGeom->UpdateEffects(); pkNewGeom->UpdateProperties(); pkNewGeom->Update(0.0f); return pkNewGeom; } //------------------------------------------------------------------------------ LPD3DXMESH NiLightMapMaterialHelper::ConvertToD3DMesh(NiGeometry* pkGeom) { if (pkGeom == NULL) return NULL; unsigned short usNumVert = pkGeom->GetVertexCount();// 顶点数量 unsigned short unNumActiveVert =pkGeom->GetActiveVertexCount(); NiPoint3* arrVerts = pkGeom->GetVertices(); // 顶点列表 DWORD dwNumFacesOrig = ((NiTriBasedGeom*)pkGeom)->GetTriangleCount(); // 原三角形数量, 会进行删减 DWORD dwNumFacesCorr = 0; // 修正后的三角形数量 WORD* pIdxBuffCorr = new WORD[dwNumFacesOrig*3]; // 修正后的三角形索引 NiTriBasedGeom* pkTriGasedGeom = NiDynamicCast(NiTriBasedGeom, pkGeom); // 对三角形进行修正, 删除有重复索引的面 for (int i=0; iGetTriangleIndices(i, i0, i1, i2); if (i0!=i1 && i0!=i2 && i1!=i2) { // 合法的三角形 pIdxBuffCorr[dwNumFacesCorr*3] = i0; pIdxBuffCorr[dwNumFacesCorr*3+1] = i1; pIdxBuffCorr[dwNumFacesCorr*3+2] = i2; dwNumFacesCorr++; } } // 创建新 Mesh LPD3DXMESH pD3DMesh = NULL; // D3D Mesh, 创建的目标 LPDIRECT3DDEVICE9 pD3DDevice = NULL; // D3D Device pD3DDevice = ((NiDX9Renderer*)NiRenderer::GetRenderer())->GetD3DDevice(); DWORD dwOption = D3DXMESH_SYSTEMMEM; // 将 mesh 放在 系统内存中.我们只用它来计算 uv, 没必要占用显存 DWORD dwFVF = D3DFVF_XYZ | D3DFVF_TEX1; // 只需要顶点位置和 第一层 UV sMeshVert* pVertBuff = NULL; // vertex buffer WORD* pIdxBuff = NULL; // Index buffer D3DXCreateMeshFVF(dwNumFacesCorr, usNumVert, dwOption, dwFVF, pD3DDevice, &pD3DMesh); if (pD3DMesh == NULL) return NULL; // 创建失败 DWORD dwNumBytesPerVert = sizeof(sMeshVert); // 顶点缓冲中每个顶点占的 byte 数 // 拷贝顶点数据 pD3DMesh->LockVertexBuffer(0, (void**)&pVertBuff); ZeroMemory(pVertBuff, sizeof(sMeshVert) * usNumVert); // 遍历所有顶点,拷贝顶点数据到 pVertBuff int iPoint3Size = sizeof(NiPoint3); for (int i=0; iUnlockVertexBuffer(); // 拷贝索引数据 pD3DMesh->LockIndexBuffer(0, (void**)&pIdxBuff); memcpy(pIdxBuff, pIdxBuffCorr, dwNumFacesCorr*3*sizeof(WORD)); pD3DMesh->UnlockIndexBuffer(); // 释放临时资源 SAFE_DELETE_ARRAY(pIdxBuffCorr); return pD3DMesh; } //------------------------------------------------------------------------------ void NiLightMapMaterialHelper::AddShaderMap(NiGeometry* pkGeom, int iTexSetID) { if (pkGeom == NULL || iTexSetID < 0) return; NiTexturingProperty* pkTexProp = (NiTexturingProperty*)pkGeom->GetProperty(NiProperty::TEXTURING); if (pkTexProp == NULL) { pkTexProp = NiNew NiTexturingProperty(); pkGeom->AttachProperty(pkTexProp); } if (pkTexProp->GetShaderMap(0) == NULL) { NiSourceTexture* pkTex = NiSourceTexture::Create("DefaultLightMap.tga"); NiTexturingProperty::ShaderMap* pkMap = NiNew NiTexturingProperty::ShaderMap(pkTex, iTexSetID); pkTexProp->SetShaderMap(0, pkMap); } // 删除 Dark Map //pkTexProp->SetDarkMap(NULL); pkGeom->UpdateProperties(); } #endif // CODE_INGAME