742 lines
24 KiB
C++
742 lines
24 KiB
C++
#include "stdafx.h"
|
|
#include <StlUtils.h>
|
|
#include <I3DEngine.h>
|
|
|
|
#include "CryModel.h"
|
|
#include "CryGeomMorphTarget.h"
|
|
#include "CryCharBody.h"
|
|
#include "CryModelState.h"
|
|
#include "ChunkFileReader.h"
|
|
#include "CryModelGeometryLoader.h"
|
|
#include "CryBoneHierarchyLoader.h"
|
|
#include "StringUtils.h"
|
|
#include "CVars.h"
|
|
#include "CgfUtils.h"
|
|
|
|
CryModelGeometryLoader::CryModelGeometryLoader ():
|
|
#ifdef DEBUG_STD_CONTAINERS
|
|
m_arrGeomBoneNameTable ("CryModelGeometryLoader.m_arrGeomBoneNameTable"),
|
|
m_mapLights ("CryModelGeometryLoader.mapLights"),
|
|
m_mapObjectNodes ("CryModelGeometryLoader.mapObjectNodes"),
|
|
m_arrTempBoneIdToIndex("TempBoneIdToIndex")
|
|
m_mapLimbPhysGeoms("CryModelGeometryLoader.mapLimbPhysGeoms"),
|
|
#endif
|
|
m_numBoneLightBinds (0),
|
|
m_pBoneLightBind(NULL),
|
|
m_pModel (NULL),
|
|
m_nLOD (-1),
|
|
m_numMorphTargets (0)
|
|
{
|
|
}
|
|
|
|
CryModelGeometryLoader::~CryModelGeometryLoader ()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void CryModelGeometryLoader::clear()
|
|
{
|
|
m_pReader = NULL;
|
|
m_arrGeomBoneNameTable.clear();
|
|
m_numBoneLightBinds = 0;
|
|
m_pBoneLightBind = NULL;
|
|
m_mapLimbPhysGeoms.clear();
|
|
m_pModel = NULL;
|
|
m_nLOD = -1;
|
|
m_mapLights.clear();
|
|
m_mapObjectNodes.clear();
|
|
m_numMorphTargets = 0;
|
|
m_arrTempBoneIdToIndex.clear();
|
|
|
|
m_bFirstTimeWarningBoneGeometryMtl = true;
|
|
m_bMeshFound = false;
|
|
m_bGeometryFound = false;
|
|
m_bBonesFound = false;
|
|
m_nGeometryChunkID = 0;
|
|
|
|
m_pPhysicalGeometryManager = GetPhysicalWorld() ? GetPhysicalWorld()->GetGeomManager() : NULL;
|
|
}
|
|
|
|
bool CryModelGeometryLoader::load (CryModel* pModel, CChunkFileReader* pReader, unsigned nLOD, float fScale)
|
|
{
|
|
//CAutoClear<CryModelGeometryLoader> _autoClean(this);
|
|
clear();
|
|
|
|
// lock the reader - we'll use the reader's memory to represent some of the structures
|
|
m_pReader = pReader;
|
|
m_pModel = pModel;
|
|
m_nLOD = nLOD;
|
|
m_fScale = fScale;
|
|
|
|
if (!prepare())
|
|
return false;
|
|
|
|
if (!loadStage1())
|
|
return false;
|
|
indicateProgress();
|
|
|
|
if (!loadMaterials())
|
|
return false;
|
|
|
|
// we load bones, mesh and bone mesh here
|
|
// in LOD 0 we initialize the bones, in LOD 1 we update their physics; lod 2 has no effect
|
|
if (!loadStage2())
|
|
return false;
|
|
|
|
if (!loadMorphTargets())
|
|
return false;
|
|
|
|
if (!rebindBoneIndices())
|
|
return false;
|
|
|
|
if (!finalize())
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CryModelGeometryLoader::prepare()
|
|
{
|
|
//read the file header
|
|
const FILE_HEADER& fh = m_pReader->getFileHeader();
|
|
|
|
if(fh.Version != GeomFileVersion || fh.FileType != FileType_Geom)
|
|
// invalid file version or type
|
|
return false;
|
|
|
|
if(!m_pModel->m_pDefaultModelState) // load bones only for 0 lod
|
|
m_pModel->m_pDefaultModelState = new CryModelState(m_pModel);
|
|
|
|
return true;
|
|
}
|
|
|
|
// loads the materials; returns true if succeeds
|
|
bool CryModelGeometryLoader::loadMaterials()
|
|
{
|
|
m_nFirstMat = (unsigned)m_pModel->m_arrMaterials.size(); // the base number of materials that was before loading this geometry (used to shift the material references)
|
|
unsigned numLoadedMaterials = m_nFirstMat; // the number of actually loaded objects
|
|
m_pModel->m_arrMaterials.resize (numLoadedMaterials + m_pReader->numChunksOfType(ChunkType_Mtl));
|
|
|
|
int i;
|
|
for (i = 0; i < m_pReader->numChunks(); i++)
|
|
{
|
|
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
|
if (chunkHeader.ChunkType == ChunkType_Mtl)
|
|
{
|
|
const void* pChunk = m_pReader->getChunkData(i);
|
|
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
|
CMatEntityNameTokenizer mt;
|
|
if (loadMaterial ((CHUNK_HEADER*)pChunk, nChunkSize, m_pModel->m_arrMaterials[numLoadedMaterials], mt))
|
|
++numLoadedMaterials;
|
|
else
|
|
{
|
|
// some materials are MULTI, and they're ignored during the load, and that's normal
|
|
//g_GetLog()->LogToFile ("*WARNING* Cannot load standard material (chunk id 0x%08X) from file %s", chunkHeader.ChunkID, getFilePathCStr());
|
|
}
|
|
}
|
|
}
|
|
|
|
m_pModel->m_arrMaterials.resize (numLoadedMaterials);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Attempts to load the material from the given material chunk
|
|
// If the chunk is MULTi material or too old version, returns an error
|
|
// If successful, the read material is copied to the rMaterial
|
|
// PARAMETERS:
|
|
// pMaterialChunk - the chunk to load from
|
|
// nChunkSize - the size of the chunk
|
|
// rMateria - [OUT] - upon success, contains the material data upon exit
|
|
// RETURNS:
|
|
// true - the material was loaded successfully
|
|
// false - the material was not loaded
|
|
bool CryModelGeometryLoader::loadMaterial (CHUNK_HEADER* pMaterialChunk, unsigned nChunkSize, MAT_ENTITY& rMaterial, CMatEntityNameTokenizer& mt)
|
|
{
|
|
IPhysMaterialEnumerator *pMatEnum = Get3DEngine()->GetPhysMaterialEnumerator();
|
|
MatChunkLoadErrorEnum nError = LoadMatEntity(*pMaterialChunk, pMaterialChunk, nChunkSize, rMaterial);
|
|
if (nError == MCLE_Success)
|
|
{
|
|
mt.tokenize (rMaterial.name);
|
|
if (pMaterialChunk->ChunkVersion == MTL_CHUNK_DESC_0746::VERSION)
|
|
{
|
|
if (*mt.szPhysMtl)
|
|
rMaterial.iGameID = pMatEnum ? pMatEnum->EnumPhysMaterial(mt.szPhysMtl) : 0;
|
|
else
|
|
rMaterial.iGameID = 0;
|
|
}
|
|
rMaterial = rMaterial;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (nError!= MCLE_IgnoredType)
|
|
g_GetLog()->LogError ("cannot read material chunk 0x%X", pMaterialChunk->ChunkID);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// loads the bone name list into m_arrGeomBoneNameTable
|
|
// m_arrGeomBoneNameTable points to the chunk data places where the actual names are; so no need to deallocate the strings
|
|
bool CryModelGeometryLoader::loadBoneNameList (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
|
{
|
|
bool bOk = LoadBoneNameList (chunkHeader, pChunk, nChunkSize, m_arrGeomBoneNameTable);
|
|
if (!bOk)
|
|
return false;
|
|
|
|
if (m_nLOD> 0)
|
|
{
|
|
if (m_arrGeomBoneNameTable.size() != m_pModel->numBoneInfos())
|
|
{
|
|
g_GetLog()->LogError ("lod %d file %s has inconsistent number of bones (%d, expected %d). Please re-export the lod", m_nLOD, getFilePathCStr(), m_arrGeomBoneNameTable.size(), m_pModel->numBoneInfos());
|
|
|
|
char str[256];
|
|
|
|
if(g_GetISystem()->GetSSFileInfo(m_pModel->m_pBody->GetFilePathCStr(),str,256))
|
|
g_GetLog()->LogToFile (" %s",str);
|
|
|
|
//return true; // we only load the table of bone names for lod 0
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// loads the bone light binding array
|
|
bool CryModelGeometryLoader::loadBoneLightBinding (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
|
{
|
|
const BONELIGHTBINDING_CHUNK_DESC_0001* pBLBChunk = (const BONELIGHTBINDING_CHUNK_DESC_0001*)pChunk;
|
|
m_pBoneLightBind = (const SBoneLightBind*)(pBLBChunk+1);
|
|
m_numBoneLightBinds = pBLBChunk->numBindings;
|
|
|
|
if (((const char*)pBLBChunk) + nChunkSize > (const char*)(m_pBoneLightBind + m_numBoneLightBinds))
|
|
{
|
|
g_GetLog()->LogError ("\003BoneLightBinding chunk in %s is truncated", m_pModel->m_pBody->GetNameCStr());
|
|
m_numBoneLightBinds = 0;
|
|
m_pBoneLightBind = NULL;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CryModelGeometryLoader::loadBoneInitialPos (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
|
{
|
|
const BONEINITIALPOS_CHUNK_DESC_0001* pBIPChunk = (const BONEINITIALPOS_CHUNK_DESC_0001*)pChunk;
|
|
if (chunkHeader.ChunkVersion != pBIPChunk->VERSION)
|
|
{
|
|
g_GetLog()->LogError ("\003BoneInitialPos chunk is of unknown version %d", chunkHeader.ChunkVersion);
|
|
return false; // unrecognized version
|
|
}
|
|
|
|
unsigned numBytesLoaded = m_BoneLoader.load (pBIPChunk, nChunkSize);
|
|
if (!numBytesLoaded)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// returns true if the bone infos have the initial position set
|
|
bool CryModelGeometryLoader::hasBoneInfoInitPos()
|
|
{
|
|
return m_BoneLoader.hasInitPos();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// for LODs > 0, builds the m_arrTempBoneIdToIndex by matching the bone names
|
|
// against the bone names in the already loaded LOD 0
|
|
void CryModelGeometryLoader::buildBoneIndicesByNames()
|
|
{
|
|
// We don't keep the bone id map in the model anymore - so we map ids by names anyway
|
|
assert (m_nLOD > 0 && m_arrTempBoneIdToIndex.empty());
|
|
|
|
// since the bone map is not native to this file, map the bone indices by names:
|
|
// take the names of bones in the LOD file and try to match them against the bone names
|
|
// in the master file (lod0). For those without corresponding names, set the default of 0
|
|
// This is used to map the LOD-bone-id to the Master-bone-index
|
|
m_arrTempBoneIdToIndex.reinit((unsigned)m_arrGeomBoneNameTable.size(), 0);
|
|
// TODO: for performance, we could once build a map name->index
|
|
for (unsigned i = 0; i < m_arrGeomBoneNameTable.size(); ++i)
|
|
{
|
|
int nBone = m_pModel->findBone (m_arrGeomBoneNameTable[i]);
|
|
if (nBone >= 0)
|
|
m_arrTempBoneIdToIndex[i] = nBone;
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// load the stuff that doesn't need anything else to be already loaded:
|
|
// + load bone name table,
|
|
// + for LOD>0, associate the bones with bone indices
|
|
// + load bone-light binding table,
|
|
// + load light and node maps,
|
|
// + calculate the number of morph targets
|
|
bool CryModelGeometryLoader::loadStage1()
|
|
{
|
|
int i;
|
|
for (i = 0; i < m_pReader->numChunks(); i++)
|
|
{
|
|
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
|
const void* pChunk = m_pReader->getChunkData(i);
|
|
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
|
switch (chunkHeader.ChunkType)
|
|
{
|
|
case ChunkType_BoneNameList:
|
|
if (!loadBoneNameList(chunkHeader, pChunk, nChunkSize))
|
|
return false;
|
|
if (m_nLOD > 0)
|
|
// if the LOD0 already was loaded, then we should find the index of each existing bone by name
|
|
buildBoneIndicesByNames();
|
|
break;
|
|
|
|
case ChunkType_BoneLightBinding:
|
|
if (!loadBoneLightBinding(chunkHeader, pChunk, nChunkSize))
|
|
return false;
|
|
break;
|
|
|
|
case ChunkType_BoneInitialPos:
|
|
if (!loadBoneInitialPos(chunkHeader, pChunk, nChunkSize))
|
|
return false;
|
|
// we should scale the initial position in case we use it for bone rest pose initialization
|
|
m_BoneLoader.scale (m_fScale);
|
|
break;
|
|
|
|
case ChunkType_MeshMorphTarget:
|
|
if (chunkHeader.ChunkVersion == MESHMORPHTARGET_CHUNK_DESC_0001::VERSION)
|
|
++m_numMorphTargets;
|
|
break;
|
|
|
|
case ChunkType_Light:
|
|
{
|
|
const LIGHT_CHUNK_DESC* pLight = (const LIGHT_CHUNK_DESC*)pChunk;
|
|
m_mapLights[chunkHeader.ChunkID] = pLight;
|
|
}
|
|
break;
|
|
|
|
case ChunkType_Node:
|
|
{
|
|
const NODE_CHUNK_DESC* pNode = (const NODE_CHUNK_DESC*)pChunk;
|
|
m_mapObjectNodes[pNode->ObjectID] = pNode;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// loads the stuff that needs the stuff loaded at stage 1
|
|
bool CryModelGeometryLoader::loadStage2()
|
|
{
|
|
//check chunks
|
|
for (int i = 0; i < m_pReader->numChunks(); i++)
|
|
{
|
|
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
|
const void* pChunk = m_pReader->getChunkData(i);
|
|
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
|
|
|
switch (chunkHeader.ChunkType)
|
|
{
|
|
case ChunkType_Mesh:
|
|
if (!loadMesh (chunkHeader, pChunk, nChunkSize))
|
|
return false;
|
|
break;
|
|
|
|
case ChunkType_BoneAnim:
|
|
if (!loadBoneAnim (chunkHeader, pChunk, nChunkSize))
|
|
return false;
|
|
break;
|
|
|
|
case ChunkType_BoneMesh:
|
|
if (!loadBoneMesh (chunkHeader, pChunk, nChunkSize))
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
if (!m_bGeometryFound)
|
|
{
|
|
g_GetLog()->LogToFile("File %s contains no geometry", getFilePathCStr());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// loads the morph targets - after the other things are known
|
|
bool CryModelGeometryLoader::loadMorphTargets()
|
|
{
|
|
if (m_nLOD > 0) // we don't support the LOD>0 morph targets yet
|
|
return true;
|
|
|
|
m_pModel->reinitMorphTargets (m_numMorphTargets, m_pModel->numLODs());
|
|
// prepare to load the morph targets
|
|
m_numMorphTargets = 0; // the morph target currently being loaded
|
|
|
|
// load the morph targets
|
|
for (int i = 0; i < m_pReader->numChunks(); i++)
|
|
{
|
|
const CHUNK_HEADER& chunkHeader = m_pReader->getChunkHeader(i);
|
|
const void* pChunk = m_pReader->getChunkData(i);
|
|
unsigned nChunkSize = m_pReader->getChunkSize(i);
|
|
switch (chunkHeader.ChunkType)
|
|
{
|
|
case ChunkType_MeshMorphTarget:
|
|
if (chunkHeader.ChunkVersion == MESHMORPHTARGET_CHUNK_DESC_0001::VERSION)
|
|
{
|
|
const MESHMORPHTARGET_CHUNK_DESC_0001* pMeshMorphTarget = (const MESHMORPHTARGET_CHUNK_DESC_0001*)pChunk;
|
|
if (pMeshMorphTarget->nChunkIdMesh == m_nGeometryChunkID)
|
|
{
|
|
CryGeomMorphTarget& rMorphTarget = m_pModel->m_arrMorphTargets[m_numMorphTargets];
|
|
if (rMorphTarget.load (m_nLOD,pMeshMorphTarget, nChunkSize) == nChunkSize)
|
|
{
|
|
// rotate the object in the world coordinates for the rest
|
|
const NODE_CHUNK_DESC* pNodeChunk = find_in_map(m_mapObjectNodes, m_nGeometryChunkID, (const NODE_CHUNK_DESC *)NULL);
|
|
if (pNodeChunk)
|
|
{
|
|
Matrix44 tm = pNodeChunk->tm;
|
|
tm *= m_fScale;
|
|
rMorphTarget.transform (m_nLOD, tm);
|
|
//rMorphTarget.scale(m_nLOD, scale);
|
|
}
|
|
else
|
|
rMorphTarget.scale (m_nLOD, m_fScale);
|
|
|
|
++m_numMorphTargets;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// clean up the unused morph targets
|
|
if (m_pModel->m_arrMorphTargets.size() != m_numMorphTargets)
|
|
m_pModel->m_arrMorphTargets.resize (m_numMorphTargets); // impossible situation in the current conditions of one mesh per file
|
|
|
|
if (!m_bBonesFound && !m_numMorphTargets && m_nLOD == 0)
|
|
{
|
|
// if there's no bones, then it's an error: the mesh without bones should not go through the Animation module
|
|
g_GetLog()->LogWarning ("\003CryModel::LoadGeomChunks: Neither bones nor morph targets found in %s", m_pModel->m_pBody->GetNameCStr());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
// corrects the bone ids in the geometry info according to the new numeration of the bones
|
|
bool CryModelGeometryLoader::rebindBoneIndices()
|
|
{
|
|
if (!m_arrGeomBoneNameTable.empty())
|
|
m_pModel->getGeometryInfo(m_nLOD)->remapBoneIds(&m_arrTempBoneIdToIndex[0], (unsigned)m_arrTempBoneIdToIndex.size());
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
bool CryModelGeometryLoader::finalize()
|
|
{
|
|
if (!m_nLOD)
|
|
{
|
|
m_pModel->m_pDefaultModelState->m_bHasPhysics = false;
|
|
// build the bone index by name and call PostInitialize for bone infos
|
|
m_pModel->onBonesChanged();
|
|
}
|
|
if (m_nLOD < 2)
|
|
{
|
|
// change the pPhysGeom from indices (chunk ids) to the actual physical geometry pointers
|
|
// This modifies the given map by deleting or zeroing elements of the map that were used during the mapping
|
|
if (m_pModel->PostInitBonePhysGeom (m_mapLimbPhysGeoms, m_nLOD))
|
|
{
|
|
m_pModel->m_pDefaultModelState->m_bHasPhysics = true;
|
|
m_pModel->onBonePhysicsChanged();
|
|
}
|
|
}
|
|
|
|
// clean up the physical geometry objects that were not used
|
|
for (ChunkIdToPhysGeomMap::iterator it = m_mapLimbPhysGeoms.begin(); it != m_mapLimbPhysGeoms.end(); ++it)
|
|
{
|
|
phys_geometry* pPhysGeom = it->second;
|
|
if (pPhysGeom)
|
|
GetPhysicalWorld()->GetGeomManager()->UnregisterGeometry(pPhysGeom);
|
|
}
|
|
m_mapLimbPhysGeoms.clear();
|
|
|
|
if(m_pModel->m_arrDamageTable.empty() && m_nLOD == 0 && !m_arrGeomBoneNameTable.empty())
|
|
{
|
|
m_pModel->m_arrDamageTable.reinit(m_arrGeomBoneNameTable.size());
|
|
m_pModel->InitDamageTable();
|
|
}
|
|
|
|
// there must be at least one shader for the object; create a default material/shader if there are no materials
|
|
if(m_pModel->m_arrMaterials.empty())
|
|
{ // make some default material
|
|
g_GetLog()->LogWarning ("\002CryModel::LoadGeomChunks: Materials not found in %s", getFilePathCStr());
|
|
MAT_ENTITY me;
|
|
memset(&me, 0, sizeof(MAT_ENTITY));
|
|
strcpy(me.map_d.name, "default");
|
|
m_pModel->m_arrMaterials.push_back(me);
|
|
}
|
|
|
|
// fix the wrong mat id's
|
|
m_pModel->getGeometryInfo(m_nLOD)->limitMaterialIDs((unsigned)m_pModel->m_arrMaterials.size());
|
|
|
|
// add the light bindings to the internal array
|
|
std::vector<CBoneLightBindInfo> arrBoneLights;
|
|
arrBoneLights.reserve (m_numBoneLightBinds);
|
|
for (unsigned i = 0; i < m_numBoneLightBinds; ++i)
|
|
{
|
|
SBoneLightBind binding = m_pBoneLightBind[i];
|
|
|
|
if (binding.nBoneId >= m_arrTempBoneIdToIndex.size())
|
|
{
|
|
g_GetLog()->LogError ("\002CryModel::LoadGeomChunks: invalid light-bone binding in the file for bone id %d, light 0x%08X. Bone id is out of range.", binding.nBoneId, binding.nLightChunkId);
|
|
continue;
|
|
}
|
|
binding.nBoneId = m_arrTempBoneIdToIndex[binding.nBoneId];
|
|
|
|
// find the light chunk and the chunk of the node of that light
|
|
const LIGHT_CHUNK_DESC* pLightChunk = find_in_map (m_mapLights, binding.nLightChunkId, (const LIGHT_CHUNK_DESC *)NULL);
|
|
// the node chunk is required to determine the light's transformation
|
|
// (it's stored in the light's node)
|
|
const NODE_CHUNK_DESC* pNodeChunk = find_in_map(m_mapObjectNodes, binding.nLightChunkId, (const NODE_CHUNK_DESC *)NULL);
|
|
|
|
if (!pLightChunk)
|
|
{
|
|
g_GetLog()->LogError ("\002invalid light-bone binding in the file for bone id %d, light 0x%08X", binding.nBoneId, binding.nLightChunkId);
|
|
continue;
|
|
}
|
|
|
|
if (!pNodeChunk)
|
|
{
|
|
g_GetLog()->LogWarning ("\002there is bone light in the file, but there is no Node for it. Unknown light name. Assuming a heat source. Bone id %d, light 0x%08X", binding.nBoneId, binding.nLightChunkId);
|
|
}
|
|
|
|
arrBoneLights.resize (arrBoneLights.size()+1);
|
|
arrBoneLights.back().load (binding, *pLightChunk, pNodeChunk?pNodeChunk->name:"_hs_Unknown", m_fScale);
|
|
}
|
|
|
|
m_pModel->addBoneLights(arrBoneLights);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CryModelGeometryLoader::loadMesh (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
|
{
|
|
m_bGeometryFound = true;
|
|
m_nGeometryChunkID = chunkHeader.ChunkID;
|
|
if(m_bMeshFound)
|
|
{
|
|
g_GetLog()->LogToFile ("\003CryModel::LoadGeomChunks: more than one geom found (not supported) in %s", m_pModel->m_pBody->GetNameCStr());
|
|
return false;
|
|
}
|
|
|
|
const MESH_CHUNK_DESC_0744* pMeshChunk = (const MESH_CHUNK_DESC_0744*)pChunk;
|
|
if (pMeshChunk->chdr.ChunkVersion == MESH_CHUNK_DESC_0744::VERSION)
|
|
{
|
|
// read the geometry info into the corresponding lod
|
|
CryGeometryInfo* pGeomInfo = m_pModel->getGeometryInfo(m_nLOD);
|
|
unsigned nReadData = pGeomInfo->Load (m_nLOD, pMeshChunk, nChunkSize);
|
|
if (!pGeomInfo->numLinks() || m_arrGeomBoneNameTable.empty())
|
|
{
|
|
// rotate the object in the world coordinates for the rest
|
|
const NODE_CHUNK_DESC* pNodeChunk = find_in_map(m_mapObjectNodes, m_nGeometryChunkID,(const NODE_CHUNK_DESC *)NULL);
|
|
if (pNodeChunk)
|
|
{
|
|
Matrix44 tm = pNodeChunk->tm;
|
|
tm *= m_fScale;
|
|
pGeomInfo->transform (tm);
|
|
//pGeomInfo->Scale (scale);
|
|
}
|
|
else
|
|
pGeomInfo->Scale (m_fScale);
|
|
}
|
|
else
|
|
pGeomInfo->Scale (m_fScale);
|
|
|
|
|
|
// check if we read the whole chunk
|
|
if (nReadData != nChunkSize)
|
|
g_GetLog()->LogToFile ("\003CryModel::LoadGeomChunks: unexpected geometry chunk size: read %d bytes of %d", nReadData, nChunkSize);
|
|
if (nReadData == 0)
|
|
return false;
|
|
|
|
// check if we read the bone info
|
|
if (pGeomInfo->numLinks() == 0 && m_numMorphTargets == 0)
|
|
{
|
|
// Mesh without bone info should not go through the Animation module
|
|
g_GetLog()->LogWarning ("\003CryModel::LoadGeomChunks: the mesh doesn't have any bone information.");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_GetLog()->LogError ("\003CryModel::LoadGeomChunks: old file format, mesh chunk version 0x%08X (expected 0x%08X)",pMeshChunk->chdr.ChunkVersion, MESH_CHUNK_DESC_0744::VERSION);
|
|
return false;
|
|
}
|
|
if (g_GetCVars()->ca_Debug())
|
|
g_GetLog()->LogToFile ("\005 %d faces in lod %d", m_pModel->getGeometryInfo(m_nLOD)->numFaces(), m_nLOD);
|
|
|
|
m_bMeshFound = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
bool CryModelGeometryLoader::loadBoneAnim (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
|
{
|
|
if (!m_bBonesFound)
|
|
{
|
|
switch(m_nLOD)
|
|
{
|
|
case 0:// load bones structure
|
|
if (!loadBones (static_cast<const BONEANIM_CHUNK_DESC*>(pChunk), nChunkSize))
|
|
return false; // failed to load bones
|
|
m_pModel->m_pDefaultModelState->CreateBones();
|
|
m_bBonesFound = true;
|
|
break;
|
|
case 1:
|
|
m_pModel->UpdateRootBonePhysics (static_cast<const BONEANIM_CHUNK_DESC*>(pChunk), nChunkSize, m_nLOD);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_GetLog()->LogWarning ("\003multiple bone animation chunks found; ignoring");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// loads the root bone (and the hierarchy) and returns true if loaded successfully
|
|
// this gets called only for LOD 0
|
|
bool CryModelGeometryLoader::loadBones (const BONEANIM_CHUNK_DESC* pChunk, unsigned nChunkSize)
|
|
{
|
|
if (pChunk->nBones != m_arrGeomBoneNameTable.size())
|
|
{
|
|
g_GetLog()->LogToFile ("\002Number of bones does not match in the bone hierarchy chunk (%d) and the bone name chunk (%d)", pChunk->nBones, m_arrGeomBoneNameTable.size());
|
|
return false;
|
|
}
|
|
|
|
unsigned numBytesRead = m_BoneLoader.load(pChunk, nChunkSize);
|
|
if (!numBytesRead)
|
|
{
|
|
g_GetLog()->LogError ("\002CryModelGeometryLoader::loadBones: cannot load the boneanimation chunk");
|
|
return false;
|
|
}
|
|
|
|
if (numBytesRead != nChunkSize)
|
|
g_GetLog()->LogWarning ("\002CryModelGeometryLoader::loadBones: bone animation chunk is too big (%u, expected %u). Ignoring the extra bytes.", numBytesRead, nChunkSize);
|
|
|
|
m_pModel->m_arrBones.resize (pChunk->nBones);
|
|
for (int nBone = 0; nBone < pChunk->nBones; ++nBone)
|
|
m_pModel->m_arrBones[nBone] = m_BoneLoader.getBoneByIndex(nBone);
|
|
|
|
unsigned numBones = m_pModel->numBoneInfos();
|
|
m_arrTempBoneIdToIndex.reinit (numBones, -1);
|
|
// fill in names of the bones, (limb ids?)
|
|
for (unsigned nBone = 0; nBone < numBones; ++nBone)
|
|
{
|
|
unsigned nBoneId = (unsigned)m_BoneLoader.mapIndexToId(nBone);
|
|
m_pModel->getBoneInfo(nBone).setName(m_arrGeomBoneNameTable[nBoneId]);
|
|
m_arrTempBoneIdToIndex[nBoneId] = nBone;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool CryModelGeometryLoader::loadBoneMesh (const CHUNK_HEADER& chunkHeader, const void* pChunk, unsigned nChunkSize)
|
|
{ // load bones geometry
|
|
|
|
const MESH_CHUNK_DESC_0744* pMeshChunk = (const MESH_CHUNK_DESC_0744*)pChunk;
|
|
if (pMeshChunk->chdr.ChunkVersion != MESH_CHUNK_DESC_0744::VERSION)
|
|
{
|
|
g_GetLog()->LogError ("\002CryModel::LoadGeomChunks: old file format, mesh chunk version 0x%08X (expected 0x%08X)",pMeshChunk->chdr.ChunkVersion, MESH_CHUNK_DESC_0744::VERSION);
|
|
return false;
|
|
}
|
|
|
|
// load the geometry from the chunk
|
|
CryGeometryInfo GeomInfo;
|
|
unsigned nReadData = GeomInfo.Load (2, pMeshChunk, nChunkSize);
|
|
if (nReadData != nChunkSize)
|
|
g_GetLog()->LogError ("\002CryModel::LoadGeomChunks: unexpected bone mesh chunk size: read %d bytes of %d", nReadData, nChunkSize);
|
|
if (nReadData != 0) // to account for a lot of broken mesh chunks; <<TODO>> change this line to "else" when all cgf are not broken
|
|
{
|
|
GeomInfo.Scale(m_fScale);
|
|
TElementaryArray<vectorf> points ("CryModelGeometryLoader::loadBoneMesh.points");
|
|
points.reinit(GeomInfo.numVertices());
|
|
TElementaryArray<int> indices ("CryModelGeometryLoader::loadBoneMesh.indices");
|
|
indices.reinit(GeomInfo.numFaces()*3);
|
|
TElementaryArray<unsigned char> pmats ("CryModelGeometryLoader::loadBoneMesh.pmats");
|
|
pmats.reinit(GeomInfo.numFaces());
|
|
|
|
unsigned j, k;
|
|
for (j = 0; j < GeomInfo.numVertices(); ++j)
|
|
points[j] = GeomInfo.getVertex(j);
|
|
|
|
for (j = 0; j < GeomInfo.numFaces(); ++j)
|
|
{
|
|
GeomMtlID nMtlID = GeomInfo.getFaceMtl(j);
|
|
const GeomFace& face = GeomInfo.getFace(j);
|
|
for (k = 0; k < 3; ++k)
|
|
indices[j*3+k] = face[k];
|
|
|
|
if( nMtlID >= 0 &&
|
|
(unsigned)nMtlID + m_nFirstMat < m_pModel->numMaterials())
|
|
pmats[j] = m_pModel->getMaterial(nMtlID+m_nFirstMat).iGameID;
|
|
else
|
|
{
|
|
if(m_bFirstTimeWarningBoneGeometryMtl && nMtlID != 255 && nMtlID != -1) // 255/-1 are the no-materials
|
|
{
|
|
g_GetLog()->LogWarning ("\004CryModel::LoadGeomChunks: Bone geometry material id is invalid: %s", getFilePathCStr());
|
|
m_bFirstTimeWarningBoneGeometryMtl = false;
|
|
}
|
|
|
|
pmats[j] = 0;
|
|
}
|
|
}
|
|
|
|
if(m_pPhysicalGeometryManager)
|
|
{
|
|
// add a new entry to the map chunk->limb geometry, so that later it can be found by the bones
|
|
// during the post-initialization mapping of pPhysGeom from Chunk id to the actual geometry pointer
|
|
IGeometry* pPhysicalGeometry = m_pPhysicalGeometryManager->CreateMesh (
|
|
// hack: the interface method has short* parameter, that should actually be const void*, because it can accept both char* and short* arrays, depending on the flags
|
|
&points[0], &indices[0], (short*)&pmats[0],
|
|
(unsigned)GeomInfo.numFaces(),
|
|
(GeomInfo.numFaces()<=20 ? mesh_SingleBB : mesh_OBB|mesh_AABB)
|
|
| mesh_multicontact0 | mesh_uchar_ids | mesh_approx_box | mesh_approx_sphere | mesh_approx_cylinder
|
|
);
|
|
assert (pPhysicalGeometry);
|
|
m_mapLimbPhysGeoms[chunkHeader.ChunkID] =
|
|
m_pPhysicalGeometryManager->RegisterGeometry (pPhysicalGeometry, pmats.empty() ? 0 : pmats[0]);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// returns the file path tot he file being currently loaded
|
|
const char* CryModelGeometryLoader::getFilePathCStr()const
|
|
{
|
|
return m_pModel->m_pBody->GetNameCStr();
|
|
}
|
|
|
|
void CryModelGeometryLoader::indicateProgress(const char*szMsg)
|
|
{
|
|
//g_GetLog()->UpdateLoadingScreenPlus(/*szMsg?szMsg:*/"\003.");
|
|
}
|