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