1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Renderer/BsSkybox.h"
4#include "Private/RTTI/BsSkyboxRTTI.h"
5#include "Scene/BsSceneObject.h"
6#include "Image/BsTexture.h"
7#include "Renderer/BsRenderer.h"
8#include "Utility/BsUUID.h"
9#include "Renderer/BsIBLUtility.h"
10#include "CoreThread/BsCoreObjectSync.h"
11
12namespace bs
13{
14 template <bool Core>
15 template <class P>
16 void TSkybox<Core>::rttiEnumFields(P p)
17 {
18 p(mBrightness);
19 p(mTexture);
20 }
21
22 Skybox::Skybox()
23 {
24 // This shouldn't normally happen, as filtered textures are generated when a radiance texture is assigned, but
25 // we check for it anyway (something could have gone wrong).
26 if(mTexture.isLoaded())
27 {
28 if (mFilteredRadiance == nullptr || mIrradiance == nullptr)
29 filterTexture();
30 }
31 }
32
33 Skybox::~Skybox()
34 {
35 if (mRendererTask != nullptr)
36 mRendererTask->cancel();
37 }
38
39 void Skybox::filterTexture()
40 {
41 // If previous rendering task exists, cancel it
42 if (mRendererTask != nullptr)
43 mRendererTask->cancel();
44
45 {
46 TEXTURE_DESC cubemapDesc;
47 cubemapDesc.type = TEX_TYPE_CUBE_MAP;
48 cubemapDesc.format = PF_RG11B10F;
49 cubemapDesc.width = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
50 cubemapDesc.height = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
51 cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
52 cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
53
54 mFilteredRadiance = Texture::_createPtr(cubemapDesc);
55 }
56
57 {
58 TEXTURE_DESC irradianceCubemapDesc;
59 irradianceCubemapDesc.type = TEX_TYPE_CUBE_MAP;
60 irradianceCubemapDesc.format = PF_RG11B10F;
61 irradianceCubemapDesc.width = ct::IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
62 irradianceCubemapDesc.height = ct::IBLUtility::IRRADIANCE_CUBEMAP_SIZE;
63 irradianceCubemapDesc.numMips = 0;
64 irradianceCubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
65
66 mIrradiance = Texture::_createPtr(irradianceCubemapDesc);
67 }
68
69 auto renderComplete = [this]()
70 {
71 mRendererTask = nullptr;
72 };
73
74 SPtr<ct::Skybox> coreSkybox = getCore();
75 SPtr<ct::Texture> coreFilteredRadiance = mFilteredRadiance->getCore();
76 SPtr<ct::Texture> coreIrradiance = mIrradiance->getCore();
77
78 auto filterSkybox = [coreFilteredRadiance, coreIrradiance, coreSkybox]()
79 {
80 // Filter radiance
81 ct::gIBLUtility().scaleCubemap(coreSkybox->getTexture(), 0, coreFilteredRadiance, 0);
82 ct::gIBLUtility().filterCubemapForSpecular(coreFilteredRadiance, nullptr);
83
84 coreSkybox->mFilteredRadiance = coreFilteredRadiance;
85
86 // Generate irradiance
87 ct::gIBLUtility().filterCubemapForIrradiance(coreSkybox->getTexture(), coreIrradiance);
88 coreSkybox->mIrradiance = coreIrradiance;
89
90 return true;
91 };
92
93 mRendererTask = ct::RendererTask::create("SkyboxFilter", filterSkybox);
94
95 mRendererTask->onComplete.connect(renderComplete);
96 ct::gRenderer()->addTask(mRendererTask);
97 }
98
99 void Skybox::setTexture(const HTexture& texture)
100 {
101 mTexture = texture;
102
103 mFilteredRadiance = nullptr;
104 mIrradiance = nullptr;
105
106 if(mTexture.isLoaded())
107 filterTexture();
108
109 _markCoreDirty((ActorDirtyFlag)SkyboxDirtyFlag::Texture);
110 }
111
112 SPtr<ct::Skybox> Skybox::getCore() const
113 {
114 return std::static_pointer_cast<ct::Skybox>(mCoreSpecific);
115 }
116
117 SPtr<Skybox> Skybox::createEmpty()
118 {
119 Skybox* skybox = new (bs_alloc<Skybox>()) Skybox();
120 SPtr<Skybox> skyboxPtr = bs_core_ptr<Skybox>(skybox);
121 skyboxPtr->_setThisPtr(skyboxPtr);
122
123 return skyboxPtr;
124 }
125
126 SPtr<Skybox> Skybox::create()
127 {
128 SPtr<Skybox> skyboxPtr = createEmpty();
129 skyboxPtr->initialize();
130
131 return skyboxPtr;
132 }
133
134 SPtr<ct::CoreObject> Skybox::createCore() const
135 {
136 SPtr<ct::Texture> radiance;
137 if (mTexture)
138 radiance = mTexture->getCore();
139
140 SPtr<ct::Texture> filteredRadiance;
141 if (mFilteredRadiance)
142 filteredRadiance = mFilteredRadiance->getCore();
143
144 SPtr<ct::Texture> irradiance;
145 if (mIrradiance)
146 irradiance = mIrradiance->getCore();
147
148 ct::Skybox* skybox = new (bs_alloc<ct::Skybox>()) ct::Skybox(radiance, filteredRadiance, irradiance);
149 SPtr<ct::Skybox> skyboxPtr = bs_shared_ptr<ct::Skybox>(skybox);
150 skyboxPtr->_setThisPtr(skyboxPtr);
151
152 return skyboxPtr;
153 }
154
155 CoreSyncData Skybox::syncToCore(FrameAlloc* allocator)
156 {
157 UINT32 size = 0;
158 size += rttiGetElemSize(getCoreDirtyFlags());
159 size += coreSyncGetElemSize((SceneActor&)*this);
160 size += coreSyncGetElemSize(*this);
161
162 UINT8* buffer = allocator->alloc(size);
163
164 char* dataPtr = (char*)buffer;
165 dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
166 dataPtr = coreSyncWriteElem((SceneActor&)*this, dataPtr);
167 dataPtr = coreSyncWriteElem(*this, dataPtr);
168
169 return CoreSyncData(buffer, size);
170 }
171
172 void Skybox::_markCoreDirty(ActorDirtyFlag flags)
173 {
174 markCoreDirty((UINT32)flags);
175 }
176
177 RTTITypeBase* Skybox::getRTTIStatic()
178 {
179 return SkyboxRTTI::instance();
180 }
181
182 RTTITypeBase* Skybox::getRTTI() const
183 {
184 return Skybox::getRTTIStatic();
185 }
186
187 namespace ct
188 {
189 Skybox::Skybox(const SPtr<Texture>& radiance, const SPtr<Texture>& filteredRadiance, const SPtr<Texture>& irradiance)
190 :mFilteredRadiance(filteredRadiance), mIrradiance(irradiance)
191 {
192 mTexture = radiance;
193 }
194
195 Skybox::~Skybox()
196 {
197 gRenderer()->notifySkyboxRemoved(this);
198 }
199
200 void Skybox::initialize()
201 {
202 gRenderer()->notifySkyboxAdded(this);
203
204 CoreObject::initialize();
205 }
206
207 void Skybox::syncToCore(const CoreSyncData& data)
208 {
209 char* dataPtr = (char*)data.getBuffer();
210
211 SkyboxDirtyFlag dirtyFlags;
212 bool oldIsActive = mActive;
213
214 dataPtr = rttiReadElem(dirtyFlags, dataPtr);
215 dataPtr = coreSyncReadElem((SceneActor&)*this, dataPtr);
216 dataPtr = coreSyncReadElem(*this, dataPtr);
217
218 if (oldIsActive != mActive)
219 {
220 if (mActive)
221 gRenderer()->notifySkyboxAdded(this);
222 else
223 gRenderer()->notifySkyboxRemoved(this);
224 }
225 else
226 {
227 if (dirtyFlags != SkyboxDirtyFlag::Texture)
228 {
229 gRenderer()->notifySkyboxRemoved(this);
230 gRenderer()->notifySkyboxAdded(this);
231 }
232 }
233 }
234 }
235}
236