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