#include "stdafx.h" #include "cvars.h" #include #include #include #include #include "CryCharReShadowVolume.h" #include "CryModelState.h" #include "VertexBufferArrayDrivers.h" #include "CryModel.h" #include "CryCharBody.h" #include "CryCharDecalManager.h" #include "CryModelSubmesh.h" #include #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* 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* 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; inumLODs(); i++) { if(!m_pLeafBuffers[i]) return; if(list2* &pMats = m_pLeafBuffers[i]->m_pMats) { for (int m=0; mCount(); 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 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& arrMats = *(pVertBuffer->m_pMats = new list2); 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; inumFaces(); 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]]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* pMats = new list2; 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; im_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; nPrimGroupm_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 (maxUpdateBuffer (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(snnumVertices()); 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(inumExtTangents()); // 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<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<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; im_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< 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< 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); }