//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2002. // ------------------------------------------------------------------------- // File name: statobjshadow.cpp // Version: v1.00 // Created: 28/5/2001 by Vladimir Kajalin // Compilers: Visual Studio.NET // Description: shadow maps // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "StatObj.h" #include "meshidx.h" #include // IEdgeConnectivityBuilder #include "StencilShadowConnectivity.h" #include "IndoorVolumes.h" #include "IndoorShadowVolumes.h" #include "CRETriMeshShadow.h" #include "3dengine.h" ItShadowVolume * CStatObj::MakeConnectivityInfo(CIndexedMesh * pMesh, const Vec3d & vOrigin, CStatObj * pStatObj) { //if it doestn exists yet, create one tShadowVolume * ptSvObj = new tShadowVolume; ptSvObj->pSvObj = new CShadowVolObject( ); for (int i=0; im_nFaceCount; i++) { CObjFace *cf=&pMesh->m_pFaces[i]; for (int v=0; v<3; v++) { cf->m_Vecs[v].x=pMesh->m_pVerts[pMesh->m_pFaces[i].v[v]].x; cf->m_Vecs[v].y=pMesh->m_pVerts[pMesh->m_pFaces[i].v[v]].y; cf->m_Vecs[v].z=pMesh->m_pVerts[pMesh->m_pFaces[i].v[v]].z; } //v //calc plane equation cf->m_Plane.CalcPlane(cf->m_Vecs[2],cf->m_Vecs[1],cf->m_Vecs[0]); } //i //set the same light area geometry tContainer tCont; tCont.pObj = pStatObj; tCont.pSV=NULL; //ptSvObj->pSvObj->AddGeometry(this); ptSvObj->pSvObj->AddGeometry(tCont); //mark the geometry as shared ptSvObj->pSvObj->m_dwFlags|=FLAG_GEOMETRY_SHARED; //set the position of the shadow volumes ptSvObj->pSvObj->SetPos(/*pParams->*/vOrigin); //precalc edges if(!ptSvObj->pSvObj->CreateConnectivityInfo()) { // delete empty objects ptSvObj->Release(); ptSvObj=NULL; } //assign to the istatobj return (ptSvObj); } ItShadowVolume * CStatObj::MakeConnectivityInfoFromCompiledData(void * pStream, int & nPos, CStatObj * pStatObj) { //if it doestn exists yet, create one tShadowVolume * ptSvObj = new tShadowVolume; ptSvObj->pSvObj = new CShadowVolObject( ); tContainer tCont; tCont.pObj = pStatObj; tCont.pSV=NULL; //ptSvObj->pSvObj->AddGeometry(this); ptSvObj->pSvObj->AddGeometry(tCont); //mark the geometry as shared ptSvObj->pSvObj->m_dwFlags|=FLAG_GEOMETRY_SHARED; //set the position of the shadow volumes ptSvObj->pSvObj->SetPos(Vec3d(0,0,0)); IEdgeConnectivityBuilder * iBuilder = Get3DEngine()->GetNewConnectivityBuilder(); ptSvObj->pSvObj->GetEdgeConnectivity() = iBuilder->ConstructConnectivity(); nPos += ptSvObj->pSvObj->GetEdgeConnectivity()->Serialize(false,&((byte*)pStream)[nPos],1000000, NULL);//GetLog()); return (ptSvObj); } // NOTE: Current implementation doesn't take the limit LOD parameter into account void CStatObj::RenderShadowVolumes(const SRendParams *pParams, int nLimitLod) { // assert(pParams->lSource); CDLight tTempLight; tTempLight = *(pParams->pShadowVolumeLightSource); Matrix44 tInvRot; if(pParams->pMatrix) { tInvRot=*pParams->pMatrix; } else { //tInvRot.Identity(); //tInvRot=GetTranslationMat(pParams->vPos)*tInvRot; //tInvRot=GetRotationZYX44(-pParams->vAngles*gf_DEGTORAD)*tInvRot; //NOTE: angles in radians and negated //OPTIMISED_BY_IVO tInvRot = Matrix34::CreateRotationXYZ(pParams->vAngles*gf_DEGTORAD, pParams->vPos ); //set scaling and translation in one function call tInvRot = GetTransposed44(tInvRot); //TODO: remove this after E3 and use Matrix34 instead of Matrix44 } tInvRot.Invert44(); Vec3d vPos=tInvRot.TransformPointOLD(tTempLight.m_Origin); tTempLight.m_vObjectSpacePos=vPos; /* { char str[256]; snprintf(str,"'%s' %p (%.2f %.2f %.2f) (%.2f %.2f %.2f)\n",pParams->lSource->m_Name,this, tTempLight.m_Origin.x,tTempLight.m_Origin.y,tTempLight.m_Origin.z, vPos.x,vPos.y,vPos.z); OutputDebugString(str); } */ //FIXME: move this code in the conn. info calculation //assign vertices and calc planes CIndexedMesh *pMesh = GetTriData(); if (!GetShadowVolume()) { if(pMesh) { SetShadowVolume(CStatObj::MakeConnectivityInfo(pMesh, Vec3d(0,0,0)/* pParams->vOrigin*/, this)); FreeTriData(); // source geometry is needed only for stencil shadows } else return; } ItShadowVolume *ptSvObj=GetShadowVolume(); CShadowVolObject *pSvObj=ptSvObj->GetShadowVolume(); IStencilShadowConnectivity* pConnectivity = pSvObj->GetEdgeConnectivity(); if(!pParams->pShadowVolumeLightSource || !pConnectivity) return; if (/*!pSvObj->GetNumFaces() && */!pConnectivity->IsStandalone()) return; if(!m_bOpenEdgesTested && pConnectivity->numOrphanEdges()) { #if !defined(LINUX) Warning(0,m_szFileName,"%d open edges found during shadow volume calculations for %s %s", pConnectivity->numOrphanEdges(), m_szFileName, m_szGeomName ); #endif m_bOpenEdgesTested = true; } //GetRenderer()->Draw3dBBox(Vec3d(vPos.x-0.5f,vPos.y-0.5f,vPos.z-0.5f),Vec3d(vPos.x+0.5f,vPos.y+0.5f,vPos.z+0.5f)); //////////////////////////////////////////////////////////////////////// //this will be done in RE right before rendering //pSvObj->CalculateDynamicShadowVolume(tTempLight); //pSvObj->CreateDynamicShadowVolumeBuffer(); if(!pSvObj->m_pReMeshShadow) pSvObj->m_pReMeshShadow = (CRETriMeshShadow *)GetRenderer()->EF_CreateRE(eDATA_TriMeshShadow); // draw shadow volumes if(pSvObj->m_pReMeshShadow) { // get the object CCObject *pObj; pObj = GetRenderer()->EF_GetObject(true); pObj->m_DynLMMask = pParams->nDLightMask; /* if(!m_nRenderStackLevel) { pObj->m_nScissorX1 = pParams->nScissorX1; pObj->m_nScissorY1 = pParams->nScissorY1; pObj->m_nScissorX2 = pParams->nScissorX2; pObj->m_nScissorY2 = pParams->nScissorY2; }*/ // the translation is in the object, otherwise we get z-fighting // with the shaders // hh unused pSvObj->m_pReMeshShadow->m_vOrigin = Vec3d(0,0,0); if (pParams->pMatrix) pObj->m_Matrix = *pParams->pMatrix; else mathCalcMatrix(pObj->m_Matrix, pParams->vPos, pParams->vAngles, Vec3d(1,1,1), Cry3DEngineBase::m_CpuFlags); pObj->m_ObjFlags |= FOB_TRANS_MASK; //assign shadow volume resources //allow the calculation to be done right before rendering pSvObj->m_pReMeshShadow->m_pSvObj = ptSvObj; pSvObj->m_pReMeshShadow->m_nCurrInst = -1; pObj->m_CustomData = pParams->pCaller; pObj->m_TempVars[0] = pParams->fShadowVolumeExtent; IShader * pEff = ((C3DEngine*)Get3DEngine())->m_pSHStencil; GetRenderer()->EF_AddEf(0, (CRendElement *)pSvObj->m_pReMeshShadow , pEff, NULL, pObj, -1, NULL, pParams->nSortValue); } } CVolume::~CVolume() { for (ContainerListIt i=m_lstObjects.begin();i!=m_lstObjects.end();) { RemoveGeometry(NULL,i); i=m_lstObjects.begin(); } //i m_lstObjects.clear(); //for the sake of clarity } bool CShadowVolObject::CreateConnectivityInfo( void ) { //IStatObj *ob=m_lstStatObjs[0]; assert(ob); IStatObj *ob=m_lstObjects[0].pObj; assert(ob); CIndexedMesh *pMesh=ob->GetTriData(); if (!pMesh) return false; // to prevent crash, this could be if the file versions are changing) //list of faces is shared from statobj CObjFace * pFaceList = pMesh->m_pFaces; int nNumFaces=pMesh->m_nFaceCount; Vec3d *pVert=pMesh->m_pVerts; IEdgeConnectivityBuilder * iBuilder = Get3DEngine()->GetNewStaticConnectivityBuilder(); assert(iBuilder); iBuilder->ReserveForTriangles(nNumFaces,pMesh->m_nVertCount); for(int i=0;im_dwFlags & FLAG_SKIP_SHADOWVOLUME) continue; // with welding unsigned short a=cf->v[0],b=cf->v[1],c=cf->v[2]; iBuilder->AddTriangleWelded(a,b,c,pVert[a],pVert[b],pVert[c]); } m_pEdgeConnectivity=iBuilder->ConstructConnectivity(); DWORD dwVertCount=0,dwTriCount=0; if(m_pEdgeConnectivity) { m_pEdgeConnectivity->GetStats(dwVertCount,dwTriCount); /* #ifdef _DEBUG char str[256]; snprintf(str, sizeof(str), "StencilEdgeConnectivity Indoor Stats %p: %d/%d Vertices %d/%d Faces\n",m_pEdgeConnectivity,dwVertCount,pMesh->m_nVertCount,dwTriCount,nNumFaces); OutputDebugString(str); #endif*/ } return dwVertCount && dwTriCount; } void CVolume::SetPos(const Vec3d &vPos) { m_vOrigin=vPos; pe_params_pos par_pos; par_pos.pos=vPos; //for (iphysobjit i=m_lstPhysObjs.begin();i!=m_lstPhysObjs.end();i++) for (ContainerListIt i=m_lstObjects.begin();i!=m_lstObjects.end();i++) { IPhysicalEntity *pEnt=(*i).pEnt; if (pEnt) pEnt->SetParams(&par_pos); } //i } void CVolume::AddGeometry(tContainer tCont) { m_vMins.CheckMin(tCont.pObj->GetBoxMin()); m_vMaxs.CheckMax(tCont.pObj->GetBoxMax()); /* m_lstStatObjs.push_back(pSource); if (pEnt) m_lstPhysObjs.push_back(pEnt); */ m_lstObjects.push_back(tCont); m_nObjCount++; } CShadowVolObject::~CShadowVolObject() { /* //do not delete 'cause the source is a cstatobj if (m_pFaceList) { delete [] m_pFaceList; m_pFaceList=NULL; } */ if(m_pEdgeConnectivity) { m_pEdgeConnectivity->Release(); m_pEdgeConnectivity=0; } FreeVertexBuffers(); } void CShadowVolObject::FreeVertexBuffers() { if (m_pSystemVertexBuffer) { delete [] m_pSystemVertexBuffer; m_pSystemVertexBuffer=0; } if (m_pReMeshShadow) { m_pReMeshShadow->Release(); m_pReMeshShadow=NULL; } /*if (m_pReMeshAdditionalShadow) { //FIXME:Move deallocation into renderelements if (m_pReMeshAdditionalShadow->m_pShadowVolEdgesList) { //those edges are shared! //delete [] m_pReMeshAdditionalShadow->m_pShadowVolEdgesList; m_pReMeshAdditionalShadow->m_pShadowVolEdgesList=NULL; } m_pReMeshAdditionalShadow->Release(); m_pReMeshAdditionalShadow=NULL; }*/ m_nNumVertices=0; } void CShadowVolObject::CheckUnload() { if(m_pReMeshShadow) m_pReMeshShadow->mfCheckUnload(); } void CShadowVolObject::RebuildShadowVolumeBuffer( const CDLight &lSource, float fReadyShadowVolumeExtent ) // lSource has to be object relative { #ifdef WIN64 #pragma warning( push ) //AMD Port #pragma warning( disable : 4311 ) #endif #ifdef WIN64 #pragma warning( pop ) //AMD Port #endif if(!m_pEdgeConnectivity || (/*!m_nNumFaces && */!m_pEdgeConnectivity->IsStandalone())) { // PrepareShadowVolumeVertexBuffer(0,0); return; } //! M.M. for debugging stencil shadows (0 is default, use !=0 to force reacalculation of indoor stencil shadow volumes) IEdgeDetector * iEdgeDetector = Get3DEngine()->GetEdgeDetector(); Vec3d vObjectSpaceLight=lSource.m_vObjectSpacePos; Vec3d vWorldSpaceLight=lSource.m_Origin; // baustelle IStatObj *ob=m_lstObjects[0].pObj; CIndexedMesh *pMesh=ob->GetTriData(); assert(m_pEdgeConnectivity->IsStandalone()); if (m_pEdgeConnectivity->IsStandalone()) { iEdgeDetector->BuildSilhuetteFromPos(m_pEdgeConnectivity, vObjectSpaceLight, NULL); } /*else { unsigned *pTriOriBitfield=iEdgeDetector->getOrientationBitfield(m_nNumFaces); for(int nFace = 0; nFace < m_nNumFaces;) { unsigned nOrientBit = 1; do { CObjFace *cf=&m_pFaceList[nFace]; if (cf->m_dwFlags & FLAG_SKIP_SHADOWVOLUME)//! transparent surfaces don't cast shadows { nFace++; continue; } float dist1=cf->m_Plane.DistFromPlane(vObjectSpaceLight); if(dist1>0) *pTriOriBitfield |= nOrientBit; nOrientBit <<= 1; } while (((++nFace) < m_nNumFaces) && (nOrientBit) != 0); ++pTriOriBitfield; } if(!pMesh) return; // to prevent crash, this could be if the file versions are changing iEdgeDetector->BuildSilhuetteFromBitfield(m_pEdgeConnectivity,pMesh->m_pVerts); }*/ unsigned nNumIndices = iEdgeDetector->numShadowVolumeIndices(); unsigned nNumVertices = iEdgeDetector->numShadowVolumeVertices(); // float fShadowVolumeExtent=20.0f; /* // [5/12/2002] Marco's NOTE: removed this part, because to allow shadows to cross // portals, we should not check for the object being inside/outside the // area / so the check is now unified like for outdoors // extend as little as possible to save fill rate M.M. *********************************************************************** Start // I have to be in indoor otherwise shadow volumes doesn't make sense // to prevent crash, but this should never be - without this info I assume a big value would do it if(inpArea) { Vec3d vMinObjBBox=GetBBoxMin(true),vMaxObjBBox=GetBBoxMax(true); // debugging // GetRenderer()->Draw3dBBox(vMinObjBBox,vMaxObjBBox); // if the lightsource is inside the object, use big value // - it's dangerous to extrude too much (althougth extrude to infinity is theoretical possible) // calculate the minimum extrusion level (to get the object bounding box extruded outside of the room) fShadowVolumeExtent = inpArea->CalculateMinimumShadowExtrusion(vWorldSpaceLight,vMinObjBBox,vMaxObjBBox); // assert(fShadowVolumeExtent>=1.0f); } else */ { // calculate optimal extent for outdoor entity // todo take into account object scale //Vec3d vMinObjBBox=GetBBoxMin(true),vMaxObjBBox=GetBBoxMax(true); /* Vec3d vMinObjBBox=GetBBoxMin(),vMaxObjBBox=GetBBoxMax(); Vec3d vObjCenter = (vMinObjBBox+vMaxObjBBox)*0.5; float fObjRadius = (vMaxObjBBox-vMinObjBBox).Length()*0.5f; float fObjLightDist = GetDistance(vObjectSpaceLight, vObjCenter) - fObjRadius; if(fObjLightDist<0.01f) fObjLightDist=0.01f; fShadowVolumeExtent = lSource.m_fRadius/fObjLightDist; if(fShadowVolumeExtent<1.f) fShadowVolumeExtent=1.f; else if(fShadowVolumeExtent>20.f) fShadowVolumeExtent=20.f;*/ } // extend as little as possible to save fill rate M.M. *********************************************************************** End PrepareShadowVolumeVertexBuffer(nNumIndices, nNumVertices); if (nNumVertices > 1 && nNumIndices > 1) { // m_arrIndices.reinit(n) iEdgeDetector->meshShadowVolume (vObjectSpaceLight, fReadyShadowVolumeExtent, (Vec3d *)m_pSystemVertexBuffer, &m_arrIndices[0]); CRETriMeshShadow::ShadVolInstanceInfo * pSVI = &m_pReMeshShadow->m_arrLBuffers[m_pReMeshShadow->m_nCurrInst]; if( pSVI->pVB && pSVI->pVB->m_SecVertCount == (int)m_nNumVertices && pSVI->pVB->m_Indices.m_nItems == (int)nNumIndices ) { pSVI->pVB->UpdateSysIndices(nNumIndices ? &m_arrIndices[0] : 0, nNumIndices); pSVI->pVB->UpdateSysVertices(m_pSystemVertexBuffer,m_nNumVertices); } else { if(pSVI->pVB) GetRenderer()->DeleteLeafBuffer(pSVI->pVB); // hack: todo: make m_arrIndices list2 pSVI->pVB = GetRenderer()->CreateLeafBufferInitialized( m_pSystemVertexBuffer, m_nNumVertices, VERTEX_FORMAT_P3F, &m_arrIndices[0], nNumIndices, R_PRIMV_TRIANGLES, "ShadowVolume", eBT_Dynamic, 1 , 0, 0, this); assert(m_nNumVertices && nNumIndices); pSVI->pVB->SetChunk(0,0,m_nNumVertices,0,nNumIndices); assert((*(pSVI->pVB)).m_pMats->Count()); } /* if(!pSVI->pVB) pSVI->pVB = GetRenderer()->CreateBuffer(m_nNumVertices, VERTEX_FORMAT_P3F, &pSVI->plstIndices->GetAt(0), pSVI->plstIndices->Count(), "InstanceShadowVolume"); GetRenderer()->UpdateBuffer(pSVI->pVB, m_pSystemVertexBuffer, m_nNumVertices,true); GetRenderer()->UpdateIndices(pSVI->pVB, &m_arrIndices[0], m_arrIndices.size()); */ /* #ifdef _DEBUG { for (unsigned i = 0; i < nNumIndices; ++i) assert(m_arrIndices[i] < nNumVertices); } #endif */ /* m_pReMeshShadow->m_vOrigin=rParams->vPos; m_pReMeshShadow->m_vAngles=rParams->vAngles; //get the effect object from the renderer CCObject *pObj = renderer->EF_GetObject(true); pObj->m_Trans = rParams->vPos; pObj->m_Angs = rParams->vAngles; pObj->m_Scale = 1.0f; m_pReMeshShadow->m_nNumIndices = nNumIndices; m_pReMeshShadow->mfCheckUpdate(); //renderer->UpdateBuffer (m_pRenShadowVolumeBuffer, m_pMemShadowVolumeBuffer, nNumVertices, true, 0); renderer->UpdateBuffer(m_pRenShadowVolumeLBuffer->m_pVertexBuffer,m_pMemShadowVolumeBuffer,nNumVertices,true); renderer->UpdateIndices(m_pRenShadowVolumeLBuffer->m_pVertexBuffer,&m_arrIndices[0],nNumIndices); renderer->EF_AddEf(0, (CRendElement *)m_pReMeshShadow , rParams->pEff, pObj, -1, NULL, rParams->nSortValue); */ // m_pReMeshShadow->m_nNumIndices = nNumIndices; // m_pReMeshShadow->mfCheckUpdate(0); // call before update, to make sure video buffer is there // assert(m_pRenShadowVolumeLBuffer); // assert(m_pRenShadowVolumeLBuffer->m_pVertexBuffer); // GetRenderer()->UpdateBuffer(m_pRenShadowVolumeLBuffer->m_pVertexBuffer,pSystemBuffer,nNumVertices,true); // GetRenderer()->UpdateIndices(m_pRenShadowVolumeLBuffer->m_pVertexBuffer,&m_arrIndices[0],nNumIndices); // m_pRenShadowVolumeLBuffer->m_UpdateVBufferMask &= ~1; } else { int i=0; } } void CVolume::RemoveGeometry(IStatObj *pSource,ContainerListIt it/* =NULL */) { ContainerListIt i = m_lstObjects.end(); if (it!=m_lstObjects.end()) i=it; else { for (ContainerListIt it2=m_lstObjects.begin();it2!=m_lstObjects.end();it2++) { tContainer tempCont=(*it2); if (tempCont.pObj==pSource) { i=it2; break; } } //it2 } if (i!=m_lstObjects.end()) { tContainer tempCont=(*i); IStatObj *pSource=tempCont.pObj; if (pSource && (!(m_dwFlags & FLAG_GEOMETRY_SHARED))) Get3DEngine()->ReleaseObject(pSource); IPhysicalEntity *pEnt=(tempCont.pEnt); if (pEnt) GetPhysicalWorld()->DestroyPhysicalEntity(pEnt); m_lstObjects.erase(i); } } // Prepare the resources for rendering shadow volumes // This include: // index and vertex in-memory arrays // render object (m_pReMeshShadow, m_pRenShadowVolumeBuffer, whatever support they need) void CShadowVolObject::PrepareShadowVolumeVertexBuffer( unsigned nNumIndices, unsigned nNumVertices ) { if (!nNumIndices || !nNumVertices) return; bool bRecreate = false; // Realloc index in-mem buffer if (m_arrIndices.size() < nNumIndices) { m_arrIndices.reinit(nNumIndices); bRecreate = true; } // Realloc vertex in-mem buffer if (m_nNumVertices < nNumVertices) { m_nNumVertices = nNumVertices; bRecreate = true; } if (bRecreate) { if (m_pSystemVertexBuffer) { delete [] m_pSystemVertexBuffer; m_pSystemVertexBuffer=NULL; } } assert(m_pReMeshShadow); // should be called from inside this object // m_pReMeshShadow = (CRETriMeshShadow *)GetRenderer()->EF_CreateRE(eDATA_TriMeshShadow); if(!m_pSystemVertexBuffer) { assert(m_nNumVertices); m_pSystemVertexBuffer = new Vec3d[m_nNumVertices]; assert(m_pSystemVertexBuffer); } }