BsLightProbeVolume.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Renderer/BsLightProbeVolume.h"
  4. #include "RTTI/BsLightProbeVolumeRTTI.h"
  5. #include "Allocators/BsFrameAlloc.h"
  6. #include "Renderer/BsRenderer.h"
  7. #include "Renderer/BsLight.h"
  8. #include "RenderAPI/BsGpuBuffer.h"
  9. #include "Image/BsTexture.h"
  10. #include "Renderer/BsIBLUtility.h"
  11. #include "Scene/BsSceneObject.h"
  12. namespace bs
  13. {
  14. LightProbeVolume::LightProbeVolume()
  15. : mVolume(AABox::UNIT_BOX), mCellCount { 1, 1, 1 }
  16. { }
  17. LightProbeVolume::LightProbeVolume(const AABox& volume, const Vector3I& cellCount)
  18. :mVolume(volume), mCellCount(cellCount)
  19. {
  20. reset();
  21. }
  22. LightProbeVolume::~LightProbeVolume()
  23. {
  24. if (mRendererTask)
  25. mRendererTask->cancel();
  26. }
  27. UINT32 LightProbeVolume::addProbe(const Vector3& position)
  28. {
  29. UINT32 handle = mNextProbeId++;
  30. mProbes[handle] = ProbeInfo(LightProbeFlags::Clean, position);
  31. _markCoreDirty();
  32. return handle;
  33. }
  34. void LightProbeVolume::removeProbe(UINT32 handle)
  35. {
  36. auto iterFind = mProbes.find(handle);
  37. if (iterFind != mProbes.end() && mProbes.size() > 4)
  38. {
  39. iterFind->second.flags = LightProbeFlags::Removed;
  40. _markCoreDirty();
  41. }
  42. }
  43. void LightProbeVolume::setProbePosition(UINT32 handle, const Vector3& position)
  44. {
  45. auto iterFind = mProbes.find(handle);
  46. if (iterFind != mProbes.end())
  47. {
  48. iterFind->second.position = position;
  49. _markCoreDirty();
  50. }
  51. }
  52. Vector3 LightProbeVolume::getProbePosition(UINT32 handle) const
  53. {
  54. auto iterFind = mProbes.find(handle);
  55. if (iterFind != mProbes.end())
  56. return iterFind->second.position;
  57. return Vector3::ZERO;
  58. }
  59. Vector<LightProbeInfo> LightProbeVolume::getProbes() const
  60. {
  61. Vector<LightProbeInfo> output;
  62. for(auto& entry : mProbes)
  63. {
  64. if (entry.second.flags == LightProbeFlags::Removed || entry.second.flags == LightProbeFlags::Empty)
  65. continue;
  66. LightProbeInfo info;
  67. info.position = entry.second.position;
  68. info.handle = entry.first;
  69. info.shCoefficients = entry.second.coefficients;
  70. output.push_back(info);
  71. }
  72. return output;
  73. }
  74. void LightProbeVolume::resize(const AABox& volume, const Vector3I& cellCount)
  75. {
  76. UINT32 numProbesX = std::max(1, mCellCount.x) + 1;
  77. UINT32 numProbesY = std::max(1, mCellCount.y) + 1;
  78. UINT32 numProbesZ = std::max(1, mCellCount.z) + 1;
  79. Vector3 size = mVolume.getSize();
  80. for(UINT32 z = 0; z < numProbesZ; ++z)
  81. {
  82. for(UINT32 y = 0; y < numProbesY; ++y)
  83. {
  84. for(UINT32 x = 0; x < numProbesX; ++x)
  85. {
  86. Vector3 position = mVolume.getMin();
  87. position.x += size.x * (x / (float)numProbesX);
  88. position.y += size.y * (y / (float)numProbesY);
  89. position.z += size.z * (z / (float)numProbesZ);
  90. if (mVolume.contains(position))
  91. continue;
  92. addProbe(position);
  93. }
  94. }
  95. }
  96. mVolume = volume;
  97. mCellCount = cellCount;
  98. _markCoreDirty();
  99. }
  100. void LightProbeVolume::reset()
  101. {
  102. UINT32 numProbesX = std::max(1, mCellCount.x) + 1;
  103. UINT32 numProbesY = std::max(1, mCellCount.y) + 1;
  104. UINT32 numProbesZ = std::max(1, mCellCount.z) + 1;
  105. UINT32 numProbes = numProbesX * numProbesY * numProbesZ;
  106. // Make sure there are adequate number of probes to fill the volume
  107. while((UINT32)mProbes.size() < numProbes)
  108. addProbe(Vector3::ZERO);
  109. UINT32 idx = 0;
  110. UINT32 rowPitch = numProbesX;
  111. UINT32 slicePitch = numProbesX * numProbesY;
  112. Vector3 size = mVolume.getSize();
  113. auto iter = mProbes.begin();
  114. while (iter != mProbes.end())
  115. {
  116. UINT32 x = idx % numProbesX;
  117. UINT32 y = (idx / rowPitch) % numProbesY;
  118. UINT32 z = (idx / slicePitch);
  119. Vector3 position = mVolume.getMin();
  120. position.x += size.x * (x / (float)(numProbesX - 1));
  121. position.y += size.y * (y / (float)(numProbesY - 1));
  122. position.z += size.z * (z / (float)(numProbesZ - 1));
  123. iter->second.position = position;
  124. iter->second.flags = LightProbeFlags::Clean;
  125. ++idx;
  126. ++iter;
  127. if (idx >= numProbes)
  128. break;
  129. }
  130. // Set remaining probes to removed state
  131. while(iter != mProbes.end())
  132. {
  133. iter->second.flags = LightProbeFlags::Removed;
  134. ++iter;
  135. }
  136. _markCoreDirty();
  137. }
  138. void LightProbeVolume::clip()
  139. {
  140. for (auto& entry : mProbes)
  141. {
  142. if (!mVolume.contains(entry.second.position))
  143. entry.second.flags = LightProbeFlags::Removed;
  144. }
  145. _markCoreDirty();
  146. }
  147. void LightProbeVolume::renderProbe(UINT32 handle)
  148. {
  149. auto iterFind = mProbes.find(handle);
  150. if (iterFind != mProbes.end())
  151. {
  152. if (iterFind->second.flags == LightProbeFlags::Clean)
  153. {
  154. iterFind->second.flags = LightProbeFlags::Dirty;
  155. _markCoreDirty();
  156. runRenderProbeTask();
  157. }
  158. }
  159. }
  160. void LightProbeVolume::renderProbes()
  161. {
  162. bool anyModified = false;
  163. for(auto& entry : mProbes)
  164. {
  165. if (entry.second.flags == LightProbeFlags::Clean)
  166. {
  167. entry.second.flags = LightProbeFlags::Dirty;
  168. anyModified = true;
  169. }
  170. }
  171. if (anyModified)
  172. {
  173. _markCoreDirty();
  174. runRenderProbeTask();
  175. }
  176. }
  177. void LightProbeVolume::runRenderProbeTask()
  178. {
  179. // If a task is already running cancel it
  180. // Note: If the task is just about to start processing, cancelling it will skip the update this frame
  181. // (which might be fine if we just changed positions of dirty probes it was about to update, but it might also
  182. // waste a frame if those positions needed to be updated anyway). For now I'm ignoring it as it seems like a rare
  183. // enough situation, plus it's one that will only happen during development time.
  184. if (mRendererTask)
  185. mRendererTask->cancel();
  186. auto renderComplete = [this]()
  187. {
  188. mRendererTask = nullptr;
  189. };
  190. SPtr<ct::LightProbeVolume> coreProbeVolume = getCore();
  191. auto renderProbes = [coreProbeVolume]()
  192. {
  193. return coreProbeVolume->renderProbes(3);
  194. };
  195. mRendererTask = ct::RendererTask::create("RenderLightProbes", renderProbes);
  196. mRendererTask->onComplete.connect(renderComplete);
  197. ct::gRenderer()->addTask(mRendererTask);
  198. }
  199. void LightProbeVolume::updateCoefficients()
  200. {
  201. // Ensure all light probe coefficients are generated
  202. if (mRendererTask)
  203. mRendererTask->wait();
  204. ct::LightProbeVolume* coreVolume = getCore().get();
  205. Vector<LightProbeCoefficientInfo> coeffInfo;
  206. auto getSaveData = [coreVolume, &coeffInfo]()
  207. {
  208. coreVolume->getProbeCoefficients(coeffInfo);
  209. };
  210. gCoreThread().queueCommand(getSaveData);
  211. gCoreThread().submit(true);
  212. for(auto& entry : coeffInfo)
  213. {
  214. auto iterFind = mProbes.find(entry.handle);
  215. if (iterFind == mProbes.end())
  216. continue;
  217. iterFind->second.coefficients = entry.coefficients;
  218. }
  219. }
  220. SPtr<ct::LightProbeVolume> LightProbeVolume::getCore() const
  221. {
  222. return std::static_pointer_cast<ct::LightProbeVolume>(mCoreSpecific);
  223. }
  224. SPtr<LightProbeVolume> LightProbeVolume::create(const AABox& volume, const Vector3I& cellCount)
  225. {
  226. LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume(volume, cellCount);
  227. SPtr<LightProbeVolume> probeVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
  228. probeVolumePtr->_setThisPtr(probeVolumePtr);
  229. probeVolumePtr->initialize();
  230. return probeVolumePtr;
  231. }
  232. SPtr<LightProbeVolume> LightProbeVolume::createEmpty()
  233. {
  234. LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume();
  235. SPtr<LightProbeVolume> probleVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
  236. probleVolumePtr->_setThisPtr(probleVolumePtr);
  237. return probleVolumePtr;
  238. }
  239. SPtr<ct::CoreObject> LightProbeVolume::createCore() const
  240. {
  241. ct::LightProbeVolume* handler = new (bs_alloc<ct::LightProbeVolume>()) ct::LightProbeVolume(mProbes);
  242. SPtr<ct::LightProbeVolume> handlerPtr = bs_shared_ptr<ct::LightProbeVolume>(handler);
  243. handlerPtr->_setThisPtr(handlerPtr);
  244. return handlerPtr;
  245. }
  246. CoreSyncData LightProbeVolume::syncToCore(FrameAlloc* allocator)
  247. {
  248. UINT32 size = 0;
  249. UINT8* buffer = nullptr;
  250. bs_frame_mark();
  251. {
  252. FrameVector<std::pair<UINT32, ProbeInfo>> dirtyProbes;
  253. FrameVector<UINT32> removedProbes;
  254. for (auto& probe : mProbes)
  255. {
  256. if (probe.second.flags == LightProbeFlags::Dirty)
  257. {
  258. dirtyProbes.push_back(std::make_pair(probe.first, probe.second));
  259. probe.second.flags = LightProbeFlags::Clean;
  260. }
  261. else if (probe.second.flags == LightProbeFlags::Removed)
  262. {
  263. removedProbes.push_back(probe.first);
  264. probe.second.flags = LightProbeFlags::Empty;
  265. }
  266. }
  267. for (auto& probe : removedProbes)
  268. mProbes.erase(probe);
  269. UINT32 numDirtyProbes = (UINT32)dirtyProbes.size();
  270. UINT32 numRemovedProbes = (UINT32)removedProbes.size();
  271. size += getActorSyncDataSize();
  272. size += rttiGetElemSize(numDirtyProbes);
  273. size += rttiGetElemSize(numRemovedProbes);
  274. size += (sizeof(UINT32) + sizeof(Vector3) + sizeof(LightProbeFlags)) * numDirtyProbes;
  275. size += sizeof(UINT32) * numRemovedProbes;
  276. buffer = allocator->alloc(size);
  277. char* dataPtr = (char*)buffer;
  278. dataPtr = syncActorTo(dataPtr);
  279. dataPtr = rttiWriteElem(numDirtyProbes, dataPtr);
  280. dataPtr = rttiWriteElem(numRemovedProbes, dataPtr);
  281. for (auto& entry : dirtyProbes)
  282. {
  283. dataPtr = rttiWriteElem(entry.first, dataPtr);
  284. dataPtr = rttiWriteElem(entry.second.position, dataPtr);
  285. dataPtr = rttiWriteElem(entry.second.flags, dataPtr);
  286. }
  287. for(auto& entry : removedProbes)
  288. dataPtr = rttiWriteElem(entry, dataPtr);
  289. }
  290. bs_frame_clear();
  291. return CoreSyncData(buffer, size);
  292. }
  293. void LightProbeVolume::_markCoreDirty(ActorDirtyFlag dirtyFlag)
  294. {
  295. markCoreDirty((UINT32)dirtyFlag);
  296. }
  297. RTTITypeBase* LightProbeVolume::getRTTIStatic()
  298. {
  299. return LightProbeVolumeRTTI::instance();
  300. }
  301. RTTITypeBase* LightProbeVolume::getRTTI() const
  302. {
  303. return LightProbeVolume::getRTTIStatic();
  304. }
  305. namespace ct
  306. {
  307. LightProbeVolume::LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes)
  308. {
  309. mInitCoefficients.resize(probes.size());
  310. mProbePositions.resize(probes.size());
  311. mProbeInfos.resize(probes.size());
  312. UINT32 probeIdx = 0;
  313. for(auto& entry : probes)
  314. {
  315. mProbeMap[entry.first] = probeIdx;
  316. mProbePositions[probeIdx] = entry.second.position;
  317. LightProbeInfo probeInfo;
  318. probeInfo.flags = LightProbeFlags::Dirty;
  319. probeInfo.bufferIdx = probeIdx;
  320. probeInfo.handle = entry.first;
  321. mProbeInfos[probeIdx] = probeInfo;
  322. mInitCoefficients[probeIdx] = entry.second.coefficients;
  323. probeIdx++;
  324. }
  325. }
  326. LightProbeVolume::~LightProbeVolume()
  327. {
  328. gRenderer()->notifyLightProbeVolumeRemoved(this);
  329. }
  330. void LightProbeVolume::initialize()
  331. {
  332. // Set SH coefficients loaded from the file
  333. UINT32 numCoefficients = (UINT32)mInitCoefficients.size();
  334. assert(mInitCoefficients.size() == mProbeMap.size());
  335. resizeCoefficientBuffer(std::max(32U, numCoefficients));
  336. mCoefficients->writeData(0, sizeof(LightProbeSHCoefficients) * numCoefficients, mInitCoefficients.data());
  337. mInitCoefficients.clear();
  338. gRenderer()->notifyLightProbeVolumeAdded(this);
  339. CoreObject::initialize();
  340. }
  341. bool LightProbeVolume::renderProbes(UINT32 maxProbes)
  342. {
  343. // Probe map only contains active probes
  344. UINT32 numUsedProbes = (UINT32)mProbeMap.size();
  345. if(numUsedProbes > mCoeffBufferSize)
  346. resizeCoefficientBuffer(std::max(32U, numUsedProbes * 2));
  347. UINT32 numProbeUpdates = 0;
  348. for (; mFirstDirtyProbe < (UINT32)mProbeInfos.size(); ++mFirstDirtyProbe)
  349. {
  350. LightProbeInfo& probeInfo = mProbeInfos[mFirstDirtyProbe];
  351. if(probeInfo.flags == LightProbeFlags::Dirty)
  352. {
  353. TEXTURE_DESC cubemapDesc;
  354. cubemapDesc.type = TEX_TYPE_CUBE_MAP;
  355. cubemapDesc.format = PF_RGBA16F;
  356. cubemapDesc.width = 256; // Note: Test different sizes and their effect on quality
  357. cubemapDesc.height = 256;
  358. cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
  359. SPtr<Texture> cubemap = Texture::create(cubemapDesc);
  360. Vector3 localPos = mProbePositions[mFirstDirtyProbe];
  361. const Transform& tfrm = getTransform();
  362. const Vector3& position = tfrm.getPosition();
  363. const Quaternion& rotation = tfrm.getRotation();
  364. Vector3 transformedPos = rotation.rotate(localPos) + position;
  365. gRenderer()->captureSceneCubeMap(cubemap, transformedPos, CaptureSettings());
  366. gIBLUtility().filterCubemapForIrradiance(cubemap, mCoefficients, probeInfo.bufferIdx);
  367. probeInfo.flags = LightProbeFlags::Clean;
  368. numProbeUpdates++;
  369. }
  370. if (maxProbes != 0 && numProbeUpdates >= maxProbes)
  371. break;
  372. }
  373. gRenderer()->notifyLightProbeVolumeUpdated(this);
  374. return mFirstDirtyProbe == (UINT32)mProbeInfos.size();
  375. }
  376. void LightProbeVolume::syncToCore(const CoreSyncData& data)
  377. {
  378. char* dataPtr = (char*)data.getBuffer();
  379. bool oldIsActive = mActive;
  380. dataPtr = syncActorFrom(dataPtr);
  381. UINT32 numDirtyProbes, numRemovedProbes;
  382. dataPtr = rttiReadElem(numDirtyProbes, dataPtr);
  383. dataPtr = rttiReadElem(numRemovedProbes, dataPtr);
  384. for (UINT32 i = 0; i < numDirtyProbes; ++i)
  385. {
  386. UINT32 handle;
  387. dataPtr = rttiReadElem(handle, dataPtr);
  388. Vector3 position;
  389. dataPtr = rttiReadElem(position, dataPtr);
  390. LightProbeFlags flags;
  391. dataPtr = rttiReadElem(flags, dataPtr);
  392. auto iterFind = mProbeMap.find(handle);
  393. if(iterFind != mProbeMap.end())
  394. {
  395. // Update existing probe information
  396. UINT32 compactIdx = iterFind->second;
  397. mProbeInfos[compactIdx].flags = LightProbeFlags::Dirty;
  398. mProbePositions[compactIdx] = position;
  399. mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
  400. }
  401. else // Add a new probe
  402. {
  403. // Empty slots always start at a specific index because we always move them to the back of the array
  404. UINT32 emptyProbeStartIdx = (UINT32)mProbeMap.size();
  405. UINT32 numProbes = (UINT32)mProbeInfos.size();
  406. // Find an empty slot to place the probe information at
  407. UINT32 compactIdx = -1;
  408. for(UINT32 j = emptyProbeStartIdx; j < numProbes; ++j)
  409. {
  410. if(mProbeInfos[j].flags == LightProbeFlags::Empty)
  411. {
  412. compactIdx = j;
  413. break;
  414. }
  415. }
  416. // Found an empty slot
  417. if (compactIdx == (UINT32)-1)
  418. {
  419. compactIdx = (UINT32)mProbeInfos.size();
  420. LightProbeInfo info;
  421. info.flags = LightProbeFlags::Dirty;
  422. info.bufferIdx = compactIdx;
  423. info.handle = handle;
  424. mProbeInfos.push_back(info);
  425. mProbePositions.push_back(position);
  426. }
  427. else // No empty slot, add a new one
  428. {
  429. LightProbeInfo& info = mProbeInfos[compactIdx];
  430. info.flags = LightProbeFlags::Dirty;
  431. info.handle = handle;
  432. mProbePositions[compactIdx] = position;
  433. }
  434. mProbeMap[handle] = compactIdx;
  435. mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
  436. }
  437. }
  438. // Mark slots for removed probes as empty, and move them back to the end of the array
  439. for (UINT32 i = 0; i < numRemovedProbes; ++i)
  440. {
  441. UINT32 idx;
  442. dataPtr = rttiReadElem(idx, dataPtr);
  443. auto iterFind = mProbeMap.find(idx);
  444. if(iterFind != mProbeMap.end())
  445. {
  446. UINT32 compactIdx = iterFind->second;
  447. LightProbeInfo& info = mProbeInfos[compactIdx];
  448. info.flags = LightProbeFlags::Empty;
  449. // Move the empty info to the back of the array so all non-empty probes are contiguous
  450. // Search from back to current index, and find first non-empty probe to switch switch
  451. UINT32 lastSearchIdx = (UINT32)mProbeInfos.size() - 1;
  452. while (lastSearchIdx >= (UINT32)compactIdx)
  453. {
  454. LightProbeFlags flags = mProbeInfos[lastSearchIdx].flags;
  455. if (flags != LightProbeFlags::Empty)
  456. {
  457. std::swap(mProbeInfos[i], mProbeInfos[lastSearchIdx]);
  458. std::swap(mProbePositions[i], mProbePositions[lastSearchIdx]);
  459. mProbeMap[mProbeInfos[lastSearchIdx].handle] = i;
  460. break;
  461. }
  462. lastSearchIdx--;
  463. }
  464. mProbeMap.erase(iterFind);
  465. }
  466. }
  467. if (oldIsActive != mActive)
  468. {
  469. if (mActive)
  470. gRenderer()->notifyLightProbeVolumeAdded(this);
  471. else
  472. gRenderer()->notifyLightProbeVolumeRemoved(this);
  473. }
  474. }
  475. void LightProbeVolume::getProbeCoefficients(Vector<LightProbeCoefficientInfo>& output) const
  476. {
  477. UINT32 numActiveProbes = (UINT32)mProbeMap.size();
  478. if (numActiveProbes == 0)
  479. return;
  480. output.resize(numActiveProbes);
  481. LightProbeSHCoefficients* coefficients = bs_stack_alloc<LightProbeSHCoefficients>(numActiveProbes);
  482. mCoefficients->readData(0, sizeof(LightProbeSHCoefficients) * numActiveProbes, coefficients);
  483. for(UINT32 i = 0; i < numActiveProbes; ++i)
  484. {
  485. output[i].coefficients = coefficients[mProbeInfos[i].bufferIdx];
  486. output[i].handle = mProbeInfos[i].handle;
  487. }
  488. bs_stack_free(coefficients);
  489. }
  490. void LightProbeVolume::resizeCoefficientBuffer(UINT32 count)
  491. {
  492. GPU_BUFFER_DESC desc;
  493. desc.type = GBT_STRUCTURED;
  494. desc.elementSize = sizeof(LightProbeSHCoefficients);
  495. desc.elementCount = count;
  496. desc.usage = GBU_STATIC;
  497. desc.format = BF_UNKNOWN;
  498. desc.randomGpuWrite = true;
  499. SPtr<GpuBuffer> newBuffer = GpuBuffer::create(desc);
  500. if (mCoefficients)
  501. newBuffer->copyData(*mCoefficients, 0, 0, mCoefficients->getSize(), true);
  502. mCoefficients = newBuffer;
  503. mCoeffBufferSize = count;
  504. }
  505. }}