//////////////////////////////////////////////////////////////////////////// // // Crytek Engine Source File. // Copyright (C), Crytek Studios, 2002. // ------------------------------------------------------------------------- // File name: decals.cpp // Version: v1.00 // Created: 28/5/2001 by Vladimir Kajalin // Compilers: Visual Studio.NET // Description: draw, create decals on the world // ------------------------------------------------------------------------- // History: // //////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "DecalManager.h" #include "3dengine.h" #include #include "ObjMan.h" #define COMPLEX_DECAL_MIN_SIZE 0.1f CDecalManager::CDecalManager(C3DEngine * p3DEngine) { m_nCurDecal = 0; memset(m_arrbActiveDecals,0,sizeof(m_arrbActiveDecals)); m_p3DEngine = p3DEngine; m_pShader_ParticleLight = GetRenderer()->EF_LoadShader("ParticleLight", eSH_World, EF_SYSTEM); m_pShader_Decal_VP = GetRenderer()->EF_LoadShader("Decal_VP", eSH_World, EF_SYSTEM); m_pShader_Decal_2D_VP = GetRenderer()->EF_LoadShader("Decal_2D_VP", eSH_World, EF_SYSTEM); } // called from the renderer, draws immideately void CDecalManager::DrawBigDecalsOnTerrain() { if(!GetCVars()->e_decals) return; //GetRenderer()->ResetToDefault(); GetRenderer()->SetState(GS_BLSRC_SRCALPHA | GS_BLDST_ONEMINUSSRCALPHA); GetRenderer()->SetColorOp(eCO_MODULATE, eCO_MODULATE, eCA_Texture|(eCA_Constant<<3), eCA_Texture|(eCA_Constant<<3)); GetRenderer()->SetCullMode(R_CULL_DISABLE); Vec3d vColor = GetSystem()->GetI3DEngine()->GetWorldColor();//* GetRenderer()->SetMaterialColor(vColor.x,vColor.y,vColor.z,1); float fCurrTime = GetTimer()->GetCurrTime(); for(int i=0; iResetToDefault(); } bool CDecalManager::AdjustDecalPosition( CryEngineDecalInfo & DecalInfo, bool bMakeFatTest ) { Matrix44 objMat, objMatInv; int nPartID = DecalInfo.nPartID; IStatObj * pEntObject = DecalInfo.pDecalOwner->GetEntityStatObj(nPartID, &objMat, true); while(!pEntObject && nPartID<16) { // find first visible entity component pEntObject = DecalInfo.pDecalOwner->GetEntityStatObj(++nPartID, &objMat, true); } if(!pEntObject || !pEntObject->GetLeafBuffer()|| nPartID>=16) return false; // transform decal into object space objMatInv = objMat; objMatInv.Invert44(); // put into normal object space hit direction of projection Vec3d vObjSpaceHitDirection = objMatInv.TransformVectorOLD(DecalInfo.vHitDirection); // put into position object space hit position Vec3d vObjSpacePos = objMatInv.TransformPointOLD(DecalInfo.vPos); // get decal directions Vec3d vRi(0,0,0), vUp(0,0,0); if(fabs(vObjSpaceHitDirection.Dot(Vec3d(0,0,1)))>0.999f) { // horiz surface vRi = Vec3d(0,1,0); vUp = Vec3d(1,0,0); } else { vRi = vObjSpaceHitDirection.Cross(Vec3d(0,0,1)); vRi.Normalize(); vUp = vObjSpaceHitDirection.Cross(vRi); vUp.Normalize(); } vRi*=DecalInfo.fSize; vUp*=DecalInfo.fSize; Vec3d vObjSpaceOutPos(0,0,0), vObjSpaceOutNormal(0,0,0), vTmp; CLeafBuffer * pLB = pEntObject->GetLeafBuffer(); if( pLB && pLB->m_pSecVertBuffer && pLB->m_SecVertCount && RayLeafBufferIntersection(pLB, vObjSpacePos, vObjSpaceHitDirection*0.1f, vObjSpaceOutPos, vObjSpaceOutNormal) ) { // now check that none of decal sides will not fly in the air Vec3d vDecalNormal = vObjSpaceOutNormal*DecalInfo.fSize; if( !bMakeFatTest || ( RayLeafBufferIntersection(pLB, vObjSpaceOutPos+vUp, -vDecalNormal, vTmp, vTmp) && RayLeafBufferIntersection(pLB, vObjSpaceOutPos-vUp, -vDecalNormal, vTmp, vTmp) && RayLeafBufferIntersection(pLB, vObjSpaceOutPos+vRi, -vDecalNormal, vTmp, vTmp) && RayLeafBufferIntersection(pLB, vObjSpaceOutPos-vRi, -vDecalNormal, vTmp, vTmp) )) { DecalInfo.vPos = objMat.TransformPointOLD(vObjSpaceOutPos); DecalInfo.vNormal = objMat.TransformVectorOLD(vObjSpaceOutNormal); return true; } } return false; } struct HitPosInfo { HitPosInfo() { memset(this,0,sizeof(HitPosInfo)); } Vec3d vPos,vNormal; float fDistance; }; int __cdecl CDecalManager__CmpHitPos(const void* v1, const void* v2) { HitPosInfo * p1 = (HitPosInfo*)v1; HitPosInfo * p2 = (HitPosInfo*)v2; if(p1->fDistance > p2->fDistance) return 1; else if(p1->fDistance < p2->fDistance) return -1; return 0; } bool CDecalManager::RayLeafBufferIntersection(CLeafBuffer * pLeafBuffer, const Vec3d & vInPos, const Vec3d & vInDir, Vec3d & vOutPos, Vec3d & vOutNormal) { // get position offset and stride int nPosStride = 0; byte * pPos = pLeafBuffer->GetPosPtr(nPosStride, 0, true); // get indices ushort *pInds = pLeafBuffer->GetIndices(0); int nInds = pLeafBuffer->m_Indices.m_nItems; assert(nInds%3 == 0); HitPosInfo arrResult[16]; memset(arrResult,0,sizeof(arrResult)); int nResultCount=0; // find faces for(int i=0; iGetObjManager() ? m_p3DEngine->GetObjManager()->m_fZoomFactor : 1.f; float fDecalDistance = L1Distance2D(DecalInfo.vPos, vCamPos); if( fDecalDistance>fMaxViewDistance || fDecalDistance*fZoom>DecalInfo.fSize*ENTITY_DECAL_DIST_FACTOR*3.f ) return; /* if(DecalInfo.pDecalOwner && DecalInfo.pDecalOwner->GetEntityRenderType() == eERType_Vegetation) { CStatObjInst * pStatObjInst = (CStatObjInst*)DecalInfo.pDecalOwner; if(pStatObjInst->m_fFinalBending) return; // do not spawn on bendable vegetations }*/ // project decal to visible geoemtry if(DecalInfo.bAdjustPos && DecalInfo.nPartID>=0 && DecalInfo.pDecalOwner && !AdjustDecalPosition( DecalInfo, DecalInfo.fSize<=COMPLEX_DECAL_MIN_SIZE )) return; // clamp all decal textures GetRenderer()->SetTexture(DecalInfo.nTid); GetRenderer()->SetTexClampMode(true); // don't go crazy if(DecalInfo.fSize > 4) DecalInfo.fSize = 4; if(DecalInfo.fSize > 1 && GetCVars()->e_decals_neighbor_max_life_time) { // force near decals to fade faster float fCurrTime = GetTimer()->GetCurrTime(); for(int i=0; i fCurrTime+GetCVars()->e_decals_neighbor_max_life_time) m_arrDecals[i].m_fLifeEndTime = fCurrTime+GetCVars()->e_decals_neighbor_max_life_time; } } // loop position in array if(m_nCurDecal>=DECAL_COUNT) m_nCurDecal=0; // free old LB GetRenderer()->DeleteLeafBuffer(m_arrDecals[m_nCurDecal].m_pBigDecalLeafBuffer); m_arrDecals[m_nCurDecal].m_pBigDecalLeafBuffer=0; // just in case DecalInfo.vNormal.Normalize(); bool bBigDecalOnTheGround = false; // remember object we need to follow m_arrDecals[m_nCurDecal].m_nDecalOwnerComponentId = DecalInfo.nPartID; m_arrDecals[m_nCurDecal].m_vWSPos = DecalInfo.vPos; m_arrDecals[m_nCurDecal].m_fWSSize = DecalInfo.fSize; // If owner entity and object is specified - make decal use entity geometry float fObjScale = 1.f; if(DecalInfo.pDecalOwner && DecalInfo.nPartID>=0 && DecalInfo.fSize>COMPLEX_DECAL_MIN_SIZE) { // get object pointer Matrix44 objMat; IStatObj * pEntObject = DecalInfo.pDecalOwner->GetEntityStatObj(DecalInfo.nPartID, &objMat); if(pEntObject) { CLeafBuffer * pSourceLeafBuffer = pEntObject->GetLeafBuffer(); // transform decal into object space objMat.Invert44(); // put into normal object space hit direction of projection DecalInfo.vNormal = -objMat.TransformVectorOLD(DecalInfo.vHitDirection); // put into position object space hit position DecalInfo.vPos = objMat.TransformPointOLD(DecalInfo.vPos); // find object scale if(DecalInfo.pDecalOwner->GetEntityRenderType()==eERType_Vegetation) fObjScale = DecalInfo.pDecalOwner->GetScale(); else { Vec3d vTest(0,0,1.f); vTest = objMat.TransformVectorOLD(vTest); fObjScale = 1.f/vTest.len(); } if(fObjScale<0.01f) return; // transform size into object space DecalInfo.fSize/=fObjScale; // make decal geometry m_arrDecals[m_nCurDecal].m_pBigDecalLeafBuffer = MakeBigDecalLeafBuffer(pSourceLeafBuffer, DecalInfo.vPos, DecalInfo.fSize, DecalInfo.vNormal, DecalInfo.nTid); if(!m_arrDecals[m_nCurDecal].m_pBigDecalLeafBuffer) return; // no geometry found ITexPic * pTexPic = GetRenderer()->EF_GetTextureByID(DecalInfo.nTid); if(pTexPic) { pTexPic->SetFilter(FILTER_LINEAR); pTexPic->SetClamp(true); } } else { Warning( 0,0,"CDecalManager::Spawn: nPartID points to empty object"); return; } } else if(DecalInfo.pDecalOwner && (!DecalInfo.pDecalOwner->IsStatic() || DecalInfo.pDecalOwner->GetEntityRenderType() == eERType_Vegetation) && DecalInfo.nPartID>=0) { // transform decal from world space into entity space Matrix44 objMat; IStatObj * pEntObject = DecalInfo.pDecalOwner->GetEntityStatObj(DecalInfo.nPartID, &objMat); assert(pEntObject); objMat.Invert44(); DecalInfo.vNormal = objMat.TransformVectorOLD(DecalInfo.vNormal); DecalInfo.vNormal.Normalize(); DecalInfo.vPos = objMat.TransformPointOLD(DecalInfo.vPos); // find object scale if(DecalInfo.pDecalOwner->GetEntityRenderType()==eERType_Vegetation) fObjScale = DecalInfo.pDecalOwner->GetScale(); else { Vec3d vTest(0,0,1.f); vTest = objMat.TransformVectorOLD(vTest); fObjScale = 1.f/vTest.len(); } ITexPic * pTexPic = GetRenderer()->EF_GetTextureByID(DecalInfo.nTid); if(pTexPic) { pTexPic->SetFilter(FILTER_LINEAR); pTexPic->SetClamp(true); } } else { if(DecalInfo.fSize>COMPLEX_DECAL_MIN_SIZE && !DecalInfo.pDecalOwner && (DecalInfo.vPos.z-pTerrain->GetZApr(DecalInfo.vPos.x,DecalInfo.vPos.y))GetHoleSafe(x,y)) return; bBigDecalOnTheGround = true; ITexPic * pTexPic = GetRenderer()->EF_GetTextureByID(DecalInfo.nTid); if(pTexPic) { pTexPic->SetFilter(FILTER_LINEAR); pTexPic->SetClamp(true); } } DecalInfo.pDecalOwner = NULL; } DecalInfo.vNormal.Normalize(); // spawn m_arrDecals[m_nCurDecal].m_vPos = DecalInfo.vPos; m_arrDecals[m_nCurDecal].m_vPos += DecalInfo.vNormal*0.01f/fObjScale; m_arrDecals[m_nCurDecal].m_bOnTheGround = bBigDecalOnTheGround; if(fabs(DecalInfo.vNormal.Dot(Vec3d(0,0,1)))>0.999f) { // horiz surface m_arrDecals[m_nCurDecal].m_vRight = Vec3d(0,1,0); m_arrDecals[m_nCurDecal].m_vUp = Vec3d(1,0,0); } else { m_arrDecals[m_nCurDecal].m_vRight = DecalInfo.vNormal.Cross(Vec3d(0,0,1)); m_arrDecals[m_nCurDecal].m_vRight.Normalize(); m_arrDecals[m_nCurDecal].m_vUp = DecalInfo.vNormal.Cross(m_arrDecals[m_nCurDecal].m_vRight); m_arrDecals[m_nCurDecal].m_vUp.Normalize(); } /* // check that decal will look ok here if(!DecalInfo.pDecalOwner && DecalInfo.fSize<=COMPLEX_DECAL_MIN_SIZE) { ray_hit hit; float fSize = DecalInfo.fSize*2; if(GetPhysicalWorld()->RayWorldIntersection( DecalInfo.vPos - DecalInfo.vNormal*fSize + m_arrDecals[m_nCurDecal].m_vUp*fSize, DecalInfo.vNormal*fSize*2, ent_all, rwi_stop_at_pierceable,&hit,1) && GetPhysicalWorld()->RayWorldIntersection( DecalInfo.vPos - DecalInfo.vNormal*fSize - m_arrDecals[m_nCurDecal].m_vUp*fSize, DecalInfo.vNormal*fSize*2, ent_all, rwi_stop_at_pierceable,&hit,1) && GetPhysicalWorld()->RayWorldIntersection( DecalInfo.vPos - DecalInfo.vNormal*fSize + m_arrDecals[m_nCurDecal].m_vRight*fSize, DecalInfo.vNormal*fSize*2, ent_all, rwi_stop_at_pierceable,&hit,1) && GetPhysicalWorld()->RayWorldIntersection( DecalInfo.vPos - DecalInfo.vNormal*fSize - m_arrDecals[m_nCurDecal].m_vRight*fSize, DecalInfo.vNormal*fSize*2, ent_all, rwi_stop_at_pierceable,&hit,1)) { int t=0; // ok } else return; // position is bad } */ // rotate vectors AngleAxis rotation(DecalInfo.fAngle,DecalInfo.vNormal); m_arrDecals[m_nCurDecal].m_vRight = rotation*m_arrDecals[m_nCurDecal].m_vRight; m_arrDecals[m_nCurDecal].m_vUp = rotation*m_arrDecals[m_nCurDecal].m_vUp; m_arrDecals[m_nCurDecal].m_vFront = DecalInfo.vNormal; m_arrDecals[m_nCurDecal].m_fSize = DecalInfo.fSize; m_arrDecals[m_nCurDecal].m_fLifeEndTime = GetTimer()->GetCurrTime() + DecalInfo.fLifeTime*GetCVars()->e_decals_life_time_scale; m_arrDecals[m_nCurDecal].m_nTexId = DecalInfo.nTid; m_arrDecals[m_nCurDecal].m_fAngle = DecalInfo.fAngle; m_arrDecals[m_nCurDecal].m_pStatObj = DecalInfo.pStatObj; m_arrDecals[m_nCurDecal].m_vAmbient = Get3DEngine()->GetAmbientColorFromPosition(DecalInfo.vPos); m_arrDecals[m_nCurDecal].m_pDecalOwner = DecalInfo.pDecalOwner; m_arrDecals[m_nCurDecal].m_fGrowTime = DecalInfo.m_fGrowTime; m_arrDecals[m_nCurDecal].m_fLifeBeginTime = GetTimer()->GetCurrTime(); if(DecalInfo.pStatObj) { DecalInfo.pStatObj->RegisterUser(); CDecal tmp; tmp.m_vPos = DecalInfo.vPos; tmp.m_vFront = DecalInfo.vNormal; tmp.m_nTexId = DecalInfo.nTid; tmp.m_fSize = DecalInfo.fSize; m_LightHoles.Add(tmp); } m_arrbActiveDecals[m_nCurDecal] = true; m_nCurDecal++; } void CDecalManager::SubmitLightHolesToRenderer() { if(!this) return; for(int i=0; iEF_SetLightHole(m_LightHoles[i].m_vPos, m_LightHoles[i].m_vFront, m_LightHoles[i].m_nTexId, m_LightHoles[i].m_fSize); m_LightHoles.Clear(); } void CDecalManager::Render() { FUNCTION_PROFILER( GetSystem(),PROFILE_3DENGINE ); if(!GetCVars()->e_decals || !m_p3DEngine->GetObjManager() || m_p3DEngine->GetCurrentLightSpec() < 1) return; CCamera * pCamera = &GetViewCamera(); Vec3d vCamPos = pCamera->GetPos(); float fCurrTime = GetTimer()->GetCurrTime(); IRenderer * pIRenderer = GetRenderer(); CObjManager * pObjManager = m_p3DEngine->GetObjManager(); float fZoom = m_p3DEngine->GetObjManager()->m_fZoomFactor; float fWaterLevel = m_p3DEngine->GetWaterLevel(); // draw for(int i=0; im_vWSPos)*fZoom < pDecal->m_fWSSize*ENTITY_DECAL_DIST_FACTOR) { float fSortOffset = pObjManager->GetSortOffset(pDecal->m_vWSPos,vCamPos,fWaterLevel); pDecal->Process(m_arrbActiveDecals[i], pIRenderer, fCurrTime, m_p3DEngine, m_pShader_ParticleLight, pCamera, fSortOffset); } } } void CDecalManager::OnEntityDeleted(IEntityRender * pEntityRender) { /* if(pEntityRender && pEntityRender->GetEntityRenderType()!=eERType_Unknown) { // remove all decals, can happend only during editing or level unloading memset(m_arrbActiveDecals,0,sizeof(m_arrbActiveDecals)); return; } */ // remove decals of this entity for(int i=0; iGetEntityRS()); m_arrbActiveDecals[i] = false; if(m_arrDecals[i].m_pBigDecalLeafBuffer) { GetRenderer()->DeleteLeafBuffer(m_arrDecals[i].m_pBigDecalLeafBuffer); m_arrDecals[i].m_pBigDecalLeafBuffer=0; } m_arrDecals[i].m_pDecalOwner = 0; } } } void CDecalManager::FillBigDecalIndices(CLeafBuffer * pLeafBuffer, Vec3d vPos, float fRadius, Vec3d vProjDir, list2 * plstIndices) { // get position offset and stride int nPosStride = 0, nNormStride = 0; byte * pPos = pLeafBuffer->GetPosPtr(nPosStride, 0, true); byte * pNorm = pLeafBuffer->GetNormalPtr(nNormStride, 0, true); ushort *pInds = pLeafBuffer->GetIndices(0); int nInds = pLeafBuffer->m_Indices.m_nItems; assert(nInds%3 == 0); plstIndices->Clear(); vProjDir.Normalize(); // Plane plane = GetPlane(vProjDir,vPos); // fill decal indices for(int i=0; iAdd(pInds[i+0]); plstIndices->Add(pInds[i+1]); plstIndices->Add(pInds[i+2]); } } } CLeafBuffer * CDecalManager::MakeBigDecalLeafBuffer(CLeafBuffer * pSourceLeafBuffer, Vec3d vPos, float fRadius, Vec3d vProjDir, int nTexID) { if(!pSourceLeafBuffer || pSourceLeafBuffer->m_SecVertCount==0) return 0; // make indices of this decal list2 lstIndices; if(pSourceLeafBuffer && pSourceLeafBuffer->m_pSecVertBuffer && pSourceLeafBuffer->m_SecVertCount) FillBigDecalIndices(pSourceLeafBuffer, vPos, fRadius, vProjDir, &lstIndices); if(!lstIndices.Count()) return 0; // make fake vert buffer with one vertex // todo: remove this list2 EmptyVertBuffer; EmptyVertBuffer.Add(struct_VERTEX_FORMAT_P3F_COL4UB()); CLeafBuffer * pLeafBuffer = GetRenderer()->CreateLeafBufferInitialized( EmptyVertBuffer.GetElements(), EmptyVertBuffer.Count(), VERTEX_FORMAT_P3F_COL4UB, lstIndices.GetElements(), lstIndices.Count(), R_PRIMV_TRIANGLES, "BigDecal", eBT_Static, 1, nTexID); pLeafBuffer->SetVertexContainer(pSourceLeafBuffer); IShader * pShader = vProjDir.IsZero() ? m_pShader_Decal_VP : m_pShader_Decal_2D_VP; pLeafBuffer->SetChunk(pShader, 0, pSourceLeafBuffer->m_SecVertCount, 0, lstIndices.Count()); pLeafBuffer->SetShader(pShader,nTexID); return pLeafBuffer; } /* CLeafBuffer * CDecalManager::MakeBigDecalLeafBuffer(CLeafBuffer * pSourceLeafBuffer, Vec3d vPos, float fRadius, Vec3d vProjDir, int nTexID) { if(!pSourceLeafBuffer || pSourceLeafBuffer->m_SecVertCount==0) return 0; // make indices of this decal list2 lstIndices; if(pSourceLeafBuffer && pSourceLeafBuffer->m_pSecVertBuffer && pSourceLeafBuffer->m_SecVertCount) FillBigDecalIndices(pSourceLeafBuffer, vPos, fRadius, vProjDir, &lstIndices); if(!lstIndices.Count()) return 0; // make fake vert buffer with one vertex // todo: remove this list2 VertBuffer; VertBuffer.PreAllocate(pSourceLeafBuffer->m_SecVertCount); int nPosStride=0, nNormStride=0; void * pPos = pSourceLeafBuffer->GetPosPtr(nPosStride); void * pNorm = pSourceLeafBuffer->GetPosPtr(nNormStride); for(int i=0; im_SecVertCount; i++) { struct_VERTEX_FORMAT_P3F_N vert; vert.xyz = *(Vec3d*)((uchar*)pPos+nPosStride*i); vert.normal = *(Vec3d*)((uchar*)pNorm+nNormStride*i); vert.normal = vert.normal*5; VertBuffer.Add(vert); } CLeafBuffer * pLeafBuffer = GetRenderer()->CreateLeafBufferInitialized( VertBuffer.GetElements(), VertBuffer.Count(), VERTEX_FORMAT_P3F_N, lstIndices.GetElements(), lstIndices.Count(), R_PRIMV_TRIANGLES, "BigDecal", eBT_Static, 1, nTexID); pLeafBuffer->SetChunk(m_pShader_Decal_VP, 0, pSourceLeafBuffer->m_SecVertCount, 0, lstIndices.Count()); pLeafBuffer->SetShader(m_pShader_Decal_VP,nTexID); return pLeafBuffer; } */ void CDecalManager::GetMemoryUsage(ICrySizer*pSizer) { pSizer->Add (*this); pSizer->AddContainer (m_LightHoles); } void CDecalManager::DeleteDecalsInRange( Vec3d vBoxMin, Vec3d vBoxMax, bool bDeleteBigTerrainDecals ) { for(int i=0; ivBoxMin.x && m_arrDecals[i].m_vPos.y>vBoxMin.y && m_arrDecals[i].m_vPos.z>vBoxMin.z ) { m_arrbActiveDecals[i] = false; GetRenderer()->DeleteLeafBuffer(m_arrDecals[i].m_pBigDecalLeafBuffer); m_arrDecals[i].m_pBigDecalLeafBuffer=0; } } } /* struct CDebugLine { Vec3d v0,v1; int nFrameId; }; list2 lstDebugLines; void AddDebugLine(Vec3d v0, Vec3d v1, int nFrameId) { CDebugLine l; l.v0 = v0; l.v1 = v1; l.nFrameId = nFrameId; lstDebugLines.Add(l); } void DrawDebugLines(IRenderer*pRenderer) { for(int i=0; iDraw3dPrim(lstDebugLines[i].v0,lstDebugLines[i].v1,DPRIM_LINE); if(pRenderer->GetFrameID()>lstDebugLines[i].nFrameId+1000) { lstDebugLines.Delete(i); i--; } } }*/