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/BsReflectionProbe.h"
4#include "Private/RTTI/BsReflectionProbeRTTI.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 ReflectionProbeBase::ReflectionProbeBase(ReflectionProbeType type, float radius, const Vector3& extents)
15 : mType(type), mRadius(radius), mExtents(extents)
16 { }
17
18 float ReflectionProbeBase::getRadius() const
19 {
20 Vector3 scale = mTransform.getScale();
21 return mRadius * std::max(std::max(scale.x, scale.y), scale.z);
22 }
23
24 void ReflectionProbeBase::updateBounds()
25 {
26 Vector3 position = mTransform.getPosition();
27 Vector3 scale = mTransform.getScale();
28
29 switch (mType)
30 {
31 case ReflectionProbeType::Sphere:
32 mBounds = Sphere(position, mRadius * std::max(std::max(scale.x, scale.y), scale.z));
33 break;
34 case ReflectionProbeType::Box:
35 mBounds = Sphere(position, (mExtents * scale).length());
36 break;
37 }
38 }
39
40 template <bool Core>
41 template <class P>
42 void TReflectionProbe<Core>::rttiEnumFields(P p)
43 {
44 p(mType);
45 p(mRadius);
46 p(mExtents);
47 p(mTransitionDistance);
48 p(mBounds);
49 p(mFilteredTexture);
50 }
51
52 ReflectionProbe::ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents)
53 : TReflectionProbe(type, radius, extents)
54 {
55 // Calling virtual method is okay here because this is the most derived type
56 updateBounds();
57 }
58
59 ReflectionProbe::~ReflectionProbe()
60 {
61 if (mRendererTask)
62 mRendererTask->cancel();
63 }
64
65 void ReflectionProbe::capture()
66 {
67 if (mCustomTexture != nullptr)
68 return;
69
70 captureAndFilter();
71 }
72
73 void ReflectionProbe::filter()
74 {
75 if (mCustomTexture == nullptr)
76 return;
77
78 captureAndFilter();
79 }
80
81 void ReflectionProbe::captureAndFilter()
82 {
83 // If previous rendering task exists, cancel it
84 if (mRendererTask != nullptr)
85 mRendererTask->cancel();
86
87 TEXTURE_DESC cubemapDesc;
88 cubemapDesc.type = TEX_TYPE_CUBE_MAP;
89 cubemapDesc.format = PF_RG11B10F;
90 cubemapDesc.width = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
91 cubemapDesc.height = ct::IBLUtility::REFLECTION_CUBEMAP_SIZE;
92 cubemapDesc.numMips = PixelUtil::getMaxMipmaps(cubemapDesc.width, cubemapDesc.height, 1, cubemapDesc.format);
93 cubemapDesc.usage = TU_STATIC | TU_RENDERTARGET;
94
95 mFilteredTexture = Texture::_createPtr(cubemapDesc);
96
97 auto renderComplete = [this]()
98 {
99 mRendererTask = nullptr;
100 };
101
102 SPtr<ct::ReflectionProbe> coreProbe = getCore();
103 SPtr<ct::Texture> coreTexture = mFilteredTexture->getCore();
104
105 if (mCustomTexture == nullptr)
106 {
107 auto renderReflProbe = [coreTexture, coreProbe]()
108 {
109 float radius = coreProbe->mType == ReflectionProbeType::Sphere ? coreProbe->mRadius :
110 coreProbe->mExtents.length();
111
112 ct::CaptureSettings settings;
113 settings.encodeDepth = true;
114 settings.depthEncodeNear = radius;
115 settings.depthEncodeFar = radius + 1; // + 1 arbitrary, make it a customizable value?
116
117 ct::gRenderer()->captureSceneCubeMap(coreTexture, coreProbe->getTransform().getPosition(), settings);
118 ct::gIBLUtility().filterCubemapForSpecular(coreTexture, nullptr);
119
120 coreProbe->mFilteredTexture = coreTexture;
121 ct::gRenderer()->notifyReflectionProbeUpdated(coreProbe.get(), true);
122
123 return true;
124 };
125
126 mRendererTask = ct::RendererTask::create("ReflProbeRender", renderReflProbe);
127 }
128 else
129 {
130 SPtr<ct::Texture> coreCustomTex = mCustomTexture->getCore();
131 auto filterReflProbe = [coreCustomTex, coreTexture, coreProbe]()
132 {
133 ct::gIBLUtility().scaleCubemap(coreCustomTex, 0, coreTexture, 0);
134 ct::gIBLUtility().filterCubemapForSpecular(coreTexture, nullptr);
135
136 coreProbe->mFilteredTexture = coreTexture;
137 ct::gRenderer()->notifyReflectionProbeUpdated(coreProbe.get(), true);
138
139 return true;
140 };
141
142 mRendererTask = ct::RendererTask::create("ReflProbeRender", filterReflProbe);
143 }
144
145 mRendererTask->onComplete.connect(renderComplete);
146 ct::gRenderer()->addTask(mRendererTask);
147 }
148
149 SPtr<ct::ReflectionProbe> ReflectionProbe::getCore() const
150 {
151 return std::static_pointer_cast<ct::ReflectionProbe>(mCoreSpecific);
152 }
153
154 SPtr<ReflectionProbe> ReflectionProbe::createSphere(float radius)
155 {
156 ReflectionProbe* probe = new (bs_alloc<ReflectionProbe>()) ReflectionProbe(ReflectionProbeType::Sphere, radius, Vector3::ZERO);
157 SPtr<ReflectionProbe> probePtr = bs_core_ptr<ReflectionProbe>(probe);
158 probePtr->_setThisPtr(probePtr);
159 probePtr->initialize();
160
161 return probePtr;
162 }
163
164 SPtr<ReflectionProbe> ReflectionProbe::createBox(const Vector3& extents)
165 {
166 ReflectionProbe* probe = new (bs_alloc<ReflectionProbe>()) ReflectionProbe(ReflectionProbeType::Box, 1.0f, extents);
167 SPtr<ReflectionProbe> probePtr = bs_core_ptr<ReflectionProbe>(probe);
168 probePtr->_setThisPtr(probePtr);
169 probePtr->initialize();
170
171 return probePtr;
172 }
173
174 SPtr<ReflectionProbe> ReflectionProbe::createEmpty()
175 {
176 ReflectionProbe* probe = new (bs_alloc<ReflectionProbe>()) ReflectionProbe();
177 SPtr<ReflectionProbe> probePtr = bs_core_ptr<ReflectionProbe>(probe);
178 probePtr->_setThisPtr(probePtr);
179
180 return probePtr;
181 }
182
183 SPtr<ct::CoreObject> ReflectionProbe::createCore() const
184 {
185 SPtr<ct::Texture> filteredTexture;
186 if (mFilteredTexture != nullptr)
187 filteredTexture = mFilteredTexture->getCore();
188
189 ct::ReflectionProbe* probe = new (bs_alloc<ct::ReflectionProbe>()) ct::ReflectionProbe(mType, mRadius, mExtents,
190 filteredTexture);
191 SPtr<ct::ReflectionProbe> probePtr = bs_shared_ptr<ct::ReflectionProbe>(probe);
192 probePtr->_setThisPtr(probePtr);
193
194 return probePtr;
195 }
196
197 CoreSyncData ReflectionProbe::syncToCore(FrameAlloc* allocator)
198 {
199 UINT32 size = 0;
200 size += rttiGetElemSize(getCoreDirtyFlags());
201 size += coreSyncGetElemSize((SceneActor&)*this);
202 size += coreSyncGetElemSize(*this);
203
204 UINT8* buffer = allocator->alloc(size);
205
206 char* dataPtr = (char*)buffer;
207 dataPtr = rttiWriteElem(getCoreDirtyFlags(), dataPtr);
208 dataPtr = coreSyncWriteElem((SceneActor&)*this, dataPtr);
209 dataPtr = coreSyncWriteElem(*this, dataPtr);
210
211 return CoreSyncData(buffer, size);
212 }
213
214 void ReflectionProbe::_markCoreDirty(ActorDirtyFlag flags)
215 {
216 markCoreDirty((UINT32)flags);
217 }
218
219 RTTITypeBase* ReflectionProbe::getRTTIStatic()
220 {
221 return ReflectionProbeRTTI::instance();
222 }
223
224 RTTITypeBase* ReflectionProbe::getRTTI() const
225 {
226 return ReflectionProbe::getRTTIStatic();
227 }
228
229 template class TReflectionProbe<true>;
230 template class TReflectionProbe<false>;
231
232 namespace ct
233 {
234 ReflectionProbe::ReflectionProbe(ReflectionProbeType type, float radius, const Vector3& extents,
235 const SPtr<Texture>& filteredTexture)
236 : TReflectionProbe(type, radius, extents), mRendererId(0)
237 {
238 mFilteredTexture = filteredTexture;
239 }
240
241 ReflectionProbe::~ReflectionProbe()
242 {
243 gRenderer()->notifyReflectionProbeRemoved(this);
244 }
245
246 void ReflectionProbe::initialize()
247 {
248 updateBounds();
249 gRenderer()->notifyReflectionProbeAdded(this);
250
251 CoreObject::initialize();
252 }
253
254 void ReflectionProbe::syncToCore(const CoreSyncData& data)
255 {
256 char* dataPtr = (char*)data.getBuffer();
257
258 UINT32 dirtyFlags = 0;
259 bool oldIsActive = mActive;
260 ReflectionProbeType oldType = mType;
261
262 dataPtr = rttiReadElem(dirtyFlags, dataPtr);
263 dataPtr = coreSyncReadElem((SceneActor&)*this, dataPtr);
264 dataPtr = coreSyncReadElem(*this, dataPtr);
265
266 updateBounds();
267
268 if (dirtyFlags == (UINT32)ActorDirtyFlag::Transform)
269 {
270 if (mActive)
271 gRenderer()->notifyReflectionProbeUpdated(this, false);
272 }
273 else
274 {
275 if (oldIsActive != mActive)
276 {
277 if (mActive)
278 gRenderer()->notifyReflectionProbeAdded(this);
279 else
280 {
281 ReflectionProbeType newType = mType;
282 mType = oldType;
283 gRenderer()->notifyReflectionProbeRemoved(this);
284 mType = newType;
285 }
286 }
287 else
288 {
289 ReflectionProbeType newType = mType;
290 mType = oldType;
291 gRenderer()->notifyReflectionProbeRemoved(this);
292 mType = newType;
293
294 gRenderer()->notifyReflectionProbeAdded(this);
295 }
296 }
297 }
298}}
299