///////////////////////////////////////////////////////////////////////////////////////////////////// // // Crytek Character Animation source code // // History: // 08/28/2002 - Created by Sergiy Migdalskiy // // 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 #include // IEdgeConnectivityBuilder #include #include #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&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&arrMaterials) { unsigned numOrphanedEdges = iEdgeBuilder->numOrphanedEdges(); if (numOrphanedEdges > 0) { // calculate the set of materials of faces which have open edges std::vector arrFaces; // the faces that have open edges arrFaces.resize (numOrphanedEdges); iEdgeBuilder->getOrphanedEdgeFaces(&arrFaces[0]); std::set 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 setShaderNames; string strShaderNameList; for (std::set::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&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& arrMtlUsage) { // for each material, construct an array of CryFaces and keep the faces (in the original order) in there typedef std::vector GeomFaceArray; typedef std::map 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 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 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 1e-3); vNormal = vNewNormal; } } for (i=0; iremapBoneIds (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 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 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; gpRE) mi->pRE->Release(); } for (i=0; im_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; im_Name) delete [] m_tgtLSources[i]->m_Name; if (m_tgtLSources[i]->m_TargetName) delete [] m_tgtLSources[i]->m_TargetName; delete m_tgtLSources[i]; }*/ }