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) 2017-2018 Francois Beaune, The appleseedhq Organization
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28 
29 // appleseed.renderer headers.
30 #include "renderer/api/object.h"
31 #include "renderer/api/project.h"
32 #include "renderer/api/rendering.h"
33 #include "renderer/api/scene.h"
34 #include "renderer/api/types.h"
35 
36 // todo: fix.
37 #include "renderer/kernel/shading/shadingray.h"
38 
39 // appleseed.foundation headers.
40 #include "foundation/math/hash.h"
41 #include "foundation/math/intersection/rayaabb.h"
42 #include "foundation/math/ray.h"
43 #include "foundation/math/rng/distribution.h"
44 #include "foundation/math/rng/xoroshiro128plus.h"
45 #include "foundation/math/scalar.h"
46 #include "foundation/math/vector.h"
47 #include "foundation/platform/types.h"
48 #include "foundation/utility/api/specializedapiarrays.h"
49 #include "foundation/utility/containers/dictionary.h"
50 #include "foundation/utility/job/iabortswitch.h"
51 #include "foundation/utility/casts.h"
52 #include "foundation/utility/searchpaths.h"
53 #include "foundation/utility/string.h"
54 
55 // appleseed.main headers.
56 #include "main/dllvisibility.h"
57 
58 // Standard headers.
59 #include <algorithm>
60 #include <cmath>
61 #include <cstddef>
62 
63 namespace asf = foundation;
64 namespace asr = renderer;
65 
66 namespace
67 {
68     //
69     // An object whose surface is defined by a signed distance field.
70     //
71 
72     const char* Model = "distance_field_object";
73 
74     class DistanceFieldObject
75       : public asr::ProceduralObject
76     {
77       public:
78         // Constructor.
DistanceFieldObject(const char * name,const asr::ParamArray & params)79         DistanceFieldObject(
80             const char*                 name,
81             const asr::ParamArray&      params)
82           : asr::ProceduralObject(name, params)
83         {
84         }
85 
86         // Delete this instance.
release()87         void release() override
88         {
89             delete this;
90         }
91 
92         // Return a string identifying this object model.
get_model() const93         const char* get_model() const override
94         {
95             return Model;
96         }
97 
98         // Compute the local space bounding box of the object over the shutter interval.
compute_local_bbox() const99         asr::GAABB3 compute_local_bbox() const override
100         {
101             return asr::GAABB3(asr::GVector3(-2.0f), asr::GVector3(2.0f));
102         }
103 
104         // Access materials slots.
get_material_slot_count() const105         size_t get_material_slot_count() const override
106         {
107             return 1;
108         }
get_material_slot(const size_t index) const109         const char* get_material_slot(const size_t index) const override
110         {
111             return "default";
112         }
113 
114         // Compute the intersection between a ray expressed in object space and
115         // the surface of this object and return detailed intersection results.
intersect(const asr::ShadingRay & ray,IntersectionResult & result) const116         void intersect(
117             const asr::ShadingRay&  ray,
118             IntersectionResult&     result) const override
119         {
120             double t;
121             asf::Vector3d p;
122             result.m_hit = raymarch(ray, t, p);
123 
124             if (result.m_hit)
125             {
126                 result.m_distance = t;
127 
128                 const float H = 1.0e-4f;
129 
130                 asf::Vector3f n(
131                     compute_distance(p.x + H, p.y, p.z) - compute_distance(p.x - H, p.y, p.z),
132                     compute_distance(p.x, p.y + H, p.z) - compute_distance(p.x, p.y - H, p.z),
133                     compute_distance(p.x, p.y, p.z + H) - compute_distance(p.x, p.y, p.z - H));
134                 n = asf::normalize(n);
135 
136                 result.m_geometric_normal = asf::Vector3d(n);
137                 result.m_shading_normal = asf::Vector3d(n);
138 
139                 result.m_uv = asf::Vector2f(0.0f);
140                 result.m_material_slot = 0;
141             }
142         }
143 
144         // Compute the intersection between a ray expressed in object space and
145         // the surface of this object and simply return whether there was a hit.
intersect(const asr::ShadingRay & ray) const146         bool intersect(
147             const asr::ShadingRay&  ray) const override
148         {
149             double t;
150             asf::Vector3d p;
151             return raymarch(ray, t, p);
152         }
153 
154       private:
155 
156         //
157         // Signed distance function.
158         //
159         // References:
160         //
161         //   http://iquilezles.org/www/articles/distfunctions/distfunctions.htm
162         //
163         //   http://blog.hvidtfeldts.net/index.php/2011/09/distance-estimated-3d-fractals-v-the-mandelbulb-different-de-approximations/
164         //
165 
compute_distance(asf::Vector3f p)166         static float compute_distance(asf::Vector3f p)
167         {
168             /*return
169                 op_substraction(
170                     prim_cube(p + asf::Vector3f(0.5f, 0.0f, 0.0f), 0.5f),
171                     prim_sphere(p, 0.5f));*/
172 
173             return prim_mandelbulb(p);
174         }
175 
176         //
177         // Modeling utilities.
178         //
179 
make_rng(const asr::ShadingRay & ray)180         static asf::Xoroshiro128plus make_rng(const asr::ShadingRay& ray)
181         {
182             return
183                 asf::Xoroshiro128plus(
184                     asf::hash_uint64(asf::binary_cast<asf::uint64>(ray.m_org.x)) ^
185                     asf::hash_uint64(asf::binary_cast<asf::uint64>(ray.m_org.y)) ^
186                     asf::hash_uint64(asf::binary_cast<asf::uint64>(ray.m_org.z)),
187                     asf::hash_uint64(asf::binary_cast<asf::uint64>(ray.m_dir.x)) ^
188                     asf::hash_uint64(asf::binary_cast<asf::uint64>(ray.m_dir.y)) ^
189                     asf::hash_uint64(asf::binary_cast<asf::uint64>(ray.m_dir.z)));
190         }
191 
op_union(const float a,const float b)192         static float op_union(const float a, const float b)
193         {
194             return std::min(a, b);
195         }
196 
op_substraction(const float a,const float b)197         static float op_substraction(const float a, const float b)
198         {
199             return std::max(a, -b);
200         }
201 
op_intersection(const float a,const float b)202         static float op_intersection(const float a, const float b)
203         {
204             return std::max(a, b);
205         }
206 
prim_sphere(const asf::Vector3f & p,const float radius)207         static float prim_sphere(const asf::Vector3f& p, const float radius)
208         {
209             return asf::norm(p) - radius;
210         }
211 
prim_cube(asf::Vector3f p,const float half_size)212         static float prim_cube(asf::Vector3f p, const float half_size)
213         {
214             p.x = std::max(abs(p.x) - half_size, 0.0f);
215             p.y = std::max(abs(p.y) - half_size, 0.0f);
216             p.z = std::max(abs(p.z) - half_size, 0.0f);
217             return asf::norm(p);
218         }
219 
prim_mandelbulb(const asf::Vector3f & p)220         static float prim_mandelbulb(const asf::Vector3f& p)
221         {
222             const float Power = 8.0f;
223             const float Bailout = 4.0f;
224             const size_t Iterations = 20;
225 
226             asf::Vector3f z = p;
227             float dr = 1.0f;
228             float r = 0.0f;
229 
230             for (size_t i = 0; i < Iterations; ++i)
231             {
232                 r = asf::norm(z);
233                 if (r > Bailout)
234                     break;
235 
236                 // Convert to polar coordinates.
237                 float theta = std::acos(z.z / r);
238                 float phi = std::atan2(z.y, z.x);
239                 dr = std::pow(r, Power - 1.0f) * Power * dr + 1.0f;
240 
241                 // Scale and rotate the point.
242                 float zr = std::pow(r, Power);
243                 theta *= Power;
244                 phi *= Power;
245 
246                 // convert back to cartesian coordinates
247                 z = zr * asf::Vector3f(sin(theta)*cos(phi), sin(phi)*sin(theta), cos(theta));
248                 z += p;
249             }
250 
251             return 0.5f * std::log(r) * r / dr;
252         }
253 
254         //
255         // Raymarcher.
256         //
257         // References:
258         //
259         //   http://jamie-wong.com/2016/07/15/ray-marching-signed-distance-functions/
260         //
261         //   http://erleuchtet.org/~cupe/permanent/enhanced_sphere_tracing.pdf
262         //
263 
compute_distance(const double x,const double y,const double z)264         static float compute_distance(const double x, const double y, const double z)
265         {
266             return compute_distance(asf::Vector3d(x, y, z));
267         }
268 
compute_distance(const asf::Vector3d & p)269         static float compute_distance(const asf::Vector3d& p)
270         {
271             return compute_distance(asf::Vector3f(p));
272         }
273 
raymarch(const asr::ShadingRay & ray,double & t_out,asf::Vector3d & p_out) const274         bool raymarch(
275             const asr::ShadingRay&  ray,
276             double&                 t_out,
277             asf::Vector3d&          p_out) const
278         {
279             const auto bbox = asf::AABB3d(compute_local_bbox());
280 
281             asr::ShadingRay clipped_ray(ray);
282             const asf::RayInfo3d clipped_ray_info(clipped_ray);
283 
284             if (!asf::clip(clipped_ray, clipped_ray_info, bbox))
285                 return false;
286 
287             const double Epsilon = 1.0e-3;
288 
289             auto t = std::max(clipped_ray.m_tmin, Epsilon);
290 
291             for (size_t i = 0; t < clipped_ray.m_tmax; ++i)
292             {
293                 const auto p = clipped_ray.point_at(t);
294                 const auto d = compute_distance(p);
295 
296                 if (d < 0.0001 || i == 64)
297                 {
298                     t_out = t;
299                     p_out = p;
300                     return true;
301                 }
302 
303                 t += d;
304             }
305 
306             return false;
307         }
308     };
309 
310 
311     //
312     // Factory for the new object model.
313     //
314 
315     class DistanceFieldObjectFactory
316       : public asr::IObjectFactory
317     {
318       public:
319         // Delete this instance.
release()320         void release() override
321         {
322             delete this;
323         }
324 
325         // Return a string identifying this object model.
get_model() const326         const char* get_model() const override
327         {
328             return Model;
329         }
330 
331         // Return metadata for this object model.
get_model_metadata() const332         asf::Dictionary get_model_metadata() const override
333         {
334             return
335                 asf::Dictionary()
336                     .insert("name", Model)
337                     .insert("label", "Distance Field Object");
338         }
339 
340         // Return metadata for the inputs of this object model.
get_input_metadata() const341         asf::DictionaryArray get_input_metadata() const override
342         {
343             asf::DictionaryArray metadata;
344             return metadata;
345         }
346 
347         // Create a new single empty object.
create(const char * name,const asr::ParamArray & params) const348         asf::auto_release_ptr<asr::Object> create(
349             const char*                 name,
350             const asr::ParamArray&      params) const override
351         {
352             return asf::auto_release_ptr<asr::Object>(new DistanceFieldObject(name, params));
353         }
354 
355         // Create objects, potentially from external assets.
create(const char * name,const asr::ParamArray & params,const asf::SearchPaths & search_paths,const bool omit_loading_assets,asr::ObjectArray & objects) const356         bool create(
357             const char*                 name,
358             const asr::ParamArray&      params,
359             const asf::SearchPaths&     search_paths,
360             const bool                  omit_loading_assets,
361             asr::ObjectArray&           objects) const override
362         {
363             objects.push_back(create(name, params).release());
364             return true;
365         }
366     };
367 }
368 
369 
370 //
371 // Plugin entry point.
372 //
373 
374 extern "C"
375 {
appleseed_create_object_factory()376     APPLESEED_DLL_EXPORT asr::IObjectFactory* appleseed_create_object_factory()
377     {
378         return new DistanceFieldObjectFactory();
379     }
380 }
381