Serious-Engine/Sources/Engine/Ska/Mesh.cpp
2016-05-09 18:51:03 +02:00

996 lines
35 KiB
C++

/* 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 <Engine/StdH.h>
#include "Mesh.h"
#define MESH_VERSION 12
#define MESH_ID "MESH"
#include <Engine/Base/Stream.h>
#include <Engine/Base/Console.h>
#include <Engine/Ska/StringTable.h>
#include <Engine/Math/Projection.h>
#include <Engine/Graphics/DrawPort.h>
#include <Engine/Graphics/Shader.h>
#include <Engine/Templates/StaticArray.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/Stock_CShader.h>
INDEX AreVerticesDiferent(INDEX iCurentIndex, INDEX iLastIndex);
struct VertexLocator
{
INDEX vl_iIndex;
INDEX vl_iSubIndex;
};
struct SortArray
{
INDEX sa_iNewIndex;
INDEX sa_iSurfaceIndex;
CStaticArray<struct VertexLocator> sa_aWeightMapList;
CStaticArray<struct VertexLocator> sa_aMorphMapList;
};
static CStaticArray <struct SortArray> _aSortArray;
CStaticArray <INDEX> _aiOptimizedIndex;
CStaticArray <INDEX> _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(ctOldTextureIDs<ctNewTextureIDs) {
// expand texture IDs array
msrf.msrf_ShadingParams.sp_aiTextureIDs.Expand(ctNewTextureIDs);
// set new texture IDs to 0
for(INDEX itx=ctOldTextureIDs;itx<ctNewTextureIDs;itx++) {
msrf.msrf_ShadingParams.sp_aiTextureIDs[itx] = -1;
}
}
// expand array of uvmaps if needed
if(ctOldUVMaps<ctNewUVMaps) {
// expand uvmaps IDs array
msrf.msrf_ShadingParams.sp_aiTexCoordsIndex.Expand(ctNewUVMaps);
// set new uvmaps indices to 0
for(INDEX itxc=ctOldUVMaps;itxc<ctNewUVMaps;itxc++) {
msrf.msrf_ShadingParams.sp_aiTexCoordsIndex[itxc] = 0;
}
}
// expand array of colors if needed
if(ctOldColors<ctNewColors) {
// expand color array
msrf.msrf_ShadingParams.sp_acolColors.Expand(ctNewColors);
// set new colors indices white
for(INDEX icol=ctOldUVMaps;icol<ctNewColors;icol++) {
msrf.msrf_ShadingParams.sp_acolColors[icol] = 0xFFFFFFFF;
}
}
// expand array of floats if needed
if(ctOldFloats<ctNewFloats) {
// expand float array
msrf.msrf_ShadingParams.sp_afFloats.Expand(ctNewFloats);
// set new floats to 0
for(INDEX ifl=ctOldFloats;ifl<ctNewFloats;ifl++) {
msrf.msrf_ShadingParams.sp_afFloats[ifl] = 0;
}
}
}
// quck sort func for comparing vertices
static int qsort_CompareArray(const void *pVx1, const void *pVx2)
{
INDEX *n1 = ((INDEX*)pVx1);
INDEX *n2 = ((INDEX*)pVx2);
return AreVerticesDiferent(*n1,*n2);
}
// clear array of sort vertices
void ClearSortArray(INDEX ctOldVertices)
{
for(int iv=0;iv<ctOldVertices;iv++)
{
_aSortArray[iv].sa_aWeightMapList.Clear();
_aSortArray[iv].sa_aMorphMapList.Clear();
}
_aiOptimizedIndex.Clear();
_aiSortedIndex.Clear();
_aSortArray.Clear();
}
// optimize mesh
void CMesh::Optimize(void)
{
INDEX ctmshlods = msh_aMeshLODs.Count();
for(int imshlod=0;imshlod<ctmshlods;imshlod++)
{
// optimize each lod in mesh
OptimizeLod(msh_aMeshLODs[imshlod]);
}
}
// optimize lod of mesh
void CMesh::OptimizeLod(MeshLOD &mLod)
{
INDEX ctVertices = mLod.mlod_aVertices.Count();
INDEX ctSurfaces = mLod.mlod_aSurfaces.Count();
INDEX ctUVMaps = mLod.mlod_aUVMaps.Count();
INDEX ctWeightMaps = mLod.mlod_aWeightMaps.Count();
INDEX ctMorphMaps = mLod.mlod_aMorphMaps.Count();
if(ctVertices<=0) return;
// create array for sorting
_aSortArray.New(ctVertices);
_aiSortedIndex.New(ctVertices);
_aiOptimizedIndex.New(ctVertices);
// put original vertex indices in SortArray
for(int iv=0;iv<ctVertices;iv++)
{
_aiSortedIndex[iv] = iv;
}
// loop each surface and expand SurfaceList in SortArray
int is;
for(is=0;is<ctSurfaces;is++)
{
INDEX ctts=mLod.mlod_aSurfaces[is].msrf_aTriangles.Count();
for(int its=0;its<ctts;its++)
{
MeshTriangle &mtTriangle = mLod.mlod_aSurfaces[is].msrf_aTriangles[its];
_aSortArray[mtTriangle.iVertex[0]].sa_iSurfaceIndex = is;
_aSortArray[mtTriangle.iVertex[1]].sa_iSurfaceIndex = is;
_aSortArray[mtTriangle.iVertex[2]].sa_iSurfaceIndex = is;
}
}
// loop each weightmap and expand sa_aWeightMapList in SortArray
for(INDEX iw=0;iw<ctWeightMaps;iw++)
{
// loop each wertex weight array in weight map array
INDEX ctwm = mLod.mlod_aWeightMaps[iw].mwm_aVertexWeight.Count();
for(INDEX iwm=0;iwm<ctwm;iwm++)
{
MeshVertexWeight &mwwWeight = mLod.mlod_aWeightMaps[iw].mwm_aVertexWeight[iwm];
// get curent list num of weightmaps
INDEX ctWeightMapList = _aSortArray[mwwWeight.mww_iVertex].sa_aWeightMapList.Count();
// expand array of sufrace lists for 1
_aSortArray[mwwWeight.mww_iVertex].sa_aWeightMapList.Expand(ctWeightMapList+1);
// set vl_iIndex to index of surface
// set vl_iSubIndex to index in triangle set
VertexLocator &vxLoc = _aSortArray[mwwWeight.mww_iVertex].sa_aWeightMapList[ctWeightMapList];
vxLoc.vl_iIndex = iw;
vxLoc.vl_iSubIndex = iwm;
}
}
// loop each morphmap and expand sa_aMorphMapList in SortArray
for(INDEX im=0;im<ctMorphMaps;im++)
{
// loop each morph map in array
INDEX ctmm = mLod.mlod_aMorphMaps[im].mmp_aMorphMap.Count();
for(INDEX imm=0;imm<ctmm;imm++)
{
MeshVertexMorph &mwmMorph = mLod.mlod_aMorphMaps[im].mmp_aMorphMap[imm];
// get curent list num of morphmaps
INDEX ctMorphMapList = _aSortArray[mwmMorph.mwm_iVxIndex].sa_aMorphMapList.Count();
// expand array of sufrace lists for 1
_aSortArray[mwmMorph.mwm_iVxIndex].sa_aMorphMapList.Expand(ctMorphMapList+1);
// set vl_iIndex to index of surface
// set vl_iSubIndex to index in triangle set
VertexLocator &vxLoc = _aSortArray[mwmMorph.mwm_iVxIndex].sa_aMorphMapList[ctMorphMapList];
vxLoc.vl_iIndex = im;
vxLoc.vl_iSubIndex = imm;
}
}
// set global pMeshLOD pointer used by quicksort
pMeshLOD = &mLod;
// sort array
qsort(&_aiSortedIndex[0],ctVertices,sizeof(&_aiSortedIndex[0]),qsort_CompareArray);
// compare vertices
INDEX iDiferentVertices = 1;
INDEX iLastIndex = _aiSortedIndex[0];
_aSortArray[iLastIndex].sa_iNewIndex = 0;
_aiOptimizedIndex[0] = iLastIndex;
for(INDEX isa=1;isa<ctVertices;isa++)
{
INDEX iCurentIndex = _aiSortedIndex[isa];
// check if vertices are diferent
if(AreVerticesDiferent(iLastIndex,iCurentIndex))
{
// add Curent index to Optimized index array
_aiOptimizedIndex[iDiferentVertices] = iCurentIndex;
iDiferentVertices++;
iLastIndex = iCurentIndex;
}
_aSortArray[iCurentIndex].sa_iNewIndex = iDiferentVertices-1;
}
// create new mesh
INDEX ctNewVertices = iDiferentVertices;
mshOptimized.mlod_aVertices.New(ctNewVertices);
mshOptimized.mlod_aNormals.New(ctNewVertices);
mshOptimized.mlod_aUVMaps.New(ctUVMaps);
for(INDEX iuvm=0;iuvm<ctUVMaps;iuvm++)
{
mshOptimized.mlod_aUVMaps[iuvm].muv_aTexCoords.New(ctNewVertices);
}
// add new vertices and normals to mshOptimized
for(INDEX iNewVx=0;iNewVx<ctNewVertices;iNewVx++)
{
mshOptimized.mlod_aVertices[iNewVx] = mLod.mlod_aVertices[_aiOptimizedIndex[iNewVx]];
mshOptimized.mlod_aNormals[iNewVx] = mLod.mlod_aNormals[_aiOptimizedIndex[iNewVx]];
for(INDEX iuvm=0;iuvm<ctUVMaps;iuvm++)
{
//???
mshOptimized.mlod_aUVMaps[iuvm].muv_iID = mLod.mlod_aUVMaps[iuvm].muv_iID;
mshOptimized.mlod_aUVMaps[iuvm].muv_aTexCoords[iNewVx] = mLod.mlod_aUVMaps[iuvm].muv_aTexCoords[_aiOptimizedIndex[iNewVx]];
}
}
// remap surface triangles
for(is=0;is<ctSurfaces;is++)
{
MeshSurface &msrf = mLod.mlod_aSurfaces[is];
INDEX iMinIndex = ctNewVertices+1;
INDEX iMaxIndex = -1;
INDEX ctts=msrf.msrf_aTriangles.Count();
// for each triangle in this surface
INDEX its;
for(its=0;its<ctts;its++)
{
MeshTriangle &mtTriangle = msrf.msrf_aTriangles[its];
// for each vertex in triangle
for(INDEX iv=0;iv<3;iv++)
{
mtTriangle.iVertex[iv] = _aSortArray[mtTriangle.iVertex[iv]].sa_iNewIndex;
// find first index in this surface
if(mtTriangle.iVertex[iv]<iMinIndex) iMinIndex = mtTriangle.iVertex[iv];
// find last index in this surface
if(mtTriangle.iVertex[iv]>iMaxIndex) 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;its<ctts;its++)
{
MeshTriangle &mtTriangle = msrf.msrf_aTriangles[its];
// for each vertex in triangle
for(INDEX iv=0;iv<3;iv++)
{
// substract vertex index in triangle with first vertex in surface
mtTriangle.iVertex[iv] -= msrf.msrf_iFirstVertex;
ASSERT(mtTriangle.iVertex[iv]<msrf.msrf_ctVertices);
}
}
}
// remap weightmaps
mshOptimized.mlod_aWeightMaps.New(ctWeightMaps);
// expand wertex veights array for each vertex
INDEX ivx;
for(ivx=0;ivx<ctNewVertices;ivx++)
{
INDEX ioptVx = _aiOptimizedIndex[ivx];
for(INDEX iwl=0;iwl<_aSortArray[ioptVx].sa_aWeightMapList.Count();iwl++)
{
VertexLocator &wml = _aSortArray[ioptVx].sa_aWeightMapList[iwl];
INDEX wmIndex = wml.vl_iIndex;
INDEX wwIndex = wml.vl_iSubIndex;
INDEX ctww = mshOptimized.mlod_aWeightMaps[wmIndex].mwm_aVertexWeight.Count();
MeshWeightMap &mwm = mshOptimized.mlod_aWeightMaps[wmIndex];
MeshVertexWeight &mww = mLod.mlod_aWeightMaps[wmIndex].mwm_aVertexWeight[wwIndex];
mwm.mwm_iID = mLod.mlod_aWeightMaps[wmIndex].mwm_iID;
mwm.mwm_aVertexWeight.Expand(ctww+1);
mwm.mwm_aVertexWeight[ctww].mww_fWeight = mww.mww_fWeight;
mwm.mwm_aVertexWeight[ctww].mww_iVertex = ivx;
}
}
// remap morphmaps
mshOptimized.mlod_aMorphMaps.New(ctMorphMaps);
// expand morph maps array for each vertex
for(ivx=0;ivx<ctNewVertices;ivx++)
{
INDEX ioptVx = _aiOptimizedIndex[ivx];
for(INDEX iml=0;iml<_aSortArray[ioptVx].sa_aMorphMapList.Count();iml++)
{
VertexLocator &mml = _aSortArray[ioptVx].sa_aMorphMapList[iml];
INDEX mmIndex = mml.vl_iIndex;
INDEX mwmIndex = mml.vl_iSubIndex;
INDEX ctmwm = mshOptimized.mlod_aMorphMaps[mmIndex].mmp_aMorphMap.Count();
MeshMorphMap &mmm = mshOptimized.mlod_aMorphMaps[mmIndex];
MeshVertexMorph &mwm = mLod.mlod_aMorphMaps[mmIndex].mmp_aMorphMap[mwmIndex];
mmm.mmp_iID = mLod.mlod_aMorphMaps[mmIndex].mmp_iID;
mmm.mmp_bRelative = mLod.mlod_aMorphMaps[mmIndex].mmp_bRelative;
mmm.mmp_aMorphMap.Expand(ctmwm+1);
mmm.mmp_aMorphMap[ctmwm].mwm_iVxIndex = ivx;
mmm.mmp_aMorphMap[ctmwm].mwm_x = mwm.mwm_x;
mmm.mmp_aMorphMap[ctmwm].mwm_y = mwm.mwm_y;
mmm.mmp_aMorphMap[ctmwm].mwm_z = mwm.mwm_z;
mmm.mmp_aMorphMap[ctmwm].mwm_nx = mwm.mwm_nx;
mmm.mmp_aMorphMap[ctmwm].mwm_ny = mwm.mwm_ny;
mmm.mmp_aMorphMap[ctmwm].mwm_nz = mwm.mwm_nz;
}
}
mLod.mlod_aVertices.CopyArray(mshOptimized.mlod_aVertices);
mLod.mlod_aNormals.CopyArray(mshOptimized.mlod_aNormals);
mLod.mlod_aMorphMaps.CopyArray(mshOptimized.mlod_aMorphMaps);
mLod.mlod_aWeightMaps.CopyArray(mshOptimized.mlod_aWeightMaps);
mLod.mlod_aUVMaps.CopyArray(mshOptimized.mlod_aUVMaps);
// clear memory
ClearSortArray(ctVertices);
mshOptimized.mlod_aVertices.Clear();
mshOptimized.mlod_aNormals.Clear();
mshOptimized.mlod_aWeightMaps.Clear();
mshOptimized.mlod_aMorphMaps.Clear();
mshOptimized.mlod_aUVMaps.Clear();
}
INDEX AreVerticesDiferent(INDEX iCurentIndex, INDEX iLastIndex)
{
#define CHECK(x,y) if(((x)-(y))!=0) return (INDEX) ((x)-(y))
#define CHECKF(x,y) if(((x)-(y))!=0) return (INDEX) Sgn((x)-(y))
// check surfaces
CHECK(_aSortArray[iCurentIndex].sa_iSurfaceIndex,_aSortArray[iLastIndex].sa_iSurfaceIndex);
// check vertices
CHECKF(pMeshLOD->mlod_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;iuvm<ctUVMaps;iuvm++)
{
CHECKF(pMeshLOD->mlod_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;iwm<ctwmCurent;iwm++)
{
// get weight map indices
INDEX iwmCurent = _aSortArray[iCurentIndex].sa_aWeightMapList[iwm].vl_iIndex;
INDEX iwmLast = _aSortArray[iLastIndex].sa_aWeightMapList[iwm].vl_iIndex;
// get wertex weight indices
INDEX iwwCurent = _aSortArray[iCurentIndex].sa_aWeightMapList[iwm].vl_iSubIndex;
INDEX iwwLast = _aSortArray[iLastIndex].sa_aWeightMapList[iwm].vl_iSubIndex;
// if weight map factors are diferent
CHECKF(pMeshLOD->mlod_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;imm<ctmmCurent;imm++)
{
// get morph map indices
INDEX immCurent = _aSortArray[iCurentIndex].sa_aMorphMapList[imm].vl_iIndex;
INDEX immLast = _aSortArray[iLastIndex].sa_aMorphMapList[imm].vl_iIndex;
// get mesh vertex morph indices
INDEX imwmCurent = _aSortArray[iCurentIndex].sa_aMorphMapList[imm].vl_iSubIndex;
INDEX imwmLast = _aSortArray[iLastIndex].sa_aMorphMapList[imm].vl_iSubIndex;
// if mesh morph map params are diferent return
CHECKF(pMeshLOD->mlod_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<float> 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<ctwm;iwm++)
{
MeshWeightMap &mwm = mlod.mlod_aWeightMaps[iwm];
for(int iww=0;iww<mwm.mwm_aVertexWeight.Count();iww++)
{
MeshVertexWeight &mwh = mwm.mwm_aVertexWeight[iww];
aWeightFactors[mwh.mww_iVertex] += mwh.mww_fWeight;
}
}
for(iwm=0;iwm<ctwm;iwm++)
{
MeshWeightMap &mwm = mlod.mlod_aWeightMaps[iwm];
for(int iww=0;iww<mwm.mwm_aVertexWeight.Count();iww++)
{
MeshVertexWeight &mwh = mwm.mwm_aVertexWeight[iww];
mwh.mww_fWeight /= aWeightFactors[mwh.mww_iVertex];
}
}
// clear weight array
aWeightFactors.Clear();
}
// normalize weights in mesh
void CMesh::NormalizeWeights()
{
INDEX ctmlods = msh_aMeshLODs.Count();
for(INDEX imlod=0;imlod<ctmlods;imlod++)
{
// normalize each lod
NormalizeWeightsInLod(msh_aMeshLODs[imlod]);
}
}
// add new mesh lod to mesh
void CMesh::AddMeshLod(MeshLOD &mlod)
{
INDEX ctmlods = msh_aMeshLODs.Count();
msh_aMeshLODs.Expand(ctmlods+1);
msh_aMeshLODs[ctmlods] = mlod;
}
// remove mesh lod from mesh
void CMesh::RemoveMeshLod(MeshLOD *pmlodRemove)
{
INDEX ctmlod = msh_aMeshLODs.Count();
// create temp space for skeleton lods
CStaticArray<struct MeshLOD> aTempMLODs;
aTempMLODs.New(ctmlod-1);
INDEX iIndexSrc=0;
// for each skeleton lod in skeleton
for(INDEX imlod=0;imlod<ctmlod;imlod++)
{
MeshLOD *pmlod = &msh_aMeshLODs[imlod];
// copy all skeleton lods except the selected one
if(pmlod != pmlodRemove)
{
aTempMLODs[iIndexSrc] = *pmlod;
iIndexSrc++;
}
}
// copy temp array of skeleton lods back in skeleton
msh_aMeshLODs.CopyArray(aTempMLODs);
// clear temp skleletons lods array
aTempMLODs.Clear();
}
// write to stream
void CMesh::Write_t(CTStream *ostrFile)
{
INDEX ctmlods = msh_aMeshLODs.Count();
// write id
ostrFile->WriteID_t(CChunkID(MESH_ID));
// write version
(*ostrFile)<<(INDEX)MESH_VERSION;
// write mlod count
(*ostrFile)<<ctmlods;
// for each lod in mesh
for(INDEX imlod=0;imlod<ctmlods;imlod++) {
MeshLOD &mLod = msh_aMeshLODs[imlod];
INDEX ctVx = mLod.mlod_aVertices.Count(); // vertex count
INDEX ctUV = mLod.mlod_aUVMaps.Count(); // uvmaps count
INDEX ctSf = mLod.mlod_aSurfaces.Count(); // surfaces count
INDEX ctWM = mLod.mlod_aWeightMaps.Count(); // weight maps count
INDEX ctMM = mLod.mlod_aMorphMaps.Count(); // morph maps count
// write source file name
(*ostrFile)<<mLod.mlod_fnSourceFile;
// write max distance
(*ostrFile)<<mLod.mlod_fMaxDistance;
// write flags
(*ostrFile)<<mLod.mlod_ulFlags;
// write wertex count
(*ostrFile)<<ctVx;
// write wertices
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)<<ctUV;
// write uvmaps
for(int iuv=0;iuv<ctUV;iuv++) {
// write uvmap ID
CTString strNameID = ska_GetStringFromTable(mLod.mlod_aUVMaps[iuv].muv_iID);
(*ostrFile)<<strNameID;
// write uvmaps texcordinates
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;isf<ctSf;isf++) {
MeshSurface &msrf = mLod.mlod_aSurfaces[isf];
INDEX ctTris = msrf.msrf_aTriangles.Count();
CTString strSurfaceID = ska_GetStringFromTable(msrf.msrf_iSurfaceID);
// write surface ID
(*ostrFile)<<strSurfaceID;
// write first vertex
(*ostrFile)<<msrf.msrf_iFirstVertex;
// write vertices count
(*ostrFile)<<msrf.msrf_ctVertices;
// write tris count
(*ostrFile)<<ctTris;
// write triangles
ostrFile->Write_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)<<bShaderExists;
if(bShaderExists) {
// get shader decription
ShaderDesc shDesc;
msrf.msrf_pShader->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)<<cttx;
// write texture coords count
(*ostrFile)<<cttc;
// write color count
(*ostrFile)<<ctcol;
// write float count
(*ostrFile)<<ctfl;
ASSERT(msrf.msrf_pShader!=NULL);
// write shader name
CTString strShaderName;
strShaderName = msrf.msrf_pShader->GetName();
(*ostrFile)<<strShaderName;
// write shader texture IDs
for(INDEX itx=0;itx<cttx;itx++)
{
INDEX iTexID = msrf.msrf_ShadingParams.sp_aiTextureIDs[itx];
(*ostrFile)<<ska_GetStringFromTable(iTexID);
}
// write shader texture coords indices
for(INDEX itc=0;itc<cttc;itc++)
{
INDEX iTexCoorsIndex = msrf.msrf_ShadingParams.sp_aiTexCoordsIndex[itc];
(*ostrFile)<<iTexCoorsIndex;
}
// write shader colors
for(INDEX icol=0;icol<ctcol;icol++)
{
COLOR colColor = msrf.msrf_ShadingParams.sp_acolColors[icol];
(*ostrFile)<<colColor;
}
// write shader floats
for(INDEX ifl=0;ifl<ctfl;ifl++)
{
FLOAT fFloat = msrf.msrf_ShadingParams.sp_afFloats[ifl];
(*ostrFile)<<fFloat;
}
// write shader flags
ULONG ulFlags = msrf.msrf_ShadingParams.sp_ulFlags;
(*ostrFile)<<ulFlags;
}
}
// write weightmaps count
(*ostrFile)<<ctWM;
// for each weightmap in array
for(INDEX iwm=0;iwm<ctWM;iwm++)
{
INDEX ctWw = mLod.mlod_aWeightMaps[iwm].mwm_aVertexWeight.Count();
// write wertex weight map ID
CTString pstrNameID = ska_GetStringFromTable(mLod.mlod_aWeightMaps[iwm].mwm_iID);
(*ostrFile)<<pstrNameID;
// write wertex weights count
(*ostrFile)<<ctWw;
// write wertex weights
ostrFile->Write_t(&mLod.mlod_aWeightMaps[iwm].mwm_aVertexWeight[0],sizeof(MeshVertexWeight)*ctWw);
}
// write morphmaps count
(*ostrFile)<<ctMM;
for(INDEX imm=0;imm<ctMM;imm++)
{
INDEX ctms = mLod.mlod_aMorphMaps[imm].mmp_aMorphMap.Count();
// write ID
CTString pstrNameID = ska_GetStringFromTable(mLod.mlod_aMorphMaps[imm].mmp_iID);
(*ostrFile)<<pstrNameID;
// write bRelative
(*ostrFile)<<mLod.mlod_aMorphMaps[imm].mmp_bRelative;
//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<ctmlods;imlod++) {
// expand mlod count for one
INDEX ctMeshLODs = msh_aMeshLODs.Count();
msh_aMeshLODs.Expand(ctMeshLODs+1);
MeshLOD &mLod = msh_aMeshLODs[ctMeshLODs];
INDEX ctVx; // vertex count
INDEX ctUV; // uvmaps count
INDEX ctSf; // surfaces count
INDEX ctWM; // weight maps count
INDEX ctMM; // morph maps count
// read source file name
(*istrFile)>>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<ctUV;iuv++) {
// read uvmap ID
CTString strNameID;
(*istrFile)>>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<ctSf;isf++) {
INDEX ctTris;
MeshSurface &msrf = mLod.mlod_aSurfaces[isf];
// read surface ID
CTString strSurfaceID;
(*istrFile)>>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<cttx;itx++) {
CTString strTexID;
(*istrFile)>>strTexID;
INDEX iTexID = ska_GetIDFromStringTable(strTexID);
pshpShaderParams->sp_aiTextureIDs[itx] = iTexID;
}
// read shader texture coords indices
for(INDEX itc=0;itc<cttc;itc++) {
INDEX iTexCoorsIndex;
(*istrFile)>>iTexCoorsIndex;
pshpShaderParams->sp_aiTexCoordsIndex[itc] = iTexCoorsIndex;
}
// read shader colors
for(INDEX icol=0;icol<ctcol;icol++) {
COLOR colColor;
(*istrFile)>>colColor;
pshpShaderParams->sp_acolColors[icol] = colColor;
}
// read shader floats
for(INDEX ifl=0;ifl<ctfl;ifl++) {
FLOAT fFloat;
(*istrFile)>>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<ctWM;iwm++) {
// read weightmap ID
CTString pstrNameID;
(*istrFile)>>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<ctMM;imm++) {
// read morphmap ID
CTString pstrNameID;
(*istrFile)>>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; imlod<ctmlod; imlod++)
{
// for each surface, clear the triangles list
MeshLOD &mlod = msh_aMeshLODs[imlod];
INDEX ctsrf = mlod.mlod_aSurfaces.Count();
for (INDEX isrf=0;isrf<ctsrf;isrf++)
{
MeshSurface &msrf = mlod.mlod_aSurfaces[isrf];
msrf.msrf_aTriangles.Clear();
// release shader form stock
if(msrf.msrf_pShader!=NULL) _pShaderStock->Release(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<ctuvm;iuvm++)
{
mlod.mlod_aUVMaps[iuvm].muv_aTexCoords.Clear();
}
// clear the uvmaps array
mlod.mlod_aUVMaps.Clear();
// clear the vertices array
mlod.mlod_aVertices.Clear();
// clear the normals array
mlod.mlod_aNormals.Clear();
}
// in the end, clear all LODs
msh_aMeshLODs.Clear();
}
// Count used memory
SLONG CMesh::GetUsedMemory(void)
{
SLONG slMemoryUsed = sizeof(*this);
INDEX ctmlods = msh_aMeshLODs.Count();
for(INDEX imlod=0;imlod<ctmlods;imlod++) {
MeshLOD &mlod = msh_aMeshLODs[imlod];
slMemoryUsed+=sizeof(mlod);
slMemoryUsed+=mlod.mlod_aVertices.Count() * sizeof(MeshVertex);
slMemoryUsed+=mlod.mlod_aNormals.Count() * sizeof(MeshNormal);
// for each uvmap
INDEX ctuvmaps = mlod.mlod_aUVMaps.Count();
for(INDEX iuvm=0;iuvm<ctuvmaps;iuvm++) {
MeshUVMap &uvmap = mlod.mlod_aUVMaps[iuvm];
slMemoryUsed+=sizeof(uvmap);
slMemoryUsed+=uvmap.muv_aTexCoords.Count() * sizeof(MeshTexCoord);
}
// for each surface
INDEX ctmsrf = mlod.mlod_aSurfaces.Count();
for(INDEX imsrf=0;imsrf<ctmsrf;imsrf++) {
MeshSurface &msrf = mlod.mlod_aSurfaces[imsrf];
slMemoryUsed+=sizeof(msrf);
slMemoryUsed+=msrf.msrf_aTriangles.Count() * sizeof(MeshTriangle);
slMemoryUsed+=sizeof(ShaderParams);
slMemoryUsed+=sizeof(INDEX) * msrf.msrf_ShadingParams.sp_aiTextureIDs.Count();
slMemoryUsed+=sizeof(INDEX) * msrf.msrf_ShadingParams.sp_aiTexCoordsIndex.Count();
slMemoryUsed+=sizeof(COLOR) * msrf.msrf_ShadingParams.sp_acolColors.Count();
slMemoryUsed+=sizeof(FLOAT) * msrf.msrf_ShadingParams.sp_afFloats.Count();
}
// for each weight map
INDEX ctwm = mlod.mlod_aWeightMaps.Count();
for(INDEX iwm=0;iwm<ctwm;iwm++) {
MeshWeightMap &mwm = mlod.mlod_aWeightMaps[iwm];
slMemoryUsed+=sizeof(mwm);
slMemoryUsed+=mwm.mwm_aVertexWeight.Count() * sizeof(MeshVertexWeight);
}
// for each morphmap
INDEX ctmm = mlod.mlod_aMorphMaps.Count();
for(INDEX imm=0;imm<ctmm;imm++) {
MeshMorphMap &mmm = mlod.mlod_aMorphMaps[imm];
slMemoryUsed+=sizeof(mmm);
slMemoryUsed+=mmm.mmp_aMorphMap.Count() * sizeof(MeshVertexMorph);
}
}
return slMemoryUsed;
}