| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "Renderer/BsLightProbeVolume.h"
- #include "RTTI/BsLightProbeVolumeRTTI.h"
- #include "Renderer/BsRenderer.h"
- #include "Renderer/BsLight.h"
- #include "Image/BsTexture.h"
- #include "Renderer/BsIBLUtility.h"
- #include "Scene/BsSceneObject.h"
- namespace bs
- {
- LightProbeVolume::LightProbeVolume()
- : mVolume(AABox::UNIT_BOX), mCellCount { 1, 1, 1 }
- { }
- LightProbeVolume::LightProbeVolume(const AABox& volume, const Vector3I& cellCount)
- :mVolume(volume), mCellCount(cellCount)
- {
- reset();
- }
- LightProbeVolume::~LightProbeVolume()
- {
- if (mRendererTask)
- mRendererTask->cancel();
- }
- UINT32 LightProbeVolume::addProbe(const Vector3& position)
- {
- UINT32 handle = mNextProbeId++;
- mProbes[handle] = ProbeInfo(LightProbeFlags::Clean, position);
- _markCoreDirty();
- return handle;
- }
- void LightProbeVolume::removeProbe(UINT32 handle)
- {
- auto iterFind = mProbes.find(handle);
- if (iterFind != mProbes.end() && mProbes.size() > 4)
- {
- iterFind->second.flags = LightProbeFlags::Removed;
- _markCoreDirty();
- }
- }
- void LightProbeVolume::setProbePosition(UINT32 handle, const Vector3& position)
- {
- auto iterFind = mProbes.find(handle);
- if (iterFind != mProbes.end())
- {
- iterFind->second.position = position;
- _markCoreDirty();
- }
- }
- Vector3 LightProbeVolume::getProbePosition(UINT32 handle) const
- {
- auto iterFind = mProbes.find(handle);
- if (iterFind != mProbes.end())
- return iterFind->second.position;
-
- return Vector3::ZERO;
- }
- Vector<LightProbeInfo> LightProbeVolume::getProbes() const
- {
- Vector<LightProbeInfo> output;
- for(auto& entry : mProbes)
- {
- if (entry.second.flags == LightProbeFlags::Removed || entry.second.flags == LightProbeFlags::Empty)
- continue;
- LightProbeInfo info;
- info.position = entry.second.position;
- info.handle = entry.first;
- info.shCoefficients = entry.second.coefficients;
- output.push_back(info);
- }
- return output;
- }
- void LightProbeVolume::resize(const AABox& volume, const Vector3I& cellCount)
- {
- UINT32 numProbesX = std::max(1, mCellCount.x) + 1;
- UINT32 numProbesY = std::max(1, mCellCount.y) + 1;
- UINT32 numProbesZ = std::max(1, mCellCount.z) + 1;
- Vector3 size = mVolume.getSize();
- for(UINT32 z = 0; z < numProbesZ; ++z)
- {
- for(UINT32 y = 0; y < numProbesY; ++y)
- {
- for(UINT32 x = 0; x < numProbesX; ++x)
- {
- Vector3 position = mVolume.getMin();
- position.x += size.x * (x / (float)numProbesX);
- position.y += size.y * (y / (float)numProbesY);
- position.z += size.z * (z / (float)numProbesZ);
- if (mVolume.contains(position))
- continue;
- addProbe(position);
- }
- }
- }
- mVolume = volume;
- mCellCount = cellCount;
- _markCoreDirty();
- }
- void LightProbeVolume::reset()
- {
- UINT32 numProbesX = std::max(1, mCellCount.x) + 1;
- UINT32 numProbesY = std::max(1, mCellCount.y) + 1;
- UINT32 numProbesZ = std::max(1, mCellCount.z) + 1;
- UINT32 numProbes = numProbesX * numProbesY * numProbesZ;
- // Make sure there are adequate number of probes to fill the volume
- while((UINT32)mProbes.size() < numProbes)
- addProbe(Vector3::ZERO);
- UINT32 idx = 0;
- UINT32 rowPitch = numProbesX;
- UINT32 slicePitch = numProbesX * numProbesY;
- Vector3 size = mVolume.getSize();
- auto iter = mProbes.begin();
- while (iter != mProbes.end())
- {
- UINT32 x = idx % numProbesX;
- UINT32 y = (idx / rowPitch) % numProbesY;
- UINT32 z = (idx / slicePitch);
- Vector3 position = mVolume.getMin();
- position.x += size.x * (x / (float)(numProbesX - 1));
- position.y += size.y * (y / (float)(numProbesY - 1));
- position.z += size.z * (z / (float)(numProbesZ - 1));
- iter->second.position = position;
- iter->second.flags = LightProbeFlags::Clean;
- ++idx;
- ++iter;
- if (idx >= numProbes)
- break;
- }
- // Set remaining probes to removed state
- while(iter != mProbes.end())
- {
- iter->second.flags = LightProbeFlags::Removed;
- ++iter;
- }
- _markCoreDirty();
- }
- void LightProbeVolume::clip()
- {
- for (auto& entry : mProbes)
- {
- if (!mVolume.contains(entry.second.position))
- entry.second.flags = LightProbeFlags::Removed;
- }
- _markCoreDirty();
- }
- void LightProbeVolume::renderProbe(UINT32 handle)
- {
- auto iterFind = mProbes.find(handle);
- if (iterFind != mProbes.end())
- {
- if (iterFind->second.flags == LightProbeFlags::Clean)
- {
- iterFind->second.flags = LightProbeFlags::Dirty;
- _markCoreDirty();
- runRenderProbeTask();
- }
- }
- }
- void LightProbeVolume::renderProbes()
- {
- bool anyModified = false;
- for(auto& entry : mProbes)
- {
- if (entry.second.flags == LightProbeFlags::Clean)
- {
- entry.second.flags = LightProbeFlags::Dirty;
- anyModified = true;
- }
- }
- if (anyModified)
- {
- _markCoreDirty();
- runRenderProbeTask();
- }
- }
- void LightProbeVolume::runRenderProbeTask()
- {
- // If a task is already running cancel it
- // Note: If the task is just about to start processing, cancelling it will skip the update this frame
- // (which might be fine if we just changed positions of dirty probes it was about to update, but it might also
- // waste a frame if those positions needed to be updated anyway). For now I'm ignoring it as it seems like a rare
- // enough situation, plus it's one that will only happen during development time.
- if (mRendererTask)
- mRendererTask->cancel();
- auto renderComplete = [this]()
- {
- mRendererTask = nullptr;
- };
- SPtr<ct::LightProbeVolume> coreProbeVolume = getCore();
- auto renderProbes = [coreProbeVolume]()
- {
- return coreProbeVolume->renderProbes(3);
- };
- mRendererTask = ct::RendererTask::create("RenderLightProbes", renderProbes);
- mRendererTask->onComplete.connect(renderComplete);
- ct::gRenderer()->addTask(mRendererTask);
- }
- void LightProbeVolume::updateCoefficients()
- {
- // Ensure all light probe coefficients are generated
- if (mRendererTask)
- mRendererTask->wait();
- ct::LightProbeVolume* coreVolume = getCore().get();
- Vector<LightProbeCoefficientInfo> coeffInfo;
- auto getSaveData = [coreVolume, &coeffInfo]()
- {
- coreVolume->getProbeCoefficients(coeffInfo);
- };
- gCoreThread().queueCommand(getSaveData);
- gCoreThread().submit(true);
- for(auto& entry : coeffInfo)
- {
- auto iterFind = mProbes.find(entry.handle);
- if (iterFind == mProbes.end())
- continue;
- iterFind->second.coefficients = entry.coefficients;
- }
- }
- SPtr<ct::LightProbeVolume> LightProbeVolume::getCore() const
- {
- return std::static_pointer_cast<ct::LightProbeVolume>(mCoreSpecific);
- }
- SPtr<LightProbeVolume> LightProbeVolume::create(const AABox& volume, const Vector3I& cellCount)
- {
- LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume(volume, cellCount);
- SPtr<LightProbeVolume> probeVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
- probeVolumePtr->_setThisPtr(probeVolumePtr);
- probeVolumePtr->initialize();
- return probeVolumePtr;
- }
- SPtr<LightProbeVolume> LightProbeVolume::createEmpty()
- {
- LightProbeVolume* probeVolume = new (bs_alloc<LightProbeVolume>()) LightProbeVolume();
- SPtr<LightProbeVolume> probleVolumePtr = bs_core_ptr<LightProbeVolume>(probeVolume);
- probleVolumePtr->_setThisPtr(probleVolumePtr);
- return probleVolumePtr;
- }
- SPtr<ct::CoreObject> LightProbeVolume::createCore() const
- {
- ct::LightProbeVolume* handler = new (bs_alloc<ct::LightProbeVolume>()) ct::LightProbeVolume(mProbes);
- SPtr<ct::LightProbeVolume> handlerPtr = bs_shared_ptr<ct::LightProbeVolume>(handler);
- handlerPtr->_setThisPtr(handlerPtr);
- return handlerPtr;
- }
- CoreSyncData LightProbeVolume::syncToCore(FrameAlloc* allocator)
- {
- UINT32 size = 0;
- UINT8* buffer = nullptr;
- bs_frame_mark();
- {
- FrameVector<std::pair<UINT32, ProbeInfo>> dirtyProbes;
- FrameVector<UINT32> removedProbes;
- for (auto& probe : mProbes)
- {
- if (probe.second.flags == LightProbeFlags::Dirty)
- {
- dirtyProbes.push_back(std::make_pair(probe.first, probe.second));
- probe.second.flags = LightProbeFlags::Clean;
- }
- else if (probe.second.flags == LightProbeFlags::Removed)
- {
- removedProbes.push_back(probe.first);
- probe.second.flags = LightProbeFlags::Empty;
- }
- }
- for (auto& probe : removedProbes)
- mProbes.erase(probe);
- UINT32 numDirtyProbes = (UINT32)dirtyProbes.size();
- UINT32 numRemovedProbes = (UINT32)removedProbes.size();
- size += getActorSyncDataSize();
- size += rttiGetElemSize(numDirtyProbes);
- size += rttiGetElemSize(numRemovedProbes);
- size += (sizeof(UINT32) + sizeof(Vector3) + sizeof(LightProbeFlags)) * numDirtyProbes;
- size += sizeof(UINT32) * numRemovedProbes;
- buffer = allocator->alloc(size);
- char* dataPtr = (char*)buffer;
- dataPtr = syncActorTo(dataPtr);
- dataPtr = rttiWriteElem(numDirtyProbes, dataPtr);
- dataPtr = rttiWriteElem(numRemovedProbes, dataPtr);
- for (auto& entry : dirtyProbes)
- {
- dataPtr = rttiWriteElem(entry.first, dataPtr);
- dataPtr = rttiWriteElem(entry.second.position, dataPtr);
- dataPtr = rttiWriteElem(entry.second.flags, dataPtr);
- }
- for(auto& entry : removedProbes)
- dataPtr = rttiWriteElem(entry, dataPtr);
- }
- bs_frame_clear();
- return CoreSyncData(buffer, size);
- }
- void LightProbeVolume::_markCoreDirty(ActorDirtyFlag dirtyFlag)
- {
- markCoreDirty((UINT32)dirtyFlag);
- }
- RTTITypeBase* LightProbeVolume::getRTTIStatic()
- {
- return LightProbeVolumeRTTI::instance();
- }
- RTTITypeBase* LightProbeVolume::getRTTI() const
- {
- return LightProbeVolume::getRTTIStatic();
- }
- namespace ct
- {
- LightProbeVolume::LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes)
- {
- mInitCoefficients.resize(probes.size());
- mProbePositions.resize(probes.size());
- mProbeInfos.resize(probes.size());
- UINT32 probeIdx = 0;
- for(auto& entry : probes)
- {
- mProbeMap[entry.first] = probeIdx;
- mProbePositions[probeIdx] = entry.second.position;
-
- LightProbeInfo probeInfo;
- probeInfo.flags = LightProbeFlags::Dirty;
- probeInfo.bufferIdx = probeIdx;
- probeInfo.handle = entry.first;
- mProbeInfos[probeIdx] = probeInfo;
- mInitCoefficients[probeIdx] = entry.second.coefficients;
- probeIdx++;
- }
- }
- LightProbeVolume::~LightProbeVolume()
- {
- gRenderer()->notifyLightProbeVolumeRemoved(this);
- }
- void LightProbeVolume::initialize()
- {
- // Set SH coefficients loaded from the file
- UINT32 numCoefficients = (UINT32)mInitCoefficients.size();
- assert(mInitCoefficients.size() == mProbeMap.size());
- resizeCoefficientTexture(std::max(32U, numCoefficients));
- SPtr<PixelData> coeffData = mCoefficients->getProperties().allocBuffer(0, 0);
- UINT32 probesPerRow = coeffData->getWidth() / 9;
- UINT32 probeIdx = 0;
- for(UINT32 y = 0; y < coeffData->getHeight(); ++y)
- {
- for(UINT32 x = 0; x < probesPerRow; ++x)
- {
- if(probeIdx >= numCoefficients)
- break;
- for(UINT32 i = 0; i < 9; i++)
- {
- Color value;
- value.r = mInitCoefficients[probeIdx].coeffsR[i];
- value.g = mInitCoefficients[probeIdx].coeffsG[i];
- value.b = mInitCoefficients[probeIdx].coeffsB[i];
- coeffData->setColorAt(value, x * 9, y);
- }
- probeIdx++;
- }
- }
- mCoefficients->writeData(*coeffData, 0, 0, true);
- mInitCoefficients.clear();
- gRenderer()->notifyLightProbeVolumeAdded(this);
- CoreObject::initialize();
- }
- bool LightProbeVolume::renderProbes(UINT32 maxProbes)
- {
- // Probe map only contains active probes
- UINT32 numUsedProbes = (UINT32)mProbeMap.size();
- if(numUsedProbes > mCoeffBufferSize)
- resizeCoefficientTexture(std::max(32U, numUsedProbes * 2));
- UINT32 numProbeUpdates = 0;
- for (; mFirstDirtyProbe < (UINT32)mProbeInfos.size(); ++mFirstDirtyProbe)
- {
- LightProbeInfo& probeInfo = mProbeInfos[mFirstDirtyProbe];
- if(probeInfo.flags == LightProbeFlags::Dirty)
- {
- TEXTURE_DESC cubemapDesc;
- cubemapDesc.type = TEX_TYPE_CUBE_MAP;
- cubemapDesc.format = PF_RGBA16F;
- cubemapDesc.width = 256; // Note: Test different sizes and their effect on quality
- cubemapDesc.height = 256;
- cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
- SPtr<Texture> cubemap = Texture::create(cubemapDesc);
- Vector3 localPos = mProbePositions[mFirstDirtyProbe];
- const Transform& tfrm = getTransform();
- const Vector3& position = tfrm.getPosition();
- const Quaternion& rotation = tfrm.getRotation();
- Vector3 transformedPos = rotation.rotate(localPos) + position;
- gRenderer()->captureSceneCubeMap(cubemap, transformedPos, CaptureSettings());
- gIBLUtility().filterCubemapForIrradiance(cubemap, mCoefficients, probeInfo.bufferIdx);
- probeInfo.flags = LightProbeFlags::Clean;
- numProbeUpdates++;
- }
- if (maxProbes != 0 && numProbeUpdates >= maxProbes)
- break;
- }
- gRenderer()->notifyLightProbeVolumeUpdated(this);
- return mFirstDirtyProbe == (UINT32)mProbeInfos.size();
- }
- void LightProbeVolume::syncToCore(const CoreSyncData& data)
- {
- char* dataPtr = (char*)data.getBuffer();
- bool oldIsActive = mActive;
- dataPtr = syncActorFrom(dataPtr);
- UINT32 numDirtyProbes, numRemovedProbes;
- dataPtr = rttiReadElem(numDirtyProbes, dataPtr);
- dataPtr = rttiReadElem(numRemovedProbes, dataPtr);
- for (UINT32 i = 0; i < numDirtyProbes; ++i)
- {
- UINT32 handle;
- dataPtr = rttiReadElem(handle, dataPtr);
- Vector3 position;
- dataPtr = rttiReadElem(position, dataPtr);
- LightProbeFlags flags;
- dataPtr = rttiReadElem(flags, dataPtr);
- auto iterFind = mProbeMap.find(handle);
- if(iterFind != mProbeMap.end())
- {
- // Update existing probe information
- UINT32 compactIdx = iterFind->second;
-
- mProbeInfos[compactIdx].flags = LightProbeFlags::Dirty;
- mProbePositions[compactIdx] = position;
- mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
- }
- else // Add a new probe
- {
- // Empty slots always start at a specific index because we always move them to the back of the array
- UINT32 emptyProbeStartIdx = (UINT32)mProbeMap.size();
- UINT32 numProbes = (UINT32)mProbeInfos.size();
- // Find an empty slot to place the probe information at
- UINT32 compactIdx = -1;
- for(UINT32 j = emptyProbeStartIdx; j < numProbes; ++j)
- {
- if(mProbeInfos[j].flags == LightProbeFlags::Empty)
- {
- compactIdx = j;
- break;
- }
- }
- // Found an empty slot
- if (compactIdx == (UINT32)-1)
- {
- compactIdx = (UINT32)mProbeInfos.size();
- LightProbeInfo info;
- info.flags = LightProbeFlags::Dirty;
- info.bufferIdx = compactIdx;
- info.handle = handle;
- mProbeInfos.push_back(info);
- mProbePositions.push_back(position);
- }
- else // No empty slot, add a new one
- {
- LightProbeInfo& info = mProbeInfos[compactIdx];
- info.flags = LightProbeFlags::Dirty;
- info.handle = handle;
- mProbePositions[compactIdx] = position;
- }
- mProbeMap[handle] = compactIdx;
- mFirstDirtyProbe = std::min(compactIdx, mFirstDirtyProbe);
- }
- }
- // Mark slots for removed probes as empty, and move them back to the end of the array
- for (UINT32 i = 0; i < numRemovedProbes; ++i)
- {
- UINT32 idx;
- dataPtr = rttiReadElem(idx, dataPtr);
- auto iterFind = mProbeMap.find(idx);
- if(iterFind != mProbeMap.end())
- {
- UINT32 compactIdx = iterFind->second;
-
- LightProbeInfo& info = mProbeInfos[compactIdx];
- info.flags = LightProbeFlags::Empty;
- // Move the empty info to the back of the array so all non-empty probes are contiguous
- // Search from back to current index, and find first non-empty probe to switch switch
- UINT32 lastSearchIdx = (UINT32)mProbeInfos.size() - 1;
- while (lastSearchIdx >= (UINT32)compactIdx)
- {
- LightProbeFlags flags = mProbeInfos[lastSearchIdx].flags;
- if (flags != LightProbeFlags::Empty)
- {
- std::swap(mProbeInfos[i], mProbeInfos[lastSearchIdx]);
- std::swap(mProbePositions[i], mProbePositions[lastSearchIdx]);
- mProbeMap[mProbeInfos[lastSearchIdx].handle] = i;
- break;
- }
- lastSearchIdx--;
- }
-
- mProbeMap.erase(iterFind);
- }
- }
- if (oldIsActive != mActive)
- {
- if (mActive)
- gRenderer()->notifyLightProbeVolumeAdded(this);
- else
- gRenderer()->notifyLightProbeVolumeRemoved(this);
- }
- }
- void LightProbeVolume::getProbeCoefficients(Vector<LightProbeCoefficientInfo>& output) const
- {
- UINT32 numActiveProbes = (UINT32)mProbeMap.size();
- if (numActiveProbes == 0)
- return;
- output.resize(numActiveProbes);
- LightProbeSHCoefficients* coefficients = bs_stack_alloc<LightProbeSHCoefficients>(numActiveProbes);
- SPtr<PixelData> coeffData = mCoefficients->getProperties().allocBuffer(0, 0);
- mCoefficients->readData(*coeffData);
- UINT32 probesPerRow = coeffData->getWidth() / 9;
- UINT32 probeIdx = 0;
- for(UINT32 y = 0; y < coeffData->getHeight(); ++y)
- {
- for(UINT32 x = 0; x < probesPerRow; ++x)
- {
- if(probeIdx >= numActiveProbes)
- break;
- for(UINT32 i = 0; i < 9; i++)
- {
- Color value = coeffData->getColorAt(x * 9, y);
- coefficients[probeIdx].coeffsR[i] = value.r;
- coefficients[probeIdx].coeffsG[i] = value.g;
- coefficients[probeIdx].coeffsB[i] = value.b;
- }
- probeIdx++;
- }
- }
- for(UINT32 i = 0; i < numActiveProbes; ++i)
- {
- output[i].coefficients = coefficients[mProbeInfos[i].bufferIdx];
- output[i].handle = mProbeInfos[i].handle;
- }
- bs_stack_free(coefficients);
- }
- void LightProbeVolume::resizeCoefficientTexture(UINT32 count)
- {
- Vector2I texSize = IBLUtility::getSHCoeffTextureSize(count, 3);
- TEXTURE_DESC desc;
- desc.width = (UINT32)texSize.x;
- desc.height = (UINT32)texSize.y;
- desc.usage = TU_LOADSTORE | TU_RENDERTARGET;
- desc.format = PF_RGBA32F;
- SPtr<Texture> newTexture = Texture::create(desc);
- newTexture->clear(Color::ZERO);
- if (mCoefficients)
- mCoefficients->copy(newTexture);
- mCoefficients = newTexture;
- mCoeffBufferSize = count;
- }
- }}
|