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