3DEngineLight.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733
  1. /*
  2. * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
  3. * its licensors.
  4. *
  5. * For complete copyright and license terms please see the LICENSE at the root of this
  6. * distribution (the "License"). All use of this software is governed by the License,
  7. * or, if provided, by the license below or the license accompanying this file. Do not
  8. * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
  9. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. *
  11. */
  12. // Original file Copyright Crytek GMBH or its affiliates, used under license.
  13. // Description : Light sources manager
  14. #include "Cry3DEngine_precompiled.h"
  15. #include "3dEngine.h"
  16. #include "ObjMan.h"
  17. #include "VisAreas.h"
  18. #include "AABBSV.h"
  19. #include "LightEntity.h"
  20. #include "ObjectsTree.h"
  21. #include "ClipVolumeManager.h"
  22. ILightSource* C3DEngine::CreateLightSource()
  23. {
  24. // construct new object
  25. CLightEntity* pLightEntity = new CLightEntity();
  26. m_lstStaticLights.Add(pLightEntity);
  27. return pLightEntity;
  28. }
  29. void C3DEngine::DeleteLightSource(ILightSource* pLightSource)
  30. {
  31. if (m_lstStaticLights.Delete((CLightEntity*)pLightSource) || pLightSource == m_pSun)
  32. {
  33. if (pLightSource == m_pSun)
  34. {
  35. m_pSun = NULL;
  36. }
  37. delete pLightSource;
  38. }
  39. else
  40. {
  41. assert(!"Light object not found");
  42. }
  43. }
  44. void CLightEntity::Release(bool)
  45. {
  46. Get3DEngine()->UnRegisterEntityDirect(this);
  47. Get3DEngine()->DeleteLightSource(this);
  48. }
  49. void CLightEntity::SetLightProperties(const CDLight& light)
  50. {
  51. C3DEngine* engine = Get3DEngine();
  52. m_light = light;
  53. m_bShadowCaster = (m_light.m_Flags & DLF_CASTSHADOW_MAPS) != 0;
  54. m_light.m_fBaseRadius = m_light.m_fRadius;
  55. m_light.m_fLightFrustumAngle = CLAMP(m_light.m_fLightFrustumAngle, 0.f, (LIGHT_PROJECTOR_MAX_FOV / 2.f));
  56. if (!(m_light.m_Flags & (DLF_PROJECT | DLF_AREA_LIGHT)))
  57. {
  58. m_light.m_fLightFrustumAngle = 90.f / 2.f;
  59. }
  60. m_light.m_pOwner = this;
  61. if (m_light.m_Flags & DLF_ATTACH_TO_SUN)
  62. {
  63. m_dwRndFlags |= ERF_RENDER_ALWAYS | ERF_HUD;
  64. }
  65. engine->GetLightEntities()->Delete((ILightSource*)this);
  66. PodArray<ILightSource*>& lightEntities = *engine->GetLightEntities();
  67. //on consoles we force all lights (except sun) to be deferred
  68. if (GetCVars()->e_DynamicLightsForceDeferred && !(m_light.m_Flags & (DLF_SUN | DLF_POST_3D_RENDERER)))
  69. {
  70. m_light.m_Flags |= DLF_DEFERRED_LIGHT;
  71. }
  72. if (light.m_Flags & DLF_DEFERRED_LIGHT)
  73. {
  74. lightEntities.Add((ILightSource*)this);
  75. }
  76. else
  77. {
  78. lightEntities.InsertBefore((ILightSource*)this, 0);
  79. }
  80. }
  81. void C3DEngine::ResetCasterCombinationsCache()
  82. {
  83. for (int nSunInUse = 0; nSunInUse < 2; nSunInUse++)
  84. {
  85. // clear user counters
  86. for (ShadowFrustumListsCacheUsers::iterator it = m_FrustumsCacheUsers[nSunInUse].begin(); it != m_FrustumsCacheUsers[nSunInUse].end(); ++it)
  87. {
  88. it->second = 0;
  89. }
  90. }
  91. }
  92. void C3DEngine::DeleteAllStaticLightSources()
  93. {
  94. for (int i = 0; i < m_lstStaticLights.Count(); i++)
  95. {
  96. delete m_lstStaticLights[i];
  97. }
  98. m_lstStaticLights.Reset();
  99. m_pSun = NULL;
  100. }
  101. void C3DEngine::InitShadowFrustums(const SRenderingPassInfo& passInfo)
  102. {
  103. assert(passInfo.IsGeneralPass());
  104. FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
  105. AZ_TRACE_METHOD();
  106. if (m_pSun)
  107. {
  108. CDLight* pLight = &m_pSun->GetLightProperties();
  109. CLightEntity* pLightEntity = (CLightEntity*)pLight->m_pOwner;
  110. if (passInfo.RenderShadows() && (pLight->m_Flags & DLF_CASTSHADOW_MAPS) && pLight->m_Id >= 0)
  111. {
  112. pLightEntity->UpdateGSMLightSourceShadowFrustum(passInfo);
  113. if (pLightEntity->m_pShadowMapInfo)
  114. {
  115. pLight->m_pShadowMapFrustums = pLightEntity->m_pShadowMapInfo->pGSM;
  116. }
  117. }
  118. _smart_ptr<IMaterial> pMat = pLightEntity->GetMaterial();
  119. if (pMat)
  120. {
  121. pLight->m_Shader = pMat->GetShaderItem();
  122. }
  123. // update copy of light ion the renderer
  124. if (pLight->m_Id >= 0)
  125. {
  126. CDLight* pRndLight = NULL;
  127. GetRenderer()->EF_Query(EFQ_LightSource, pLight->m_Id, pRndLight);
  128. assert(pLight->m_Id == pRndLight->m_Id);
  129. pRndLight->m_pShadowMapFrustums = pLight->m_pShadowMapFrustums;
  130. pRndLight->m_Shader = pLight->m_Shader;
  131. pRndLight->m_Flags = pLight->m_Flags;
  132. }
  133. // add per object shadow frustums
  134. m_nCustomShadowFrustumCount = 0;
  135. if (passInfo.RenderShadows() && GetCVars()->e_ShadowsPerObject > 0)
  136. {
  137. const uint nFrustumCount = m_lstPerObjectShadows.size();
  138. if (nFrustumCount > m_lstCustomShadowFrustums.size())
  139. {
  140. m_lstCustomShadowFrustums.resize(nFrustumCount);
  141. }
  142. for (uint i = 0; i < nFrustumCount; ++i)
  143. {
  144. if (m_lstPerObjectShadows[i].pCaster)
  145. {
  146. ShadowMapFrustum* pFr = &m_lstCustomShadowFrustums[i];
  147. pFr->m_eFrustumType = ShadowMapFrustum::e_PerObject;
  148. CLightEntity::ProcessPerObjectFrustum(pFr, &m_lstPerObjectShadows[i], m_pSun, passInfo);
  149. ++m_nCustomShadowFrustumCount;
  150. }
  151. }
  152. }
  153. }
  154. if (passInfo.RenderShadows())
  155. {
  156. ResetCasterCombinationsCache();
  157. }
  158. }
  159. void C3DEngine::AddPerObjectShadow(IShadowCaster* pCaster, float fConstBias, float fSlopeBias, float fJitter, const Vec3& vBBoxScale, uint nTexSize)
  160. {
  161. SPerObjectShadow* pOS = GetPerObjectShadow(pCaster);
  162. if (!pOS)
  163. {
  164. pOS = &m_lstPerObjectShadows.AddNew();
  165. }
  166. pOS->pCaster = pCaster;
  167. pOS->fConstBias = fConstBias;
  168. pOS->fSlopeBias = fSlopeBias;
  169. pOS->fJitter = fJitter;
  170. pOS->vBBoxScale = vBBoxScale;
  171. pOS->nTexSize = nTexSize;
  172. }
  173. void C3DEngine::RemovePerObjectShadow(IShadowCaster* pCaster)
  174. {
  175. SPerObjectShadow* pOS = GetPerObjectShadow(pCaster);
  176. if (pOS)
  177. {
  178. FRAME_PROFILER("C3DEngine::RemovePerObjectShadow", GetSystem(), PROFILE_3DENGINE);
  179. size_t nIndex = (size_t)(pOS - m_lstPerObjectShadows.begin());
  180. m_lstPerObjectShadows.Delete(nIndex);
  181. }
  182. }
  183. struct SPerObjectShadow* C3DEngine::GetPerObjectShadow(IShadowCaster* pCaster)
  184. {
  185. for (int i = 0; i < m_lstPerObjectShadows.Count(); ++i)
  186. {
  187. if (m_lstPerObjectShadows[i].pCaster == pCaster)
  188. {
  189. return &m_lstPerObjectShadows[i];
  190. }
  191. }
  192. return NULL;
  193. }
  194. void C3DEngine::GetCustomShadowMapFrustums(ShadowMapFrustum*& arrFrustums, int& nFrustumCount)
  195. {
  196. arrFrustums = m_lstCustomShadowFrustums.begin();
  197. nFrustumCount = m_nCustomShadowFrustumCount;
  198. }
  199. // delete pLight->m_pProjCamera;
  200. //pLight->m_pProjCamera=0;
  201. //if(pLight->m_pShader)
  202. // SAFE_RELEASE(pLight->m_pShader);
  203. namespace
  204. {
  205. static inline bool CmpCastShadowFlag(const CDLight* p1, const CDLight* p2)
  206. {
  207. // move sun first
  208. if ((p1->m_Flags & DLF_SUN) > (p2->m_Flags & DLF_SUN))
  209. {
  210. return true;
  211. }
  212. else if ((p1->m_Flags & DLF_SUN) < (p2->m_Flags & DLF_SUN))
  213. {
  214. return false;
  215. }
  216. // move shadow casters first
  217. if ((p1->m_Flags & DLF_CASTSHADOW_MAPS) > (p2->m_Flags & DLF_CASTSHADOW_MAPS))
  218. {
  219. return true;
  220. }
  221. else if ((p1->m_Flags & DLF_CASTSHADOW_MAPS) < (p2->m_Flags & DLF_CASTSHADOW_MAPS))
  222. {
  223. return false;
  224. }
  225. // get some sorting consistency for shadow casters
  226. if (p1->m_pOwner > p2->m_pOwner)
  227. {
  228. return true;
  229. }
  230. else if (p1->m_pOwner < p2->m_pOwner)
  231. {
  232. return false;
  233. }
  234. return false;
  235. }
  236. }
  237. //////////////////////////////////////////////////////////////////////////
  238. void C3DEngine::SubmitSun(const SRenderingPassInfo& passInfo)
  239. {
  240. assert(passInfo.IsGeneralPass());
  241. FUNCTION_PROFILER_3DENGINE_LEGACYONLY;
  242. AZ_TRACE_METHOD();
  243. if (m_pSun)
  244. {
  245. CDLight* light = &m_pSun->GetLightProperties();
  246. GetRenderer()->EF_ADDDlight(light, passInfo);
  247. }
  248. }
  249. void C3DEngine::RemoveEntityLightSources(IRenderNode* pEntity)
  250. {
  251. for (int i = 0; i < m_lstStaticLights.Count(); i++)
  252. {
  253. if (m_lstStaticLights[i] == pEntity)
  254. {
  255. m_lstStaticLights.Delete(i);
  256. if (pEntity == m_pSun)
  257. {
  258. m_pSun = NULL;
  259. }
  260. i--;
  261. }
  262. }
  263. }
  264. ILightSource* C3DEngine::GetSunEntity()
  265. {
  266. return m_pSun;
  267. }
  268. void C3DEngine::OnCasterDeleted(IShadowCaster* pCaster)
  269. {
  270. FUNCTION_PROFILER(gEnv->pSystem, PROFILE_3DENGINE);
  271. { // make sure pointer to object will not be used somewhere in the renderer
  272. if (m_pSun)
  273. {
  274. m_pSun->OnCasterDeleted(pCaster);
  275. }
  276. if (GetRenderer()->GetActiveGPUCount() > 1)
  277. {
  278. if (ShadowFrustumMGPUCache* pFrustumCache = GetRenderer()->GetShadowFrustumMGPUCache())
  279. {
  280. pFrustumCache->DeleteFromCache(pCaster);
  281. }
  282. }
  283. // remove from per object shadows list
  284. RemovePerObjectShadow(pCaster);
  285. }
  286. }
  287. //////////////////////////////////////////////////////////////////////////
  288. //////////////////////////////////////////////////////////////////////////
  289. void CLightVolumesMgr::Init()
  290. {
  291. m_bUpdateLightVolumes = false;
  292. for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
  293. {
  294. m_pLightVolumes[i].reserve(LV_MAX_COUNT);
  295. m_pLightVolsInfo[i].reserve(LV_MAX_COUNT);
  296. }
  297. memset(m_nWorldCells, 0, sizeof(m_nWorldCells));
  298. memset(m_pWorldLightCells, 0, sizeof(m_pWorldLightCells));
  299. }
  300. void CLightVolumesMgr::Reset()
  301. {
  302. for (int i = 0; i < RT_COMMAND_BUF_COUNT; ++i)
  303. {
  304. stl::free_container(m_pLightVolumes[i]);
  305. }
  306. m_bUpdateLightVolumes = false;
  307. memset(m_nWorldCells, 0, sizeof(m_nWorldCells));
  308. memset(m_pWorldLightCells, 0, sizeof(m_pWorldLightCells));
  309. }
  310. //////////////////////////////////////////////////////////////////////////
  311. uint16 CLightVolumesMgr::RegisterVolume(const Vec3& vPos, f32 fRadius, uint8 nClipVolumeRef, const SRenderingPassInfo& passInfo)
  312. {
  313. DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[passInfo.ThreadID()];
  314. IF ((m_bUpdateLightVolumes && (lightVolsInfo.size() < LV_MAX_COUNT)) && fRadius < 256.0f, 1)
  315. {
  316. FUNCTION_PROFILER_3DENGINE;
  317. int32 nPosx = (int32)(floorf(vPos.x * LV_CELL_RSIZEX));
  318. int32 nPosy = (int32)(floorf(vPos.y * LV_CELL_RSIZEY));
  319. int32 nPosz = (int32)(floorf(vPos.z * LV_CELL_RSIZEZ));
  320. // Check if world cell has any light volume, else add new one
  321. uint16 nHashIndex = GetWorldHashBucketKey(nPosx, nPosy, nPosz);
  322. uint16* pCurrentVolumeID = &m_nWorldCells[nHashIndex];
  323. while (*pCurrentVolumeID != 0)
  324. {
  325. SLightVolInfo& sVolInfo = *lightVolsInfo[*pCurrentVolumeID - 1];
  326. int32 nVolumePosx = (int32)(floorf(sVolInfo.vVolume.x * LV_CELL_RSIZEX));
  327. int32 nVolumePosy = (int32)(floorf(sVolInfo.vVolume.y * LV_CELL_RSIZEY));
  328. int32 nVolumePosz = (int32)(floorf(sVolInfo.vVolume.z * LV_CELL_RSIZEZ));
  329. if (nPosx == nVolumePosx &&
  330. nPosy == nVolumePosy &&
  331. nPosz == nVolumePosz &&
  332. nClipVolumeRef == sVolInfo.nClipVolumeID)
  333. {
  334. return (uint16) * pCurrentVolumeID;
  335. }
  336. pCurrentVolumeID = &sVolInfo.nNextVolume;
  337. }
  338. // create new volume
  339. SLightVolInfo* pLightVolInfo = new SLightVolInfo(vPos, fRadius, nClipVolumeRef);
  340. lightVolsInfo.push_back(pLightVolInfo);
  341. *pCurrentVolumeID = lightVolsInfo.size();
  342. return *pCurrentVolumeID;
  343. }
  344. return 0;
  345. }
  346. //////////////////////////////////////////////////////////////////////////
  347. void CLightVolumesMgr::RegisterLight(const CDLight& pDL, uint32 nLightID, [[maybe_unused]] const SRenderingPassInfo& passInfo)
  348. {
  349. IF ((m_bUpdateLightVolumes && !(pDL.m_Flags & LV_DLF_LIGHTVOLUMES_MASK)), 1)
  350. {
  351. FUNCTION_PROFILER_3DENGINE;
  352. const f32 fColCheck = (f32) fsel(pDL.m_Color.r + pDL.m_Color.g + pDL.m_Color.b - 0.333f, 1.0f, 0.0f); //light color > threshold
  353. const f32 fRadCheck = (f32) fsel(pDL.m_fRadius - 0.5f, 1.0f, 0.0f); //light radius > threshold
  354. if (fColCheck * fRadCheck)
  355. {
  356. //if the radius is large than certain value, all the the world light cells will be lighted anyway. So we just add the light to all the cells
  357. //the input radius restriction will be added too
  358. if(floorf(pDL.m_fRadius*LV_LIGHT_CELL_R_SIZE) > LV_LIGHTS_WORLD_BUCKET_SIZE)
  359. {
  360. for (int32 idx = 0; idx < LV_LIGHTS_WORLD_BUCKET_SIZE; idx++)
  361. {
  362. SLightCell& lightCell = m_pWorldLightCells[idx];
  363. CryPrefetch(&lightCell);
  364. if (lightCell.nLightCount < LV_LIGHTS_MAX_COUNT)
  365. {
  366. lightCell.nLightID[lightCell.nLightCount] = nLightID;
  367. lightCell.nLightCount += 1;
  368. }
  369. }
  370. }
  371. else
  372. {
  373. int32 nMiny = (int32)(floorf((pDL.m_Origin.y - pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
  374. int32 nMaxy = (int32)(floorf((pDL.m_Origin.y + pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
  375. int32 nMinx = (int32)(floorf((pDL.m_Origin.x - pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
  376. int32 nMaxx = (int32)(floorf((pDL.m_Origin.x + pDL.m_fRadius) * LV_LIGHT_CELL_R_SIZE));
  377. // Register light into all cells touched by light radius
  378. for (int32 y = nMiny, ymax = nMaxy; y <= ymax; ++y)
  379. {
  380. for (int32 x = nMinx, xmax = nMaxx; x <= xmax; ++x)
  381. {
  382. SLightCell& lightCell = m_pWorldLightCells[GetWorldHashBucketKey(x, y, 1, LV_LIGHTS_WORLD_BUCKET_SIZE)];
  383. CryPrefetch(&lightCell);
  384. if (lightCell.nLightCount < LV_LIGHTS_MAX_COUNT)
  385. {
  386. //only if the las light added to the cell wasn't the same light
  387. if (!(lightCell.nLightCount > 0 && lightCell.nLightID[lightCell.nLightCount - 1] == nLightID))
  388. {
  389. lightCell.nLightID[lightCell.nLightCount] = nLightID;
  390. lightCell.nLightCount += 1;
  391. }
  392. }
  393. }
  394. }
  395. }
  396. }
  397. }
  398. }
  399. //////////////////////////////////////////////////////////////////////////
  400. void CLightVolumesMgr::AddLight(const SRenderLight& pLight, const SLightVolInfo* __restrict pVolInfo, SLightVolume& pVolume)
  401. {
  402. // Check for clip volume
  403. if (pLight.m_nStencilRef[0] == pVolInfo->nClipVolumeID || pLight.m_nStencilRef[1] == pVolInfo->nClipVolumeID ||
  404. pLight.m_nStencilRef[0] == CClipVolumeManager::AffectsEverythingStencilRef)
  405. {
  406. const Vec4* __restrict vLight = (Vec4*) &pLight.m_Origin.x;
  407. const Vec4& vVolume = pVolInfo->vVolume;
  408. const f32 fDimCheck = (f32) fsel(vLight->w - vVolume.w * 0.1f, 1.0f, 0.0f); //light radius not more than 10x smaller than volume radius
  409. const f32 fOverlapCheck = (f32) fsel(sqr(vVolume.x - vLight->x) + sqr(vVolume.y - vLight->y) + sqr(vVolume.z - vLight->z) - sqr(vVolume.w + vLight->w), 0.0f, 1.0f);// touches volumes
  410. if (fDimCheck * fOverlapCheck)
  411. {
  412. float fAttenuationBulbSize = pLight.m_fAttenuationBulbSize;
  413. Vec3 lightColor = *((Vec3*)&pLight.m_Color);
  414. // Adjust light intensity so that the intended brightness is reached 1 meter from the light's surface
  415. IF (!(pLight.m_Flags & (DLF_AREA_LIGHT | DLF_AMBIENT)), 1)
  416. {
  417. fAttenuationBulbSize = max(fAttenuationBulbSize, 0.001f);
  418. // Solve I * 1 / (1 + d/lightsize)^2 = 1
  419. float intensityMul = 1.0f + 1.0f / fAttenuationBulbSize;
  420. intensityMul *= intensityMul;
  421. lightColor *= intensityMul;
  422. }
  423. pVolume.pData.push_back();
  424. SLightVolume::SLightData& lightData = pVolume.pData[pVolume.pData.size() - 1];
  425. lightData.vPos = *vLight;
  426. lightData.vColor = Vec4(lightColor, fAttenuationBulbSize);
  427. lightData.vParams = Vec4(0.f, 0.f, 0.f, 0.f);
  428. IF (pLight.m_Flags & DLF_PROJECT, 1)
  429. {
  430. lightData.vParams = Vec4(pLight.m_ObjMatrix.GetColumn0(), cos_tpl(DEG2RAD(pLight.m_fLightFrustumAngle)));
  431. }
  432. }
  433. }
  434. }
  435. //////////////////////////////////////////////////////////////////////////
  436. void CLightVolumesMgr::Update(const SRenderingPassInfo& passInfo)
  437. {
  438. uint32 nThreadID = passInfo.ThreadID();
  439. DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[nThreadID];
  440. if (!m_bUpdateLightVolumes || lightVolsInfo.empty())
  441. {
  442. return;
  443. }
  444. FUNCTION_PROFILER_3DENGINE;
  445. TArray<SRenderLight>* pLights = GetRenderer()->EF_GetDeferredLights(passInfo);
  446. const uint32 nLightCount = pLights->size();
  447. uint32 nLightVols = lightVolsInfo.size();
  448. LightVolumeVector& lightVols = m_pLightVolumes[nThreadID];
  449. uint32 existingLightVolsCount = 0; //This is 0, that just means we will be overwriting all existing light volumes
  450. //If this is a recursive pass (not the first time that this is called this frame), we're just going to be adding on new light volumes to the existing collection
  451. if (passInfo.IsRecursivePass())
  452. {
  453. existingLightVolsCount = lightVols.size();
  454. //If no new light volumes have been added, don't bother updating
  455. if (nLightVols == existingLightVolsCount)
  456. {
  457. return;
  458. }
  459. }
  460. lightVols.resize(nLightVols);
  461. if (!nLightCount)
  462. {
  463. //Start out existingLightVolsCount to avoid clearing out existing light volumes when we don't need to
  464. for (uint32 v = existingLightVolsCount; v < nLightVols; ++v)
  465. {
  466. lightVols[v].pData.resize(0);
  467. }
  468. return;
  469. }
  470. const int MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE = 1024;
  471. if (nLightCount > MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE)
  472. {
  473. CryWarning(VALIDATOR_MODULE_3DENGINE, VALIDATOR_WARNING, "More lights in the scene (%d) than supported by the Light Volume Update function (%d). Extra lights will be ignored.",
  474. nLightCount, MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE);
  475. }
  476. //This can be a uint8 array because nLightVols should never be greater than 256(LV_MAX_COUNT)
  477. assert(LV_MAX_COUNT <= 256);
  478. uint8 lightProcessedStateArray[MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE];
  479. //Start at the number of light volumes that already exist so that we don't end up re-updating light volumes unnecessarily.
  480. for (uint32 v = existingLightVolsCount; v < nLightVols; ++v)
  481. {
  482. const Vec4* __restrict vBVol = &lightVolsInfo[v]->vVolume;
  483. int32 nMiny = (int32)(floorf((vBVol->y - vBVol->w) * LV_LIGHT_CELL_R_SIZE));
  484. int32 nMaxy = (int32)(floorf((vBVol->y + vBVol->w) * LV_LIGHT_CELL_R_SIZE));
  485. int32 nMinx = (int32)(floorf((vBVol->x - vBVol->w) * LV_LIGHT_CELL_R_SIZE));
  486. int32 nMaxx = (int32)(floorf((vBVol->x + vBVol->w) * LV_LIGHT_CELL_R_SIZE));
  487. lightVols[v].pData.resize(0);
  488. // Loop through active light cells touching bounding volume (~avg 2 cells)
  489. for (int32 y = nMiny, ymax = nMaxy; y <= ymax; ++y)
  490. {
  491. for (int32 x = nMinx, xmax = nMaxx; x <= xmax; ++x)
  492. {
  493. const SLightCell& lightCell = m_pWorldLightCells[GetWorldHashBucketKey(x, y, 1, LV_LIGHTS_WORLD_BUCKET_SIZE)];
  494. CryPrefetch(&lightCell);
  495. const SRenderLight& pFirstDL = (*pLights)[lightCell.nLightID[0]];
  496. CryPrefetch(&pFirstDL);
  497. CryPrefetch(&pFirstDL.m_ObjMatrix);
  498. for (uint32 l = 0; (l < lightCell.nLightCount) & (lightVols[v].pData.size() < LIGHTVOLUME_MAXLIGHTS); ++l)
  499. {
  500. const int32 nLightId = lightCell.nLightID[l];
  501. //Only allow IDs < MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE to continue or else we'll overflow access to
  502. //lightProcessedStateArray[MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE]. Skipping the extra lights shouldn't really matter
  503. //since A) folks won't be using that many lights, and B) for the case of light emitting particles, they tend to be grouped
  504. //so that the individual contributions tend to bleed together anyway.
  505. if (nLightId >= MAX_NUM_LIGHTS_FOR_LIGHT_VOLUME_UPDATE)
  506. {
  507. continue;
  508. }
  509. if (static_cast<uint32>(nLightId) < nLightCount)
  510. {
  511. const SRenderLight& pDL = (*pLights)[nLightId];
  512. const int32 nNextLightId = lightCell.nLightID[(l + 1) & (LIGHTVOLUME_MAXLIGHTS - 1)];
  513. const SRenderLight& pNextDL = (*pLights)[nNextLightId];
  514. CryPrefetch(&pNextDL);
  515. CryPrefetch(&pNextDL.m_ObjMatrix);
  516. IF(lightProcessedStateArray[nLightId] != v + 1, 1)
  517. {
  518. lightProcessedStateArray[nLightId] = v + 1;
  519. AddLight(pDL, &*lightVolsInfo[v], lightVols[v]);
  520. }
  521. }
  522. }
  523. }
  524. }
  525. }
  526. }
  527. //////////////////////////////////////////////////////////////////////////
  528. void CLightVolumesMgr::Clear(const SRenderingPassInfo& passInfo)
  529. {
  530. DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[passInfo.ThreadID()];
  531. m_bUpdateLightVolumes = false;
  532. if (GetCVars()->e_LightVolumes && passInfo.IsGeneralPass() && GetCVars()->e_DynamicLights)
  533. {
  534. memset(m_nWorldCells, 0, sizeof(m_nWorldCells));
  535. memset(m_pWorldLightCells, 0, sizeof(m_pWorldLightCells));
  536. //Clean up volume info data
  537. for (size_t i = 0; i < lightVolsInfo.size(); ++i)
  538. {
  539. delete lightVolsInfo[i];
  540. }
  541. m_pLightVolsInfo[passInfo.ThreadID()].clear();
  542. m_bUpdateLightVolumes = (GetCVars()->e_LightVolumes == 1) ? true : false;
  543. }
  544. }
  545. //////////////////////////////////////////////////////////////////////////
  546. void CLightVolumesMgr::GetLightVolumes(threadID nThreadID, SLightVolume*& pLightVols, uint32& nNumVols)
  547. {
  548. pLightVols = 0;
  549. nNumVols = 0;
  550. if (GetCVars()->e_LightVolumes == 1 && GetCVars()->e_DynamicLights && !m_pLightVolumes[nThreadID].empty())
  551. {
  552. pLightVols = &m_pLightVolumes[nThreadID][0];
  553. nNumVols = m_pLightVolumes[nThreadID].size();
  554. }
  555. }
  556. void C3DEngine::GetLightVolumes(threadID nThreadID, SLightVolume*& pLightVols, uint32& nNumVols)
  557. {
  558. m_LightVolumesMgr.GetLightVolumes(nThreadID, pLightVols, nNumVols);
  559. }
  560. uint16 C3DEngine::RegisterVolumeForLighting(const Vec3& vPos, f32 fRadius, uint8 nClipVolumeRef, const SRenderingPassInfo& passInfo)
  561. {
  562. return m_LightVolumesMgr.RegisterVolume(vPos, fRadius, nClipVolumeRef, passInfo);
  563. }
  564. //////////////////////////////////////////////////////////////////////////
  565. #ifndef _RELEASE
  566. void CLightVolumesMgr::DrawDebug(const SRenderingPassInfo& passInfo)
  567. {
  568. DynArray<SLightVolInfo*>& lightVolsInfo = m_pLightVolsInfo[passInfo.ThreadID()];
  569. IRenderer* pRenderer = GetRenderer();
  570. IRenderAuxGeom* pAuxGeom = GetRenderer()->GetIRenderAuxGeom();
  571. if (!pAuxGeom || !passInfo.IsGeneralPass())
  572. {
  573. return;
  574. }
  575. ColorF cWhite = ColorF(1, 1, 1, 1);
  576. ColorF cBad = ColorF(1.0f, 0.0, 0.0f, 1.0f);
  577. ColorF cWarning = ColorF(1.0f, 1.0, 0.0f, 1.0f);
  578. ColorF cGood = ColorF(0.0f, 0.5, 1.0f, 1.0f);
  579. ColorF cSingleCell = ColorF(0.0f, 1.0, 0.0f, 1.0f);
  580. const uint32 nLightVols = lightVolsInfo.size();
  581. LightVolumeVector& lightVols = m_pLightVolumes[passInfo.ThreadID()];
  582. const Vec3 vCamPos = passInfo.GetCamera().GetPosition();
  583. float fYLine = 8.0f, fYStep = 20.0f;
  584. GetRenderer()->Draw2dLabel(8.0f, fYLine += fYStep, 2.0f, (float*)&cWhite.r, false, "Light Volumes Info (count %d)", nLightVols);
  585. for (uint32 v = 0; v < nLightVols; ++v) // draw each light volume
  586. {
  587. SLightVolume& lv = lightVols[v];
  588. SLightVolInfo& lvInfo = *lightVolsInfo[v];
  589. ColorF& cCol = (lv.pData.size() >= 10) ? cBad : ((lv.pData.size() >= 5) ? cWarning : cGood);
  590. const Vec3 vPos = Vec3(lvInfo.vVolume.x, lvInfo.vVolume.y, lvInfo.vVolume.z);
  591. const float fCamDistSq = (vPos - vCamPos).len2();
  592. cCol.a = max(0.25f, min(1.0f, 1024.0f / (fCamDistSq + 1e-6f)));
  593. pRenderer->DrawLabelEx(vPos, 1.3f, (float*)&cCol.r, true, true, "Id: %d\nPos: %.2f %.2f %.2f\nRadius: %.2f\nLights: %d\nOutLights: %d",
  594. v, vPos.x, vPos.y, vPos.z, lvInfo.vVolume.w, lv.pData.size(), (*(int32*)&lvInfo.vVolume.w) & (1 << 31) ? 1 : 0);
  595. if (GetCVars()->e_LightVolumesDebug == 2)
  596. {
  597. const float fSideSize = 0.707f * sqrtf(lvInfo.vVolume.w * lvInfo.vVolume.w * 2);
  598. pAuxGeom->DrawAABB(AABB(vPos - Vec3(fSideSize), vPos + Vec3(fSideSize)), false, cCol, eBBD_Faceted);
  599. }
  600. if (GetCVars()->e_LightVolumesDebug == 3)
  601. {
  602. cBad.a = 1.0f;
  603. const Vec3 vCellPos = Vec3(floorf((lvInfo.vVolume.x) * LV_CELL_RSIZEX) * LV_CELL_SIZEX,
  604. floorf((lvInfo.vVolume.y) * LV_CELL_RSIZEY) * LV_CELL_SIZEY,
  605. floorf((lvInfo.vVolume.z) * LV_CELL_RSIZEZ) * LV_CELL_SIZEZ);
  606. const Vec3 vMin = vCellPos;
  607. const Vec3 vMax = vMin + Vec3(LV_CELL_SIZEX, LV_CELL_SIZEY, LV_CELL_SIZEZ);
  608. pAuxGeom->DrawAABB(AABB(vMin, vMax), false, cBad, eBBD_Faceted);
  609. }
  610. }
  611. }
  612. #endif