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#pragma once
4
5#include "BsCorePrerequisites.h"
6#include "CoreThread/BsCoreObject.h"
7#include "Math/BsAABox.h"
8#include "Math/BsVector3.h"
9#include "Math/BsQuaternion.h"
10#include "Math/BsVector3I.h"
11#include "Scene/BsSceneActor.h"
12
13namespace bs
14{
15 namespace ct
16 {
17 class RendererTask;
18 }
19
20 /** @addtogroup Implementation
21 * @{
22 */
23
24 /** Potential states the light probe can be in. */
25 enum class LightProbeFlags
26 {
27 Empty, Clean, Dirty, Removed
28 };
29
30 /** @} */
31
32 /** @addtogroup Renderer-Internal
33 * @{
34 */
35
36 namespace ct { class LightProbeVolume; }
37
38 /** Vector representing spherical harmonic coefficients for a light probe. */
39 struct LightProbeSHCoefficients
40 {
41 LightProbeSHCoefficients()
42 :coeffsR(), coeffsG(), coeffsB()
43 { }
44
45 float coeffsR[9];
46 float coeffsG[9];
47 float coeffsB[9];
48 };
49
50 /** SH coefficients for a specific light probe, and its handle. */
51 struct LightProbeCoefficientInfo
52 {
53 UINT32 handle;
54 LightProbeSHCoefficients coefficients;
55 };
56
57 /** Information about a single probe in the light probe volume. */
58 struct BS_SCRIPT_EXPORT(m:Rendering,pl:true) LightProbeInfo
59 {
60 UINT32 handle;
61 Vector3 position;
62
63 BS_SCRIPT_EXPORT(ex:true)
64 LightProbeSHCoefficients shCoefficients;
65 };
66
67 /**
68 * Allows you to define a volume of light probes that will be used for indirect lighting. Lighting information in the
69 * scene will be interpolated from nearby probes to calculate the amount of indirect lighting at that position. It is
70 * up to the caller to place the light probes in areas where the lighting changes in order to yield the best results.
71 *
72 * The volume can never have less than 4 probes.
73 */
74 class BS_CORE_EXPORT LightProbeVolume : public IReflectable, public CoreObject, public SceneActor
75 {
76 /** Internal information about a single light probe. */
77 struct ProbeInfo
78 {
79 ProbeInfo() = default;
80 ProbeInfo(LightProbeFlags flags, const Vector3& position)
81 :flags(flags), position(position)
82 { }
83
84 LightProbeFlags flags;
85 Vector3 position;
86
87 /** Coefficients are only valid directly after deserialization, or after updateCoefficients() is called. */
88 LightProbeSHCoefficients coefficients;
89 };
90 public:
91 ~LightProbeVolume();
92
93 /**
94 * Adds a new probe at the specified position and returns a handle to the probe. The position is relative to
95 * the volume origin.
96 */
97 UINT32 addProbe(const Vector3& position);
98
99 /** Updates the position of the probe with the specified handle. */
100 void setProbePosition(UINT32 handle, const Vector3& position);
101
102 /** Retrieves the position of the probe with the specified handle. */
103 Vector3 getProbePosition(UINT32 handle) const;
104
105 /**
106 * Removes the probe with the specified handle. Note that if this is one of the last four remaining probes in the
107 * volume it cannot be removed.
108 */
109 void removeProbe(UINT32 handle);
110
111 /** Returns a list of positions of all light probes in the volume. */
112 Vector<LightProbeInfo> getProbes() const;
113
114 /**
115 * Causes the information for this specific light probe to be updated. You generally want to call this when the
116 * probe is moved or the scene around the probe changes.
117 */
118 void renderProbe(UINT32 handle);
119
120 /**
121 * Causes the information for all lights probes to be updated. You generally want to call this if you move the
122 * entire light volume or the scene around the volume changes.
123 */
124 void renderProbes();
125
126 /**
127 * Resizes the light probe grid and inserts new light probes, if the new size is larger than previous size.
128 * New probes are inserted in a grid pattern matching the new size and density parameters.
129 *
130 * Note that shrinking the volume will not remove light probes. In order to remove probes outside of the new volume
131 * call clip().
132 *
133 * Resize will not change the positions of current light probes. If you wish to reset all probes to the currently
134 * set grid position, call reset().
135
136 * @param[in] volume Axis aligned volume to be covered by the light probes.
137 * @param[in] cellCount Number of grid cells to split the volume into. Minimum number of 1, in which case each
138 * corner of the volume is represented by a single probe. Higher values subdivide the
139 * volume in an uniform way.
140 */
141 void resize(const AABox& volume, const Vector3I& cellCount = Vector3I(1, 1, 1));
142
143 /** Removes any probes outside of the current grid volume. */
144 void clip();
145
146 /**
147 * Resets all probes to match the original grid pattern. This will reset probe positions, as well as add/remove
148 * probes as necessary, essentially losing any custom changes to the probes.
149 */
150 void reset();
151
152 /** Returns the volume that's used for adding probes in a uniform grid pattern. */
153 const AABox& getGridVolume() const { return mVolume; }
154
155 /** Returns the cell count that's used for determining the density of probes within a grid volume. */
156 const Vector3I& getCellCount() const { return mCellCount; }
157
158 /** Retrieves an implementation of the object usable only from the core thread. */
159 SPtr<ct::LightProbeVolume> getCore() const;
160
161 /**
162 * Creates a new light volume with probes aligned in a grid pattern.
163 *
164 * @param[in] volume Axis aligned volume to be covered by the light probes.
165 * @param[in] cellCount Number of grid cells to split the volume into. Minimum number of 1, in which case each
166 * corner of the volume is represented by a single probe. Higher values subdivide the
167 * volume in an uniform way.
168 */
169 static SPtr<LightProbeVolume> create(const AABox& volume = AABox::UNIT_BOX,
170 const Vector3I& cellCount = Vector3I(1, 1, 1));
171 protected:
172 friend class ct::LightProbeVolume;
173
174 LightProbeVolume(const AABox& volume, const Vector3I& cellCount);
175
176 /** Renders the light probe data on the core thread. */
177 void runRenderProbeTask();
178
179 /**
180 * Fetches latest SH coefficient data from the core thread. Note this method will block the caller thread until
181 * the data is fetched from the core thread. It will also force any in-progress light probe updates to finish.
182 */
183 void updateCoefficients();
184
185 /** @copydoc CoreObject::createCore */
186 SPtr<ct::CoreObject> createCore() const override;
187
188 /** @copydoc SceneActor::_markCoreDirty */
189 void _markCoreDirty(ActorDirtyFlag dirtFlags = ActorDirtyFlag::Everything) override;
190
191 /** @copydoc CoreObject::syncToCore */
192 CoreSyncData syncToCore(FrameAlloc* allocator) override;
193
194 /** Creates a light volume with without initializing it. Used for serialization. */
195 static SPtr<LightProbeVolume> createEmpty();
196
197 private:
198 UnorderedMap<UINT32, ProbeInfo> mProbes;
199 AABox mVolume = AABox::UNIT_BOX;
200 Vector3I mCellCount = { 1, 1, 1 };
201
202 UINT32 mNextProbeId = 0;
203 SPtr<ct::RendererTask> mRendererTask;
204
205 /************************************************************************/
206 /* RTTI */
207 /************************************************************************/
208 public:
209 friend class LightProbeVolumeRTTI;
210 static RTTITypeBase* getRTTIStatic();
211 RTTITypeBase* getRTTI() const override;
212
213 protected:
214 LightProbeVolume() = default; // Serialization only
215 };
216
217 namespace ct
218 {
219 /** Information about a single light probe in a light probe volume. */
220 struct LightProbeInfo
221 {
222 /** Unique handle representing the probe. Always remains the same. */
223 UINT32 handle;
224
225 /** Flags representing the current state of the probe. */
226 LightProbeFlags flags;
227
228 /** Index into the GPU buffer where probe coefficients are stored. -1 if not assigned. Transient. */
229 UINT32 bufferIdx;
230 };
231
232 /** Core thread usable version of bs::LightProbeVolume. */
233 class BS_CORE_EXPORT LightProbeVolume : public CoreObject, public SceneActor
234 {
235 public:
236 ~LightProbeVolume();
237
238 /** Sets an ID that can be used for uniquely identifying this object by the renderer. */
239 void setRendererId(UINT32 id) { mRendererId = id; }
240
241 /** Retrieves an ID that can be used for uniquely identifying this object by the renderer. */
242 UINT32 getRendererId() const { return mRendererId; }
243
244 /** Returns the number of light probes that are active. */
245 UINT32 getNumActiveProbes() const { return (UINT32)mProbeMap.size(); }
246
247 /** Returns a list of positions for all light probes. Only the first getNumActiveProbes() entries are active. */
248 const Vector<Vector3>& getLightProbePositions() const { return mProbePositions; }
249
250 /**
251 * Returns non-positional information about all light probes. Only the first getNumActiveProbes() entries are
252 * active.
253 */
254 const Vector<LightProbeInfo>& getLightProbeInfos() const { return mProbeInfos; }
255
256 /** Populates the vector with SH coefficients for each light probe. Involves reading the GPU buffer. */
257 void getProbeCoefficients(Vector<LightProbeCoefficientInfo>& output) const;
258
259 /** Returns the texture containing SH coefficients for all probes in the volume. */
260 SPtr<Texture> getCoefficientsTexture() const { return mCoefficients; }
261 protected:
262 friend class bs::LightProbeVolume;
263
264 LightProbeVolume(const UnorderedMap<UINT32, bs::LightProbeVolume::ProbeInfo>& probes);
265
266 /** @copydoc CoreObject::initialize */
267 void initialize() override;
268
269 /** @copydoc CoreObject::syncToCore */
270 void syncToCore(const CoreSyncData& data) override;
271
272 /**
273 * Renders dirty probes and updates their SH coefficients in the local GPU buffer.
274 *
275 * @param[in] maxProbes Maximum number of probes to render. Set to zero to render all dirty probes. Limiting the
276 * number of probes allows the rendering to be distributed over multiple frames.
277 * @return True if there are no more dirty probes to process.
278 */
279 bool renderProbes(UINT32 maxProbes);
280
281 /**
282 * Resizes the internal texture that stores light probe SH coefficients, to the specified size (in the number
283 * of probes).
284 */
285 void resizeCoefficientTexture(UINT32 count);
286
287 UINT32 mRendererId = 0;
288 UnorderedMap<UINT32, UINT32> mProbeMap; // Map from static indices to compact list of probes
289 UINT32 mFirstDirtyProbe = 0;
290
291 Vector<Vector3> mProbePositions;
292 Vector<LightProbeInfo> mProbeInfos;
293
294 // Contains SH coefficients for the probes
295 SPtr<Texture> mCoefficients;
296 UINT32 mCoeffBufferSize = 0;
297
298 // Temporary until initialization
299 Vector<LightProbeSHCoefficients> mInitCoefficients;
300 };
301 }
302
303 /** @} */
304}
305