Files
FC1/CryAnimation/CryGeometryInfo.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1188 lines
35 KiB
C++

/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Crytek Character Animation source code
//
// History:
// 08/28/2002 - Created by Sergiy Migdalskiy <sergiy@crytek.de>
//
// Contains:
// Implementation of CryGeometryInfo, a structure holding geometry in the form that is to
// the data structure of CGF, and supporting subclass(es)
/////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <I3DEngine.h>
#include <IEdgeConnectivityBuilder.h> // IEdgeConnectivityBuilder
#include <StlUtils.h>
#include <StringUtils.h>
#include "MeshIdx.h"
#include "VertexBufferArrayDrivers.h"
#include "CryGeometryInfo.h"
#include "ChunkFileReader.h"
#include "CrySkinBuilder.h"
#include "CrySkinBasisBuilder.h"
#include "CrySkinRigidBasis.h"
#include "CrySkinFull.h"
#include "CryBone.h"
#include "RenderUtils.h"
#include "CVars.h"
#include "CryCompiledFile.h"
CryGeometryInfo::CryGeometryInfo():
m_pStencilShadowConnectivity(NULL),
m_numUsedVertices(0),
m_arrVertBuf ("CryGeometryInfo.VertBuf"),
m_nVertBufFormat (-1),
m_arrExtTangents ("CryGeometryInfo.ExtTangents"),
m_arrExtToIntMap ("CryGeometryInfo.ExtToIntMap"),
m_arrExtUVs ("CryGeometryInfo.ExtUVs")
, m_arrVertices ("CryGeometryInfo.Vertices")
, m_arrNormals ("CryGeometryInfo.Normals")
, m_arrTexFaces ("CryGeometryInfo.TexFaces")
, m_arrUVs ("CryGeometryInfo.UVs")
, m_arrLinks ("CryGeometryInfo.Links")
, m_arrIntToExtMap ("CryGeometryInfo.IntToExtMap")
{
}
CryGeometryInfo::~CryGeometryInfo()
{
if (m_pStencilShadowConnectivity)
m_pStencilShadowConnectivity->Release();
}
void CryGeometryInfo::buildGeomSkins(const CryBoneInfo* pBoneInfo, unsigned numBoneInfos)
{
assert(m_SkinGeom.empty());
// build the skinner that will skin the vertices
{
CrySkinVertexSource SkinVertexSource (this);
CrySkinBuilder builder (&SkinVertexSource);
builder.initSkinFull(&m_SkinGeom);
}
// build a separate skinner for normals. The skinner itself
// actually doesn't even know that those are normals, it skins them just like normal vertices
{
CrySkinNormalSource SkinNormalSource (this, pBoneInfo);
CrySkinBuilder builder (&SkinNormalSource);
builder.initSkinFull(&m_SkinNormal);
}
}
bool CryGeometryInfo::loadVertexSkin (const void* pData, unsigned nSize)
{
return m_SkinGeom.Serialize_PC(false, (void*)pData, nSize) != 0;
}
bool CryGeometryInfo::loadNormalSkin (const void* pData, unsigned nSize)
{
return m_SkinNormal.Serialize_PC(false, (void*)pData, nSize) != 0;
}
bool CryGeometryInfo::loadTangSkin (const void* pData, unsigned nSize)
{
return m_TangSkin.Serialize(false, (void*)pData, nSize) != 0;
}
// creates (allocates with new[]) an array of bindings for each normal
// there are numVertices() elements in this array
CryVertexBinding* CryGeometryInfo::newNormalLinks (const CryBoneInfo* pBoneInfo)
{
CryVertexBinding* pNormals = new CryVertexBinding[numVertices()];
for (unsigned i = 0; i < numVertices(); ++i)
{
pNormals[i].resize (1);
unsigned nBone = pNormals[i][0].BoneID = getLink(i)[0].BoneID;
pNormals[i][0].Blending = 1;//getLink(0)[0].Blending;
//CHANGED_BY_IVO
pNormals[i][0].offset = pBoneInfo[nBone].getInvDefGlobal().TransformVectorOLD(getNormal (i));
// Temporary fixed by Sergiy. TransformVector is not the same as TransformPoint
//pNormals[i][0].offset = GetTransposed44(pBoneInfo[nBone].getInvDefGlobal())*( getNormal(i) );
}
return pNormals;
}
bool CryGeometryInfo::hasGeomSkin()const
{
return !m_SkinGeom.empty();
}
CrySkinFull* CryGeometryInfo::getGeomSkin()
{
assert(!m_SkinGeom.empty());
return &m_SkinGeom;
}
CrySkinFull* CryGeometryInfo::getNormalSkin()
{
assert(!m_SkinNormal.empty());
return &m_SkinNormal;
}
// computes, caches and returns the connectivity object for stencil shadows
// (or just returns, if the object is already cached)
IStencilShadowConnectivity* CryGeometryInfo::getStencilShadowConnectivity (const std::vector<MAT_ENTITY>&arrMaterials)
{
if (!m_pStencilShadowConnectivity)
buildStencilShadowConnectivity(arrMaterials);
return m_pStencilShadowConnectivity;
}
// deserializes the stencil shadow connectivity object
void CryGeometryInfo::setStencilShadowConnectivity (IStencilShadowConnectivity* pConnectivity)
{
if (m_pStencilShadowConnectivity)
m_pStencilShadowConnectivity->Release();
m_pStencilShadowConnectivity = pConnectivity;
}
void CryGeometryInfo::OutputOrphanedEdgeWarning(class IEdgeConnectivityBuilder *iEdgeBuilder, const std::vector<MAT_ENTITY>&arrMaterials)
{
unsigned numOrphanedEdges = iEdgeBuilder->numOrphanedEdges();
if (numOrphanedEdges > 0)
{
// calculate the set of materials of faces which have open edges
std::vector<unsigned> arrFaces; // the faces that have open edges
arrFaces.resize (numOrphanedEdges);
iEdgeBuilder->getOrphanedEdgeFaces(&arrFaces[0]);
std::set<unsigned> setShaders; // the shaders to which the faces belong
for (unsigned i = 0; i < numOrphanedEdges; ++i)
{
assert (arrFaces[i] < m_arrFaceMtl.size());
setShaders.insert (m_arrFaceMtl[arrFaces[i]]);
}
// the set of shader names
std::set<string> setShaderNames;
string strShaderNameList;
for (std::set<unsigned>::iterator it = setShaders.begin(); it != setShaders.end(); ++it)
{
unsigned nShader = *it;
if (nShader < arrMaterials.size())
setShaderNames.insert (arrMaterials[nShader].name);
else
{
char szBuf[64];
sprintf (szBuf, "#Unknown#%d#", nShader);
}
}
strShaderNameList = "Shaders:" + CryStringUtils::toString (setShaderNames);
g_GetLog()->LogWarning ("\003Shadow Connectivity Builder: %u orphaned edges discovered in the model %s", iEdgeBuilder->numOrphanedEdges(), strShaderNameList.c_str());
}
}
//////////////////////////////////////////////////////////////////////////
// builds the connectivity object for stencil shadows
// PARAMETERS:
// iEdgeBuilder - the builder to use to create the connectivity info
// pbCastShadow - the array of flags, 1 flag per 1 material, if the flag is true, then this material casts shadow, otherwise not
// numMaterials - number of items in the pbCastShadow array
void CryGeometryInfo::buildStencilShadowConnectivity (const std::vector<MAT_ENTITY>&arrMaterials)
{
IEdgeConnectivityBuilder *iEdgeBuilder = Get3DEngine()->GetNewConnectivityBuilder();
assert(iEdgeBuilder); // should be always there
#ifdef _DEBUG
if (m_pStencilShadowConnectivity && g_GetCVars()->ca_DebugRebuildShadowVolumes())
{
m_pStencilShadowConnectivity->Release();
m_pStencilShadowConnectivity = NULL;
}
#endif
if (!m_pStencilShadowConnectivity)
{
unsigned numFaces = (unsigned)this->numFaces();
iEdgeBuilder->ReserveForTriangles(numFaces,numUsedVertices());
for (unsigned i = 0; i < numFaces; ++i)
{
GeomFace Face = getFace(i);
GeomMtlID nMaterial = getFaceMtl(i);
assert (nMaterial < arrMaterials.size());
if (nMaterial < arrMaterials.size() && arrMaterials[nMaterial].Dyn_StaticFriction == 1)
continue;
// with welding
iEdgeBuilder->AddTriangleWelded (Face[0], Face[1], Face[2], m_arrVertices[Face[0]], m_arrVertices[Face[1]], m_arrVertices[Face[2]]);
}
OutputOrphanedEdgeWarning(iEdgeBuilder, arrMaterials);
//if (iEdgeBuilder->numOrphanedEdges() > 0)
// GetLog()->Log ("Shadow Connectivity Builder: %u orphaned edges discovered in the model", iEdgeBuilder->numOrphanedEdges());
m_pStencilShadowConnectivity=iEdgeBuilder->ConstructConnectivity();
#ifdef _DEBUG
if(m_pStencilShadowConnectivity)
{
char str[256];
DWORD dwVertCount,dwTriCount;
m_pStencilShadowConnectivity->GetStats(dwVertCount,dwTriCount);
sprintf(str,"StencilEdgeConnectivity Indoor Stats: %d/%d Vertices %d/%d Faces\n",dwVertCount,numUsedVertices(),dwTriCount,numFaces);
#ifdef WIN32
OutputDebugString(str);
#endif
#ifdef GAMECUBE
OSReport(str);
#endif
}
#endif
clearConstructionData();
}
}
// Allocates arrays that keep the vertices and tangents
void CryGeometryInfo::PrepareVertices (unsigned numVertices)
{
m_numUsedVertices =numVertices;
m_arrVertices.reinit (numVertices);
m_arrNormals.reinit (numVertices);
}
// Allocattes the texture coordinate array
void CryGeometryInfo::PrepareUVs (int numUVs)
{
m_arrUVs.reinit(numUVs);
}
// Allocates texture face array
void CryGeometryInfo::PrepareTexFaces (int numTexFaces)
{
assert(numFaces() == numTexFaces);
m_arrTexFaces.reinit(numTexFaces);
}
// Allocates the external to internal map entries
void CryGeometryInfo::PrepareExtToIntMap (int numExtVertices)
{
m_arrExtToIntMap.reinit (numExtVertices, 0);
m_arrExtTangents.reinit (numExtVertices);
}
// Create the Internal-to-external map
void CryGeometryInfo::CreateIntToExtMap ()
{
m_arrIntToExtMap.reinit (numUsedVertices(), 0);
unsigned numExternalVertices = (unsigned)m_arrExtToIntMap.size();
for (unsigned nExternalVertex = 0; nExternalVertex < numExternalVertices; ++nExternalVertex)
{
m_arrIntToExtMap[m_arrExtToIntMap[nExternalVertex]] = nExternalVertex;
}
}
// returns the number of texture faces, which is either NULL,
// or the same as the number of geometry faces
unsigned CryGeometryInfo::numTexFaces() const
{
return m_arrTexFaces.empty()? 0 : (unsigned)numFaces();
}
void CryGeometryInfo::PrepareLinks (int numVertices)
{
assert (
numVertices == this->numVertices()
);
m_arrLinks.reinit (numVertices);
}
// returns the number of links, link array is either NULL or an array with numVertices() elements
unsigned CryGeometryInfo::numLinks() const
{
return m_arrLinks.empty()? 0 : numUsedVertices();
}
void CryGeometryInfo::MakeIntFaces(const CryFace* pCryFaces, unsigned numFaces)
{
m_arrFaces.resize (numFaces);
m_arrFaceMtl.resize (numFaces);
for (unsigned nFace = 0; nFace < numFaces; ++nFace)
for (unsigned v = 0; v < 3; ++v)
{
m_arrFaces[nFace][v] = (unsigned short)pCryFaces[nFace][v];
m_arrFaceMtl[nFace] = (GeomMtlID)pCryFaces[nFace].MatID;
}
}
//////////////////////////////////////////////////////////////////////////
// sorts the faces by the material indices.
// in the given array, sets elements corresponding to used materials to true; doesn't touch the other elements
// (i.e. the array elements must be set to false before calling this function)
// IMPORTANT: Keeps the original order of faces in each material group
void CryGeometryInfo::sortFacesByMaterial (std::vector<bool>& arrMtlUsage)
{
// for each material, construct an array of CryFaces and keep the faces (in the original order) in there
typedef std::vector<GeomFace> GeomFaceArray;
typedef std::map<GeomMtlID, GeomFaceArray> IntFaceMtlMap;
IntFaceMtlMap mapMtlFace;
unsigned i;
// put each face into its material group;
// the corresponding groups CryFaceArray will be created by the map automatically
// upon the first request of that group
//
for (i = 0; i < numFaces(); ++i)
{
GeomMtlID nMaterial = getFaceMtl (i);
mapMtlFace[nMaterial].push_back(getFace(i));
if ((unsigned)nMaterial < arrMtlUsage.size())
arrMtlUsage[nMaterial] = true;
else
assert (0); // material is out of range
}
// Scan through all the faces and put the sorted face infos back into the original array
// scan through each material group sequentally, so that the material sort order is maintained
unsigned nNewFace = 0;
for (IntFaceMtlMap::iterator itMtl = mapMtlFace.begin(); itMtl != mapMtlFace.end(); ++itMtl)
{
GeomFaceArray& arrFaces = itMtl->second;
for (GeomFaceArray::iterator itFace = arrFaces.begin(); itFace != arrFaces.end(); ++itFace)
{
m_arrFaces[nNewFace] = *itFace;
m_arrFaceMtl[nNewFace] = itMtl->first;
++nNewFace;
}
}
assert (nNewFace == numFaces());
}
//////////////////////////////////////////////////////////////////////////
// creates an instance of the CIndexedMesh with the info from this geometry info.
// this is needed to create the leaf buffers (while GenerateRenderArrays() execution)
// The client is responsible for releasing (by delete operator) the created instance.
CIndexedMesh* CryGeometryInfo::new3DEngineIndexedMesh()const
{
CryFace* pCryFaces = newCryFaces();
CIndexedMesh* pMesh = new CIndexedMesh(
(unsigned)numFaces(), pCryFaces, getTexFaces(),
(unsigned)numVertices(), getVertices(), getNormals(),
(unsigned)numUVs(), getUVs()
);
delete[]pCryFaces;
return pMesh;
}
// creates a fake array of faces
CryFace* CryGeometryInfo::newCryFaces()const
{
CryFace* pCryFaces = new CryFace[numFaces()];
const GeomFace* pFaces = getFaces();
for (unsigned nFace = 0; nFace < numFaces(); ++nFace)
{
pCryFaces[nFace].v0 = pFaces[nFace][0];
pCryFaces[nFace].v1 = pFaces[nFace][1];
pCryFaces[nFace].v2 = pFaces[nFace][2];
pCryFaces[nFace].SmGroup = 1;
pCryFaces[nFace].MatID = getFaceMtl(nFace);
}
return pCryFaces;
}
// calculates the bounding box for the object
// if the bbox can't be calculated (e.g. the skin hasn't been yet loaded), returns false
bool CryGeometryInfo::computeBBox (CryAABB& bb)
{
if (m_arrVertices.empty())
{
// set the (0,0,0)-(0,0,0) empty bounding box
bb = CryAABB( Vec3d(0,0,0), Vec3d(0,0,0) );
return false;
}
Vec3dArray::iterator it = m_arrVertices.begin(), itEnd = it + numVertices();
bb = CryAABB(*it,*it);
++it;
for (; it != itEnd; ++it)
{
bb.include(*it);
}
return true;
}
DWORD toDword(const CryIRGB color)
{
return ((DWORD)(((BYTE)(color.r)|((WORD)((BYTE)(color.g))<<8))|(((DWORD)(BYTE)(color.b))<<16)))| 0xFF000000;
}
//////////////////////////////////////////////////////////////////////////
// Loads the given geometry into this geometry info.
// The geometry is given as the part of the CGF file
// PARAMETERS:
// nLOD - treat the geometry as the specified LOD
// pChunk - pointer to the chunk
// nChunkSize - chunk size, in bytes
// RETURNS:
// the number of bytes read; 0 if the chunk is useless
unsigned CryGeometryInfo::Load (unsigned nLOD, const MESH_CHUNK_DESC_0744* pChunk, unsigned nChunkSize)
{
const void *pRawData = pChunk+1;
if (nChunkSize < sizeof(*pChunk))
return 0;
nChunkSize -= sizeof(*pChunk);
if(pChunk->nVerts<=0 || pChunk->nVerts>60000)
return 0;
PrepareVertices (pChunk->nVerts);
assert (pChunk->nVerts);
const CryVertex* pSrcVertices = (const CryVertex*)pRawData;
if ((const char*)(pSrcVertices + pChunk->nVerts) > (const char*)pRawData + nChunkSize)
return 0; // the cry vertex info is truncated
for (int i = 0; i < pChunk->nVerts; ++i)
{
m_arrVertices[i] = pSrcVertices[i].p;
m_arrNormals[i] = pSrcVertices[i].n;
}
pRawData = pSrcVertices + pChunk->nVerts;
//read faces
std::vector<CryFace> arrCryFaces;
arrCryFaces.resize(pChunk->nFaces);
if (!EatRawData (&arrCryFaces[0], pChunk->nFaces, pRawData, nChunkSize))
return 0;
MakeIntFaces(&arrCryFaces[0], pChunk->nFaces);
// convert the CryFace's into int faces
arrCryFaces.clear();
if (!ValidateFaceIndices())
g_GetConsole()->Exit("CryGeometryInfo::Load: Indices out of range");
//read tverts
if(pChunk->nTVerts)
{
PrepareUVs(pChunk->nTVerts);
if (!EatRawData(getUVs(), pChunk->nTVerts, pRawData, nChunkSize))
return 0;
// flip tex coords (since it was flipped in max?)
for (unsigned t = 0; t < (unsigned)pChunk->nTVerts; ++t)
getUV(t).v = 1.f-getUV(t).v;
//read Tfaces
PrepareTexFaces(pChunk->nFaces);
if (!EatRawData (getTexFaces(), pChunk->nFaces, pRawData, nChunkSize))
return 0;
if (!ValidateTexFaceIndices())
g_GetConsole()->Exit("CryGeometryInfo::Load: Texture vertex Indices out of range");
}
//read Links if there is the bone info.
// NOTE: we allow the bone info to be absent if it's not needed, even if the HasBoneInfo flag is set.
// this is due to the bug in the exporter utility that can set the flag but not export the actual bones
bool bHasBoneInfo = pChunk->HasBoneInfo;
bool bNoLinksWarning = false;
if (nChunkSize == 0)
bHasBoneInfo = false; // ignore the flag if the chunk doesn't actually contain the bone info
if(bHasBoneInfo)
{
PrepareLinks(pChunk->nVerts);
for (int i=0; i < pChunk->nVerts; i++)
{
// the links for the current vertex in the geometry info structure
CryVertexBinding& arrLinks = getLink(i);
// read the number of links and initialize the link array
{
// the number of links for the current vertex
unsigned numLinks;
if (!EatRawData(&numLinks,1,pRawData, nChunkSize))
return 0;
if(numLinks > 32u)
{
g_GetLog()->LogError ("\001CryGeometryInfo::Load: Number of links for vertex is invalid: %u", numLinks);
return 0;
}
arrLinks.resize(numLinks);
}
if (arrLinks.empty())
{
if (!bNoLinksWarning)
{
g_GetLog()->LogError ("\003file contains unbound vertices");
bNoLinksWarning = true;
}
arrLinks.resize(1);
arrLinks[0].Blending = 1;
arrLinks[0].BoneID = 0;
arrLinks[0].offset = Vec3d (0,0,0);
continue;
}
else
if (!EatRawData(&arrLinks[0], (unsigned)arrLinks.size(), pRawData, nChunkSize))
return 0;
arrLinks.sortByBlendingDescending();
// limit links number
if(arrLinks.size() > 3)
arrLinks.resize(3);
arrLinks.normalizeBlendWeights();
arrLinks.pruneSmallWeights (g_GetCVars()->ca_MinVertexWeightLOD(nLOD));
arrLinks.normalizeBlendWeights();
}
// optimize the vertex order for skinning - will interfere with morph targets
//sortVertices();
}
//read Vertex Colors
if(pChunk->HasVertexCol)
{
if (g_GetCVars()->ca_AnimWarningLevel()>2)
g_GetLog()->LogWarning ("\004Vertex colors are found in mesh");
TElementaryArray<CryIRGB> arrVColors;
arrVColors.reinit (pChunk->nVerts);
if (EatRawData (&arrVColors[0], pChunk->nVerts, pRawData, nChunkSize))
{
m_arrVColors.resize (pChunk->nVerts);
for (int i = 0; i < pChunk->nVerts; ++i)
m_arrVColors[i] = toDword (arrVColors[i]);
}
//nChunkSize -= sizeof(CryIRGB) * pChunk->nVerts;
//pRawData = (CryIRGB*)pRawData + pChunk->nVerts;
}
return ((byte*)pRawData) - ((byte*)pChunk);
}
// scales the model. Multiplies all vertex coordinates by the given factor
void CryGeometryInfo::Scale (float fScale)
{
Vec3dArray::iterator itVertex = m_arrVertices.begin(), itVertexEnd = itVertex + numVertices();
for (; itVertex != itVertexEnd; ++itVertex)
*itVertex *= fScale;
VertexBindingArray::iterator itLink = m_arrLinks.begin(), itLinkEnd = itLink + numLinks();
for (; itLink != itLinkEnd; ++itLink)
itLink->scaleOffsets(fScale);
}
// Checks the face indices, if they are in range (0..numVertices())
// returns true upon successful validation, false means there are indices out of range
bool CryGeometryInfo::ValidateFaceIndices ()
{
// prepare (cache) lookup values
unsigned numVertices = this->numUsedVertices();
FaceArray::iterator it, itEnd = m_arrFaces.end();
// check indices of each face
for (it = m_arrFaces.begin(); it != itEnd; ++it)
{
for (int v = 0; v < 3; ++v)
if ((unsigned)((*it)[v]) >= numVertices)
return false;
}
return true; // validation passed successfully
}
// Checks the texture face indices, if they are in range (0..numVertices())
// returns true upon successful validation, false means there are indices out of range
bool CryGeometryInfo::ValidateTexFaceIndices ()
{
// prepare (cache) lookup values
int numUVs = this->numUVs();
TexFaceArray::iterator
it = m_arrTexFaces.begin(),
itEnd = it + numTexFaces();
// check indices of each face
for (; it != itEnd; ++it)
{
if ( it->t0 >= numUVs || it->t0 < 0
|| it->t1 >= numUVs || it->t1 < 0
|| it->t2 >= numUVs || it->t2 < 0 )
return false;
}
return true; // validation passed successfully
}
// forces the material indices to be in the range [0..numMaterials]
void CryGeometryInfo::limitMaterialIDs(unsigned numMaterials)
{
for (unsigned i = 0; i < numFaces(); ++i)
{
if ((unsigned)m_arrFaceMtl[i] >= numMaterials)
m_arrFaceMtl[i] = 0;
}
}
// recalculates all the normals, assuming smooth geometry (non-smooth vertices were already split in the exporter)
void CryGeometryInfo::recalculateNormals()
{
unsigned i, nActiveVerts = numVertices();
#ifdef _DEBUG
Vec3dElementaryArray arrOldNormals(nActiveVerts);
for (i = 0; i < nActiveVerts; ++i)
arrOldNormals[i] = getNormal(i);
#endif
for (i=0; i<nActiveVerts; i++)
getNormal(i)(0,0,0);
for (unsigned n = 0; n < numFaces(); ++n)
{
const GeomFace& fc = getFace(n);
Vec3d v1, v2, vTmpNormal;
v1 = getVertex(fc[0]) - getVertex(fc[1]);
v2 = getVertex(fc[0]) - getVertex(fc[2]);
vTmpNormal = v1 ^ v2;
//vTmpNormal.Normalize();
for (int v = 0; v < 3; ++v)
{
Vec3d& vNormal = getNormal(fc[v]);
Vec3d vNewNormal = vTmpNormal + vNormal;
//assert (vNewNormal.Length() > 1e-3);
vNormal = vNewNormal;
}
}
for (i=0; i<nActiveVerts; i++)
{
Vec3d& vN = getNormal(i);
float fLength = vN.Length();
// the threshold must be very low because the areas of rectangles
// are very small (in square meters), it can easily be 1e-4 (square mm) or even 1e-6 (square cm)
// and we don't loose anything if we set some random (but not NaN) normal instead of 0,1,0 which is also random
if (fLength < 1e-12)
vN = Vec3d(0,1,0);
else
vN /= fLength;
}
/*#ifdef _DEBUG
for (i = 0; i < nActiveVerts; ++i)
{
Vec3d vOld = arrOldNormals[i];
Vec3d vNew = getNormal(i);
assert (GetDistance(vOld, vNew) < 0.5);
}
#endif*/
}
// returns true if the geometry is degraded (e.g. no vertices or no faces)
bool CryGeometryInfo::empty()
{
return numVertices() == 0 || numFaces () == 0;
}
// remaps the ids of the bones in the CryLink structures
void CryGeometryInfo::remapBoneIds (const unsigned* arrBoneIdMap, unsigned numBoneIds)
{
VertexBindingArray::iterator it = m_arrLinks.begin(), itEnd = it + numLinks();
for (; it != itEnd; ++it)
{
it->remapBoneIds (arrBoneIdMap, numBoneIds);
}
}
// remaps the ids of materials. arrMtlIdMap[nOldMtlId] == nNewMtlId
void CryGeometryInfo::remapMtlIds (const unsigned* arrMtlIdMap, unsigned numMtlIds)
{
for (unsigned nFace = 0; nFace < numFaces(); ++nFace)
{
GeomMtlID nOldMtlId = getFaceMtl (nFace);
GeomMtlID nNewMtlId = 0;
if (nOldMtlId < numMtlIds)
nNewMtlId = arrMtlIdMap[nOldMtlId];
m_arrFaceMtl[nFace] = nNewMtlId;
}
}
// rotates the model; transforms each vector with the specified matrix, using only its
// ROTATIONAL components (no translation)
void CryGeometryInfo::rotate (const Matrix44& tm)
{
Vec3dArray::iterator it = m_arrVertices.begin(), itEnd = it + numVertices();
for (; it != itEnd; ++it)
//CHANGED_BY_IVO - INVALID CHANGE, PLEASE REVISE
*it = tm.TransformVectorOLD(*it);
//*it = GetTransposed44(tm)*(*it);
}
// transforms the model with the given transform matrix
void CryGeometryInfo::transform (const Matrix44& tm)
{
Vec3dArray::iterator it = m_arrVertices.begin(), itEnd = it + numVertices();
for (; it != itEnd; ++it)
*it = tm.TransformPointOLD(*it);
}
// destroys the UV array
void CryGeometryInfo::removeUVs()
{
m_arrUVs.clear();
}
// destroys the texture faces
void CryGeometryInfo::removeTexFaces()
{
m_arrTexFaces.clear();
}
void CryGeometryInfo::initExtUVs (const CVertexBufferUVArrayDriver& pUVs)
{
unsigned i, numExtUVs = numExtToIntMapEntries();
m_arrExtUVs.reinit (numExtUVs);
for (i = 0; i < numExtUVs; ++i)
{
m_arrExtUVs[i] = pUVs[i];
}
}
#if 0
// after strip-generation of the mesh, rearranges the geometry info to match the sequence of vertices
// in the stripified mesh
void CryGeometryInfo::remapVerticesToExt ()
{
// reorder: Vertices, Normals, Face Indices, Links
// then destroy the ext<->int mappings
GetLog()->LogToFile ("\005CryGeometryInfo::remapVerticesToExt:%d->%d", numVertices(), numExtToIntMapEntries());
unsigned numExtVerts = numExtToIntMapEntries();
Vec3dArray arrExtVertices, arrExtNormals;
arrExtVertices.reinit (numExtVerts);
arrExtNormals.reinit (numExtVerts);
VertexBindingArray arrExtLinks;
arrExtLinks.reinit (numExtVerts);
for (unsigned nExtVert = 0; nExtVert < numExtVerts; ++nExtVert)
{
unsigned nIntVert = getExtToIntMapEntry(nExtVert);
arrExtVertices[nExtVert] = m_arrVertices[nIntVert];
arrExtNormals[nExtVert] = m_arrNormals[nIntVert];
arrExtLinks[nExtVert] = m_arrLinks[nIntVert];
}
m_arrLinks.swap (arrExtLinks);
m_arrVertices.swap (arrExtVertices);
m_arrNormals.swap (arrExtNormals);
for (unsigned nFace = 0; nFace < numFaces(); ++nFace)
{
CryFace& face = m_arrFaces[nFace];
face.v0 = m_arrIntToExtMap[face.v0];
face.v1 = m_arrIntToExtMap[face.v1];
face.v2 = m_arrIntToExtMap[face.v2];
}
m_arrExtToIntMap.clear();
m_arrIntToExtMap.clear();
}
#endif
class CSkinVertexSorter
{
public:
CSkinVertexSorter (CryGeometryInfo* pGeom):
m_pGeom(pGeom)
{}
bool operator() (unsigned nLeft, unsigned nRight)const
{
CryVertexBinding& rLeftBind = m_pGeom->getLink(nLeft);
CryVertexBinding& rRightBind = m_pGeom->getLink(nRight);
const CryLink& rLeftLink = rLeftBind[0];
const CryLink& rRightLink = rRightBind[0];
if (rLeftBind.size() == 1 && rRightBind.size() > 1)
return true;
if (rLeftBind.size() > 1 && rRightBind.size() == 1)
return false;
if (rLeftLink.BoneID < rRightLink.BoneID)
return true;
if (rLeftLink.BoneID > rRightLink.BoneID)
return false;
return rLeftLink.Blending < rRightLink.Blending;
}
protected:
CryGeometryInfo* m_pGeom;
};
// optimize the vertex order for skinning
// WARNING: will interfere with morph targets
void CryGeometryInfo::sortVertices()
{
// sort the vertices by the most influential bone index
CSkinVertexSorter sorter (this);
TElementaryArray<unsigned> arrMap, arrUnmap;
arrMap.reinit (numVertices());
unsigned i;
for (i = 0; i < numVertices(); ++i)
arrMap[i] = i;
std::sort (arrMap.begin(), arrMap.begin() + numVertices(), sorter);
arrUnmap.reinit (numVertices());
for (i = 0; i < numVertices(); ++i)
arrUnmap[arrMap[i]] = i;
// swap vertices and normals
// face vertex indices and links
Vec3dElementaryArray arrNewVertices("sortVertices"), arrNewNormals("sortVertices");
arrNewVertices.reinit (numVertices());
arrNewNormals.reinit (numVertices());
VertexBindingArray arrNewLinks ("sortVertices");
arrNewLinks.reinit (numVertices());
// sort the vertices, normals and links
for (i = 0; i < numVertices(); ++i)
{
arrNewVertices[i] = m_arrVertices[arrMap[i]];
arrNewNormals[i] = m_arrNormals[arrMap[i]];
arrNewLinks[i] = m_arrLinks[arrMap[i]];
}
m_arrVertices.swap (arrNewVertices);
m_arrNormals.swap (arrNewNormals);
m_arrLinks.swap (arrNewLinks);
for (unsigned nFace = 0; nFace < numFaces(); ++nFace)
{
GeomFace& face = m_arrFaces[nFace];
for (int v = 0; v < 3; ++v)
face[v] = arrUnmap[face[v]];
}
}
void CryGeometryInfo::buildTangSkins (const CryBoneInfo* pBoneInfo, unsigned numBoneInfos)
{
CrySkinVertexSource VertexSource(this);
// form the array of inverse-default-global matrices for the bones
std::vector<Matrix44> arrMatInvDef;
arrMatInvDef.resize (numBoneInfos);
for (unsigned i = 0; i < numBoneInfos; ++i)
arrMatInvDef[i] = pBoneInfo[i].getInvDefGlobal();
CrySkinBasisBuilder builder (&VertexSource, &arrMatInvDef[0], numBoneInfos);
builder.setDestinationInterval(0,0xFFFFFFFF);
builder.initRigidBasisSkin (&m_TangSkin);
}
// the tangent bases are calculated in pieces, each piece calculating some number of vertices
// the piece
class CrySkinRigidBasis* CryGeometryInfo::getTangSkin()
{
return &m_TangSkin;
}
// after constructing the skins etc. some data is not ever more used
// this data can be cleared here. This should be called after all the initialization has passed
void CryGeometryInfo::clearConstructionData()
{
if (!g_GetCVars()->ca_ZDeleteConstructionData())
return;
m_arrNormals.clear();
m_arrUVs.clear();
m_arrTexFaces.clear();
// we may need faces for decals and stencil shadows
if (!m_arrLinks.empty())
{
// if we don't have links, therefore we don't have bones, and so
// we need the vertices for morphing etc.
// The same applies to tangents
m_arrLinks.clear();
if (g_GetCVars()->ca_EnableTangentSkinning())
m_arrExtTangents.clear();
#ifdef _DEBUG
if (!g_GetCVars()->ca_DebugRebuildShadowVolumes())
#endif
if (m_pStencilShadowConnectivity) // if there's no shadow connectivity yet, we'll need vertices for construction (HACK: we could get rid of it if we didn't split the vertices because of different normals per one vertex)
m_arrVertices.clear();
}
m_arrIntToExtMap.clear();
}
// this is the number of vertices that are really used by the faces
unsigned CryGeometryInfo::numUsedVertices()const
{
return m_numUsedVertices;
}
bool CryGeometryInfo::hasSkin()const
{
return !m_TangSkin.empty();
}
void CryGeometryInfo::GetSize (ICrySizer* pSizer)const
{
SIZER_SUBCOMPONENT_NAME(pSizer, "Geometry");
unsigned nSize = sizeof(*this);
unsigned i;
nSize += sizeofArray (m_arrExtTangents);
nSize += sizeofArray (m_arrExtToIntMap);
nSize += sizeofArray (m_arrExtUVs, numExtUVs());
nSize += sizeofArray (m_arrIntToExtMap, numVertices());
nSize += sizeofArray (m_arrPrimGroups);
nSize += sizeofArray (m_arrIndices);
nSize += sizeofArray (m_arrFaces);
nSize += sizeofArray (m_arrVColors);
if (!m_arrLinks.empty())
{
nSize += sizeofArray (m_arrLinks, numLinks());
for (i = 0; i < numLinks(); ++i)
nSize += sizeofArray(m_arrLinks[i]);
}
nSize += sizeofArray (m_arrTexFaces, numTexFaces());
nSize += sizeofArray (m_arrUVs);
nSize += sizeofArray (m_arrVertices, numVertices());
nSize += sizeofArray (m_arrNormals, numVertices());
{
SIZER_SUBCOMPONENT_NAME(pSizer, "Meshes");
if (!pSizer->AddObject (this, nSize))
return;
}
{
// calculate the skin memory separately, don't take the objects into accout
// since they were already calculated in the sizeof(*this) above
SIZER_SUBCOMPONENT_NAME(pSizer, "Skins");
nSize = m_TangSkin.sizeofThis() - sizeof(m_TangSkin);
nSize += m_SkinGeom.sizeofThis() - sizeof(m_SkinGeom);
nSize += m_SkinNormal.sizeofThis() - sizeof(m_SkinNormal);
// NOTE: if this call will lead to assert, we should find some other id for these subcomponents
pSizer->AddObject(&m_TangSkin, nSize);
}
if (m_pStencilShadowConnectivity)
{
SIZER_SUBCOMPONENT_NAME(pSizer, "Shadow Connectivity");
m_pStencilShadowConnectivity->GetMemoryUsage(pSizer);
}
if (!m_arrVertBuf.empty())
{
SIZER_SUBCOMPONENT_NAME(pSizer, "System Buffers");
pSizer->Add (&m_arrVertBuf[0], numExtVertices()*m_VertexSize[m_nVertBufFormat]);
}
}
// returns the vertex buffer of the given format; in this buffer, UVs will
// already be set up properly and extra fields like color will be NULLed
// Will clean the buffer and return NULL when the vertex format is < 0
char* CryGeometryInfo::getVertBuf (int nVertFormat)
{
if (nVertFormat == m_nVertBufFormat)
return &m_arrVertBuf[0];
// clean the buffer and return NULL if invalid vertex format
if (nVertFormat <= 0 || nVertFormat >= VERTEX_FORMAT_NUMS)
{
m_arrVertBuf.clear();
return NULL;
}
m_nVertBufFormat = nVertFormat;
unsigned nVertSize = m_VertexSize[nVertFormat];
m_arrVertBuf.reinit (nVertSize * numExtVertices());
// zero the memory for some formats
switch (nVertFormat)
{
case VERTEX_FORMAT_P3F:
case VERTEX_FORMAT_P3F_TEX2F:
// no need to zero since the format is tightly packed and we'll fill it
// with actual data
break;
default:
memset (m_arrVertBuf.begin(), 0, nVertSize*numExtVertices());
break;
}
// fill the UVs in
int nUVOffset = g_VertFormatUVOffsets[nVertFormat];
if (nUVOffset >= 0)
{
for (unsigned i = 0; i < numExtVertices(); ++i)
*(CryUV*)(m_arrVertBuf.begin() + nUVOffset + nVertSize * i) = getExtUV (i);
}
int nColorOffset = g_VertFormatRGBAOffsets[nVertFormat];
if (nColorOffset >= 0)
{
unsigned i;
if (m_arrVColors.empty() || m_arrExtToIntMap.empty() || !g_GetCVars()->ca_EnableVertexColors())
for (i = 0; i < numExtVertices(); ++i)
*(DWORD*)(m_arrVertBuf.begin() + nColorOffset + nVertSize * i) = 0xFFFFFFFF;
else
for (i = 0; i < numExtVertices(); ++i)
*(DWORD*)(m_arrVertBuf.begin() + nColorOffset + nVertSize * i) = m_arrVColors[m_arrExtToIntMap[i]];
}
return m_arrVertBuf.begin();
}
/*
#ifdef _DEBUG
void CryGeometryInfo::selfValidate()
{
for (unsigned i = 0; i < numNormals(); ++i)
assert (getNormal(i).Length2() > 0.9);
}
#endif
*/
// initializes the ext-to-int map
void CryGeometryInfo::initExtToIntMap (const unsigned short* pExtToIntMap, unsigned numEntries)
{
m_arrExtToIntMap.resize (numEntries);
for (unsigned i = 0; i < numEntries; ++i)
m_arrExtToIntMap[i] = pExtToIntMap[i];
}
// creates and initializes the array of extern UVs
void CryGeometryInfo::initExtUVs (const CryUV* pUVs, unsigned numUVs)
{
m_arrExtUVs.reinit (numUVs);
memcpy (&m_arrExtUVs[0], pUVs, sizeof(CryUV)*numUVs);
}
// creates and copies the data into the new face array
void CryGeometryInfo::initFaces (unsigned numFaces, const CCFIntFace* pFaces, const void* pFaceMtls)
{
unsigned nFace;
m_arrFaces.resize (numFaces);
for (nFace = 0; nFace < numFaces; ++nFace)
m_arrFaces[nFace] = pFaces[nFace];
m_arrFaceMtl.resize (numFaces);
for (nFace = 0; nFace < numFaces; ++nFace)
m_arrFaceMtl[nFace] = ((const CCFIntFaceMtlID*)pFaceMtls)[nFace];
}
void CryGeometryInfo::exportASC (FILE* f)
{
// vertices of the geometry (first morphed, then skinned)
const CryUV* pExtUVs = getExtUVs();
TangData * pExtTangents = getExtTangents();
unsigned numExtTangents = this->numExtTangents();
Vec3d* pVerts = getVertices();
if (!pVerts || !pExtTangents)
{
fprintf (f, "Error: no vertices or ext tangents. Please restart with ca_ZDeleteConstructionData = 0, don't use stencil shadows and compiled cgfs\n");
return;
}
if (!pExtUVs)
{
fprintf (f, "No externals UVs. Cannot export.\n");
return;
}
fprintf (f, "Vertex \t binormal \t tangent \t normal \t UV\n");
for (unsigned i = 0; i < numExtTangents; ++i)
{
#define V(v) v.x, v.y, v.z
fprintf (f, "%12.9ff,%12.9ff,%12.9ff, %12.9ff,%12.9ff,%12.9ff, %12.9ff,%12.9ff,%12.9ff, %12.9ff,%12.9ff,%12.9ff, %12.9ff,%12.9ff, //0x%04x \n",
V(pVerts[m_arrExtToIntMap[i]]), V(pExtTangents[i].tangent), V(pExtTangents[i].binormal), V(pExtTangents[i].tnormal), pExtUVs[i].u, pExtUVs[i].v, i );
#undef V
}
}
// vlad: I placed it temporary here because of Timur chandes in rc.exe
CIndexedMesh::~CIndexedMesh()
{
if(m_pFaces)
free(m_pFaces);
if(m_pVerts)
free(m_pVerts);
if(m_pCoors)
free(m_pCoors);
if(m_pNorms)
free(m_pNorms);
if(m_pColor)
free(m_pColor);
for(int g=0; g<m_lstGeomNames.Count(); g++)
delete [] m_lstGeomNames[g];
int i;
for (i=0; i<m_lstMatTable.Count(); i++)
{
CMatInfo *mi = &m_lstMatTable[i];
if (mi->pRE)
mi->pRE->Release();
}
for (i=0; i<m_lstLSources.Count(); i++)
{
/* if (m_lstLSources[i]->m_Name)
delete [] m_lstLSources[i]->m_Name;
if (m_lstLSources[i]->m_TargetName)
delete [] m_lstLSources[i]->m_TargetName;
if(m_lstLSources[i]->m_OrigLight)
delete m_lstLSources[i]->m_OrigLight;*/
delete m_lstLSources[i];
}
// We don't need to release target light sources because they're released before (during releasing of main lights in destructor)
/*for (i=0; i<m_tgtLSources.Count(); i++)
{
if (m_tgtLSources[i]->m_Name)
delete [] m_tgtLSources[i]->m_Name;
if (m_tgtLSources[i]->m_TargetName)
delete [] m_tgtLSources[i]->m_TargetName;
delete m_tgtLSources[i];
}*/
}