1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 #pragma once
31
32 // appleseed.foundation headers.
33 #include "foundation/core/concepts/noncopyable.h"
34 #include "foundation/math/scalar.h"
35 #include "foundation/math/vector.h"
36 #include "foundation/platform/compiler.h"
37
38 // Standard headers.
39 #include <cassert>
40 #include <cstddef>
41 #include <vector>
42
43 namespace foundation
44 {
45
46 //
47 // A regular 3D grid of voxel.
48 //
49
50 template <typename ValueType, typename CoordType>
51 class VoxelGrid3
52 : public NonCopyable
53 {
54 public:
55 // Types.
56 typedef Vector<CoordType, 3> PointType;
57
58 // Constructor.
59 VoxelGrid3(
60 const size_t nx,
61 const size_t ny,
62 const size_t nz,
63 const size_t channel_count);
64
65 // Get the grid properties.
66 size_t get_xres() const;
67 size_t get_yres() const;
68 size_t get_zres() const;
69 size_t get_channel_count() const;
70
71 // Direct access to a given voxel.
72 ValueType* voxel(
73 const size_t x,
74 const size_t y,
75 const size_t z);
76 const ValueType* voxel(
77 const size_t x,
78 const size_t y,
79 const size_t z) const;
80
81 // Perform an unfiltered lookup of the voxel grid.
82 // 'point' must be expressed in the unit cube [0,1]^3.
83 void nearest_lookup(
84 const PointType& point,
85 ValueType* values) const;
86
87 // Perform a trilinearly interpolated lookup of the voxel grid.
88 // 'point' must be expressed in the unit cube [0,1]^3.
89 void linear_lookup(
90 const PointType& point,
91 ValueType* values) const;
92
93 // Perform a triquadratically interpolated lookup of the voxel grid.
94 // 'point' must be expressed in the unit cube [0,1]^3.
95 void quadratic_lookup(
96 const PointType& point,
97 ValueType* values) const;
98
99 private:
100 const size_t m_nx;
101 const size_t m_ny;
102 const size_t m_nz;
103 const CoordType m_scalar_nx;
104 const CoordType m_scalar_ny;
105 const CoordType m_scalar_nz;
106 const CoordType m_max_x;
107 const CoordType m_max_y;
108 const CoordType m_max_z;
109 const size_t m_channel_count;
110 const size_t m_row_size;
111 const size_t m_slice_size;
112 std::vector<ValueType> m_values;
113 };
114
115
116 //
117 // VoxelGrid3 class implementation.
118 //
119
120 template <typename ValueType, typename CoordType>
VoxelGrid3(const size_t nx,const size_t ny,const size_t nz,const size_t channel_count)121 VoxelGrid3<ValueType, CoordType>::VoxelGrid3(
122 const size_t nx,
123 const size_t ny,
124 const size_t nz,
125 const size_t channel_count)
126 : m_nx(nx)
127 , m_ny(ny)
128 , m_nz(nz)
129 , m_scalar_nx(static_cast<CoordType>(nx))
130 , m_scalar_ny(static_cast<CoordType>(ny))
131 , m_scalar_nz(static_cast<CoordType>(nz))
132 , m_max_x(static_cast<CoordType>(nx - 1))
133 , m_max_y(static_cast<CoordType>(ny - 1))
134 , m_max_z(static_cast<CoordType>(nz - 1))
135 , m_channel_count(channel_count)
136 , m_row_size(channel_count * nx) // number of values in one row of voxels
137 , m_slice_size(channel_count * nx * ny) // number of values in one slice of voxels
138 , m_values(nx * ny * nz * channel_count, ValueType(0.0))
139 {
140 assert(m_nx > 0);
141 assert(m_ny > 0);
142 assert(m_nz > 0);
143 assert(m_channel_count > 0);
144 }
145
146 template <typename ValueType, typename CoordType>
get_xres()147 APPLESEED_FORCE_INLINE size_t VoxelGrid3<ValueType, CoordType>::get_xres() const
148 {
149 return m_nx;
150 }
151
152 template <typename ValueType, typename CoordType>
get_yres()153 APPLESEED_FORCE_INLINE size_t VoxelGrid3<ValueType, CoordType>::get_yres() const
154 {
155 return m_ny;
156 }
157
158 template <typename ValueType, typename CoordType>
get_zres()159 APPLESEED_FORCE_INLINE size_t VoxelGrid3<ValueType, CoordType>::get_zres() const
160 {
161 return m_nz;
162 }
163
164 template <typename ValueType, typename CoordType>
get_channel_count()165 APPLESEED_FORCE_INLINE size_t VoxelGrid3<ValueType, CoordType>::get_channel_count() const
166 {
167 return m_channel_count;
168 }
169
170 template <typename ValueType, typename CoordType>
voxel(const size_t x,const size_t y,const size_t z)171 APPLESEED_FORCE_INLINE ValueType* VoxelGrid3<ValueType, CoordType>::voxel(
172 const size_t x,
173 const size_t y,
174 const size_t z)
175 {
176 assert(x < m_nx);
177 assert(y < m_ny);
178 assert(z < m_nz);
179 return &m_values[((z * m_ny + y) * m_nx + x) * m_channel_count];
180 }
181
182 template <typename ValueType, typename CoordType>
voxel(const size_t x,const size_t y,const size_t z)183 APPLESEED_FORCE_INLINE const ValueType* VoxelGrid3<ValueType, CoordType>::voxel(
184 const size_t x,
185 const size_t y,
186 const size_t z) const
187 {
188 assert(x < m_nx);
189 assert(y < m_ny);
190 assert(z < m_nz);
191 return &m_values[((z * m_ny + y) * m_nx + x) * m_channel_count];
192 }
193
194 template <typename ValueType, typename CoordType>
nearest_lookup(const PointType & point,ValueType * APPLESEED_RESTRICT values)195 void VoxelGrid3<ValueType, CoordType>::nearest_lookup(
196 const PointType& point,
197 ValueType* APPLESEED_RESTRICT values) const
198 {
199 // Compute the coordinates of the voxel containing the lookup point.
200 const CoordType x = clamp(point.x * m_scalar_nx, CoordType(0.0), m_max_x);
201 const CoordType y = clamp(point.y * m_scalar_ny, CoordType(0.0), m_max_y);
202 const CoordType z = clamp(point.z * m_scalar_nz, CoordType(0.0), m_max_z);
203 const size_t ix = truncate<size_t>(x);
204 const size_t iy = truncate<size_t>(y);
205 const size_t iz = truncate<size_t>(z);
206
207 // Return the values of that voxel.
208 const ValueType* APPLESEED_RESTRICT source = voxel(ix, iy, iz);
209 for (size_t i = 0; i < m_channel_count; ++i)
210 *values++ = *source++;
211 }
212
213 template <typename ValueType, typename CoordType>
linear_lookup(const PointType & point,ValueType * APPLESEED_RESTRICT values)214 void VoxelGrid3<ValueType, CoordType>::linear_lookup(
215 const PointType& point,
216 ValueType* APPLESEED_RESTRICT values) const
217 {
218 // Compute the coordinates of the voxel containing the lookup point.
219 const CoordType x = saturate(point.x) * m_max_x;
220 const CoordType y = saturate(point.y) * m_max_y;
221 const CoordType z = saturate(point.z) * m_max_z;
222 const size_t ix = truncate<size_t>(x);
223 const size_t iy = truncate<size_t>(y);
224 const size_t iz = truncate<size_t>(z);
225
226 // Compute interpolation weights.
227 const ValueType x1 = static_cast<ValueType>(x - ix);
228 const ValueType y1 = static_cast<ValueType>(y - iy);
229 const ValueType z1 = static_cast<ValueType>(z - iz);
230 const ValueType x0 = ValueType(1.0) - x1;
231 const ValueType y0 = ValueType(1.0) - y1;
232 const ValueType z0 = ValueType(1.0) - z1;
233 const ValueType y0z0 = y0 * z0;
234 const ValueType y1z0 = y1 * z0;
235 const ValueType y0z1 = y0 * z1;
236 const ValueType y1z1 = y1 * z1;
237 const ValueType w000 = x0 * y0z0;
238 const ValueType w100 = x1 * y0z0;
239 const ValueType w010 = x0 * y1z0;
240 const ValueType w110 = x1 * y1z0;
241 const ValueType w001 = x0 * y0z1;
242 const ValueType w101 = x1 * y0z1;
243 const ValueType w011 = x0 * y1z1;
244 const ValueType w111 = x1 * y1z1;
245
246 // Compute source pointers.
247 const size_t dx = ix == m_nx - 1 ? 0 : m_channel_count;
248 const size_t dy = iy == m_ny - 1 ? 0 : m_row_size;
249 const size_t dz = iz == m_nz - 1 ? 0 : m_slice_size;
250 const ValueType* APPLESEED_RESTRICT src = voxel(ix, iy, iz);
251 const ValueType* APPLESEED_RESTRICT src000 = src;
252 const ValueType* APPLESEED_RESTRICT src100 = src + dx;
253 const ValueType* APPLESEED_RESTRICT src010 = src + dy;
254 const ValueType* APPLESEED_RESTRICT src001 = src + dz;
255 const ValueType* APPLESEED_RESTRICT src110 = src100 + dy;
256 const ValueType* APPLESEED_RESTRICT src101 = src100 + dz;
257 const ValueType* APPLESEED_RESTRICT src011 = src010 + dz;
258 const ValueType* APPLESEED_RESTRICT src111 = src110 + dz;
259
260 // Blend.
261 for (size_t i = 0; i < m_channel_count; ++i)
262 {
263 *values++ =
264 *src000++ * w000 +
265 *src100++ * w100 +
266 *src010++ * w010 +
267 *src110++ * w110 +
268 *src001++ * w001 +
269 *src101++ * w101 +
270 *src011++ * w011 +
271 *src111++ * w111;
272 }
273 }
274
275 template <typename ValueType, typename CoordType>
quadratic_lookup(const PointType & point,ValueType * APPLESEED_RESTRICT values)276 void VoxelGrid3<ValueType, CoordType>::quadratic_lookup(
277 const PointType& point,
278 ValueType* APPLESEED_RESTRICT values) const
279 {
280 //
281 // The quadratic interpolation polynomial used here is defined as follow:
282 //
283 // We start with the general form of a degree 2 polynomial:
284 //
285 // P(t) = a.t^2 + b.t + c P'(t) = 2.a.t + b
286 //
287 // The parameter t extends from t = -1/2 (halfway between v0 and v1)
288 // to t = +1/2 (halfway between v1 and v2). To simplify the expression
289 // of the polynomial, we let s = t + 1/2 and define the polynomial over s.
290 //
291 // We compute a, b and c so that the following conditions are met:
292 //
293 // C0 continuity at s = 0.0: P (0.0) = (v0 + v1) / 2
294 // C0 continuity at s = 1.0: P (1.0) = (v1 + v2) / 2
295 // C1 continuity at s = 0.0: P'(0.0) = v1 - v0
296 // C1 continuity at s = 1.0: P'(1.0) = v2 - v1
297 //
298 // One of the condition is redundant, as only three conditions are
299 // required to completely define the polynomial.
300 //
301 // Given the above conditions, we find the following coefficients for P(s):
302 //
303 // a = 1/2 * (v0 - 2.v1 + v2)
304 // b = v1 - v0
305 // c = 1/2 * (v0 + v1)
306 //
307 // And the weights for v0, v1 and v2:
308 //
309 // w0 = 1/2 * (t^2 - 2.t + 1)
310 // w1 = -t^2 + t + 1/2
311 // w2 = 1/2 * t^2
312 //
313
314 // Compute the coordinates of the voxel containing the lookup point.
315 const CoordType x = saturate(point.x) * m_max_x;
316 const CoordType y = saturate(point.y) * m_max_y;
317 const CoordType z = saturate(point.z) * m_max_z;
318 const size_t ix = truncate<size_t>(x + CoordType(0.5));
319 const size_t iy = truncate<size_t>(y + CoordType(0.5));
320 const size_t iz = truncate<size_t>(z + CoordType(0.5));
321
322 // Compute interpolation weights.
323 const ValueType tx = static_cast<ValueType>(x - ix) + ValueType(0.5);
324 const ValueType ty = static_cast<ValueType>(y - iy) + ValueType(0.5);
325 const ValueType tz = static_cast<ValueType>(z - iz) + ValueType(0.5);
326 const ValueType tx2 = tx * tx;
327 const ValueType ty2 = ty * ty;
328 const ValueType tz2 = tz * tz;
329 const ValueType wx2 = ValueType(0.5) * tx2;
330 const ValueType wx1 = tx - tx2 + ValueType(0.5);
331 const ValueType wx0 = wx2 - tx + ValueType(0.5);
332 const ValueType wy2 = ValueType(0.5) * ty2;
333 const ValueType wy1 = ty - ty2 + ValueType(0.5);
334 const ValueType wy0 = wy2 - ty + ValueType(0.5);
335 const ValueType wz2 = ValueType(0.5) * tz2;
336 const ValueType wz1 = tz - tz2 + ValueType(0.5);
337 const ValueType wz0 = wz2 - tz + ValueType(0.5);
338
339 // Compute source pointers.
340 const size_t dxn = ix == 0 ? 0 : m_channel_count;
341 const size_t dyn = iy == 0 ? 0 : m_row_size;
342 const size_t dzn = iz == 0 ? 0 : m_slice_size;
343 const size_t dxp = ix == m_nx - 1 ? 0 : m_channel_count;
344 const size_t dyp = iy == m_ny - 1 ? 0 : m_row_size;
345 const size_t dzp = iz == m_nz - 1 ? 0 : m_slice_size;
346 const ValueType* APPLESEED_RESTRICT src = voxel(ix, iy, iz);
347 const ValueType* APPLESEED_RESTRICT src111 = src;
348 const ValueType* APPLESEED_RESTRICT src011 = src111 - dxn;
349 const ValueType* APPLESEED_RESTRICT src211 = src111 + dxp;
350 const ValueType* APPLESEED_RESTRICT src101 = src111 - dyn;
351 const ValueType* APPLESEED_RESTRICT src001 = src011 - dyn;
352 const ValueType* APPLESEED_RESTRICT src201 = src211 - dyn;
353 const ValueType* APPLESEED_RESTRICT src121 = src111 + dyp;
354 const ValueType* APPLESEED_RESTRICT src021 = src011 + dyp;
355 const ValueType* APPLESEED_RESTRICT src221 = src211 + dyp;
356 const ValueType* APPLESEED_RESTRICT src110 = src111 - dzn;
357 const ValueType* APPLESEED_RESTRICT src010 = src011 - dzn;
358 const ValueType* APPLESEED_RESTRICT src210 = src211 - dzn;
359 const ValueType* APPLESEED_RESTRICT src100 = src101 - dzn;
360 const ValueType* APPLESEED_RESTRICT src000 = src001 - dzn;
361 const ValueType* APPLESEED_RESTRICT src200 = src201 - dzn;
362 const ValueType* APPLESEED_RESTRICT src120 = src121 - dzn;
363 const ValueType* APPLESEED_RESTRICT src020 = src021 - dzn;
364 const ValueType* APPLESEED_RESTRICT src220 = src221 - dzn;
365 const ValueType* APPLESEED_RESTRICT src112 = src111 + dzp;
366 const ValueType* APPLESEED_RESTRICT src012 = src011 + dzp;
367 const ValueType* APPLESEED_RESTRICT src212 = src211 + dzp;
368 const ValueType* APPLESEED_RESTRICT src102 = src101 + dzp;
369 const ValueType* APPLESEED_RESTRICT src002 = src001 + dzp;
370 const ValueType* APPLESEED_RESTRICT src202 = src201 + dzp;
371 const ValueType* APPLESEED_RESTRICT src122 = src121 + dzp;
372 const ValueType* APPLESEED_RESTRICT src022 = src021 + dzp;
373 const ValueType* APPLESEED_RESTRICT src222 = src221 + dzp;
374
375 // Blend.
376 for (size_t i = 0; i < m_channel_count; ++i)
377 {
378 const ValueType p00 = *src000++ * wx0 + *src100++ * wx1 + *src200++ * wx2;
379 const ValueType p01 = *src001++ * wx0 + *src101++ * wx1 + *src201++ * wx2;
380 const ValueType p02 = *src002++ * wx0 + *src102++ * wx1 + *src202++ * wx2;
381
382 const ValueType p10 = *src010++ * wx0 + *src110++ * wx1 + *src210++ * wx2;
383 const ValueType p11 = *src011++ * wx0 + *src111++ * wx1 + *src211++ * wx2;
384 const ValueType p12 = *src012++ * wx0 + *src112++ * wx1 + *src212++ * wx2;
385
386 const ValueType p20 = *src020++ * wx0 + *src120++ * wx1 + *src220++ * wx2;
387 const ValueType p21 = *src021++ * wx0 + *src121++ * wx1 + *src221++ * wx2;
388 const ValueType p22 = *src022++ * wx0 + *src122++ * wx1 + *src222++ * wx2;
389
390 const ValueType q0 = p00 * wy0 + p10 * wy1 + p20 * wy2;
391 const ValueType q1 = p01 * wy0 + p11 * wy1 + p21 * wy2;
392 const ValueType q2 = p02 * wy0 + p12 * wy1 + p22 * wy2;
393
394 *values++ = q0 * wz0 + q1 * wz1 + q2 * wz2;
395 }
396 }
397
398 } // namespace foundation
399