Files
FC1/Cry3DEngine/3DEngineLight.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1440 lines
46 KiB
C++

////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: 3denginelight.cpp
// Version: v1.00
// Created: 28/5/2001 by Vladimir Kajalin
// Compilers: Visual Studio.NET
// Description: Light sources manager
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "3dEngine.h"
#include "objman.h"
#include "visareas.h"
#include "AABBSV.h"
#include "partman.h"
#include <LMCompStructures.h>
#ifndef PI
#define PI 3.14159f
#endif
void C3DEngine::RegisterLightSourceInSectors(CDLight * pDynLight)
{
if(!m_pTerrain)
return;
/*
pDynLight->m_Color.r = min(pDynLight->m_Color.r,1.f);
pDynLight->m_Color.g = min(pDynLight->m_Color.g,1.f);
pDynLight->m_Color.b = min(pDynLight->m_Color.b,1.f);
pDynLight->m_Color.a = min(pDynLight->m_Color.a,1.f);*/
//pDynLight->m_SpecColor.r = min(pDynLight->m_SpecColor.r,1.f);
//pDynLight->m_SpecColor.g = min(pDynLight->m_SpecColor.g,1.f);
//pDynLight->m_SpecColor.b = min(pDynLight->m_SpecColor.b,1.f);
//pDynLight->m_SpecColor.a = min(pDynLight->m_SpecColor.a,1.f);
/*
if(pDynLight->m_Flags & (DLF_LIGHTSOURCE|DLF_HEATSOURCE))
pDynLight->m_Flags &= ~DLF_FAKE;
else
pDynLight->m_Flags |= DLF_FAKE;
*/
// Register in renderer and set lsource id
// GetRenderer()->EF_ADDDlight(pDynLight);
if(pDynLight->m_Id == -1) // ignored by renderer
return; // fake
else
if (pDynLight->m_pShader!=0 && (pDynLight->m_pShader->GetLFlags() & LMF_DISABLE))
return; // fake
// Register in sectors
// find 2d bounds in sectors array
int min_x = (int)(((pDynLight->m_Origin.x - pDynLight->m_fRadius - TERRAIN_SECTORS_MAX_OVERLAPPING)/CTerrain::GetSectorSize()));
int min_y = (int)(((pDynLight->m_Origin.y - pDynLight->m_fRadius - TERRAIN_SECTORS_MAX_OVERLAPPING)/CTerrain::GetSectorSize()));
int max_x = (int)(((pDynLight->m_Origin.x + pDynLight->m_fRadius + TERRAIN_SECTORS_MAX_OVERLAPPING)/CTerrain::GetSectorSize()));
int max_y = (int)(((pDynLight->m_Origin.y + pDynLight->m_fRadius + TERRAIN_SECTORS_MAX_OVERLAPPING)/CTerrain::GetSectorSize()));
if( min_x<0 ) min_x = 0; else if( min_x>=CTerrain::GetSectorsTableSize() ) min_x = CTerrain::GetSectorsTableSize()-1;
if( min_y<0 ) min_y = 0; else if( min_y>=CTerrain::GetSectorsTableSize() ) min_y = CTerrain::GetSectorsTableSize()-1;
if( max_x<0 ) max_x = 0; else if( max_x>=CTerrain::GetSectorsTableSize() ) max_x = CTerrain::GetSectorsTableSize()-1;
if( max_y<0 ) max_y = 0; else if( max_y>=CTerrain::GetSectorsTableSize() ) max_y = CTerrain::GetSectorsTableSize()-1;
// set lmask in all affected sectors
if(pDynLight->m_Id>=0)
for(int x=min_x; x<=max_x; x++)
for(int y=min_y; y<=max_y; y++)
{
CSectorInfo * pSecInfo = m_pTerrain->m_arrSecInfoTable[x][y];
assert(pSecInfo->m_nDynLightMask>=0);
assert(pSecInfo->m_nDynLightMaskNoSun>=0);
pSecInfo->m_nDynLightMask |= (1<<pDynLight->m_Id);
if(!(pDynLight->m_Flags & DLF_SUN)) // skip sun for static world
pSecInfo->m_nDynLightMaskNoSun |= (1<<pDynLight->m_Id);
assert(pSecInfo->m_nDynLightMask>0);
assert(pSecInfo->m_nDynLightMaskNoSun>=0);
}
}
// render light pass on terrain
void CTerrain::RenderDLightOnHeightMap(CDLight * pDLight)
{
/* if( (!(pDLight->m_Flags & DLF_DIRECTIONAL)) &&
!Get3DEngine()->IsSphereVisibleOnTheScreen(pDLight->m_Origin, pDLight->m_fRadius, 0))
return; // invisible
*/
// skip indoor lightsource
if( pDLight->m_pOwner && pDLight->m_pOwner->GetEntityVisArea() )
return;
if(pDLight->m_Flags & DLF_IGNORE_TERRAIN)
return;
if ((pDLight->m_Flags & DLF_ONLY_FOR_HIGHSPEC) && (m_LightConfigSpec < CONFIG_HIGH_SPEC))
return; // Skip high spec only light on medium and low spec configs.
if(!(pDLight->m_Flags & DLF_DIRECTIONAL))
{ // draw light on the ground
Vec3d vPos = pDLight->m_Origin;
float fRadius = pDLight->m_fRadius;
// check terrain bounds
if(vPos.x < -fRadius || vPos.y < -fRadius)
return;
if(vPos.x >= CTerrain::GetTerrainSize() + fRadius || vPos.y >= CTerrain::GetTerrainSize() + fRadius)
return;
const int nUsintSize = CTerrain::GetHeightMapUnitSize();
fRadius += nUsintSize;
vPos.x = float(int(vPos.x + 0.5f*nUsintSize)/nUsintSize*nUsintSize);
vPos.y = float(int(vPos.y + 0.5f*nUsintSize)/nUsintSize*nUsintSize);
vPos.z = float(int(vPos.z + 0.5f*nUsintSize)/nUsintSize*nUsintSize);
fRadius = float(int(fRadius + 0.5f*nUsintSize)/nUsintSize*nUsintSize);
if( fabs(vPos.z - GetZSafe(int(vPos.x),int(vPos.y))) > fRadius )
return; // too far from ground surface
IShader * pEffStencilTest = pDLight->m_Flags & DLF_CASTSHADOW_VOLUME ? GetRenderer()->EF_LoadShader("StencilState_Terrain",eSH_Misc, EF_SYSTEM) : 0;
// if(m_pVisAreaManager->IsOutdoorAreasVisible())
RenderAreaLeafBuffers(
vPos,
fRadius,
1<<pDLight->m_Id,
pDLight->m_arrLightLeafBuffers,
sizeof(pDLight->m_arrLightLeafBuffers)/sizeof(pDLight->m_arrLightLeafBuffers[0]),
GetIdentityCCObject(), m_pTerrainLightPassEf, true, pDLight->m_sDebugName,
pEffStencilTest); // todo: do not recalculate always
}
}
INT_PTR C3DEngine::AddStaticLightSource(const class CDLight & LSource, IEntityRender *__pCreator, ICryCharInstance * pCryCharInstance, const char * szBoneName) //AMD Port
{
// try to delete source if it's already present
/* for(int i=0; i<m_lstStaticLights.Count(); i++)
{
if( m_lstStaticLights[i]->m_pLight->m_pCharInstance == pCryCharInstance )//&& m_lstStaticLights[i] == pCreator )
{
delete m_lstStaticLights[i];
m_lstStaticLights.Delete(i);
i--;
}
}
*/
// construct new object
CDLight * pNewLight = new CDLight();
*pNewLight = LSource;
CLightEntity * pLightEntity = new CLightEntity( );
pNewLight->m_pOwner = pLightEntity;
pLightEntity->m_pLight = pNewLight;
pLightEntity->m_vPos = LSource.m_Origin;
if(pCryCharInstance) // character instance will update position
{
if (pCryCharInstance->AttachLight(pNewLight,pCryCharInstance->GetModel()->GetBoneByName(szBoneName)))
pNewLight->m_pCharInstance = pCryCharInstance;
}
Get3DEngine()->RegisterEntity(pLightEntity);
m_lstStaticLights.Add(pLightEntity);
return (INT_PTR)pLightEntity; //AMD Port
}
bool C3DEngine::DeleteStaticLightSource(INT_PTR nLightId) //AMD Port
{
CLightEntity * pLightEntity = (CLightEntity*)nLightId;
if(m_lstStaticLights.Delete(pLightEntity))
{
delete pLightEntity;
return true;
}
return false; // not found
}
const list2<CDLight*> * C3DEngine::GetStaticLightSources()
{
// tmp solution since .h files are checked out
static list2<CDLight*> lstLights;
lstLights.Reset();
for(int i=0; i<m_lstStaticLights.Count(); i++)
lstLights.Add(m_lstStaticLights[i]->m_pLight);
return &lstLights;
}
void C3DEngine::UpdateStaticLightSources()
{
FUNCTION_PROFILER( GetSystem(),PROFILE_3DENGINE );
for(int i=0; i<m_lstStaticLights.Count(); i++)
{
// if(i==94)
// i=i;
CLightEntity * pLightEntity = m_lstStaticLights[i];
CDLight * pLight = pLightEntity->m_pLight;
// pLight->m_nStaticLightId = i;
/*
if(pLight->m_pCharInstance)
{
//Matrix44 EntityMatrix;
//EntityMatrix.Identity();
//EntityMatrix = GetTranslationMat(pLightEntity->GetPos(true))*EntityMatrix;
//EntityMatrix = GetRotationZYX44(-gf_DEGTORAD*pLightEntity->GetAngles(0))*EntityMatrix;
//EntityMatrix = GetScale33( Vec3d(pLight->m_pOwner->GetScale(),pLight->m_pOwner->GetScale(),pLight->m_pOwner->GetScale()))*EntityMatrix;
//OPTIMISED_BY_IVO
Matrix33diag diag = Vec3(pLight->m_pOwner->GetScale(),pLight->m_pOwner->GetScale(),pLight->m_pOwner->GetScale() ); //use diag-matrix for scaling
Matrix34 rt34 = Matrix34::GetRotationXYZ34(gf_DEGTORAD*pLightEntity->GetAngles(0),pLightEntity->GetPos(true) ); //set scaling and translation in one function call
Matrix44 EntityMatrix = rt34*diag; //optimised concatenation: m34*diag
EntityMatrix.Transpose(); //TODO: remove this after E3 and use Matrix34 instead of Matrix44
AddDynamicLightSource(*pLight,pLight->m_pOwner, 0, &EntityMatrix );
}
else*/
AddDynamicLightSource(*pLight,pLight->m_pOwner);
}
}
void C3DEngine::DeleteAllStaticLightSources()
{
for(int i=0; i<m_lstStaticLights.Count(); i++)
delete m_lstStaticLights[i];
m_lstStaticLights.Reset();
}
Vec3d ConvertProjAngles(const Vec3d & vIn)
{
Vec3d vOut;
vOut.x = vIn.x;
vOut.y = vIn.y;
vOut.z = vIn.z;
return vOut;
}
void C3DEngine::AddDynamicLightSource(const class CDLight & LSource, IEntityRender *pEnt, int nEntityLightId, const Matrix44 * pMatrix)
{
////////////////////////////////////////////////////////////////////////////////////////////////
// Check errors
////////////////////////////////////////////////////////////////////////////////////////////////
assert(pEnt);
if(!pEnt)
{
Warning(0,0,"C3DEngine::AddDynamicLightSource: Entity not specified");
return;
}
if(!_finite(LSource.m_Origin.x) || !_finite(LSource.m_Origin.y) || !_finite(LSource.m_fRadius))
{
Warning(0,0,"C3DEngine::AddDynamicLightSource: undefined light source position: %s", LSource.m_sDebugName);
return;
}
if ((LSource.m_Flags & DLF_ONLY_FOR_HIGHSPEC) && (m_LightConfigSpec < CONFIG_HIGH_SPEC))
return; // Skip high spec only light on medium and low spec configs.
if(m_lstDynLights.Count()==128)
Warning(0,0,"C3DEngine::AddDynamicLightSource: more than 128 dynamic light sources created");
else if(m_lstDynLights.Count()>128)
return;
if(LSource.m_Flags & DLF_LOCAL)
return; // this lsource affects only owner
if(LSource.m_Flags & DLF_TEMP)
return; // this lsource is used only during preprocess
if( !GetCVars()->e_dynamic_light_debug && GetRenderer()->EF_GetHeatVision() && !(LSource.m_Flags & DLF_HEATSOURCE) )
return;
if( !GetRenderer()->EF_GetHeatVision() && !(LSource.m_Flags & DLF_LIGHTSOURCE ) )
return;
if(!GetCVars()->e_dynamic_light)
return;
////////////////////////////////////////////////////////////////////////////////////////////////
// Detect sun case
////////////////////////////////////////////////////////////////////////////////////////////////
if(pEnt == (IEntityRender*)-1)
{ // sun
if(LSource.m_Color.r + LSource.m_Color.g + LSource.m_Color.b == 0)
return; // sun disabled
pEnt = 0;
}
else if(GetCVars()->e_dynamic_light_exact_vis_test)
{
if(GetFrameID() - pEnt->GetDrawFrame(0) > MAX_FRAME_ID_STEP_PER_FRAME)
return;
}
Vec3d vWSOrigin = LSource.m_Origin;
if(pMatrix)
vWSOrigin = pMatrix->TransformPointOLD(LSource.m_vObjectSpacePos);
if(GetCVars()->e_dynamic_light_exact_vis_test)
if(!GetViewCamera().IsSphereVisibleFast( Sphere(vWSOrigin,LSource.m_fRadius) ))
return; // invisible
////////////////////////////////////////////////////////////////////////////////////////////////
// Make lsource name for debugging
////////////////////////////////////////////////////////////////////////////////////////////////
char sName[sizeof(LSource.m_sDebugName)]="";
if (pEnt)
{
// crash test
if(pEnt->GetEntityRS() && pEnt->m_pVisArea)
{
int nCount = pEnt->m_pVisArea->m_lstConnections.Count();
if(nCount==1233)
return;
}
const char * pName = pEnt->GetEntityClassName();
if(!pName || !pName[0])
pName = pEnt->GetName();
strncpy(sName, pName ? pName : "???", sizeof(sName));
sName[sizeof(sName)-1]=0;
if(GetCVars()->e_dynamic_light_exact_vis_test)
if(!m_pObjManager || !m_pVisAreaManager->IsEntityVisAreaVisible(pEnt,!(LSource.m_Flags & DLF_THIS_AREA_ONLY)))
{
bool bFirstPersonOwner = pEnt && pEnt->GetRndFlags() & ERF_FIRST_PERSON_CAMERA_OWNER;
if(!bFirstPersonOwner)
return;
}
}
else
strncpy(sName, "Sun", sizeof(sName));
////////////////////////////////////////////////////////////////////////////////////////////////
// Try to update present lsource
////////////////////////////////////////////////////////////////////////////////////////////////
for (int i=0; i<m_lstDynLights.Count(); i++)
{
if( m_lstDynLights[i].m_pOwner == pEnt && m_lstDynLights[i].m_nEntityLightId == nEntityLightId )
{
// copy lsource (do not owerwrite m_arrLightLeafBuffers, pEntityRS and nEntityLightId)
CLeafBuffer * TmpLightLeafBuffers[8];
assert(sizeof(TmpLightLeafBuffers) == sizeof(m_lstDynLights[i].m_arrLightLeafBuffers));
memcpy(TmpLightLeafBuffers,m_lstDynLights[i].m_arrLightLeafBuffers,sizeof(TmpLightLeafBuffers));
CCObject *pObj[4][4];
memcpy(&pObj[0][0], &m_lstDynLights[i].m_pObject[0][0], sizeof(pObj));
m_lstDynLights[i] = LSource;
// !HACK: Needs to decrement refcounter of shader because m_lstDynLights never release light sources
if (LSource.m_pShader)
LSource.m_pShader->Release();
memcpy(&m_lstDynLights[i].m_pObject[0][0], &pObj[0][0], sizeof(pObj));
memcpy(m_lstDynLights[i].m_arrLightLeafBuffers,TmpLightLeafBuffers,sizeof(TmpLightLeafBuffers));
m_lstDynLights[i].m_pOwner = pEnt;
m_lstDynLights[i].m_nEntityLightId = nEntityLightId;
m_lstDynLights[i].m_pCharInstance= 0;
// reinit start time
m_lstDynLights[i].m_fStartTime = GetTimer()->GetCurrTime();
m_lstDynLights[i].m_fLifeTime = LSource.m_fLifeTime;
// transform if needed
if(pMatrix)
{
m_lstDynLights[i].m_Origin = vWSOrigin;//pMatrix->TransformPoint(LSource.m_vObjectSpacePos);
//CHANGED_BY_IVO
//m_lstDynLights[i].m_ProjAngles = pMatrix->TransformVector(LSource.m_ProjAngles);
m_lstDynLights[i].m_ProjAngles = GetTransposed44(*pMatrix)*(LSource.m_ProjAngles);
}
strncpy(m_lstDynLights[i].m_sDebugName,sName,8);
/*
Vec3d Angles(pDLight->m_ProjAngles[1], 0, pDLight->m_ProjAngles[2]+90.0f);
cam.SetAngle(Angles);
cam.Init(1, 1, (pDLight->m_fLightFrustumAngle*2)/180.0f*PI, pDLight->m_fRadius, 1.0f, 0.1f);
cam.Update(); */
m_lstDynLights[i].m_ProjAngles = ConvertProjAngles(LSource.m_ProjAngles);
// set base params
m_lstDynLights[i].m_BaseOrigin = m_lstDynLights[i].m_Origin;
m_lstDynLights[i].m_BaseProjAngles = m_lstDynLights[i].m_ProjAngles;
if ((m_lstDynLights[i].m_Flags & DLF_SPECULAR_ONLY_FOR_HIGHSPEC) && (m_LightConfigSpec < CONFIG_HIGH_SPEC))
{
m_lstDynLights[i].m_SpecColor = Col_Black;
}
return;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Add new lsource into list and set some parameters
////////////////////////////////////////////////////////////////////////////////////////////////
m_lstDynLights.Add(LSource);
// add ref to avoid shader deleting
if (m_lstDynLights.Last().m_pShader)
m_lstDynLights.Last().m_pShader->AddRef();
m_lstDynLights.Last().m_Flags |= DLF_COPY;
m_lstDynLights.Last().m_fStartTime = GetTimer()->GetCurrTime();
m_lstDynLights.Last().m_fLifeTime = LSource.m_fLifeTime;
if (!LSource.m_fEndRadius)
m_lstDynLights.Last().m_fStartRadius = LSource.m_fRadius;
m_lstDynLights.Last().m_pOwner = pEnt;
m_lstDynLights.Last().m_nEntityLightId = nEntityLightId;
// transform if needed
if(pMatrix)
{
m_lstDynLights.Last().m_Origin = vWSOrigin;//pMatrix->TransformPoint(LSource.m_vObjectSpacePos);
//CHANGED_BY_IVO
//m_lstDynLights.Last().m_ProjAngles = pMatrix->TransformVector(LSource.m_ProjAngles);
m_lstDynLights.Last().m_ProjAngles = GetTransposed44(*pMatrix)*(LSource.m_ProjAngles);
}
strncpy(m_lstDynLights.Last().m_sDebugName,sName,8);
m_lstDynLights.Last().m_ProjAngles = ConvertProjAngles(LSource.m_ProjAngles);
// set base params
m_lstDynLights.Last().m_BaseOrigin = m_lstDynLights.Last().m_Origin;
m_lstDynLights.Last().m_BaseProjAngles = m_lstDynLights.Last().m_ProjAngles;
if ((m_lstDynLights.Last().m_Flags & DLF_SPECULAR_ONLY_FOR_HIGHSPEC) && (m_LightConfigSpec < CONFIG_HIGH_SPEC))
{
m_lstDynLights.Last().m_SpecColor = Col_Black;
}
}
void C3DEngine::PrepareLightSourcesForRendering()
{
FUNCTION_PROFILER( GetSystem(),PROFILE_3DENGINE );
// reset lists of lsource pointers in sectors
if(m_pTerrain)
m_pTerrain->ResetDLightMaskInSectors();
// GetRenderer()->EF_ClearLightsList();
m_lstDynLightsNoLight.Clear();
// update lmasks in terrain sectors
if(m_pObjManager->m_nRenderStackLevel)
{ // do not delete lsources during reqursion, becasue hmap lpasses are shared between levels
for (int i=0; i<m_nRealLightsNum/*m_lstDynLights.Count()*/; i++)
{
m_lstDynLights[i].m_Id = -1;
GetRenderer()->EF_ADDDlight(&m_lstDynLights[i]);
assert(m_lstDynLights[i].m_Id == i);
if(m_lstDynLights[i].m_Id != -1)
RegisterLightSourceInSectors(&m_lstDynLights[i]);
}
}
else
{
for (int i=0; i<m_lstDynLights.Count(); i++)
{
m_lstDynLights[i].m_Id = -1;
/*
if(i >= MAX_LIGHTS_NUM)
{ // no more sources can be accepted by renderer
Warning( 0,0,"C3DEngine::PrepareLightSourcesForRendering: No more than %d lsources allowed on the screen", (int)MAX_LIGHTS_NUM);
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue;
}
*/
if( m_lstDynLights[i].m_pOwner && m_lstDynLights[i].m_pOwner->GetEntityVisArea())
{ // vis area lsource
if(!GetViewCamera().IsSphereVisibleFast( Sphere(m_lstDynLights[i].m_Origin,m_lstDynLights[i].m_fRadius) ))
{
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue; // invisible
}
// check if lsource is in visible area
if(GetCVars()->e_dynamic_light_exact_vis_test)
if(!m_lstDynLights[i].m_pOwner->IsEntityAreasVisible())
{
if(m_lstDynLights[i].m_Flags & DLF_THIS_AREA_ONLY)
{
IEntityRender * pEnt = m_lstDynLights[i].m_pOwner;
if(pEnt->m_pVisArea)
{
int nRndFrameId = pEnt->m_pVisArea->m_nRndFrameId;
if(GetFrameID() - pEnt->GetDrawFrame(0) > MAX_FRAME_ID_STEP_PER_FRAME)
{
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue; // area invisible
}
}
else
{
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue; // area invisible
}
}
}
}
else
{ // outdoor lsource
if( (!(m_lstDynLights[i].m_Flags & DLF_DIRECTIONAL)) &&
!IsSphereVisibleOnTheScreen(m_lstDynLights[i].m_Origin, m_lstDynLights[i].m_fRadius, 0))
{
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue; // outdoor invisible
}
}
if(GetRenderer()->EF_IsFakeDLight(&m_lstDynLights[i]))
{ // ignored by renderer
m_lstDynLightsNoLight.Add(m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue;
}
GetRenderer()->EF_ADDDlight(&m_lstDynLights[i]);
if(m_lstDynLights[i].m_Id == -1)
{ // ignored by renderer
assert(i >= MAX_LIGHTS_NUM);
if(i >= MAX_LIGHTS_NUM)
{ // no more sources can be accepted by renderer
Warning( 0,0,"C3DEngine::PrepareLightSourcesForRendering: No more than %d real lsources allowed on the screen", (int)MAX_LIGHTS_NUM);
}
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue;
}
if (m_lstDynLights[i].m_pShader!=0 && (m_lstDynLights[i].m_pShader->GetLFlags() & LMF_DISABLE))
{ // fake
assert(0); // should not be called, but no problem
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i); i--;
continue;
}
if(i != m_lstDynLights[i].m_Id)
Warning( 0,0,"C3DEngine::PrepareLightSourcesForRendering: Invalid light source id");
if(m_lstDynLights[i].m_Flags & DLF_PROJECT
&& m_lstDynLights[i].m_fLightFrustumAngle<90 // actual fov is twice bigger
&& m_lstDynLights[i].m_pLightImage
&& !(m_lstDynLights[i].m_pLightImage->GetFlags2() & FT2_REPLICATETOALLSIDES)
&& (m_lstDynLights[i].m_pLightImage->GetFlags2() & FT2_CUBEASSINGLETEXTURE))
{ // prepare projector camera for frustum test
CCamera & cam = m_arrCameraProjectors[i];
cam.SetPos(m_lstDynLights[i].m_Origin);
Vec3d Angles(m_lstDynLights[i].m_ProjAngles[1], 0, m_lstDynLights[i].m_ProjAngles[2]+90.0f);
cam.SetAngle(Angles);
cam.Init(1, 1, (m_lstDynLights[i].m_fLightFrustumAngle*2)/180.0f*PI, m_lstDynLights[i].m_fRadius, 1.0f, 0.1f);
cam.Update();
}
if(m_lstDynLights[i].m_fRadius >= 0.5f)
{
assert(m_lstDynLights[i].m_fRadius >= 0.5f && !(m_lstDynLights[i].m_Flags & DLF_FAKE));
RegisterLightSourceInSectors(&m_lstDynLights[i]);
}
}
m_nRealLightsNum = m_lstDynLights.Count();
m_lstDynLights.AddList(m_lstDynLightsNoLight);
for(int i=m_nRealLightsNum; i<m_lstDynLights.Count(); i++)
{ // ignored by renderer
m_lstDynLights[i].m_Id = -1;
GetRenderer()->EF_ADDDlight(&m_lstDynLights[i]);
assert(m_lstDynLights[i].m_Id == -1);
}
m_lstDynLightsNoLight.Clear();
}
}
void C3DEngine::FreeLightSourceComponents(CDLight *pLight)
{
if(pLight->m_pCharInstance)
pLight->m_pCharInstance->DetachLight(pLight);
pLight->m_pCharInstance=0;
for (int i=0; i<4; i++)
{
for (int j=0; j<4; j++)
{
if (pLight->m_pObject[i][j])
pLight->m_pObject[i][j]->RemovePermanent();
pLight->m_pObject[i][j]=0;
}
}
// free leafbuffers
for(int nLeafId=0; nLeafId<(sizeof(pLight->m_arrLightLeafBuffers)/sizeof(pLight->m_arrLightLeafBuffers[0])); nLeafId++)
if(pLight->m_arrLightLeafBuffers[nLeafId])
{
GetRenderer()->DeleteLeafBuffer(pLight->m_arrLightLeafBuffers[nLeafId]);
pLight->m_arrLightLeafBuffers[nLeafId] = 0;
}
// delete pLight->m_pProjCamera;
//pLight->m_pProjCamera=0;
//if(pLight->m_pShader)
// SAFE_RELEASE(pLight->m_pShader);
}
void C3DEngine::UpdateLightSources()
{
// process lsources
float fCurrTime = GetTimer()->GetCurrTime();
for(int i=0; i<m_lstDynLights.Count(); i++)
{
CDLight *pLight = &m_lstDynLights[i];
if(pLight->m_fStartTime + pLight->m_fLifeTime+0.001f < fCurrTime)
{
// time is come to delete this lsource
FreeLightSourceComponents(pLight);
m_lstDynLights.Delete(i); i--;
continue;
}
bool bDeleteNow = GetRenderer()->EF_UpdateDLight(pLight);
//pLight->m_Color.Clamp();
//pLight->m_SpecColor.Clamp();
if(pLight->m_fLifeTime && !pLight->m_pShader)
{ // evaluate radius
float fAtten = 1.f - (fCurrTime - pLight->m_fStartTime) / pLight->m_fLifeTime;
if(fAtten<0)
{
fAtten = 0;
bDeleteNow = true;
}
else if(fAtten>1.f)
fAtten = 1.f;
pLight->m_fRadius = pLight->m_fBaseRadius*fAtten;
}
if(bDeleteNow)
{
// time is come to delete this lsource
FreeLightSourceComponents(pLight);
m_lstDynLights.Delete(i); i--;
continue;
}
SetupLightScissors(&m_lstDynLights[i]);
}
}
void C3DEngine::SetupLightScissors(CDLight * pLight)
{
pLight->m_sX = 0;
pLight->m_sY = 0;
pLight->m_sWidth = 0;
pLight->m_sHeight = 0;
return;
Vec3d vLeft = GetViewCamera().GetVCMatrixD3D9().GetOrtX();
Vec3d vUp = GetViewCamera().GetVCMatrixD3D9().GetOrtY();
Vec3d vFront = GetViewCamera().GetVCMatrixD3D9().GetOrtZ();
float color[] = {1,1,1,1};
float fRadius = (GetCVars()->e_scissor_debug>1) ? pLight->m_fRadius/GetCVars()->e_scissor_debug : pLight->m_fRadius;
// get TopLeft2d
Vec3d vTopLeft2d, vTopLeft = pLight->m_Origin + vUp*fRadius - vLeft*fRadius;
GetRenderer()->ProjectToScreen(vTopLeft.x, vTopLeft.y, vTopLeft.z, &vTopLeft2d.x, &vTopLeft2d.y, &vTopLeft2d.z);
vTopLeft2d.x = vTopLeft2d.x*GetRenderer()->GetWidth()/100;
vTopLeft2d.y = vTopLeft2d.y*GetRenderer()->GetHeight()/100;
if(vTopLeft2d.z<0 || vTopLeft2d.z>1.f)
return;
// get vBotRight2d
Vec3d vBotRight2d, vBotRight = pLight->m_Origin - vUp*fRadius + vLeft*fRadius;
GetRenderer()->ProjectToScreen(vBotRight.x, vBotRight.y, vBotRight.z, &vBotRight2d.x, &vBotRight2d.y, &vBotRight2d.z);
vBotRight2d.x = vBotRight2d.x*GetRenderer()->GetWidth()/100;
vBotRight2d.y = vBotRight2d.y*GetRenderer()->GetHeight()/100;
if(vBotRight2d.z<0 || vBotRight2d.z>1.f)
return;
if(GetCVars()->e_scissor_debug)
{
/* GetRenderer()->Draw2dLabel(vBotRight2d.x, vBotRight2d.y, 2 , color, false, "br");
GetRenderer()->Draw2dLabel(vTopLeft2d.x, vBotRight2d.y, 2 , color, false, "bl");
GetRenderer()->Draw2dLabel(vBotRight2d.x, vTopLeft2d.y, 2 , color, false, "tr");
GetRenderer()->Draw2dLabel(vTopLeft2d.x, vTopLeft2d.y, 2 , color, false, "tl");*/
}
// get vCenter2d
/*if(GetCVars()->e_scissor_debug)
{
Vec3d vCenter2d, vCenter = pLight->m_Origin;
GetRenderer()->ProjectToScreen(vCenter.x, vCenter.y, vCenter.z, &vCenter2d.x, &vCenter2d.y, &vCenter2d.z);
vCenter2d.x = vCenter2d.x*GetRenderer()->GetWidth()/100;
vCenter2d.y = vCenter2d.y*GetRenderer()->GetHeight()/100;
GetRenderer()->Draw2dLabel(vCenter2d.x, vCenter2d.y, 2 , color, false, "x");
} */
// set 2d bounds
/*pLight->m_sX = max(0,int(vTopLeft2d.x));
pLight->m_sY = max(0,int(vTopLeft2d.y));
pLight->m_sWidth = min(GetRenderer()->GetWidth(), int(vBotRight2d.x - vTopLeft2d.x));
pLight->m_sHeight = min(GetRenderer()->GetHeight(), int(vBotRight2d.y - vTopLeft2d.y));
// set depth bounds
float fDist = GetViewCamera().GetPlane(FR_PLANE_NEAR)->DistFromPlane(pLight->m_Origin);
pLight->m_fNear = fDist - fRadius;
pLight->m_fFar = fDist + fRadius;
*/
pLight->m_sX = 0;
pLight->m_sY = 0;
pLight->m_sWidth = 0;
pLight->m_sHeight = 0;
}
void C3DEngine::LightSourcesDebug()
{
if(!GetCVars()->e_dynamic_light_debug)
return;
#ifndef PS2
#ifndef _XBOX
CCamera vCam = GetViewCamera();
// add test dynamic source
// don't panic, it's just for test
static CDLight Light;
static bool bFreeze = false;
static Vec3d sAngs;
static float sRad = 10.0f;
if (true)
{
#if !defined(LINUX)
if ((GetAsyncKeyState('F') & 0x8000))
bFreeze = 1;
else
if ((GetAsyncKeyState('U') & 0x8000))
bFreeze = 0;
#endif
//Light.m_Orientation.m_vForward = Vec3d(1,0,0);
//Light.m_Orientation.m_vUp = Vec3d(0,0,1);
//Light.m_Orientation.m_vRight = Vec3d(0,1,0);
if (!Light.m_pShader)
Light.m_pShader = GetRenderer()->EF_LoadShader("GlowingMonkeyEyes", eSH_World, EF_SYSTEM);
#if !defined(LINUX)
if (GetAsyncKeyState(VK_DELETE) & 0x8000)
sAngs[2] += 1.0f;
if (GetAsyncKeyState(VK_NEXT) & 0x8000)
sAngs[2] -= 1.0f;
if (GetAsyncKeyState(VK_END) & 0x8000)
sAngs[1] += 1.0f;
if (GetAsyncKeyState(VK_HOME) & 0x8000)
sAngs[1] -= 1.0f;
if (GetAsyncKeyState(VK_NUMPAD1) & 0x8000)
sAngs[0] -= 1.0f;
if (GetAsyncKeyState(VK_NUMPAD2) & 0x8000)
sAngs[0] += 1.0f;
if (GetAsyncKeyState(VK_NUMPAD6) & 0x8000)
sRad += 0.25f;
if (GetAsyncKeyState(VK_NUMPAD4) & 0x8000)
sRad -= 0.25f;
#endif
if (sRad < 0)
sRad = 0;
if (sRad > 100)
sRad = 100;
#if !defined(LINUX)
if (GetAsyncKeyState(VK_INSERT) & 0x8000)
Light.m_fLightFrustumAngle -= 1.0f;
if (GetAsyncKeyState(VK_PRIOR) & 0x8000)
Light.m_fLightFrustumAngle += 1.0f;
#endif
if (Light.m_fLightFrustumAngle < 1.0f)
Light.m_fLightFrustumAngle = 1.0f;
if (Light.m_fLightFrustumAngle > 88.0f)
Light.m_fLightFrustumAngle = 88.0f;
Light.m_ProjAngles = sAngs;
//Light.m_Orientation.rotate(Vec3d(1,0,0), sAngs[0]);
//Light.m_Orientation.rotate(Vec3d(0,1,0), sAngs[1]);
//Light.m_Orientation.rotate(Vec3d(0,0,1), sAngs[2]);
Light.m_Color = CFColor(1.0f,1.0f,1.0f, 1.0f);
Light.m_SpecColor = CFColor(1.0f,1.0f,1.0f);
//Light.m_AmbColor = CFColor(0.6f,0.6f,0.6f);
if (!bFreeze)
{
Light.m_Origin = vCam.GetPos();
//Light.m_Origin += Vec3d(1, 0, 0.0f);
}
#if !defined(LINUX)
if (GetAsyncKeyState('6') & 0x8000)
Light.m_Flags = 0;
else
if (GetAsyncKeyState('7') & 0x8000)
Light.m_Flags = DLF_DIRECTIONAL;
else
if (GetAsyncKeyState('P') & 0x8000)
Light.m_Flags = DLF_PROJECT;
else
if (GetAsyncKeyState('O') & 0x8000)
Light.m_Flags = DLF_POINT;
Light.m_Flags |= DLF_HEATSOURCE | DLF_LIGHTSOURCE;
#endif
if (!(Light.m_Flags & DLF_LIGHTTYPE_MASK))
return;
if (Light.m_Flags & DLF_DIRECTIONAL)
Light.m_fRadius = 10000.0f;
else
Light.m_fRadius = sRad;
if (!Light.m_pLightImage)
{
//Light.m_pLightImage = m_IndInterface.m_pRenderer->EF_LoadTexture("FlashLightCube", 0, FT2_FORCECUBEMAP, eTT_Cubemap);
Light.m_pLightImage = GetRenderer()->EF_LoadTexture("Textures/Lights/gk_spotlight_sm", 0, FT2_FORCECUBEMAP, eTT_Cubemap);
Light.m_fAnimSpeed = 0.0f;
}
GetRenderer()->EF_UpdateDLight(&Light);
AddDynamicLightSource(Light, (IEntityRender *)-1, 0);
}
#endif
#endif
}
int __cdecl C3DEngine__Cmp_LightAmount(const void* v1, const void* v2)
{
DLightAmount * p1 = ((DLightAmount*)v1);
DLightAmount * p2 = ((DLightAmount*)v2);
if(!p1 || !p2)
return 0;
if(p1->fAmount > p2->fAmount)
return -1;
else if(p1->fAmount < p2->fAmount)
return 1;
return 0;
}
uint C3DEngine::GetFullLightMask()
{
uint nRes=0;
for(int n=0; n<m_nRealLightsNum/*m_lstDynLights.Count()*/; n++)
{
const int nId = m_lstDynLights[n].m_Id;
assert(nId>=0);
nRes |= (1<<nId);
}
return nRes;
}
CDLight * C3DEngine::CheckDistancesToLightSources(uint & nDLightMask,
const Vec3d vObjPos, const float fObjRadius,
IEntityRender * pEntityRender, int nMaxLightBitsNum,
CDLight ** pSelectedLights, int nMaxSelectedLights,
Vec3d * pvSummLightAmmount )
{
FUNCTION_PROFILER( GetSystem(),PROFILE_3DENGINE );
m_lstDL_CDTLS.Clear();
CVisArea * pEntityVisArea = (CVisArea *)(pEntityRender ? pEntityRender->GetEntityVisArea() : 0);
// make list of all affecting light sources
Vec3d vSummLightAmmount(0,0,0);
int nProjectorsNum=0;
for(int n=0; n<32 && n<m_nRealLightsNum/*m_lstDynLights.Count()*/; n++)
{
const int nId = m_lstDynLights[n].m_Id;
// [marco] commented out 'cos it happens every frame
assert(nId == n);
if(nId>=0 && nDLightMask & (1<<nId) && nId==n)
{
CDLight * pDLight = &m_lstDynLights[n];
assert(!pEntityRender || pDLight == (CDLight*)GetRenderer()->EF_Query(EFQ_LightSource, nId));
assert(pDLight->m_Id == n);
assert(!pDLight->m_pShader || !(pDLight->m_pShader->GetLFlags() & LMF_DISABLE));
// todo: remove another similar check
if(pEntityRender && pDLight->m_Flags & DLF_THIS_AREA_ONLY)
if(pDLight->m_pOwner && pDLight->m_pOwner->GetEntityVisArea())
if(pEntityRender->GetEntityVisArea() != pDLight->m_pOwner->GetEntityVisArea())
if(!pEntityRender->GetEntityVisArea() || !pEntityRender->GetEntityVisArea()->IsPortal())
// if(!(pEntityRender->GetRndFlags() & ERF_DONOTCHECKVIS)) // not for 1p weapons
continue; // different areas
if(pDLight->m_Flags & DLF_IGNORE_OWNER && pEntityRender && pEntityRender == pDLight->m_pOwner)
continue; // skip this object lights (vehicle lights)
if ((pDLight->m_Flags & DLF_ONLY_FOR_HIGHSPEC) && (m_LightConfigSpec < CONFIG_HIGH_SPEC))
continue; // Skip high spec only light on medium and low spec configs.
if(pEntityRender &&
pDLight->m_Flags & DLF_LM &&
!(pDLight->m_Flags & DLF_CASTSHADOW_VOLUME) &&
pEntityRender->GetRndFlags() & ERF_USELIGHTMAPS &&
pEntityRender->HasLightmap(0))
{ // in case of lightmaps
if(pDLight->m_Flags & DLF_PROJECT)
continue; // ignore specular only lights if projector since specular projectors not supported
if (!(pDLight->m_Flags & DLF_CASTSHADOW_MAPS))
{
if ((pDLight->m_SpecColor == Col_Black) ||
((pDLight->m_Flags & DLF_SPECULAR_ONLY_FOR_HIGHSPEC) && (m_LightConfigSpec < CONFIG_HIGH_SPEC)))
continue; // ignore specular only lights if specular disabled
}
}
if(!pEntityRender && (pDLight->m_Flags & DLF_THIS_AREA_ONLY))
if( pDLight->m_pOwner && pDLight->m_pOwner->GetEntityVisArea())
continue; // indoor lsource do not affect on vegetations
float fProjectorAmmountPlus = 0;
if(pDLight->m_Flags & DLF_PROJECT && pEntityRender
&& pDLight->m_fLightFrustumAngle<90 // actual fov is twice bigger
&& !(pDLight->m_pLightImage!=0 && pDLight->m_pLightImage->GetFlags2() & FT2_REPLICATETOALLSIDES))
{ // check projector frustum
// use pDLight->m_TextureMatrix to construct Plane
/*GetRenderer()->Draw3dBBox(pDLight->m_Origin, pDLight->m_Origin+pDLight->m_TextureMatrix.GetOrtX(), DPRIM_LINE);
GetRenderer()->DrawLabel(pDLight->m_Origin+pDLight->m_TextureMatrix.GetOrtX(),1,"x");
GetRenderer()->Draw3dBBox(pDLight->m_Origin, pDLight->m_Origin+pDLight->m_TextureMatrix.GetOrtY(), DPRIM_LINE);
GetRenderer()->DrawLabel(pDLight->m_Origin+pDLight->m_TextureMatrix.GetOrtY(),1,"y");
GetRenderer()->Draw3dBBox(pDLight->m_Origin, pDLight->m_Origin+pDLight->m_TextureMatrix.GetOrtZ(), DPRIM_LINE);
GetRenderer()->DrawLabel(pDLight->m_Origin+pDLight->m_TextureMatrix.GetOrtZ(),1,"z");*/
if(GetCVars()->e_projector_exact_test)
{
CCamera & cam = m_arrCameraProjectors[n];
Vec3d vBoxMin,vBoxMax;
pEntityRender->GetRenderBBox(vBoxMin,vBoxMax);
if (!cam.IsAABBVisibleFast( AABB(vBoxMin,vBoxMax) ))
continue;
}
else
{
Plane p;
p.CalcPlane( pDLight->m_Origin, pDLight->m_Origin+pDLight->m_TextureMatrix.GetOrtY(), pDLight->m_Origin-pDLight->m_TextureMatrix.GetOrtZ() );
if( p.DistFromPlane(vObjPos) + fObjRadius < 0 )
continue;
}
fProjectorAmmountPlus = 10.f;
nProjectorsNum++;
}
else if(pEntityRender)
{ // check sphere/bbox intersection
Vec3d vBoxMin,vBoxMax;
pEntityRender->GetBBox(vBoxMin,vBoxMax);
if(!Overlap::Sphere_AABB(Sphere(pDLight->m_Origin, pDLight->m_fRadius), AABB(vBoxMin,vBoxMax)))
continue;
}
// find amount of light
float fDist = GetDistance(pDLight->m_Origin,vObjPos);
float fLightAttenuation = (pDLight->m_Flags & DLF_DIRECTIONAL) ? 1.f : 1.f - (fDist-fObjRadius) / (pDLight->m_fRadius);
if(fLightAttenuation<0)
fLightAttenuation=0;
float fLightAmount =
(pDLight->m_Color.r+pDLight->m_Color.g+pDLight->m_Color.b)*0.233f +
(pDLight->m_SpecColor.r+pDLight->m_SpecColor.g+pDLight->m_SpecColor.b)*0.1f;
fLightAmount = fLightAmount*fLightAttenuation + fProjectorAmmountPlus;
if(fLightAmount>0.05f)
{
// if entity is inside some area - allow lightsources only from this area
if(pEntityRender)
{
if(pEntityVisArea)
{
if( pDLight->m_pOwner && pDLight->m_pOwner->GetEntityRS() &&
pDLight->m_pOwner->m_pVisArea)
{
CVisArea * pLightArea = pDLight->m_pOwner->m_pVisArea;
if(pEntityVisArea != pLightArea && !(pEntityRender->GetRndFlags() & ERF_DONOTCHECKVIS))
{ // try also neighbor volumes
int nSearchDepth;
if(pDLight->m_Flags & DLF_PROJECT && !(pDLight->m_Flags & DLF_THIS_AREA_ONLY))
nSearchDepth = 4 + int(pLightArea->IsPortal()); // allow projector to go depther
else
nSearchDepth = 1 + int((pDLight->m_Flags & DLF_THIS_AREA_ONLY)==0) + int(pLightArea->IsPortal());
bool bNearFound = pEntityVisArea->FindVisArea(pLightArea, nSearchDepth, true);
if(!bNearFound)
continue; // areas do not much
// construct frustum from light pos and portal, check that object is visible from light position
if(!(pDLight->m_Flags & DLF_THIS_AREA_ONLY) && pEntityVisArea && !pEntityVisArea->IsPortal())
{
Vec3d vBoxMin,vBoxMax;
pEntityRender->GetBBox(vBoxMin,vBoxMax);
AABB aabbReceiver(vBoxMin,vBoxMax);
Shadowvolume sv;
int p=0;
for(; p<pEntityVisArea->m_lstConnections.Count(); p++)
{
CVisArea * pArea = pEntityVisArea->m_lstConnections[p];
if(pArea->IsPortal())
{
AABB aabbCaster(pArea->m_vBoxMin, pArea->m_vBoxMax);
NAABB_SV::AABB_ShadowVolume(pDLight->m_Origin, aabbCaster, sv, pDLight->m_fRadius);
bool bIntersect = NAABB_SV::Is_AABB_In_ShadowVolume(sv, aabbReceiver);
if(bIntersect)
break;
}
}
if(p==pEntityVisArea->m_lstConnections.Count())
continue; // object is not visible from light position
}
}
}
else if(!pEntityVisArea->m_bAfectedByOutLights)
continue; // outdoor lsource
}
else // entity is outside
if( pDLight->m_pOwner && pDLight->m_pOwner->GetEntityVisArea())
continue; // indoor lsource should not affect outdoor entity
}
DLightAmount la;
la.pDLight = pDLight;
la.fAmount = fLightAmount;
vSummLightAmmount += Vec3d(pDLight->m_Color.r,pDLight->m_Color.g,pDLight->m_Color.b)*fLightAttenuation;
m_lstDL_CDTLS.Add(la);
}
}
}
nDLightMask = 0;
if(!m_lstDL_CDTLS.Count())
return 0; // no lsources found
// sort by light amount
qsort(&m_lstDL_CDTLS[0], m_lstDL_CDTLS.Count(), sizeof(m_lstDL_CDTLS[0]), C3DEngine__Cmp_LightAmount);
nMaxLightBitsNum = min(nMaxLightBitsNum,GetCVars()->e_max_entity_lights+nProjectorsNum);
// if(pEntityRender && pEntityRender->GetRndFlags() & ERF_USELIGHTMAPS && pEntityRender->HasLightmap(0))
// nMaxLightBitsNum--;
// increate max lights num for big objects
if(pEntityRender && pEntityRender->m_fWSRadius>8 && nMaxLightBitsNum)
nMaxLightBitsNum++;
// limit number of effective light sources
CDLight * pStrongestShadowCaster = NULL;
for(int n=0; n<nMaxLightBitsNum && n<m_lstDL_CDTLS.Count(); n++)
{
const int nId = m_lstDL_CDTLS[n].pDLight->m_Id;
nDLightMask |= (1<<nId);
if(pSelectedLights && n<nMaxSelectedLights)
pSelectedLights[n] = m_lstDL_CDTLS[n].pDLight;
// always search for shadow map caster even if shadow volume caster is already selected
if((!pStrongestShadowCaster || pStrongestShadowCaster->m_Flags & DLF_CASTSHADOW_VOLUME) && m_lstDL_CDTLS[n].pDLight->m_Flags & DLF_CASTSHADOW_MAPS)
pStrongestShadowCaster = m_lstDL_CDTLS[n].pDLight;
else if(!pStrongestShadowCaster && m_lstDL_CDTLS[n].pDLight->m_Flags & DLF_CASTSHADOW_VOLUME)
pStrongestShadowCaster = m_lstDL_CDTLS[n].pDLight;
}
if(pvSummLightAmmount)
*pvSummLightAmmount = vSummLightAmmount;
return pStrongestShadowCaster ?
pStrongestShadowCaster : ((nMaxLightBitsNum>0) ? m_lstDL_CDTLS[0].pDLight : 0); // return strongest
}
void C3DEngine::RemoveEntityLightSources(IEntityRender * pEntity)
{
for (int i=0; i<m_lstDynLights.Count(); i++)
{
if(m_lstDynLights[i].m_pOwner == pEntity)
{
FreeLightSourceComponents(&m_lstDynLights[i]);
m_lstDynLights.Delete(i);
i--;
}
}
/*
for (int i=0; i<m_lstStaticLights.Count(); i++)
{
if(m_lstStaticLights[i] == pEntity && !pEntity->IsStatic())
{
CLightEntity * pLightEntity = m_lstStaticLights[i];
m_lstStaticLights.Delete(i);
delete pLightEntity;
i--;
}
}*/
}
typedef LMStatLightFileHeader LightFileHeader;
bool ReadString(FILE * hFile, char * szBuff, int nMaxChars, ICryPak * pPak)
{
UINT iStrLen = 0;
if (pPak->FRead(&iStrLen, sizeof(UINT), 1, hFile) != 1)
return false;
if((int)iStrLen >= nMaxChars)
return false;
if (pPak->FRead(szBuff, 1, iStrLen, hFile) != iStrLen)
return false;
szBuff[iStrLen]=0;
return true;
}
#ifdef WIN64
#pragma pack(push,4)
// dummy structure that's binary compatible with the 32-bit version of CDLight
struct CDLightDummy32
{
typedef int ptr32;
int m_Id;
Vec3 m_Origin; //world space position
Vec3 m_BaseOrigin; //world space position
CFColor m_Color; //!< clampled diffuse light color
CFColor m_BaseColor; //!< clampled diffuse light color
CFColor m_SpecColor;
CFColor m_BaseSpecColor;
Vec3 m_vObjectSpacePos; //Object space position
float m_fRadius;
float m_fBaseRadius;
float m_fDirectFactor;
float m_fStartRadius;
float m_fEndRadius;
float m_fLastTime;
int m_NumCM;
// Scissor parameters (2d extent)
short m_sX;
short m_sY;
short m_sWidth;
short m_sHeight;
// Far/near planes
float m_fNear;
float m_fFar;
ptr32 m_pOwner;
//for static spot light sources casting volumetric shadows
int m_nReserved; // compensates for the vtbl
COrthoNormalBasis m_Orientation;
int m_CustomTextureId;
Matrix44 m_TextureMatrix;
ptr32 m_pObject[4][4]; //!< Object for light coronas and light flares
//the light image
ptr32 m_pLightImage;
float m_fLightFrustumAngle;
float m_fBaseLightFrustumAngle;
float m_fAnimSpeed;
ptr32 m_pShader;
Vec3 m_ProjAngles;
Vec3 m_BaseProjAngles;
uint m_Flags; //!< flags from above (prefix DLF_)
char m_Name[64];
int m_nLightStyle;
float m_fCoronaScale;
float m_fStartTime;
float m_fLifeTime; //!< lsource will be removed after this number of seconds
char m_sDebugName[8]; //!< name of light creator (for debuging, pointer can't be used since entity may be deleted)
ptr32 m_pShadowMapLightSource; //!<
ptr32 m_arrLightLeafBuffers[8]; //!< array of leafbuffers used for heightmap lighting pass
int m_nEntityLightId; //!<
int m_nFrameID; //!<
ptr32 m_pCharInstance; // pointer to character this source is attached to
void copyTo(CDLight& dl)
{
memset(&dl, 0, sizeof(dl));
#define COPY(x) dl.x=x
#define MEMCOPY(x) memcpy (dl.x,x,sizeof(x))
COPY(m_Id);
COPY(m_Origin); //world space position
COPY(m_BaseOrigin); //world space position
COPY(m_Color); //!< clampled diffuse light color
COPY(m_BaseColor); //!< clampled diffuse light color
COPY(m_SpecColor);
COPY(m_BaseSpecColor);
COPY(m_vObjectSpacePos); //Object space position
COPY(m_fRadius);
COPY(m_fBaseRadius);
COPY(m_fDirectFactor);
COPY(m_fStartRadius);
COPY(m_fEndRadius);
COPY(m_fLastTime);
COPY(m_NumCM);
// Scissor parameters (2d extent)
COPY(m_sX);
COPY(m_sY);
COPY(m_sWidth);
COPY(m_sHeight);
// Far/near planes
COPY(m_fNear);
COPY(m_fFar);
COPY(m_Orientation);
COPY(m_CustomTextureId);
COPY(m_TextureMatrix);
COPY(m_fLightFrustumAngle);
COPY(m_fBaseLightFrustumAngle);
COPY(m_fAnimSpeed);
COPY(m_ProjAngles);
COPY(m_BaseProjAngles);
COPY(m_Flags); //!< flags from above (prefix DLF_)
MEMCOPY(m_Name);
COPY(m_nLightStyle);
COPY(m_fCoronaScale);
COPY(m_fStartTime);
COPY(m_fLifeTime); //!< lsource will be removed after this number of seconds
MEMCOPY(m_sDebugName); //!< name of light creator (for debuging, pointer can't be used since entity may be deleted)
COPY(m_nEntityLightId);
COPY(m_nFrameID);
#undef MEMCOPY
#undef COPY
}
};
bool C3DEngine::LoadStaticLightSources (const char *pszFileName)
{
LightFileHeader sHeader, sHdrCmp;
FILE * hFile = GetSystem()->GetIPak()->FOpen(pszFileName, "rb");
if (hFile == NULL)
return false;
if (GetSystem()->GetIPak()->FRead(&sHeader, sizeof(LightFileHeader), 1, hFile) != 1)
{
GetSystem()->GetIPak()->FClose(hFile);
return false;
}
CDLightDummy32 dummy;
if (sHeader.iNumDLights == 0 ||
sHeader.iVersion != 0 ||
sHeader.iSizeOfDLight != sizeof(CDLightDummy32))
{
GetConsole()->Exit ("C3DEngine::LoadStaticLightSources: StatLights.dat version error (%d,%d). Please reexport using latest version of 32-bit editor.",
sHeader.iSizeOfDLight, sizeof(dummy));
return false;
}
m_lstStaticLights.Reset();
m_lstStaticLights.PreAllocate(sHeader.iNumDLights);
for (UINT iCurLight=0; iCurLight<sHeader.iNumDLights; iCurLight++)
{
CDLight newLight;
CDLightDummy32 dummy32Light;
if (GetSystem()->GetIPak()->FRead(&dummy32Light, sizeof(dummy32Light), 1, hFile) != 1)
{
GetSystem()->GetIPak()->FClose(hFile);
return false;
}
dummy32Light.copyTo(newLight);
newLight.m_pLightImage = 0;
newLight.m_pCharInstance = 0;
newLight.m_pOwner = 0;
newLight.m_pShader = 0;
int nTextureFlags2 = 0;
if (GetSystem()->GetIPak()->FRead(&nTextureFlags2, sizeof(int), 1, hFile) != 1)
{
GetSystem()->GetIPak()->FClose(hFile);
return false;
}
char szTextureName[MAX_PATH_LENGTH]="";
bool b0 = ReadString(hFile, szTextureName, MAX_PATH_LENGTH, GetSystem()->GetIPak());
char szShaderName[MAX_PATH_LENGTH]="";
bool b1 = ReadString(hFile, szShaderName, MAX_PATH_LENGTH, GetSystem()->GetIPak());
// bool b(nTextureFlags2&FT2_FORCECUBEMAP);
if(szTextureName[0])
newLight.m_pLightImage = GetRenderer()->EF_LoadTexture(szTextureName, 0, nTextureFlags2/*FT2_FORCECUBEMAP*/, eTT_Cubemap);
if(szShaderName[0])
newLight.m_pShader = GetRenderer()->EF_LoadShader(szShaderName, eSH_World, EF_SYSTEM);
AddStaticLightSource(newLight,0,0,0);
}
GetSystem()->GetIPak()->FClose(hFile);
return true;
}
#else
bool C3DEngine::LoadStaticLightSources(const char *pszFileName)
{
LightFileHeader sHeader, sHdrCmp;
FILE * hFile = GetSystem()->GetIPak()->FOpen(pszFileName, "rb");
if (hFile == NULL)
return false;
if (GetSystem()->GetIPak()->FRead(&sHeader, sizeof(LightFileHeader), 1, hFile) != 1)
{
GetSystem()->GetIPak()->FClose(hFile);
return false;
}
CDLight dummy;
if (sHeader.iNumDLights == 0 ||
sHeader.iVersion != sHdrCmp.iVersion ||
sHeader.iSizeOfDLight != sHdrCmp.iSizeOfDLight)
{
GetConsole()->Exit("C3DEngine::LoadStaticLightSources: StatLights.dat version error, please reexport using latest version of editor.");
return false;
}
m_lstStaticLights.Reset();
m_lstStaticLights.PreAllocate(sHeader.iNumDLights);
for (UINT iCurLight=0; iCurLight<sHeader.iNumDLights; iCurLight++)
{
CDLight newLight;
if (GetSystem()->GetIPak()->FRead(&newLight, sizeof(CDLight), 1, hFile) != 1)
{
GetSystem()->GetIPak()->FClose(hFile);
return false;
}
newLight.m_pLightImage = 0;
newLight.m_pCharInstance = 0;
newLight.m_pOwner = 0;
newLight.m_pShader = 0;
int nTextureFlags2 = 0;
if (GetSystem()->GetIPak()->FRead(&nTextureFlags2, sizeof(int), 1, hFile) != 1)
{
GetSystem()->GetIPak()->FClose(hFile);
return false;
}
char szTextureName[MAX_PATH_LENGTH]="";
bool b0 = ReadString(hFile, szTextureName, MAX_PATH_LENGTH, GetSystem()->GetIPak());
char szShaderName[MAX_PATH_LENGTH]="";
bool b1 = ReadString(hFile, szShaderName, MAX_PATH_LENGTH, GetSystem()->GetIPak());
// bool b(nTextureFlags2&FT2_FORCECUBEMAP);
if(szTextureName[0])
newLight.m_pLightImage = GetRenderer()->EF_LoadTexture(szTextureName, 0, nTextureFlags2/*FT2_FORCECUBEMAP*/, eTT_Cubemap);
if(szShaderName[0])
newLight.m_pShader = GetRenderer()->EF_LoadShader(szShaderName, eSH_World, EF_SYSTEM);
AddStaticLightSource(newLight,0,0,0);
}
GetSystem()->GetIPak()->FClose(hFile);
return true;
}
#endif