/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "Mesh.h" #define MESH_VERSION 12 #define MESH_ID "MESH" #include #include #include #include #include #include #include #include #include INDEX AreVerticesDiferent(INDEX iCurentIndex, INDEX iLastIndex); struct VertexLocator { INDEX vl_iIndex; INDEX vl_iSubIndex; }; struct SortArray { INDEX sa_iNewIndex; INDEX sa_iSurfaceIndex; CStaticArray sa_aWeightMapList; CStaticArray sa_aMorphMapList; }; static CStaticArray _aSortArray; CStaticArray _aiOptimizedIndex; CStaticArray _aiSortedIndex; MeshLOD *pMeshLOD;// curent mesh lod (for quick sort) MeshLOD mshOptimized; CMesh::CMesh() { } CMesh::~CMesh() { } // release old shader and obtain new shader for mesh surface (expand ShaderParams if needed) void ChangeSurfaceShader_t(MeshSurface &msrf,CTString fnNewShader) { CShader *pShaderNew = _pShaderStock->Obtain_t(fnNewShader); ASSERT(pShaderNew!=NULL); if(msrf.msrf_pShader!=NULL) _pShaderStock->Release(msrf.msrf_pShader); msrf.msrf_pShader = pShaderNew; // get new shader description ShaderDesc shDesc; msrf.msrf_pShader->GetShaderDesc(shDesc); // if needed expand size of arrays for new shader // reset new values!!!! INDEX ctOldTextureIDs = msrf.msrf_ShadingParams.sp_aiTextureIDs.Count(); INDEX ctNewTextureIDs = shDesc.sd_astrTextureNames.Count(); INDEX ctOldUVMaps = msrf.msrf_ShadingParams.sp_aiTexCoordsIndex.Count(); INDEX ctNewUVMaps = shDesc.sd_astrTexCoordNames.Count(); INDEX ctOldColors = msrf.msrf_ShadingParams.sp_acolColors.Count(); INDEX ctNewColors = shDesc.sd_astrColorNames.Count(); INDEX ctOldFloats = msrf.msrf_ShadingParams.sp_afFloats.Count(); INDEX ctNewFloats = shDesc.sd_astrFloatNames.Count(); if(ctOldTextureIDsiMaxIndex) iMaxIndex = mtTriangle.iVertex[iv]; } } // remember first index in vertices array msrf.msrf_iFirstVertex = iMinIndex; // remember vertices count msrf.msrf_ctVertices = iMaxIndex-iMinIndex+1; // for each triangle in surface for(its=0;itsmlod_aVertices[iCurentIndex].y,pMeshLOD->mlod_aVertices[iLastIndex].y); CHECKF(pMeshLOD->mlod_aVertices[iCurentIndex].x,pMeshLOD->mlod_aVertices[iLastIndex].x); CHECKF(pMeshLOD->mlod_aVertices[iCurentIndex].z,pMeshLOD->mlod_aVertices[iLastIndex].z); // check normals CHECKF(pMeshLOD->mlod_aNormals[iCurentIndex].ny,pMeshLOD->mlod_aNormals[iLastIndex].ny); CHECKF(pMeshLOD->mlod_aNormals[iCurentIndex].nx,pMeshLOD->mlod_aNormals[iLastIndex].nx); CHECKF(pMeshLOD->mlod_aNormals[iCurentIndex].nz,pMeshLOD->mlod_aNormals[iLastIndex].nz); // check uvmaps INDEX ctUVMaps = pMeshLOD->mlod_aUVMaps.Count(); for(INDEX iuvm=0;iuvmmlod_aUVMaps[iuvm].muv_aTexCoords[iCurentIndex].u,pMeshLOD->mlod_aUVMaps[iuvm].muv_aTexCoords[iLastIndex].u); CHECKF(pMeshLOD->mlod_aUVMaps[iuvm].muv_aTexCoords[iCurentIndex].v,pMeshLOD->mlod_aUVMaps[iuvm].muv_aTexCoords[iLastIndex].v); } // count weight and morph maps INDEX ctwmCurent = _aSortArray[iCurentIndex].sa_aWeightMapList.Count(); INDEX ctwmLast = _aSortArray[iLastIndex].sa_aWeightMapList.Count(); INDEX ctmmCurent = _aSortArray[iCurentIndex].sa_aMorphMapList.Count(); INDEX ctmmLast = _aSortArray[iLastIndex].sa_aMorphMapList.Count(); // check if vertices have same weight and morph maps count CHECK(ctwmCurent,ctwmLast); CHECK(ctmmCurent,ctmmLast); // check if vertices have same weight map factors for(INDEX iwm=0;iwmmlod_aWeightMaps[iwmCurent].mwm_aVertexWeight[iwwCurent].mww_fWeight,pMeshLOD->mlod_aWeightMaps[iwmLast].mwm_aVertexWeight[iwwLast].mww_fWeight); } // check if vertices have same morph map factors for(INDEX imm=0;immmlod_aMorphMaps[immCurent].mmp_aMorphMap[imwmCurent].mwm_x, pMeshLOD->mlod_aMorphMaps[immLast].mmp_aMorphMap[imwmLast].mwm_x); CHECKF(pMeshLOD->mlod_aMorphMaps[immCurent].mmp_aMorphMap[imwmCurent].mwm_y, pMeshLOD->mlod_aMorphMaps[immLast].mmp_aMorphMap[imwmLast].mwm_y); CHECKF(pMeshLOD->mlod_aMorphMaps[immCurent].mmp_aMorphMap[imwmCurent].mwm_z, pMeshLOD->mlod_aMorphMaps[immLast].mmp_aMorphMap[imwmLast].mwm_z); CHECKF(pMeshLOD->mlod_aMorphMaps[immCurent].mmp_aMorphMap[imwmCurent].mwm_nx, pMeshLOD->mlod_aMorphMaps[immLast].mmp_aMorphMap[imwmLast].mwm_nx); CHECKF(pMeshLOD->mlod_aMorphMaps[immCurent].mmp_aMorphMap[imwmCurent].mwm_ny, pMeshLOD->mlod_aMorphMaps[immLast].mmp_aMorphMap[imwmLast].mwm_ny); CHECKF(pMeshLOD->mlod_aMorphMaps[immCurent].mmp_aMorphMap[imwmCurent].mwm_nz, pMeshLOD->mlod_aMorphMaps[immLast].mmp_aMorphMap[imwmLast].mwm_nz); } return 0; } // normalize weights in mlod void CMesh::NormalizeWeightsInLod(MeshLOD &mlod) { CStaticArray aWeightFactors; int ctvtx = mlod.mlod_aVertices.Count(); int ctwm = mlod.mlod_aWeightMaps.Count(); // create array for weights aWeightFactors.New(ctvtx); memset(&aWeightFactors[0],0,sizeof(aWeightFactors[0])*ctvtx); int iwm; for(iwm=0;iwm aTempMLODs; aTempMLODs.New(ctmlod-1); INDEX iIndexSrc=0; // for each skeleton lod in skeleton for(INDEX imlod=0;imlodWriteID_t(CChunkID(MESH_ID)); // write version (*ostrFile)<<(INDEX)MESH_VERSION; // write mlod count (*ostrFile)<Write_t(&mLod.mlod_aVertices[0],sizeof(MeshVertex)*ctVx); // write normals ostrFile->Write_t(&mLod.mlod_aNormals[0],sizeof(MeshNormal)*ctVx); // write uvmaps count (*ostrFile)<Write_t(&mLod.mlod_aUVMaps[iuv].muv_aTexCoords[0],sizeof(MeshTexCoord)*ctVx); } // write surfaces count ostrFile->Write_t(&ctSf,sizeof(INDEX)); // write surfaces for(INDEX isf=0;isfWrite_t(&mLod.mlod_aSurfaces[isf].msrf_aTriangles[0],sizeof(MeshTriangle)*ctTris); // write bool that this surface has a shader INDEX bShaderExists = (msrf.msrf_pShader!=NULL); (*ostrFile)<GetShaderDesc(shDesc); INDEX cttx=shDesc.sd_astrTextureNames.Count(); INDEX cttc=shDesc.sd_astrTexCoordNames.Count(); INDEX ctcol=shDesc.sd_astrColorNames.Count(); INDEX ctfl=shDesc.sd_astrFloatNames.Count(); // data count must be at same as size defined in shader or higher ASSERT(cttx<=msrf.msrf_ShadingParams.sp_aiTextureIDs.Count()); ASSERT(cttc<=msrf.msrf_ShadingParams.sp_aiTexCoordsIndex.Count()); ASSERT(ctcol<=msrf.msrf_ShadingParams.sp_acolColors.Count()); ASSERT(ctfl<=msrf.msrf_ShadingParams.sp_afFloats.Count()); ASSERT(msrf.msrf_pShader->GetShaderDesc!=NULL); // write texture count (*ostrFile)<GetName(); (*ostrFile)<Write_t(&mLod.mlod_aWeightMaps[iwm].mwm_aVertexWeight[0],sizeof(MeshVertexWeight)*ctWw); } // write morphmaps count (*ostrFile)<Write_t(&mLod.mlod_aMorphMaps[imm].mmp_bRelative,sizeof(BOOL)); // write morph sets count ostrFile->Write_t(&ctms,sizeof(INDEX)); // write morph sets ostrFile->Write_t(&mLod.mlod_aMorphMaps[imm].mmp_aMorphMap[0],sizeof(MeshVertexMorph)*ctms); } } } //read from stream void CMesh::Read_t(CTStream *istrFile) { INDEX ctmlods; INDEX iFileVersion; // read chunk id istrFile->ExpectID_t(CChunkID(MESH_ID)); // check file version (*istrFile)>>iFileVersion; // if file version is not 11 nor 12 if(iFileVersion != 11 && iFileVersion!=12) { ThrowF_t(TRANS("File '%s'.\nInvalid Mesh file version.\nExpected Ver \"%d\" but found \"%d\"\n"), (const char*)istrFile->GetDescription(),MESH_VERSION,iFileVersion); return; } // read mlod count (*istrFile)>>ctmlods; // for each lod in mesh for(INDEX imlod=0;imlod>mLod.mlod_fnSourceFile; // read max distance (*istrFile)>>mLod.mlod_fMaxDistance; // read flags (*istrFile)>>mLod.mlod_ulFlags; // :) if(iFileVersion<=11) { mLod.mlod_ulFlags = 0; } if(mLod.mlod_ulFlags==0xCDCDCDCD) { mLod.mlod_ulFlags = 0; } // read vertex count (*istrFile)>>ctVx; // create vertex and normal arrays mLod.mlod_aVertices.New(ctVx); mLod.mlod_aNormals.New(ctVx); // read vertices for (INDEX i = 0; i < ctVx; i++) (*istrFile)>>mLod.mlod_aVertices[i]; // read normals for (INDEX i = 0; i < ctVx; i++) (*istrFile)>>mLod.mlod_aNormals[i]; // read uvmaps count (*istrFile)>>ctUV; // create array for uvmaps mLod.mlod_aUVMaps.New(ctUV); // read uvmaps for(int iuv=0;iuv>strNameID; mLod.mlod_aUVMaps[iuv].muv_iID = ska_GetIDFromStringTable(strNameID); // create array for uvmaps texcordinates mLod.mlod_aUVMaps[iuv].muv_aTexCoords.New(ctVx); // read uvmap texcordinates for (INDEX i = 0; i < ctVx; i++) (*istrFile)>>mLod.mlod_aUVMaps[iuv].muv_aTexCoords[i]; } // read surfaces count (*istrFile)>>ctSf; // create array for surfaces mLod.mlod_aSurfaces.New(ctSf); // read surfaces for(INDEX isf=0;isf>strSurfaceID; msrf.msrf_iSurfaceID = ska_GetIDFromStringTable(strSurfaceID); // read first vertex (*istrFile)>>msrf.msrf_iFirstVertex; // read vertices count (*istrFile)>>msrf.msrf_ctVertices; // read tris count (*istrFile)>>ctTris; // create triangles array mLod.mlod_aSurfaces[isf].msrf_aTriangles.New(ctTris); // read triangles for (INDEX i = 0; i < ctTris; i++) (*istrFile)>>mLod.mlod_aSurfaces[isf].msrf_aTriangles[i]; // read bool that this surface has a shader INDEX bShaderExists; (*istrFile)>>bShaderExists; // if shader exists read its params if(bShaderExists) { INDEX cttx,cttc,ctcol,ctfl; // read texture count (*istrFile)>>cttx; // read texture coords count (*istrFile)>>cttc; // read color count (*istrFile)>>ctcol; // read float count (*istrFile)>>ctfl; //CShader *pshMeshShader = NULL; ShaderParams *pshpShaderParams = NULL; CShader shDummyShader; // dummy shader if shader is not found ShaderParams shpDummyShaderParams;// dummy shader params if shader is not found // read shader name CTString strShaderName; (*istrFile)>>strShaderName; // try to load shader try{ msrf.msrf_pShader = _pShaderStock->Obtain_t(strShaderName); //pshMeshShader = msrf.msrf_pShader; pshpShaderParams = &msrf.msrf_ShadingParams; } catch(char *strErr) { CPrintF("%s\n",strErr); msrf.msrf_pShader = NULL; //pshMeshShader = &shDummyShader; pshpShaderParams = &shpDummyShaderParams; } // if mesh shader exisits if(msrf.msrf_pShader!=NULL) { // get shader description ShaderDesc shDesc; msrf.msrf_pShader->GetShaderDesc(shDesc); // check if saved params count match shader params count if(shDesc.sd_astrTextureNames.Count() != cttx) ThrowF_t("File '%s'\nWrong texture count %d",(const char*)GetName(),cttx); if(shDesc.sd_astrTexCoordNames.Count() != cttc) ThrowF_t("File '%s'\nWrong uvmaps count %d",(const char*)GetName(),cttc); if(shDesc.sd_astrColorNames.Count() != ctcol) ThrowF_t("File '%s'\nWrong colors count %d",(const char*)GetName(),ctcol); if(shDesc.sd_astrFloatNames.Count() != ctfl) ThrowF_t("File '%s'\nWrong floats count %d",(const char*)GetName(),ctfl); } // create arrays for shader params pshpShaderParams->sp_aiTextureIDs.New(cttx); pshpShaderParams->sp_aiTexCoordsIndex.New(cttc); pshpShaderParams->sp_acolColors.New(ctcol); pshpShaderParams->sp_afFloats.New(ctfl); // read shader texture IDs for(INDEX itx=0;itx>strTexID; INDEX iTexID = ska_GetIDFromStringTable(strTexID); pshpShaderParams->sp_aiTextureIDs[itx] = iTexID; } // read shader texture coords indices for(INDEX itc=0;itc>iTexCoorsIndex; pshpShaderParams->sp_aiTexCoordsIndex[itc] = iTexCoorsIndex; } // read shader colors for(INDEX icol=0;icol>colColor; pshpShaderParams->sp_acolColors[icol] = colColor; } // read shader floats for(INDEX ifl=0;ifl>fFloat; pshpShaderParams->sp_afFloats[ifl] = fFloat; } // there were no flags in shader before ver 12 if(iFileVersion>11) { ULONG ulFlags; (*istrFile)>>ulFlags; pshpShaderParams->sp_ulFlags = ulFlags; } else { pshpShaderParams->sp_ulFlags = 0; } } else { // this surface does not have shader msrf.msrf_pShader=NULL; } } // read weightmaps count (*istrFile)>>ctWM; // create weightmap array mLod.mlod_aWeightMaps.New(ctWM); // read each weightmap for(INDEX iwm=0;iwm>pstrNameID; mLod.mlod_aWeightMaps[iwm].mwm_iID = ska_GetIDFromStringTable(pstrNameID); // read wertex weight count INDEX ctWw; (*istrFile)>>ctWw; // create wertex weight array mLod.mlod_aWeightMaps[iwm].mwm_aVertexWeight.New(ctWw); // read wertex weights for (INDEX i = 0; i < ctWw; i++) (*istrFile)>>mLod.mlod_aWeightMaps[iwm].mwm_aVertexWeight[i]; } // read morphmap count (*istrFile)>>ctMM; // create morphmaps array mLod.mlod_aMorphMaps.New(ctMM); // read morphmaps for(INDEX imm=0;imm>pstrNameID; mLod.mlod_aMorphMaps[imm].mmp_iID = ska_GetIDFromStringTable(pstrNameID); // read bRelative (*istrFile)>>mLod.mlod_aMorphMaps[imm].mmp_bRelative; // read morph sets count INDEX ctms; (*istrFile)>>ctms; // create morps sets array mLod.mlod_aMorphMaps[imm].mmp_aMorphMap.New(ctms); // read morph sets for (INDEX i = 0; i < ctms; i++) (*istrFile)>>mLod.mlod_aMorphMaps[imm].mmp_aMorphMap[i]; } } } // clear mesh void CMesh::Clear(void) { // for each LOD INDEX ctmlod = msh_aMeshLODs.Count(); for (INDEX imlod=0; imlodRelease(msrf.msrf_pShader); msrf.msrf_pShader = NULL; } // clear the surfaces array mlod.mlod_aSurfaces.Clear(); // for each uvmap, clear the texcord list INDEX ctuvm = mlod.mlod_aUVMaps.Count(); for (INDEX iuvm=0;iuvm