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 | |
12 | namespace 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 | |