1674 lines
53 KiB
C++
1674 lines
53 KiB
C++
#include "stdafx.h"
|
|
#include "cvars.h"
|
|
#include <MakMatInfoFromMAT_ENTITY.h>
|
|
#include <MeshIdx.h>
|
|
#include <CryCompiledFile.h>
|
|
#include <IEdgeConnectivityBuilder.h>
|
|
#include "CryCharReShadowVolume.h"
|
|
#include "CryModelState.h"
|
|
#include "VertexBufferArrayDrivers.h"
|
|
#include "CryModel.h"
|
|
#include "CryCharBody.h"
|
|
#include "CryCharDecalManager.h"
|
|
#include "CryModelSubmesh.h"
|
|
#include <VertexBufferSource.h>
|
|
#include "IncContHeap.h"
|
|
#include "CryModEffMorph.h"
|
|
#include "CrySkinMorph.h"
|
|
#include "CrySkinFull.h"
|
|
#include "CryCharInstance.h"
|
|
#include "DebugUtils.h"
|
|
#include "Cry_Camera.h"
|
|
|
|
// initializes and binds the submesh to the given model
|
|
// there's no way to change the model at runtime
|
|
CryModelSubmesh::CryModelSubmesh (CryModelState* pParent, CryModel* pMesh):
|
|
#ifdef DEBUG_STD_CONTAINERS
|
|
m_arrMorphEffectors ("CryModelState.MorphTargets"),
|
|
#endif
|
|
m_pParent(pParent),
|
|
m_pDecalManager (NULL),
|
|
m_nLastSkinBBoxUpdateFrameId(0),
|
|
m_nLastTangentsUpdatedFrameId(0),
|
|
m_nLastTangentsUpdatedLOD(-1),
|
|
m_pMesh(pMesh),
|
|
m_nFlags (FLAG_DEFAULT_FLAGS)
|
|
{
|
|
// the submeshes that are not the default model submeshes lock their corresponding
|
|
// CryModels. The default model can't lock its parent CryModel, because it will
|
|
// be circular dependency. Owner ship is as follows: States->CryModel->Default State
|
|
|
|
// It's for sure that the default model state is being created if there's none in the mesh yet.
|
|
if (pParent != pMesh->m_pDefaultModelState && NULL != pMesh->m_pDefaultModelState)
|
|
pMesh->m_pBody->AddRef();
|
|
|
|
for (unsigned i = 0; i < SIZEOF_ARRAY(m_pLeafBuffers); ++i)
|
|
m_pLeafBuffers[i] = NULL;
|
|
if (pMesh->m_pDefaultModelState != pParent && pMesh->m_pDefaultModelState)
|
|
CopyLeafBuffers(pMesh->m_pDefaultModelState->GetCryModelSubmesh(0)->m_pLeafBuffers);
|
|
|
|
list2<CMatInfo>* pMats = getLeafBufferMaterials();
|
|
|
|
if (pMats)
|
|
{
|
|
getShaderTemplates(0).reinit(pMats->Count(), -1);
|
|
getShaderTemplates(1).reinit(pMats->Count(), -1);
|
|
}
|
|
|
|
m_nLastUpdatedLodLevel = -1;
|
|
for (int i = 0; i < g_nMaxLods; ++i)
|
|
{
|
|
m_nLastSkinnedFrameID[i] = -1;
|
|
m_nLastTangedFrameID[i] = -1;
|
|
}
|
|
}
|
|
|
|
// this is the array that's returned from the LeafBuffer
|
|
list2<CMatInfo>* CryModelSubmesh::getLeafBufferMaterials()
|
|
{
|
|
if (m_pLeafBuffers[0])
|
|
return m_pLeafBuffers[0]->m_pMats;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
CryModelSubmesh::~CryModelSubmesh()
|
|
{
|
|
#ifdef UNIQUE_VERT_BUFF_PER_INSTANCE
|
|
DeleteLeafBuffers();
|
|
#endif
|
|
// if the vertex buffers are not unique per modelstate, then they're deleted by the CryModel destructor
|
|
if (m_pDecalManager)
|
|
{
|
|
delete m_pDecalManager;
|
|
m_pDecalManager = NULL;
|
|
}
|
|
|
|
// the submeshes that are not the default model submeshes lock their corresponding
|
|
// CryModels. The default model can't lock its parent CryModel, because it will
|
|
// be circular dependency. Owner ship is as follows: States->CryModel->Default State
|
|
if (m_pParent != m_pMesh->m_pDefaultModelState)
|
|
m_pMesh->m_pBody->Release();
|
|
}
|
|
|
|
void CryModelSubmesh::CopyLeafBuffers (CLeafBuffer** pLeafBuffers)
|
|
{
|
|
// copy leaf buffers
|
|
for(unsigned nLod=0; nLod < m_pMesh->numLODs(); nLod++)
|
|
{
|
|
assert(pLeafBuffers[nLod]);
|
|
#ifdef UNIQUE_VERT_BUFF_PER_INSTANCE
|
|
CLeafBuffer* pSrcLeafBuffer = pLeafBuffers[nLod];
|
|
CLeafBuffer* pNewLeafBuffer = g_GetIRenderer()->CreateLeafBuffer(eBT_Dynamic);//new CLeafBuffer;
|
|
pSrcLeafBuffer->CopyTo(pNewLeafBuffer, false);
|
|
m_pLeafBuffers[nLod] = pNewLeafBuffer;
|
|
#else
|
|
m_pLeafBuffers[nLod] = pLeafBuffers[nLod];
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
// returns the model of the submesh, or NULL in case of failure
|
|
ICryCharModel* CryModelSubmesh::GetModel ()
|
|
{
|
|
return m_pMesh->m_pBody;
|
|
}
|
|
|
|
// Disposes the leaf buffers allocated for
|
|
void CryModelSubmesh::DeleteLeafBuffers()
|
|
{
|
|
for(unsigned i=0; i<m_pMesh->numLODs(); i++)
|
|
{
|
|
if(!m_pLeafBuffers[i])
|
|
return;
|
|
|
|
if(list2<CMatInfo>* &pMats = m_pLeafBuffers[i]->m_pMats)
|
|
{
|
|
for (int m=0; m<pMats->Count(); m++)
|
|
{
|
|
CMatInfo *tmp = pMats->Get(m);
|
|
SAFE_RELEASE(tmp->pRE);
|
|
SShaderItem Sh = tmp->GetShaderItem();
|
|
SAFE_RELEASE(Sh.m_pShader);
|
|
SAFE_RELEASE(Sh.m_pShaderResources);
|
|
}
|
|
|
|
// during GenerateRenderArrays, we allocate this
|
|
if (m_pParent == m_pMesh->m_pDefaultModelState && m_pMesh->m_bDeleteLBMats)
|
|
{
|
|
assert (m_pParent->GetCryModelSubmesh(0) == this);
|
|
delete pMats;
|
|
pMats = NULL;
|
|
}
|
|
}
|
|
|
|
g_GetIRenderer()->DeleteLeafBuffer(m_pLeafBuffers[i]);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Generation of the leaf buffers for the default model state.
|
|
// Done only once - when the default model state is initialized.
|
|
// szFileName is the full path to the file
|
|
void CryModelSubmesh::GenerateRenderArrays(const char * szFileName)
|
|
{
|
|
AUTO_PROFILE_SECTION(g_dTimeGenRenderArrays);
|
|
for(unsigned nLod = 0; nLod < m_pMesh->numLODs(); nLod++)
|
|
{
|
|
CryGeometryInfo * pGeomInfo = m_pMesh->getGeometryInfo(nLod);
|
|
|
|
if(!pGeomInfo->numVertices())
|
|
break;
|
|
|
|
|
|
// LeafBuffer generation:
|
|
// Allocate (CreateLeafBuffer) the leaf buffer from the renderer;
|
|
// Initialize (CreateBuffer) it from IndexedMesh generated out of GeomInfo
|
|
// Initialize the material list for the leaf buffer, and copy some material properties from
|
|
// the CryModel
|
|
// Sort the faces in the GeomInfo to synchronize their indices with those in the leaf buffer
|
|
{
|
|
CLeafBuffer * pVertBuffer =
|
|
m_pLeafBuffers[nLod] =
|
|
g_GetIRenderer()->CreateLeafBuffer(eBT_Dynamic, "CryModelArray");
|
|
|
|
CIndexedMesh * pIndexedMesh = pGeomInfo->new3DEngineIndexedMesh();
|
|
pGeomInfo->removeUVs(); // we won't need them any more
|
|
|
|
std::vector<bool> arrMtlUsage;
|
|
arrMtlUsage.resize (m_pMesh->numMaterials(), false);
|
|
// [Sergiy] HACK Sort faces by material to synchronize with the actual leaf buffer
|
|
// mark the used materials
|
|
pGeomInfo->sortFacesByMaterial(arrMtlUsage);
|
|
{
|
|
AUTO_PROFILE_SECTION(g_dTimeShaderLoad);
|
|
|
|
CMatInfo defaultMaterial;
|
|
|
|
//calculate the index of max actually used material
|
|
unsigned nMaterial, numUsedMtls = 0;
|
|
for (nMaterial = 0; nMaterial < arrMtlUsage.size(); ++nMaterial)
|
|
if (arrMtlUsage[nMaterial])
|
|
numUsedMtls = nMaterial + 1;
|
|
|
|
//g_GetLog()->LogPlus("\005%d of %d mtls used", numUsedMtls, arrMtlUsage.size());
|
|
|
|
// make material table with clean elements
|
|
list2<CMatInfo>& arrMats = *(pVertBuffer->m_pMats = new list2<CMatInfo>);
|
|
arrMats.resize(numUsedMtls);
|
|
|
|
for (nMaterial = 0; nMaterial < numUsedMtls; ++nMaterial)
|
|
{
|
|
CMatInfo& rMaterial = arrMats[nMaterial];
|
|
// this is the hack that's required because list2 doesn't construct the elements: to copy the vtable
|
|
*(unsigned int*)&rMaterial = *(unsigned int*)&defaultMaterial;
|
|
|
|
if (arrMtlUsage[nMaterial])
|
|
{
|
|
rMaterial.m_Id = nMaterial;
|
|
|
|
CIndexedMesh__LoadMaterial (NULL, CryStringUtils::GetParentDirectory(string(szFileName)).c_str(), rMaterial, g_GetIRenderer(), &m_pMesh->getMaterial(nMaterial));
|
|
|
|
if (rMaterial.pRE)
|
|
rMaterial.pRE->m_Flags |= FCEF_DYNAMIC;
|
|
}
|
|
else
|
|
rMaterial = defaultMaterial;
|
|
}
|
|
}
|
|
pVertBuffer->CreateBuffer (pIndexedMesh, g_GetCVars()->ca_StripifyGeometry()?true:false,
|
|
false); // do not remove any geometry because it will destroy mapping of animation data to leaf buffer data
|
|
delete pIndexedMesh;
|
|
|
|
}
|
|
|
|
//assert (m_pLeafBuffers[nLod]->m_pMats->size() == m_pMesh->numMaterials());
|
|
|
|
{
|
|
// this is for the new skinning pass
|
|
pGeomInfo->PrepareExtToIntMap (m_pLeafBuffers[nLod]->m_SecVertCount);
|
|
|
|
pGeomInfo->initExtUVs (CVertexBufferUVArrayDriver (m_pLeafBuffers[nLod]));
|
|
|
|
ushort * pInds = m_pLeafBuffers[nLod]->m_pIndicesPreStrip->Get(0);
|
|
uint * pVertReMaping = m_pLeafBuffers[nLod]->m_arrVertStripMap;
|
|
|
|
for (unsigned i=0; i<pGeomInfo->numFaces(); i++)
|
|
{
|
|
if( pInds[0] >= m_pLeafBuffers[nLod]->m_SecVertCount ||
|
|
pInds[1] >= m_pLeafBuffers[nLod]->m_SecVertCount ||
|
|
pInds[2] >= m_pLeafBuffers[nLod]->m_SecVertCount )
|
|
{
|
|
g_GetConsole()->Exit("CryModelState::GenerateRenderArrays: indices out of range: %s", szFileName);
|
|
break;
|
|
}
|
|
|
|
GeomFace face = pGeomInfo->getFace(i);
|
|
|
|
if(pVertReMaping)
|
|
{
|
|
for (int v = 0; v < 3; ++v)
|
|
{
|
|
assert(pVertReMaping[pInds[v]]<pGeomInfo->numExtToIntMapEntries());//flip
|
|
pGeomInfo->getExtToIntMapEntry(pVertReMaping[pInds[v]]) = face[v];//flip
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int v = 0; v < 3; ++v)
|
|
pGeomInfo->getExtToIntMapEntry(pInds[v]) = face[v];//flip
|
|
}
|
|
|
|
pInds += 3;
|
|
}
|
|
|
|
pGeomInfo->CreateIntToExtMap();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Creates Leaf buffers for all LODs
|
|
// szTextureDir is the texture directory used to create the shaders
|
|
void CryModelSubmesh::GenerateRenderArraysCCG(const char * szTextureDir)
|
|
{
|
|
AUTO_PROFILE_SECTION(g_dTimeGenRenderArrays);
|
|
|
|
for(unsigned nLod = 0; nLod < m_pMesh->numLODs(); nLod++)
|
|
{
|
|
CryGeometryInfo * pGeomInfo = m_pMesh->getGeometryInfo(nLod);
|
|
|
|
///////////
|
|
// construct the vertices for the renderer to calculate its stuff
|
|
//
|
|
CrySkinFull* pSkin = pGeomInfo->getGeomSkin();
|
|
g_Temp.reserve (sizeof(Vec3d)*(pGeomInfo->numUsedVertices()+pGeomInfo->numExtToIntMapEntries()));
|
|
Vec3d* pVertices = (Vec3d*)g_Temp.data();
|
|
pSkin->skin (m_pParent->getBoneGlobalMatrices(), pVertices);
|
|
Vec3d* pExtVertices = pVertices + pGeomInfo->numUsedVertices();
|
|
|
|
const unsigned* pExtToIntMap = pGeomInfo->getExtToIntMapEntries();
|
|
for (unsigned i = 0; i < pGeomInfo->numExtToIntMapEntries(); ++i)
|
|
pExtVertices[i] = pVertices[pExtToIntMap[i]];
|
|
|
|
CMatInfo defaultMaterial;
|
|
list2<CMatInfo>* pMats = new list2<CMatInfo>;
|
|
pMats->resize(m_pMesh->numMaterials());
|
|
|
|
{
|
|
AUTO_PROFILE_SECTION(g_dTimeShaderLoad);
|
|
|
|
for (unsigned nMaterial = 0; nMaterial < pMats->size(); ++nMaterial)
|
|
{
|
|
CMatInfo& rMaterial = (*pMats)[nMaterial];
|
|
// this is the hack that's required because list2 doesn't construct the elements: to copy the vtable
|
|
*(unsigned int*)&rMaterial = *(unsigned int*)&defaultMaterial;
|
|
rMaterial.m_Id = nMaterial;
|
|
|
|
CIndexedMesh__LoadMaterial (NULL, szTextureDir, rMaterial, g_GetIRenderer(), &m_pMesh->getMaterial(nMaterial));
|
|
|
|
if (rMaterial.pRE)
|
|
rMaterial.pRE->m_Flags |= FCEF_DYNAMIC;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
// Construct the Leaf Buffer
|
|
//assert (m_pMesh->m_arrShaders.size() == m_pMesh->m_arrMaterials.size());
|
|
m_pLeafBuffers[nLod] = g_GetIRenderer()->CreateLeafBuffer(eBT_Dynamic, "ModelState");
|
|
|
|
VertexBufferSource vbs;
|
|
vbs.numMaterials = (unsigned)m_pMesh->m_arrMaterials.size();
|
|
vbs.pMaterials = &m_pMesh->m_arrMaterials[0];
|
|
vbs.pShaders = NULL;
|
|
vbs.pMats = pMats;
|
|
vbs.numPrimGroups = (unsigned)pGeomInfo->m_arrPrimGroups.size();
|
|
vbs.pPrimGroups = &pGeomInfo->m_arrPrimGroups[0];
|
|
vbs.numIndices = (unsigned)pGeomInfo->m_arrIndices.size();
|
|
vbs.pIndices = &pGeomInfo->m_arrIndices[0];
|
|
vbs.numVertices = pGeomInfo->numExtToIntMapEntries();
|
|
vbs.pUVs = pGeomInfo->getExtUVs();
|
|
vbs.pVertices = pExtVertices;
|
|
vbs.nREFlags = FCEF_DYNAMIC;
|
|
vbs.pAndFlags = NULL;
|
|
vbs.pOrFlags = NULL;
|
|
|
|
m_pLeafBuffers[nLod]->CreateBuffer (&vbs);
|
|
}
|
|
}
|
|
|
|
void CryModelSubmesh::AddCurrentRenderData(CCObject *obj, CCObject *obj1, const SRendParams & rParams)
|
|
{
|
|
//g_GetLog()->LogToFile("\003 %d.rendering %x %s", g_nFrameID, this, m_pMesh->getFilePathCStr());
|
|
int nSort = rParams.nSortValue;
|
|
|
|
// prevent from render list changing
|
|
// todo: use define here
|
|
if (obj->m_ObjFlags & FOB_NEAREST)
|
|
nSort = eS_Nearest | (nSort & EFSLIST_MASK);
|
|
|
|
int nLod = m_pParent->m_nLodLevel;
|
|
obj->m_nLod = nLod;
|
|
if (obj1)
|
|
{
|
|
obj1->m_nLod = nLod;
|
|
//obj->m_ObjFlags |= FOB_REFRACTED; // moved from Game01
|
|
}
|
|
CLeafBuffer* pLeafBuffer = m_pLeafBuffers[nLod];
|
|
pLeafBuffer->m_vBoxMin = m_SubBBox.vMin;
|
|
pLeafBuffer->m_vBoxMax = m_SubBBox.vMax;
|
|
|
|
// m_pLeafBuffers[nLod]->SetRECustomData(&(m_arrHeatSourcePos[0].x));
|
|
|
|
int nTempl1;
|
|
|
|
CryGeometryInfo* pGeomInfo = m_pMesh->getGeometryInfo(nLod);
|
|
if (pGeomInfo->m_arrPrimGroups.empty())
|
|
{
|
|
// the old version : we don't have mapping between materials in mesh and materials in the Leaf Buffers
|
|
for(unsigned i=0; i<pLeafBuffer->m_pMats->size(); i++)
|
|
{
|
|
SShaderItem si = (*pLeafBuffer->m_pMats)[i].shaderItem;
|
|
// Override object material.
|
|
if (rParams.pMaterial)
|
|
{
|
|
rParams.pMaterial->OverrideShaderItem( i,si );
|
|
}
|
|
CREOcLeaf * pREOcLeaf = (*pLeafBuffer->m_pMats)[i].pRE;
|
|
|
|
if (si.m_pShader && i<(unsigned)pLeafBuffer->m_pMats->Count() && pREOcLeaf)
|
|
{
|
|
nTempl1 = -1;
|
|
int nTempl = rParams.nShaderTemplate;
|
|
if (nTempl == -2)
|
|
{
|
|
nTempl = getShaderTemplates(0)[i];
|
|
nTempl1 = getShaderTemplates(1)[i];
|
|
}
|
|
else
|
|
if (rParams.nShaderTemplate > 0)
|
|
{
|
|
int nTempl = rParams.nShaderTemplate;
|
|
si.m_pShader->AddTemplate(si.m_pShaderResources, nTempl, NULL);
|
|
}
|
|
if (nTempl > 0)
|
|
obj->m_nTemplId = nTempl;
|
|
g_GetIRenderer()->EF_AddEf(rParams.nFogVolumeID, pREOcLeaf, si.m_pShader, si.m_pShaderResources, obj, nTempl, m_pParent->m_pShaderStateCull?m_pParent->m_pShaderStateCull: rParams.pStateShader , nSort);
|
|
if (nTempl1 > 0)
|
|
g_GetIRenderer()->EF_AddEf(rParams.nFogVolumeID, pREOcLeaf, si.m_pShader, si.m_pShaderResources, obj1, nTempl1, m_pParent->m_pShaderStateCull?m_pParent->m_pShaderStateCull:rParams.pStateShader , nSort);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the new version : we do have mapping between materials in mesh and materials in the Leaf Buffers
|
|
for (unsigned nPrimGroup=0; nPrimGroup<pGeomInfo->m_arrPrimGroups.size(); nPrimGroup++)
|
|
{
|
|
unsigned nMaterial = pGeomInfo->m_arrPrimGroups[nPrimGroup].nMaterial;
|
|
CMatInfo& mi = (*pLeafBuffer->m_pMats)[nMaterial];
|
|
CREOcLeaf * pREOcLeaf = mi.pRE;
|
|
SShaderItem si = mi.shaderItem;//m_pMesh->getShader(nMaterial);
|
|
|
|
// Override object material.
|
|
if (rParams.pMaterial)
|
|
{
|
|
// Assume that the root material is the first material, sub materials start from index 1.
|
|
if (nPrimGroup == 0)
|
|
si = rParams.pMaterial->GetShaderItem();
|
|
else if (nPrimGroup-1 < unsigned(rParams.pMaterial->GetSubMtlCount()))
|
|
{
|
|
si = rParams.pMaterial->GetSubMtl(nPrimGroup-1)->GetShaderItem();
|
|
}
|
|
}
|
|
|
|
assert (nPrimGroup<(unsigned)pLeafBuffer->m_pMats->size() && pREOcLeaf);
|
|
|
|
if (si.m_pShader)
|
|
{
|
|
nTempl1 = -1;
|
|
int nTempl = rParams.nShaderTemplate;
|
|
if (nTempl == -2)
|
|
{
|
|
nTempl = getShaderTemplates(0)[nPrimGroup];
|
|
nTempl1 = getShaderTemplates(1)[nPrimGroup];
|
|
}
|
|
else
|
|
if (rParams.nShaderTemplate > 0)
|
|
{
|
|
int nTempl = rParams.nShaderTemplate;
|
|
si.m_pShader->AddTemplate(si.m_pShaderResources, nTempl, NULL);
|
|
}
|
|
if (nTempl > 0)
|
|
obj->m_nTemplId = nTempl;
|
|
if (nTempl1 > 0)
|
|
obj1->m_nTemplId = nTempl1;
|
|
|
|
// vlad: set default sorting in case of cryvision when set as custom teplate
|
|
if(rParams.nShaderTemplate && si.m_pShader->GetTemplate(nTempl)->GetSort() == eS_HeatVision)
|
|
{
|
|
g_GetIRenderer()->EF_AddEf(rParams.nFogVolumeID, pREOcLeaf, si.m_pShader, si.m_pShaderResources, obj, nTempl, m_pParent->m_pShaderStateCull?m_pParent->m_pShaderStateCull:rParams.pStateShader);
|
|
continue;
|
|
}
|
|
|
|
g_GetIRenderer()->EF_AddEf(rParams.nFogVolumeID, pREOcLeaf, si.m_pShader, si.m_pShaderResources, obj, nTempl, m_pParent->m_pShaderStateCull?m_pParent->m_pShaderStateCull:rParams.pStateShader , nSort);
|
|
if (nTempl1 > 0)
|
|
g_GetIRenderer()->EF_AddEf(rParams.nFogVolumeID, pREOcLeaf, si.m_pShader, si.m_pShaderResources, obj1, nTempl1, m_pParent->m_pShaderStateCull?m_pParent->m_pShaderStateCull:rParams.pStateShader , nSort); }
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// renders the decals - adds the render element to the renderer
|
|
void CryModelSubmesh::AddDecalRenderData (CCObject *pObj, const SRendParams & rRendParams)
|
|
{
|
|
if (m_pDecalManager) // we don't render the character decals in lod!=0
|
|
m_pDecalManager->AddRenderData (pObj, rRendParams);
|
|
}
|
|
|
|
// returns true if calling Morph () is really necessary (there are any pending morphs)
|
|
bool CryModelSubmesh::NeedMorph ()
|
|
{
|
|
return !m_arrMorphEffectors.empty();
|
|
}
|
|
|
|
// morphs the LOD 0 into the given destination (already skinned) buffer
|
|
void CryModelSubmesh::MorphWithSkin (Vec3d* pDst, Vec3dA16* pDstNormalsA16)
|
|
{
|
|
for (unsigned nMorphEffector = 0; nMorphEffector < m_arrMorphEffectors.size(); ++nMorphEffector)
|
|
{
|
|
const CryModEffMorph& rMorphEffector = m_arrMorphEffectors[nMorphEffector];
|
|
int nMorphTargetId = rMorphEffector.getMorphTargetId ();
|
|
if (nMorphTargetId < 0)
|
|
continue;
|
|
const CrySkinMorph& rMorphSkin = m_pMesh->getMorphSkin (0, nMorphTargetId);
|
|
float fBlending = rMorphEffector.getBlending();
|
|
|
|
if (pDstNormalsA16 && g_GetCVars()->ca_MorphNormals())
|
|
rMorphSkin.skin (m_pParent->getBoneGlobalMatrices(), fBlending, pDst, pDstNormalsA16, 1.0f * g_GetCVars()->ca_MorphNormals());
|
|
else
|
|
rMorphSkin.skin (m_pParent->getBoneGlobalMatrices(), fBlending, pDst);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Software skinning: calculate positions and normals
|
|
//////////////////////////////////////////////////////////////////////
|
|
void CryModelSubmesh::Deform( int nLodToDeform, unsigned nDeformFlags)
|
|
{
|
|
DEFINE_PROFILER_FUNCTION();
|
|
#ifdef _DEBUG
|
|
if (m_pParent->m_bOriginalPose && CryStringUtils::stristr(m_pMesh->getFilePathCStr(),"weapon"))
|
|
g_GetLog()->LogWarning ("\001%d. Deforming (lod %d, model %s) without any animations applied!", g_nFrameID, nLodToDeform, m_pMesh->getFilePathCStr());
|
|
#endif
|
|
if ((unsigned)nLodToDeform>=SIZEOF_ARRAY(m_pLeafBuffers))
|
|
{
|
|
g_GetLog()->LogError ("\001CryModelState::Deform(lod=%d,%s,%s,%s):invalid lod", nLodToDeform, (nDeformFlags&FLAG_DEFORM_UPDATE_TANGENTS)?"Update Tangents":"", (nDeformFlags&FLAG_DEFORM_UPDATE_NORMALS)?"Update Normals":"", (nDeformFlags&FLAG_DEFORM_FORCE_UPDATE)?"Force Update":"");
|
|
return;
|
|
}
|
|
|
|
CLeafBuffer *lb = m_pLeafBuffers[nLodToDeform];
|
|
|
|
if (!lb)
|
|
{
|
|
g_GetLog()->LogError ("\001CryModelState::Deform(lod=%d,%s,%s,%s):no lod leaf buffer", nLodToDeform, (nDeformFlags&FLAG_DEFORM_UPDATE_TANGENTS)?"Update Tangents":"", (nDeformFlags&FLAG_DEFORM_UPDATE_NORMALS)?"Update Normals":"", (nDeformFlags&FLAG_DEFORM_FORCE_UPDATE)?"Force Update":"");
|
|
return;
|
|
}
|
|
|
|
CVertexBuffer* pRenderVertexBuffer = lb->GetVertexContainer()->m_pVertexBuffer;
|
|
if (!pRenderVertexBuffer)
|
|
{
|
|
g_GetLog()->LogError ("\001CryModelState::Deform(lod=%d,%s,%s,%s):no vertex buffer in leaf buffer %x", nLodToDeform, (nDeformFlags&FLAG_DEFORM_UPDATE_TANGENTS)?"Update Tangents":"", (nDeformFlags&FLAG_DEFORM_UPDATE_NORMALS)?"Update Normals":"", (nDeformFlags&FLAG_DEFORM_FORCE_UPDATE)?"Force Update":"", lb);
|
|
return;
|
|
}
|
|
|
|
int nVertexFormat = pRenderVertexBuffer->m_vertexformat;
|
|
int nNormalsOffset = g_VertFormatNormalOffsets[nVertexFormat];
|
|
|
|
if (nNormalsOffset >= 0)
|
|
nDeformFlags |= FLAG_DEFORM_UPDATE_NORMALS;
|
|
|
|
#ifdef _DEBUG
|
|
static bool bContinue[3] = {true,true,true};
|
|
if (!bContinue[nLodToDeform])
|
|
return;
|
|
#endif
|
|
|
|
if(!lb)
|
|
return;
|
|
// if we write directly into video memory - wait for fences first
|
|
{
|
|
if (!lb->m_pVertexBuffer)
|
|
{
|
|
// Strange: we are to copy to the videobuffer, but there's no videobuffer.
|
|
assert(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool bRealizeDecals = false;
|
|
|
|
// we only currently support decals for LOD 0
|
|
if (NeedRealizeDecals())
|
|
{
|
|
if (nLodToDeform == 0)
|
|
bRealizeDecals = true;
|
|
else
|
|
DiscardDecalRequests();
|
|
}
|
|
|
|
int nFrameId = g_GetIRenderer()->GetFrameID();
|
|
|
|
// get vertices of selected lod
|
|
CryGeometryInfo * pGeomInfo = m_pMesh->getGeometryInfo(nLodToDeform);
|
|
|
|
const CryUV* pExtUVs = pGeomInfo->getExtUVs();
|
|
|
|
TangData * pExtTangents = pGeomInfo->getExtTangents();
|
|
unsigned numExtTangents = pGeomInfo->numExtTangents();
|
|
|
|
Vec3d vNorm, vTang, vBinorm;
|
|
CrySkinRigidBasis* pTangSkin = NULL;
|
|
|
|
bool bSpawnParticles = !m_pParent->m_ParticleManager.empty() && m_nLastSkinnedFrameID[nLodToDeform] < nFrameId;
|
|
|
|
// the tangents will be skinned here; if it remains NULL, they won't be skinned at all
|
|
SPipTangentsA* pTangentBases = NULL;
|
|
|
|
// these are the sizes of buffers needed in the temporary buffer, in bytes
|
|
unsigned
|
|
sizeVertices = pGeomInfo->numUsedVertices() * sizeof(Vec3dA16),
|
|
sizeNormals = (nDeformFlags&FLAG_DEFORM_UPDATE_NORMALS)?sizeVertices:0,
|
|
sizeTangents = 0;
|
|
|
|
if ((nDeformFlags&FLAG_DEFORM_UPDATE_TANGENTS) && m_pMesh->numBoneInfos())
|
|
{
|
|
if (
|
|
(nDeformFlags&FLAG_DEFORM_FORCE_UPDATE) || // we calculate tangents if the videobuffer has changed and we're forced to recalculate all buffers
|
|
!m_nLastTangentsUpdatedFrameId || // we calculate tangents if they haven't been calculated yet
|
|
m_nLastTangentsUpdatedLOD != nLodToDeform ||
|
|
(nFrameId >= m_nLastTangentsUpdatedFrameId + g_GetCVars()->ca_TangentBasisCalcPeriod()))
|
|
{
|
|
// we need to recalculate and update the tangents
|
|
pTangSkin = pGeomInfo->getTangSkin ();
|
|
// we need to update (possibly without recalculation) the tangent bases
|
|
// we won't need the tangent bases and vertices simultaneously
|
|
sizeTangents = pTangSkin->size()*sizeof(SPipTangentsA);
|
|
}
|
|
else
|
|
nDeformFlags &= ~FLAG_DEFORM_UPDATE_TANGENTS;
|
|
}
|
|
// we won't need tangents simultaneously with the vertices/normals
|
|
g_Temp.reserve (max(sizeTangents, sizeVertices+sizeNormals));
|
|
|
|
const unsigned* pExtToIntMap = pGeomInfo->getExtToIntMapEntries();
|
|
|
|
|
|
//----------------------------------------------------------------
|
|
//----------------------------------------------------------------
|
|
//----------------------------------------------------------------
|
|
|
|
Vec3dA16* pNormals = NULL;
|
|
if ((nDeformFlags&FLAG_DEFORM_UPDATE_NORMALS))
|
|
pNormals = SelfNormalSkin (nLodToDeform, (((Vec3dA16*)g_Temp.data())+pGeomInfo->numUsedVertices()));
|
|
|
|
// most probably we'll skin here; sometimes we don't skin and just change the pointer tothe original geometry data
|
|
// so, get the pointer to the skin. SelfSkin can also modify normals according to morph targets
|
|
const Vec3d* pVertices = NULL;
|
|
|
|
if ((nDeformFlags&FLAG_DEFORM_UPDATE_VERTICES) || bRealizeDecals || bSpawnParticles)
|
|
pVertices = SelfSkin(nLodToDeform, (Vec3d*)g_Temp.data(), pNormals);
|
|
|
|
if (bRealizeDecals)
|
|
RealizeDecals (pVertices);
|
|
|
|
if (bSpawnParticles)
|
|
{
|
|
CryCharParticleManager::SpawnParams params;
|
|
params.setVertices (pVertices, pGeomInfo->numUsedVertices());
|
|
params.setFaces (pGeomInfo->getFaces(), (unsigned)pGeomInfo->numFaces());
|
|
params.pModelMatrix = &m_pParent->m_ModelMatrix44;
|
|
params.pNormalsA16 = pNormals;
|
|
if (params.numBoneMatrices = m_pMesh->numBoneInfos())
|
|
params.pBoneGlobalMatrices = m_pParent->getBoneGlobalMatrices();
|
|
|
|
//spawn particles now, if they don't need normals, because in this case
|
|
// the vertices will be destroyed by the normals
|
|
m_pParent->m_ParticleManager.spawn (params);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------------
|
|
|
|
Vec3d* pVideobuffer = (Vec3d*)pRenderVertexBuffer->m_VS[VSF_GENERAL].m_VData;
|
|
|
|
unsigned numVerts = (unsigned)lb->m_SecVertCount;
|
|
assert (numVerts == pGeomInfo->numExtToIntMapEntries());
|
|
if ((nDeformFlags&FLAG_DEFORM_UPDATE_VERTICES))
|
|
{
|
|
DEFINE_PROFILER_SECTION("ModelDeformVideoMemCopy1");
|
|
//CopyPosUVsToVideomemory (pVertices, pExtUVs, numVerts, pExtToIntMap, pVideobuffer, nVertexFormat);
|
|
|
|
char* pVertBuf = pGeomInfo->getVertBuf(nVertexFormat);
|
|
int nVertexFormatSize = m_VertexSize[nVertexFormat];
|
|
|
|
|
|
//----------------------------------------------------------
|
|
|
|
{
|
|
DEFINE_PROFILER_SECTION("Reconstruct-VertexBuffer-PUV");
|
|
|
|
u32 max=0;
|
|
if (!pNormals || nNormalsOffset < 0)
|
|
{
|
|
for (unsigned i = 0; i < numVerts; ++i){
|
|
u32 index=pExtToIntMap[i];
|
|
//_mm_prefetch( (char*)&pVertices[index], _MM_HINT_NTA );
|
|
*(Vec3d*)(pVertBuf + i * nVertexFormatSize) = pVertices[index];
|
|
if (max<index) max=index;
|
|
}
|
|
|
|
} else {
|
|
for (unsigned i = 0; i < numVerts; ++i)
|
|
{
|
|
*(Vec3d*)(pVertBuf + i * nVertexFormatSize) = pVertices[pExtToIntMap[i]];
|
|
*(Vec3d*)(pVertBuf + i * nVertexFormatSize + nNormalsOffset) = pNormals[pExtToIntMap[i]].v;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
|
|
g_GetIRenderer()->UpdateBuffer (pRenderVertexBuffer, pVertBuf, numVerts, true, 0, VSF_GENERAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int CTang=0;
|
|
CTang=(CTang+1) & 0x00;
|
|
|
|
|
|
if((nDeformFlags&FLAG_DEFORM_UPDATE_TANGENTS)/* && pTangents*/)
|
|
{
|
|
|
|
if (CTang==0)
|
|
{
|
|
|
|
unsigned numTangents;
|
|
SPipTangents* pSrc;
|
|
if(pTangSkin && g_GetCVars()->ca_EnableTangentSkinning())
|
|
{
|
|
// this is the number of bases and the bases themselves, as
|
|
// they are to be copied to the videomemory (directly)
|
|
|
|
pTangentBases = (SPipTangentsA*)g_Temp.data(); // use the same mem for the tangents
|
|
assert(m_pMesh->numBoneInfos());
|
|
numTangents = pTangSkin->size();
|
|
#if defined(_CPU_X86) && !defined(LINUX)
|
|
if (g_GetCVars()->ca_SSEEnable() && cpu::hasSSE())
|
|
pTangSkin->skinSSE (m_pParent->getBoneGlobalMatrices(), pTangentBases);
|
|
else
|
|
#endif
|
|
pTangSkin->skin (m_pParent->getBoneGlobalMatrices(), pTangentBases);
|
|
|
|
#if 0 && defined(_DEBUG)
|
|
for (unsigned nTang = 0; nTang < numTangents; ++nTang)
|
|
{
|
|
SPipTangentsA& t = pTangentBases[nTang];
|
|
assert (t.m_Binormal.Length2() >0.4);
|
|
assert (t.m_Tangent.Length2() >0.4);
|
|
assert (t.m_TNormal.Length2() >0.4);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if sizeofSPipTangentsA == 0x30
|
|
packVec3d16 (pTangentBases, pTangSkin->size()*3);
|
|
#endif
|
|
|
|
// we've calculated the tangents that are exactly in the same format as in videomem
|
|
// (packed 3-float vectors) so we may copy them directly
|
|
pSrc = (SPipTangents*)pTangentBases;
|
|
assert (pTangSkin->size() == pGeomInfo->numExtToIntMapEntries());
|
|
}
|
|
else
|
|
{
|
|
numTangents = numExtTangents;
|
|
pSrc = (SPipTangents*)pExtTangents;
|
|
}
|
|
|
|
|
|
|
|
// copy it from the calculated array
|
|
{
|
|
DEFINE_PROFILER_SECTION ("ModelDeformVideoMemCopy2");
|
|
g_GetIRenderer()->UpdateBuffer (pRenderVertexBuffer, pSrc, numTangents, true, 0, VSF_TANGENTS);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//memcpy (pTangents, pSrc, sizeof(SPipTangents) * numTangents );
|
|
m_nLastTangentsUpdatedFrameId = nFrameId;
|
|
m_nLastTangentsUpdatedLOD = nLodToDeform;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns true if the decals need realization (otherwise RealizeDecals() might not be called)
|
|
bool CryModelSubmesh::NeedRealizeDecals ()
|
|
{
|
|
return m_pDecalManager && m_pDecalManager->NeedRealize();
|
|
}
|
|
|
|
// the modelstate gives itself a chance to process decals (realize)
|
|
// in this call. The decal realization means creation of geometry that carries the decal
|
|
void CryModelSubmesh::RealizeDecals (const Vec3d* pPositions)
|
|
{
|
|
if (m_pDecalManager)
|
|
m_pDecalManager->Realize (pPositions);
|
|
}
|
|
|
|
|
|
|
|
// discards all outstanding decal requests - the decals that have not been meshed (realized) yet
|
|
// and have no chance to be correctly meshed in the future
|
|
void CryModelSubmesh::DiscardDecalRequests()
|
|
{
|
|
if (m_pDecalManager)
|
|
m_pDecalManager->DiscardRequests();
|
|
}
|
|
|
|
|
|
// do not calculate normals and do not copy into video buffers
|
|
// Returns: number of vertices
|
|
const Vec3d* CryModelSubmesh::DeformForShadowVolume( int nLodToDeform )
|
|
{
|
|
return SelfSkin(nLodToDeform);
|
|
}
|
|
|
|
// skins this model (into the given temporary storage, if needed)
|
|
// if no storage is provided, then allocates its own storage (g_Temp.data())
|
|
// can return the model pre-skinned data (i.e. the returned value is not guaranteed to
|
|
// be the same as the buffer passed
|
|
// The normals, if not NULL, are modified according to the vertex movements
|
|
const Vec3d* CryModelSubmesh::SelfSkin(int nLOD, Vec3d*pVertices, Vec3dA16* pNormalsA16)
|
|
{
|
|
// if we have links to bones, use them to skin the model
|
|
CryGeometryInfo * pGeomInfo = m_pMesh->getGeometryInfo(nLOD);
|
|
if (m_pMesh->numBoneInfos())
|
|
{
|
|
unsigned numVertices = pGeomInfo->numUsedVertices();
|
|
#if ( defined(_CPU_X86) || defined (_AMD64_) ) && !defined(LINUX)
|
|
if (g_GetCVars()->ca_SSEEnable() && cpu::hasSSE())
|
|
{
|
|
if (!pVertices)
|
|
{
|
|
g_Temp.reserve (sizeof(Vec3dA16)*numVertices);
|
|
pVertices = (Vec3d*)g_Temp.data();
|
|
}
|
|
CrySkinFull* pSkin = pGeomInfo->getGeomSkin();
|
|
pSkin->skinSSE (m_pParent->getBoneGlobalMatrices(), (Vec3dA16*)pVertices);
|
|
packVec3d16 (pVertices, numVertices);
|
|
|
|
CryAABB caabb;
|
|
caabb.vMin=pSkin->g_BBox.vMin.v;
|
|
caabb.vMax=pSkin->g_BBox.vMax.v;
|
|
m_SubBBox = caabb;
|
|
m_nLastSkinBBoxUpdateFrameId = g_nFrameID;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!pVertices)
|
|
{
|
|
g_Temp.reserve (sizeof(Vec3d)*numVertices);
|
|
pVertices = (Vec3d*)g_Temp.data();
|
|
}
|
|
pGeomInfo->getGeomSkin()->skin (m_pParent->getBoneGlobalMatrices(), pVertices);
|
|
}
|
|
|
|
if (nLOD == 0 && NeedMorph() && !g_GetCVars()->ca_NoMorph())
|
|
{
|
|
// morph the object into the given vertex array to use subsequently in skinning
|
|
MorphWithSkin (pVertices, pNormalsA16);
|
|
}
|
|
return pVertices;
|
|
}
|
|
else
|
|
{
|
|
return pGeomInfo->getVertices();
|
|
}
|
|
}
|
|
|
|
|
|
// Normal-skins this model (into the given storage)
|
|
Vec3dA16* CryModelSubmesh::SelfNormalSkin (int nLOD, Vec3dA16* pNormals)
|
|
{
|
|
// if we have links to bones, use them to skin the model
|
|
CryGeometryInfo * pGeomInfo = m_pMesh->getGeometryInfo(nLOD);
|
|
if (m_pMesh->numBoneInfos())
|
|
{
|
|
CrySkinFull* pNormalSkin = pGeomInfo->getNormalSkin();
|
|
if (!pNormalSkin->empty())
|
|
{
|
|
//reserve for the normals in the internal indexation and for the normals in outer indexation
|
|
pNormalSkin->skinAsVec3d16 (m_pParent->getBoneGlobalMatrices(), pNormals);
|
|
return pNormals;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//this is called only at the beginning once for each LOD
|
|
void CryModelSubmesh::DeformFirst()
|
|
{
|
|
// get current LOD geometry
|
|
int nLod = m_pParent->m_nLodLevel;
|
|
CryGeometryInfo * pGeomInfo = m_pMesh->getGeometryInfo(nLod);
|
|
unsigned numVertices = pGeomInfo->numVertices();
|
|
unsigned i;
|
|
|
|
// deform positions into pGeomInfo->m_pVertices
|
|
for(i = 0; i < numVertices; i++)
|
|
{
|
|
Vec3d & p = pGeomInfo->getVertex(i);
|
|
|
|
if (pGeomInfo->numLinks())
|
|
{
|
|
p.x = p.y = p.z = 0;
|
|
const CryVertexBinding& arrLinks = pGeomInfo->getLink(i);
|
|
|
|
for (CryVertexBinding::const_iterator itLink = arrLinks.begin(); itLink != arrLinks.end(); ++itLink)
|
|
{
|
|
Matrix44& matBoneGlobal = m_pParent->getBoneMatrixGlobal(itLink->BoneID);
|
|
p += matBoneGlobal.TransformPointOLD(itLink->offset) * itLink->Blending;
|
|
}
|
|
}
|
|
}
|
|
|
|
// calc nomals for default pose for this LOD into pGeomInfo->m_pVertices
|
|
pGeomInfo->recalculateNormals();
|
|
|
|
// at this point in pGeomInfo there are positions and normals for zero frame
|
|
|
|
if(!m_pLeafBuffers[nLod])
|
|
return;
|
|
|
|
// now put pos and normals into leaf secondary buffer
|
|
CLeafBuffer * lb = m_pLeafBuffers[nLod];
|
|
const unsigned* pExtToIntMap = pGeomInfo->getExtToIntMapEntries();
|
|
int StrPos;
|
|
byte *pPos = lb->GetPosPtr(StrPos, 0, true);
|
|
int StrNor;
|
|
byte *pNor = lb->GetNormalPtr(StrNor, 0, true);
|
|
for (i=0; i<(unsigned)lb->m_SecVertCount; i++, pPos+=StrPos, pNor+=StrNor)
|
|
{
|
|
unsigned sn = pExtToIntMap[i];
|
|
assert(sn<pGeomInfo->numVertices());
|
|
if (pPos)
|
|
{
|
|
Vec3d *v = (Vec3d *)pPos;
|
|
v->x = pGeomInfo->getVertex(sn).x;
|
|
v->y = pGeomInfo->getVertex(sn).y;
|
|
v->z = pGeomInfo->getVertex(sn).z;
|
|
}
|
|
assert ( GetLengthSquared(pGeomInfo->getNormal(sn)) > 0.4);
|
|
if (pNor)
|
|
{
|
|
Vec3d *v = (Vec3d *)pNor;
|
|
v->x = pGeomInfo->getNormal(sn).x;
|
|
v->y = pGeomInfo->getNormal(sn).y;
|
|
v->z = pGeomInfo->getNormal(sn).z;
|
|
}
|
|
}
|
|
|
|
// calculate tangent basises
|
|
lb->CreateTangBuffer();
|
|
|
|
if((unsigned)lb->m_SecVertCount > pGeomInfo->numExtTangents())
|
|
g_GetConsole()->Exit("Error: CryModelState::DeformFirst: lb->m_SecVertCount > pGeomInfo->m_nAllocatedTangNum (%d)", lb->m_SecVertCount);
|
|
|
|
// copy tangent basises into pGeom
|
|
//pSecBuff = (PipVertex *)lb->m_pSecVertBuffer->m_data;
|
|
SPipTangents *pTangBuff = (SPipTangents *)lb->m_pSecVertBuffer->m_VS[VSF_TANGENTS].m_VData;
|
|
TangData* pExtTangents = pGeomInfo->getExtTangents();
|
|
for (i = 0; i < (unsigned)lb->m_SecVertCount; ++i)
|
|
{
|
|
assert(i<pGeomInfo->numExtTangents());
|
|
|
|
// For Debug:
|
|
// This is the current vertex:
|
|
// (((*pGeomInfo).m_arrVertices).m_pData)[((*pGeomInfo).m_arrExtToIntMap).m_pData[i]]
|
|
// This is its normal (may be not from the initial position):
|
|
// (((*pGeomInfo).m_arrNormals).m_pData)[((*pGeomInfo).m_arrExtToIntMap).m_pData[i]]
|
|
//
|
|
/*
|
|
assert (pTangBuff[i].m_Binormal.Length2() > 0.4);
|
|
assert (pTangBuff[i].m_Tangent.Length2() > 0.4);
|
|
assert (pTangBuff[i].m_TNormal.Length2() > 0.4);
|
|
*/
|
|
|
|
pExtTangents[i].binormal = pTangBuff[i].m_Binormal;
|
|
pExtTangents[i].tangent = pTangBuff[i].m_Tangent;
|
|
pExtTangents[i].tnormal = pTangBuff[i].m_TNormal;
|
|
}
|
|
//lb->FreeSystemBuffer();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//Calculate shadow volumes,fill buffers and render shadow volumes into the stencil buffer
|
|
//TODO: Optimize everything
|
|
void CryModelSubmesh::RenderShadowVolumes (const SRendParams *rParams, int nLimitLOD)
|
|
{
|
|
DEFINE_PROFILER_FUNCTION();
|
|
|
|
#ifdef _DEBUG
|
|
if (g_GetCVars()->ca_DebugRebuildShadowVolumes())
|
|
m_pMesh->buildStencilShadowInfos();
|
|
#endif
|
|
|
|
// all of the operations here (except when another PROFILE_FRAME_* is used) are related to deformations, extrusions etc.
|
|
// for the shadow volume
|
|
|
|
int nShadowLOD = max (m_pParent->m_nLodLevel, min2((int)m_pMesh->numLODs()-1, max2(g_GetCVars()->ca_LimitShadowLOD(), nLimitLOD)));
|
|
|
|
// detect the shadow edges
|
|
IStencilShadowConnectivity *pConnectivity = m_pMesh->getStencilShadowConnectivity(nShadowLOD);
|
|
DWORD dwVertexCount, dwTriangleCount;
|
|
pConnectivity->GetStats(dwVertexCount, dwTriangleCount);
|
|
if (dwVertexCount == 0 || dwTriangleCount == 0)
|
|
return; // empty shadow volume, don't do anything
|
|
|
|
IEdgeDetector *iEdgeDetector = Get3DEngine()->GetEdgeDetector();
|
|
CryCharReShadowVolume* pReShadowVolume = m_ReShadowManager.newShadow();
|
|
if (!pReShadowVolume)
|
|
return;
|
|
|
|
unsigned numVertices = m_pMesh->getGeometryInfo(nShadowLOD)->numUsedVertices();
|
|
// compute the deformed character vertices; the character will keep the allocated buffer itself (it won't be allocated)
|
|
const Vec3d* pModelVerts = DeformForShadowVolume(nShadowLOD);
|
|
|
|
// Vec3d vLightPos = rParams->lSource->m_Origin;
|
|
Vec3d vLightPos = rParams->pShadowVolumeLightSource->m_Origin;
|
|
|
|
float fShadowVolumeExtent = g_GetCVars()->ca_ShadowVolumeExtent();
|
|
if (fShadowVolumeExtent == 0)
|
|
{
|
|
if(rParams->fShadowVolumeExtent)
|
|
fShadowVolumeExtent = rParams->fShadowVolumeExtent;
|
|
else
|
|
fShadowVolumeExtent = 20.0f; // the default extent
|
|
}
|
|
|
|
|
|
//translate the light pos with respect to the character
|
|
Vec3d vLightTrans = vLightPos - rParams->vPos;
|
|
//rotate the light pos to compensate the rotation of the shadow volume
|
|
//these operations are done to avoid to translate and rotate all shadow volume edges
|
|
|
|
float fCos = cry_cosf(DEG2RAD(-rParams->vAngles.z));
|
|
float fSin = cry_sinf(DEG2RAD(-rParams->vAngles.z));
|
|
Vec3d vLSourcePos (
|
|
vLightTrans.x*fCos-vLightTrans.y*fSin,
|
|
vLightTrans.x*fSin+vLightTrans.y*fCos,
|
|
vLightTrans.z);
|
|
|
|
if(!pConnectivity)return; // nothing more to do
|
|
|
|
// deformed model vertices
|
|
iEdgeDetector->BuildSilhuetteFromPos (pConnectivity, vLSourcePos, pModelVerts);
|
|
|
|
unsigned numShadowIndices = iEdgeDetector->numShadowVolumeIndices();
|
|
unsigned numShadowVertices = iEdgeDetector->numShadowVolumeVertices();
|
|
|
|
if (numShadowVertices > 1 && numShadowIndices > 1)
|
|
{
|
|
{
|
|
DEFINE_PROFILER_SECTION("ModelShadowVideoAlloc");
|
|
pReShadowVolume->prepare (numShadowIndices, numShadowVertices);
|
|
}
|
|
|
|
iEdgeDetector->meshShadowVolume (vLSourcePos, fShadowVolumeExtent, pReShadowVolume->getVertexBuffer(), pReShadowVolume->getIndexBuffer());
|
|
|
|
{
|
|
DEFINE_PROFILER_SECTION("ModelShadowVideoUpdate");
|
|
pReShadowVolume->submit (rParams, m_pParent->m_pShaderStateShadowCull);
|
|
}
|
|
}
|
|
else
|
|
g_GetLog () -> Log ("WARNING: shadow volume with %d indices and %d vertices generated", numShadowIndices, numShadowVertices);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Compute all effectors and remove those whose life time has ended,
|
|
// then apply effectors to the bones
|
|
void CryModelSubmesh::ProcessSkinning(const Vec3& t, const Matrix44& mtxModel, int nTemplate, int nLod, bool bForceUpdate)
|
|
{
|
|
if (nLod < 0)
|
|
nLod = m_pParent->m_nLodLevel;
|
|
m_pParent->m_ModelMatrix44 = mtxModel;
|
|
|
|
bool bNeedVertices = true;
|
|
|
|
CLeafBuffer *lb = m_pLeafBuffers[nLod];
|
|
|
|
SShaderItem si;
|
|
for (int i = 0; i < lb->m_pMats->Count(); ++i)
|
|
{
|
|
si = lb->m_pMats->Get(i)->shaderItem;
|
|
if (si.m_pShader)
|
|
break;
|
|
}
|
|
IShader *ef;
|
|
assert (si.m_pShader);
|
|
ef = si.m_pShader->GetTemplate(nTemplate);
|
|
assert (ef);
|
|
|
|
#ifdef _DEBUG
|
|
bool bAllowToCopyIntoVideoBufferDirectly = ((ef->GetFlags3() & EF3_NEEDSYSBUF) == 0);
|
|
#endif
|
|
|
|
bool bShowNormals = g_GetCVars()->r_ShowNormals() != 0;
|
|
bool bShowTangents = g_GetCVars()->r_ShowTangents() != 0;
|
|
|
|
bool bNeedTangents = ef && (ef->GetFlags() & EF_NEEDTANGENTS);
|
|
bool bNeedNormals = ef && (ef->GetFlags() & EF_NEEDNORMALS);
|
|
|
|
int nCurrentFrameID = g_GetIRenderer()->GetFrameID();
|
|
#ifdef UNIQUE_VERT_BUFF_PER_INSTANCE
|
|
// check if already skinned in this frame
|
|
|
|
bNeedVertices = bNeedVertices && m_nLastSkinnedFrameID[nLod] != nCurrentFrameID;
|
|
bNeedTangents = bNeedTangents && m_nLastTangedFrameID[nLod] != nCurrentFrameID;
|
|
|
|
//const unsigned nInternalForceUpdatePow = 6;
|
|
|
|
if(!bNeedVertices && !bForceUpdate && !bNeedTangents &&
|
|
(m_pParent->m_uFlags&(CryModelState::nFlagNeedReskinLOD<<nLod)) == 0 &&
|
|
m_pLeafBuffers[nLod] &&
|
|
m_pLeafBuffers[nLod]->m_pVertexBuffer
|
|
// force update every 2^nInternalForceUpdatePow frames: this comparison
|
|
// produces false and doesn't let this conditional return
|
|
//&&(!g_GetCVars()->ca_SafeReskin() /*|| ((g_GetIRenderer()->GetFrameID() & nInternalForceUpdatePow) != (m_nInstanceNumber & nInternalForceUpdatePow))*/)
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
CryAABB bbox = m_pParent->getBBox();
|
|
//Vec3 trans=mtxModel.GetTranslationOLD();
|
|
//Matrix44 m44 = m_pParent->getBoneMatrixGlobal(0);
|
|
// Vec3 trans=m_pParent->m_vOffset;
|
|
|
|
|
|
AABB aabb;
|
|
aabb.min=bbox.vMin+t;
|
|
aabb.max=bbox.vMax+t;
|
|
|
|
Vec3 WorldMiddle=(aabb.min+aabb.max)*0.5f;
|
|
bool NearCam=0;
|
|
f32 dist = GetLength( GetViewCamera().GetPos()-WorldMiddle );
|
|
if (dist<2.0f) NearCam=1;
|
|
|
|
|
|
|
|
Matrix34 m34=Matrix34::CreateIdentity();
|
|
m34 = Matrix34( GetTransposed44(mtxModel) );
|
|
m34.SetTranslation(t);
|
|
|
|
/*{
|
|
//debug - this draws the bounding box around the character
|
|
static float fColorBBox[4] = {1,1,1,1};
|
|
fColorBBox[1]+=0.01f; if (fColorBBox[1]>1.0f) fColorBBox[1]=fColorBBox[1]-1.0f;
|
|
debugDrawBBox (m34, CryAABB(bbox.vMin,bbox.vMax), 2, fColorBBox);
|
|
// g_pIRenderer->Draw3dBBox(aabb.min,aabb.max);
|
|
float fColor[4] = {0,1,0,1};
|
|
extern float g_YLine;
|
|
// g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"trans: %15.10f %15.10f %15.10f bForceUpdate:%d",t.x,t.y,t.z, bForceUpdate ); g_YLine+=16.0f;
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"aabbsize: %15.10f %15.10f %15.10f",(aabb.max.x-aabb.min.x),(aabb.max.y-aabb.min.y),(aabb.max.z-aabb.min.z) ); g_YLine+=16.0f;
|
|
//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"trans: %15.10f %15.10f %15.10f bForceUpdate:%d",trans.x,trans.y,trans.z,bForceUpdate ); g_YLine+=16.0f;
|
|
//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"min: %15.10f %15.10f %15.10f",aabb.min.x,aabb.min.y,aabb.min.z ); g_YLine+=16.0f;
|
|
//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"max: %15.10f %15.10f %15.10f",aabb.max.x,aabb.max.y,aabb.max.z ); g_YLine+=16.0f;
|
|
//g_YLine+=16.0f;
|
|
}*/
|
|
|
|
|
|
//if ( !(GetAsyncKeyState('I') & 0x8000) )
|
|
|
|
// if ( GetViewCamera().IsAABBVisibleFast(aabb) || bForceUpdate || NearCam )
|
|
{
|
|
|
|
// Deform current LOD
|
|
if ( !g_GetCVars()->ca_NoDeform() || bForceUpdate)
|
|
{
|
|
unsigned nDeformFlags = 0;
|
|
|
|
if (bNeedTangents||bShowTangents)
|
|
nDeformFlags |= FLAG_DEFORM_UPDATE_TANGENTS;
|
|
if (bNeedNormals||bShowNormals)
|
|
nDeformFlags |= FLAG_DEFORM_UPDATE_NORMALS;
|
|
if (bNeedVertices)
|
|
nDeformFlags |= FLAG_DEFORM_UPDATE_VERTICES;
|
|
if (bForceUpdate)
|
|
nDeformFlags |= FLAG_DEFORM_FORCE_UPDATE;
|
|
|
|
Deform(nLod, nDeformFlags);
|
|
|
|
m_pParent->m_uFlags &= ~(CryModelState::nFlagNeedReskinLOD<<nLod);
|
|
m_nLastUpdatedLodLevel = nLod;
|
|
|
|
if (bNeedVertices)
|
|
m_nLastSkinnedFrameID[nLod] = nCurrentFrameID;
|
|
if (bNeedTangents)
|
|
m_nLastTangedFrameID[nLod] = nCurrentFrameID;
|
|
m_nLastUpdatedLodLevel = nLod;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool CryModelSubmesh::SetShaderTemplateName (const char *TemplName, int Id, const char *ShaderName,IMatInfo *pCustomMaterial,unsigned nFlags)
|
|
{
|
|
|
|
bool bRes = false;
|
|
for (unsigned nLOD = 0; nLOD < m_pMesh->numLODs(); ++nLOD)
|
|
{
|
|
CLeafBuffer *lb = m_pLeafBuffers[nLOD];
|
|
if (lb->m_pMats->Count() > (int)getShaderTemplates(Id).size())
|
|
return false;
|
|
int nTempl = -1;
|
|
if (!ShaderName)
|
|
{
|
|
int numMaterials = lb->m_pMats->Count();
|
|
assert (numMaterials >= 0);
|
|
for (unsigned i=0; i < (unsigned)numMaterials; i++)
|
|
{
|
|
//assert (m_pModelState->m_pMesh == m_pCryCharBody->GetModel());
|
|
SShaderItem si = lb->m_pMats->Get(i)->shaderItem;
|
|
// Override object material.
|
|
if (pCustomMaterial)
|
|
{
|
|
pCustomMaterial->OverrideShaderItem( i,si );
|
|
}
|
|
|
|
if (si.m_pShader && (*lb->m_pMats)[i].pRE && (*lb->m_pMats)[i].nNumIndices)
|
|
{
|
|
if (TemplName && TemplName[0])
|
|
{
|
|
si.m_pShader->AddTemplate(si.m_pShaderResources, nTempl, TemplName, false);
|
|
}
|
|
getShaderTemplates(Id)[i] = nTempl;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
for (unsigned i=0; i < getShaderTemplates(Id).size(); ++i)
|
|
{
|
|
//assert (m_pMesh == m_pModel );
|
|
SShaderItem si = lb->m_pMats->Get(i)->shaderItem;
|
|
|
|
if (!si.m_pShader)
|
|
continue;
|
|
|
|
if (!stricmp(ShaderName, si.m_pShader->GetName()))
|
|
{
|
|
bRes = true;
|
|
if (TemplName && TemplName[0])
|
|
si.m_pShader->AddTemplate(si.m_pShaderResources, nTempl, TemplName);
|
|
getShaderTemplates(Id)[i] = nTempl;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return bRes;
|
|
}
|
|
|
|
CLeafBuffer* CryModelSubmesh::GetLeafBuffer ()
|
|
{
|
|
return m_pLeafBuffers[m_pParent->m_nLodLevel];
|
|
}
|
|
|
|
|
|
void CryModelSubmesh::SetShaderFloat (const char *Name, float Val, const char *ShaderName)
|
|
{
|
|
|
|
char name[128];
|
|
int i;
|
|
|
|
strcpy(name, Name);
|
|
strlwr(name);
|
|
|
|
int nMat = -1;
|
|
if (ShaderName)
|
|
{
|
|
unsigned nLOD, numLODs = m_pMesh->numLODs();
|
|
for (nLOD = 0; nLOD < numLODs; ++nLOD)
|
|
{
|
|
CLeafBuffer *lb = m_pLeafBuffers[nLOD];
|
|
int numMaterials = lb->m_pMats->Count();
|
|
assert (numMaterials >= 0);
|
|
for (i=0; i<numMaterials; i++)
|
|
{
|
|
CMatInfo *mi = lb->m_pMats->Get(i);
|
|
if (!mi->pRE)
|
|
continue;
|
|
SShaderItem si = mi->shaderItem;
|
|
if (!stricmp(si.m_pShader->GetName(), ShaderName))
|
|
{
|
|
nMat = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned j;
|
|
for (j = 0; j < m_ShaderParams.size(); ++j)
|
|
{
|
|
if (!strcmp(name, m_ShaderParams[j].m_Name) && m_ShaderParams[j].m_nMaterial == nMat)
|
|
break;
|
|
}
|
|
|
|
if (j == m_ShaderParams.size())
|
|
{
|
|
SShaderParam pr;
|
|
strncpy(pr.m_Name, name, 32);
|
|
pr.m_nMaterial = nMat;
|
|
m_ShaderParams.push_back(pr);
|
|
//g_GetLog()->LogToFile("SetShaderFloat realloc: %d", m_ShaderParams.size());
|
|
}
|
|
m_ShaderParams[j].m_Type = eType_FLOAT;
|
|
m_ShaderParams[j].m_Value.m_Float = Val;
|
|
}
|
|
|
|
|
|
void CryModelSubmesh::Render(const struct SRendParams & RendParams, Matrix44& mtxObjMatrix, CryCharInstanceRenderParams& rCharParams, const Vec3& translation)
|
|
{
|
|
|
|
if ( translation.GetLength() == 0.0f )
|
|
{
|
|
int i=0;
|
|
}
|
|
|
|
/*
|
|
Vec3 trans=RendParams.vPos;
|
|
float fColor[4] = {0,1,0,1};
|
|
extern float g_YLine;
|
|
g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"render trans: %15.10f %15.10f %15.10f",trans.x,trans.y,trans.z ); g_YLine+=16.0f;
|
|
g_YLine+=16.0f;
|
|
*/
|
|
|
|
if (0 == (m_nFlags&FLAG_VISIBLE))
|
|
return;
|
|
|
|
CCObject * pObj = g_GetIRenderer()->EF_GetObject(true);
|
|
|
|
//pObj->m_Translation=translation;
|
|
|
|
pObj->m_SortId = RendParams.fCustomSortOffset;
|
|
|
|
pObj->m_ObjFlags |= FOB_TRANS_MASK;
|
|
|
|
// set scissor
|
|
// pObj->m_nScissorX1 = RendParams.nScissorX1;
|
|
// pObj->m_nScissorY1 = RendParams.nScissorY1;
|
|
// pObj->m_nScissorX2 = RendParams.nScissorX2;
|
|
// pObj->m_nScissorY2 = RendParams.nScissorY2;
|
|
|
|
//checl if it should be drawn close to the player
|
|
if ((RendParams.dwFObjFlags & FOB_NEAREST) || (rCharParams.m_nFlags & CS_FLAG_DRAW_NEAR) ) { pObj->m_ObjFlags|=FOB_NEAREST; }
|
|
else pObj->m_ObjFlags&=~FOB_NEAREST;
|
|
|
|
pObj->m_Color = rCharParams.m_Color;
|
|
pObj->m_Color.a *= RendParams.fAlpha;
|
|
|
|
//some indoor flags
|
|
//if (RendParams.dwFlags & RPF_ZPASS) pObj->m_ObjFlags |= FOB_ZPASS;
|
|
//else { if (RendParams.dwFlags & RPF_LIGHTPASS) pObj->m_ObjFlags |= FOB_LIGHTPASS; }
|
|
|
|
//if (RendParams.dwFObjFlags & RPF_INDOOR) pObj->m_ObjFlags |= FOB_INDOOR;
|
|
|
|
pObj->m_AmbColor = RendParams.vAmbientColor;
|
|
|
|
if (g_GetIRenderer()->EF_GetHeatVision()) pObj->m_ObjFlags |= FOB_HOTAMBIENT;
|
|
|
|
pObj->m_ObjFlags |= RendParams.dwFObjFlags;
|
|
|
|
pObj->m_Matrix = mtxObjMatrix;
|
|
|
|
int nTemplID = RendParams.nShaderTemplate;
|
|
|
|
pObj->m_pShadowCasters = RendParams.pShadowMapCasters;
|
|
|
|
if (!m_ShaderParams.empty()) pObj->m_ShaderParams = &m_ShaderParams;
|
|
|
|
if(RendParams.pShadowMapCasters && RendParams.pShadowMapCasters->size()) pObj->m_ObjFlags |= FOB_INSHADOW;
|
|
else pObj->m_ObjFlags &= ~FOB_INSHADOW;
|
|
|
|
pObj->m_pCharInstance = this;
|
|
|
|
pObj->m_nTemplId = nTemplID;
|
|
|
|
pObj->m_DynLMMask = RendParams.nDLightMask;
|
|
|
|
if (g_GetCVars()->ca_ambient_light_range() > 0.05f && !(RendParams.dwFObjFlags & FOB_FOGPASS) && !(RendParams.dwFObjFlags & FOB_LIGHTPASS))
|
|
{
|
|
float col = (1.0f - RendParams.fDistance/g_GetCVars()->ca_ambient_light_range())*g_GetCVars()->ca_ambient_light_intensity();
|
|
|
|
if (col >= 0.0f)
|
|
{
|
|
Vec3 ambientColor(pObj->m_AmbColor.x, pObj->m_AmbColor.y, pObj->m_AmbColor.z);
|
|
if (ambientColor.GetLengthSquared()>0.001)
|
|
{
|
|
rCharParams.m_ambientLight.m_Color = CFColor(ambientColor * col);
|
|
|
|
// pObj->m_AmbColor -= Vec3d(rCharParams.m_ambientLight.m_Color.r*2.0f, rCharParams.m_ambientLight.m_Color.g*2.0f, rCharParams.m_ambientLight.m_Color.b*2.0f);
|
|
|
|
rCharParams.m_ambientLight.m_Origin = Vec3d(0, 0, 5000.0f);
|
|
rCharParams.m_ambientLight.m_fRadius = 100000000;
|
|
rCharParams.m_ambientLight.m_Color = Vec3(col, col, col);
|
|
rCharParams.m_ambientLight.m_SpecColor = Vec3(0.0f, 0.0f, 0.0f);
|
|
rCharParams.m_ambientLight.m_Flags |= DLF_DIRECTIONAL | DLF_AMBIENT_LIGHT;
|
|
g_GetIRenderer()->EF_ADDDlight (&rCharParams.m_ambientLight);
|
|
|
|
if(rCharParams.m_ambientLight.m_Id>=0)
|
|
pObj->m_DynLMMask |= (1<<rCharParams.m_ambientLight.m_Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
CCObject * pObj1 = NULL;
|
|
if (getShaderTemplates(1)[0] > 0)
|
|
{
|
|
pObj1 = g_GetIRenderer()->EF_GetObject(true);
|
|
pObj1->CloneObject(pObj);
|
|
pObj1->m_ObjFlags |= FOB_TRANS_MASK;
|
|
pObj->m_SortId += 10;
|
|
}
|
|
|
|
// HACK HACK HACK
|
|
// If we have less then 10 bones it's not the character
|
|
if (m_pMesh->numBoneInfos() > 10)
|
|
pObj->m_ObjFlags |= FOB_ENVLIGHTING;
|
|
|
|
if (g_GetCVars()->ca_EnableLightUpdate() && this == m_pParent->GetSubmesh(0))
|
|
{
|
|
m_pParent->UpdateHeatSources (pObj, RendParams);
|
|
m_pParent->UpdateDynBoundLights (pObj, RendParams);
|
|
}
|
|
|
|
if (!g_GetCVars()->ca_NoDraw())
|
|
AddCurrentRenderData (pObj,pObj1,RendParams);
|
|
|
|
// if (g_GetCVars()->ca_EnableDecals() && !RendParams.bRenderIntoShadowMap)
|
|
if (g_GetCVars()->ca_EnableDecals() && !(RendParams.dwFObjFlags & FOB_RENDER_INTO_SHADOWMAP) )
|
|
AddDecalRenderData (pObj,RendParams);
|
|
|
|
|
|
//---------------------------------------------------------------
|
|
|
|
/*
|
|
CCObject * pObj = g_GetIRenderer()->EF_GetObject(true);
|
|
|
|
pObj->m_ObjFlags |= FOB_TRANS_MASK;
|
|
|
|
// set scissor
|
|
pObj->m_nScissorX1 = RendParams.nScissorX1;
|
|
pObj->m_nScissorY1 = RendParams.nScissorY1;
|
|
pObj->m_nScissorX2 = RendParams.nScissorX2;
|
|
pObj->m_nScissorY2 = RendParams.nScissorY2;
|
|
|
|
//checl if it should be drawn close to the player
|
|
if ((RendParams.dwFlags & RPF_DRAWNEAR) || (rCharParams.m_nFlags & CS_FLAG_DRAW_NEAR)) { pObj->m_ObjFlags|=FOB_NEAREST; }
|
|
else pObj->m_ObjFlags&=~FOB_NEAREST;
|
|
|
|
pObj->m_Color = rCharParams.m_Color;
|
|
pObj->m_Color.a *= RendParams.fAlpha;
|
|
|
|
//some indoor flags
|
|
if (RendParams.dwFlags & RPF_ZPASS) pObj->m_ObjFlags |= FOB_ZPASS;
|
|
else { if (RendParams.dwFlags & RPF_LIGHTPASS) pObj->m_ObjFlags |= FOB_LIGHTPASS; }
|
|
|
|
if (RendParams.dwFlags & RPF_INDOOR) pObj->m_ObjFlags |= FOB_INDOOR;
|
|
|
|
pObj->m_AmbColor = RendParams.vAmbientColor;
|
|
|
|
if (g_GetIRenderer()->EF_GetHeatVision()) pObj->m_ObjFlags |= FOB_HOTAMBIENT;
|
|
|
|
pObj->m_ObjFlags |= RendParams.dwFObjFlags;
|
|
|
|
pObj->m_Matrix = mtxObjMatrix;
|
|
|
|
int nTemplID = RendParams.nShaderTemplate;
|
|
|
|
pObj->m_pShadowCasters = RendParams.pShadowMapCasters;
|
|
|
|
if (!m_ShaderParams.empty()) pObj->m_ShaderParams = &m_ShaderParams;
|
|
|
|
if(RendParams.pShadowMapCasters && RendParams.pShadowMapCasters->size()) pObj->m_ObjFlags |= FOB_INSHADOW;
|
|
else pObj->m_ObjFlags &= ~FOB_INSHADOW;
|
|
|
|
pObj->m_pCharInstance = this;
|
|
|
|
pObj->m_nTemplId = nTemplID;
|
|
|
|
pObj->m_DynLMMask = RendParams.nDLightMask;
|
|
|
|
if (g_GetCVars()->ca_ambient_light_range() > 0.05f && !(RendParams.dwFObjFlags & FOB_FOGPASS) && !(RendParams.dwFObjFlags & FOB_LIGHTPASS))
|
|
{
|
|
float col = (1.0f - RendParams.fDistance/g_GetCVars()->ca_ambient_light_range())*g_GetCVars()->ca_ambient_light_intensity();
|
|
|
|
if (col >= 0.0f)
|
|
{
|
|
Vec3 ambientColor(pObj->m_AmbColor.x, pObj->m_AmbColor.y, pObj->m_AmbColor.z);
|
|
if (ambientColor.GetLengthSquared()>0.001)
|
|
{
|
|
rCharParams.m_ambientLight.m_Color = CFColor(ambientColor * col);
|
|
|
|
pObj->m_AmbColor -= Vec3d(rCharParams.m_ambientLight.m_Color.r*2.0f, rCharParams.m_ambientLight.m_Color.g*2.0f, rCharParams.m_ambientLight.m_Color.b*2.0f);
|
|
|
|
rCharParams.m_ambientLight.m_Origin = Vec3d(0, 0, 5000.0f);
|
|
rCharParams.m_ambientLight.m_fRadius = 100000000;
|
|
rCharParams.m_ambientLight.m_Color = Vec3(col, col, col);
|
|
rCharParams.m_ambientLight.m_SpecColor = Vec3(0.0f, 0.0f, 0.0f);
|
|
rCharParams.m_ambientLight.m_Flags |= DLF_DIRECTIONAL | DLF_AMBIENT_LIGHT;
|
|
g_GetIRenderer()->EF_ADDDlight (&rCharParams.m_ambientLight);
|
|
|
|
if(rCharParams.m_ambientLight.m_Id>=0) pObj->m_DynLMMask |= (1<<rCharParams.m_ambientLight.m_Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
CCObject * pObj1 = NULL;
|
|
if (getShaderTemplates(1)[0] > 0)
|
|
{
|
|
pObj1 = g_GetIRenderer()->EF_GetObject(true);
|
|
pObj1->CloneObject(pObj);
|
|
pObj1->m_ObjFlags |= FOB_TRANS_MASK;
|
|
pObj->m_SortId += 10;
|
|
}
|
|
|
|
// HACK HACK HACK
|
|
// If we have less then 10 bones it's not the character
|
|
if (m_pMesh->numBoneInfos() > 10)
|
|
pObj->m_ObjFlags |= FOB_ENVLIGHTING;
|
|
|
|
if (g_GetCVars()->ca_EnableLightUpdate() && this == m_pParent->GetSubmesh(0))
|
|
{
|
|
m_pParent->UpdateHeatSources (pObj, RendParams);
|
|
m_pParent->UpdateDynBoundLights (pObj, RendParams);
|
|
}
|
|
|
|
if (!g_GetCVars()->ca_NoDraw())
|
|
AddCurrentRenderData (pObj,pObj1,RendParams);
|
|
|
|
if (g_GetCVars()->ca_EnableDecals() && !RendParams.bRenderIntoShadowMap)
|
|
AddDecalRenderData (pObj,RendParams);
|
|
*/
|
|
}
|
|
|
|
void CryModelSubmesh::UpdateMorphEffectors (float fDeltaTimeSec)
|
|
{
|
|
unsigned numMorphTargets = (unsigned)m_arrMorphEffectors.size();
|
|
unsigned nMorphTarget;
|
|
for (nMorphTarget = 0; nMorphTarget < numMorphTargets; ++nMorphTarget)
|
|
{
|
|
CryModEffMorph& rMorph = m_arrMorphEffectors[nMorphTarget];
|
|
rMorph.Tick(fDeltaTimeSec);
|
|
}
|
|
// clean up the array of morph targets from unused ones
|
|
while (!m_arrMorphEffectors.empty() && !m_arrMorphEffectors.back().isActive())
|
|
m_arrMorphEffectors.pop_back();
|
|
}
|
|
|
|
void CryModelSubmesh::RunMorph (int nMorphTargetId, const CryCharMorphParams&Params)
|
|
{
|
|
// find the first free slot in the array of morph target, or create a new one and start morphing there
|
|
unsigned nMorphEffector;
|
|
for (nMorphEffector = 0; nMorphEffector < m_arrMorphEffectors.size(); ++nMorphEffector)
|
|
{
|
|
if (!m_arrMorphEffectors[nMorphEffector].isActive())
|
|
{
|
|
m_arrMorphEffectors[nMorphEffector].StartMorph (nMorphTargetId, Params);
|
|
return;
|
|
}
|
|
}
|
|
m_arrMorphEffectors.resize (m_arrMorphEffectors.size()+1);
|
|
m_arrMorphEffectors.back().StartMorph (nMorphTargetId, Params);
|
|
}
|
|
|
|
bool CryModelSubmesh::RunMorph (const char* szMorphTarget,const CryCharMorphParams&Params,bool bShowNotFoundWarning)
|
|
{
|
|
assert(IsValidString(szMorphTarget));
|
|
|
|
int nMorphTargetId = m_pMesh->findMorphTarget(szMorphTarget);
|
|
if (nMorphTargetId < 0)
|
|
{
|
|
if(g_GetCVars()->ca_AnimWarningLevel() > 0 && bShowNotFoundWarning)
|
|
g_GetLog()->LogWarning ("\002CryModelSubmesh::RunMorph: Morph Target \"%s\" Not Found for character \"%s\"", szMorphTarget, m_pMesh->getFilePathCStr());
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
RunMorph (nMorphTargetId, Params);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool CryModelSubmesh::StopMorph (int nMorphTargetId)
|
|
{
|
|
unsigned numStopped = 0;
|
|
for (;;)
|
|
{
|
|
CryModEffMorph* pEffector = getMorphTarget(nMorphTargetId);
|
|
if (!pEffector)
|
|
break;
|
|
pEffector->stop();
|
|
++numStopped;
|
|
}
|
|
return numStopped > 0;
|
|
}
|
|
|
|
bool CryModelSubmesh::SetMorphTime (int nMorphTargetId, float fTime)
|
|
{
|
|
CryModEffMorph* pEffector = getMorphTarget(nMorphTargetId);
|
|
if (pEffector)
|
|
{
|
|
pEffector->setTime (fTime);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool CryModelSubmesh::SetMorphSpeed(int nMorphTargetId, float fSpeed)
|
|
{
|
|
CryModEffMorph* pEffector = getMorphTarget(nMorphTargetId);
|
|
if (pEffector)
|
|
{
|
|
pEffector->setSpeed (fSpeed);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CryModelSubmesh::StopAllMorphs()
|
|
{
|
|
m_arrMorphEffectors.clear();
|
|
}
|
|
|
|
void CryModelSubmesh::FreezeAllMorphs()
|
|
{
|
|
for (MorphEffectorArray::iterator it = m_arrMorphEffectors.begin(); it != m_arrMorphEffectors.end(); ++it)
|
|
it->freeze();
|
|
}
|
|
|
|
//returns the morph target effector, or NULL if no such effector found
|
|
CryModEffMorph* CryModelSubmesh::getMorphTarget (int nMorphTargetId)
|
|
{
|
|
unsigned nMorphEffector;
|
|
for (nMorphEffector = 0; nMorphEffector < m_arrMorphEffectors.size(); ++nMorphEffector)
|
|
if (m_arrMorphEffectors[nMorphEffector].getMorphTargetId() == nMorphTargetId)
|
|
return &m_arrMorphEffectors[nMorphEffector];
|
|
return NULL;
|
|
}
|
|
|
|
void CryModelSubmesh::AddDecal (CryEngineDecalInfo& Decal)
|
|
{
|
|
if (!m_pDecalManager)
|
|
m_pDecalManager = new CryCharDecalManager (m_pMesh->getGeometryInfo(0));
|
|
|
|
m_pDecalManager->Add (Decal);
|
|
}
|
|
|
|
|
|
// returns true if the given submesh is visible
|
|
bool CryModelSubmesh::IsVisible()
|
|
{
|
|
return (m_nFlags & FLAG_VISIBLE) != 0;
|
|
}
|
|
|
|
// depending on bVisible, either makes the submesh visible or invisible
|
|
void CryModelSubmesh::SetVisible (bool bVisible)
|
|
{
|
|
if (bVisible)
|
|
m_nFlags |= FLAG_VISIBLE;
|
|
else
|
|
m_nFlags &= ~FLAG_VISIBLE;
|
|
}
|
|
|
|
void CryModelSubmesh::ClearDecals()
|
|
{
|
|
if (m_pDecalManager)
|
|
m_pDecalManager->clear();
|
|
}
|
|
|
|
void CryModelSubmesh::PreloadResources(float fDistance, float fTime, int nFlags)
|
|
{
|
|
for (unsigned i = 0; i < m_pMesh->numLODs(); ++i)
|
|
if (m_pLeafBuffers[i])
|
|
g_GetIRenderer()->EF_PrecacheResource(m_pLeafBuffers[i], fDistance, fTime, nFlags);
|
|
}
|