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