//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// #include "Renderer/BsSkybox.h" #include "RTTI/BsSkyboxRTTI.h" #include "Scene/BsSceneObject.h" #include "Allocators/BsFrameAlloc.h" #include "Image/BsTexture.h" #include "Renderer/BsRenderer.h" #include "Utility/BsUUID.h" #include "Renderer/BsIBLUtility.h" namespace bs { SkyboxBase::SkyboxBase() : mBrightness(1.0f) { } Skybox::Skybox() { // This shouldn't normally happen, as filtered textures are generated when a radiance texture is assigned, but // we check for it anyway (something could have gone wrong). if(mTexture.isLoaded()) { if (mFilteredRadiance == nullptr || mIrradiance == nullptr) filterTexture(); } } Skybox::~Skybox() { if (mRendererTask != nullptr) mRendererTask->cancel(); } void Skybox::filterTexture() { // If previous rendering task exists, cancel it if (mRendererTask != nullptr) mRendererTask->cancel(); { TEXTURE_DESC cubemapDesc; cubemapDesc.type = TEX_TYPE_CUBE_MAP; cubemapDesc.format = PF_RG11B10F; cubemapDesc.width = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE; cubemapDesc.height = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE; cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format); cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET; mFilteredRadiance = Texture::_createPtr(cubemapDesc); } { TEXTURE_DESC irradianceCubemapDesc; irradianceCubemapDesc.type = TEX_TYPE_CUBE_MAP; irradianceCubemapDesc.format = PF_RG11B10F; irradianceCubemapDesc.width = ct::IBLUtility::IRRADIANCE_CUBEMAP_SIZE; irradianceCubemapDesc.height = ct::IBLUtility::IRRADIANCE_CUBEMAP_SIZE; irradianceCubemapDesc.numMips = 0; irradianceCubemapDesc.usage = TU_STATIC | TU_RENDERTARGET; mIrradiance = Texture::_createPtr(irradianceCubemapDesc); } auto renderComplete = [this]() { mRendererTask = nullptr; }; SPtr coreSkybox = getCore(); SPtr coreFilteredRadiance = mFilteredRadiance->getCore(); SPtr coreIrradiance = mIrradiance->getCore(); auto filterSkybox = [coreFilteredRadiance, coreIrradiance, coreSkybox]() { // Filter radiance ct::gIBLUtility().scaleCubemap(coreSkybox->getTexture(), 0, coreFilteredRadiance, 0); ct::gIBLUtility().filterCubemapForSpecular(coreFilteredRadiance, nullptr); coreSkybox->mFilteredRadiance = coreFilteredRadiance; // Generate irradiance ct::gIBLUtility().filterCubemapForIrradiance(coreFilteredRadiance, coreIrradiance); coreSkybox->mIrradiance = coreIrradiance; return true; }; mRendererTask = ct::RendererTask::create("SkyboxFilter", filterSkybox); mRendererTask->onComplete.connect(renderComplete); ct::gRenderer()->addTask(mRendererTask); } void Skybox::setTexture(const HTexture& texture) { mTexture = texture; mFilteredRadiance = nullptr; mIrradiance = nullptr; if(mTexture.isLoaded()) filterTexture(); _markCoreDirty((ActorDirtyFlag)SkyboxDirtyFlag::Texture); } SPtr Skybox::getCore() const { return std::static_pointer_cast(mCoreSpecific); } SPtr Skybox::createEmpty() { Skybox* skybox = new (bs_alloc()) Skybox(); SPtr skyboxPtr = bs_core_ptr(skybox); skyboxPtr->_setThisPtr(skyboxPtr); return skyboxPtr; } SPtr Skybox::create() { SPtr skyboxPtr = createEmpty(); skyboxPtr->initialize(); return skyboxPtr; } SPtr Skybox::createCore() const { SPtr radiance; if (mTexture) radiance = mTexture->getCore(); SPtr filteredRadiance; if (mFilteredRadiance) filteredRadiance = mFilteredRadiance->getCore(); SPtr irradiance; if (mIrradiance) irradiance = mIrradiance->getCore(); ct::Skybox* skybox = new (bs_alloc()) ct::Skybox(radiance, filteredRadiance, irradiance); SPtr skyboxPtr = bs_shared_ptr(skybox); skyboxPtr->_setThisPtr(skyboxPtr); return skyboxPtr; } CoreSyncData Skybox::syncToCore(FrameAlloc* allocator) { UINT32 size = 0; size += getActorSyncDataSize(); size += rttiGetElemSize(mBrightness); size += sizeof(SPtr); size += rttiGetElemSize(getCoreDirtyFlags()); UINT8* buffer = allocator->alloc(size); char* dataPtr = (char*)buffer; dataPtr = syncActorTo(dataPtr); dataPtr = rttiWriteElem(mBrightness, dataPtr); dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr); SPtr* texture = new (dataPtr) SPtr(); if (mTexture.isLoaded(false)) *texture = mTexture->getCore(); else *texture = nullptr; dataPtr += sizeof(SPtr); return CoreSyncData(buffer, size); } void Skybox::_markCoreDirty(ActorDirtyFlag flags) { markCoreDirty((UINT32)flags); } RTTITypeBase* Skybox::getRTTIStatic() { return SkyboxRTTI::instance(); } RTTITypeBase* Skybox::getRTTI() const { return Skybox::getRTTIStatic(); } namespace ct { Skybox::Skybox(const SPtr& radiance, const SPtr& filteredRadiance, const SPtr& irradiance) :mFilteredRadiance(filteredRadiance), mIrradiance(irradiance) { mTexture = radiance; } Skybox::~Skybox() { gRenderer()->notifySkyboxRemoved(this); } void Skybox::initialize() { gRenderer()->notifySkyboxAdded(this); CoreObject::initialize(); } void Skybox::syncToCore(const CoreSyncData& data) { char* dataPtr = (char*)data.getBuffer(); SkyboxDirtyFlag dirtyFlags; bool oldIsActive = mActive; dataPtr = syncActorFrom(dataPtr); dataPtr = rttiReadElem(mBrightness, dataPtr); dataPtr = rttiReadElem(dirtyFlags, dataPtr); SPtr* texture = (SPtr*)dataPtr; mTexture = *texture; texture->~SPtr(); dataPtr += sizeof(SPtr); if (oldIsActive != mActive) { if (mActive) gRenderer()->notifySkyboxAdded(this); else gRenderer()->notifySkyboxRemoved(this); } else { if (dirtyFlags != SkyboxDirtyFlag::Texture) { gRenderer()->notifySkyboxRemoved(this); gRenderer()->notifySkyboxAdded(this); } } } } }