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/ray.h"
41 #include "foundation/math/scalar.h"
42 #include "foundation/math/vector.h"
43 #include "foundation/utility/api/specializedapiarrays.h"
44 #include "foundation/utility/containers/dictionary.h"
45 #include "foundation/utility/job/iabortswitch.h"
46 #include "foundation/utility/searchpaths.h"
47 #include "foundation/utility/string.h"
48 
49 // appleseed.main headers.
50 #include "main/dllvisibility.h"
51 
52 // Standard headers.
53 #include <algorithm>
54 #include <cmath>
55 
56 namespace asf = foundation;
57 namespace asr = renderer;
58 
59 namespace
60 {
61     //
62     // A sphere object.
63     //
64     // The sphere is assumed to be centered at the origin.
65     //
66 
67     const char* Model = "example_sphere_object";
68 
69     class SphereObject
70       : public asr::ProceduralObject
71     {
72       public:
73         // Constructor.
SphereObject(const char * name,const asr::ParamArray & params)74         SphereObject(
75             const char*                 name,
76             const asr::ParamArray&      params)
77           : asr::ProceduralObject(name, params)
78         {
79         }
80 
81         // Delete this instance.
release()82         void release() override
83         {
84             delete this;
85         }
86 
87         // Return a string identifying this object model.
get_model() const88         const char* get_model() const override
89         {
90             return Model;
91         }
92 
93         // This method is called once before rendering each frame.
94         // Returns true on success, false otherwise.
on_frame_begin(const asr::Project & project,const asr::BaseGroup * parent,asr::OnFrameBeginRecorder & recorder,asf::IAbortSwitch * abort_switch)95         bool on_frame_begin(
96             const asr::Project&         project,
97             const asr::BaseGroup*       parent,
98             asr::OnFrameBeginRecorder&  recorder,
99             asf::IAbortSwitch*          abort_switch) override
100         {
101             if (!asr::ProceduralObject::on_frame_begin(project, parent, recorder, abort_switch))
102                 return false;
103 
104             m_radius = get_uncached_radius();
105             m_rcp_radius = 1.0 / m_radius;
106 
107             return true;
108         }
109 
110         // Compute the local space bounding box of the object over the shutter interval.
compute_local_bbox() const111         asr::GAABB3 compute_local_bbox() const override
112         {
113             const auto r = static_cast<asr::GScalar>(get_uncached_radius());
114             return asr::GAABB3(asr::GVector3(-r), asr::GVector3(r));
115         }
116 
117         // Access materials slots.
get_material_slot_count() const118         size_t get_material_slot_count() const override
119         {
120             return 1;
121         }
get_material_slot(const size_t index) const122         const char* get_material_slot(const size_t index) const override
123         {
124             return "default";
125         }
126 
127         // Compute the intersection between a ray expressed in object space and
128         // the surface of this object and return detailed intersection results.
intersect(const asr::ShadingRay & ray,IntersectionResult & result) const129         void intersect(
130             const asr::ShadingRay&  ray,
131             IntersectionResult&     result) const override
132         {
133             const double Epsilon = 1.0e-6;
134 
135             const double a = asf::dot(ray.m_org, ray.m_dir);
136             const double b = asf::square(a) - dot(ray.m_org, ray.m_org) + asf::square(m_radius);
137 
138             if (b < 0.0)
139             {
140                 result.m_hit = false;
141                 return;
142             }
143 
144             const double c = std::sqrt(b);
145 
146             double t = -a - c;
147             if (t < std::max(ray.m_tmin, Epsilon) || t >= ray.m_tmax)
148             {
149                 t = -a + c;
150                 if (t < std::max(ray.m_tmin, Epsilon) || t >= ray.m_tmax)
151                 {
152                     result.m_hit = false;
153                     return;
154                 }
155             }
156 
157             result.m_hit = true;
158             result.m_distance = t;
159 
160             const asf::Vector3d n = asf::normalize(ray.point_at(t));
161             result.m_geometric_normal = n;
162             result.m_shading_normal = n;
163 
164             const asf::Vector3f p(ray.point_at(t) * m_rcp_radius);
165             result.m_uv[0] = std::acos(p.y) * asf::RcpPi<float>();
166             result.m_uv[1] = std::atan2(-p.z, p.x) * asf::RcpTwoPi<float>();
167 
168             result.m_material_slot = 0;
169         }
170 
171         // Compute the intersection between a ray expressed in object space and
172         // the surface of this object and simply return whether there was a hit.
intersect(const asr::ShadingRay & ray) const173         bool intersect(
174             const asr::ShadingRay&  ray) const override
175         {
176             const double Epsilon = 1.0e-6;
177 
178             const double a = asf::dot(ray.m_org, ray.m_dir);
179             const double b = asf::square(a) - dot(ray.m_org, ray.m_org) + asf::square(m_radius);
180 
181             if (b < 0.0)
182                 return false;
183 
184             const double c = std::sqrt(b);
185 
186             const double t1 = -a - c;
187             if (t1 >= std::max(ray.m_tmin, Epsilon) && t1 < ray.m_tmax)
188                 return true;
189 
190             const double t2 = -a + c;
191             if (t2 >= std::max(ray.m_tmin, Epsilon) && t2 < ray.m_tmax)
192                 return true;
193 
194             return false;
195         }
196 
197       private:
198         double  m_radius;
199         double  m_rcp_radius;
200 
get_uncached_radius() const201         double get_uncached_radius() const
202         {
203             return m_params.get_optional<double>("radius");
204         }
205     };
206 
207 
208     //
209     // Factory for the new object model.
210     //
211 
212     class SphereObjectFactory
213       : public asr::IObjectFactory
214     {
215       public:
216         // Delete this instance.
release()217         void release() override
218         {
219             delete this;
220         }
221 
222         // Return a string identifying this object model.
get_model() const223         const char* get_model() const override
224         {
225             return Model;
226         }
227 
228         // Return metadata for this object model.
get_model_metadata() const229         asf::Dictionary get_model_metadata() const override
230         {
231             return
232                 asf::Dictionary()
233                     .insert("name", Model)
234                     .insert("label", "Sphere Object");
235         }
236 
237         // Return metadata for the inputs of this object model.
get_input_metadata() const238         asf::DictionaryArray get_input_metadata() const override
239         {
240             asf::DictionaryArray metadata;
241 
242             metadata.push_back(
243                 asf::Dictionary()
244                     .insert("name", "radius")
245                     .insert("label", "Radius")
246                     .insert("type", "numeric")
247                     .insert("min",
248                         asf::Dictionary()
249                             .insert("value", "0.0")
250                             .insert("type", "hard"))
251                     .insert("max",
252                         asf::Dictionary()
253                             .insert("value", "10.0")
254                             .insert("type", "soft"))
255                     .insert("use", "optional")
256                     .insert("default", "1.0"));
257 
258             return metadata;
259         }
260 
261         // Create a new single empty object.
create(const char * name,const asr::ParamArray & params) const262         asf::auto_release_ptr<asr::Object> create(
263             const char*                 name,
264             const asr::ParamArray&      params) const override
265         {
266             return asf::auto_release_ptr<asr::Object>(new SphereObject(name, params));
267         }
268 
269         // 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) const270         bool create(
271             const char*                 name,
272             const asr::ParamArray&      params,
273             const asf::SearchPaths&     search_paths,
274             const bool                  omit_loading_assets,
275             asr::ObjectArray&           objects) const override
276         {
277             objects.push_back(create(name, params).release());
278             return true;
279         }
280     };
281 }
282 
283 
284 //
285 // Plugin entry point.
286 //
287 
288 extern "C"
289 {
appleseed_create_object_factory()290     APPLESEED_DLL_EXPORT asr::IObjectFactory* appleseed_create_object_factory()
291     {
292         return new SphereObjectFactory();
293     }
294 }
295