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) 2019 Esteban Tovagliari, 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 // interface header.
30 #include "rectangleobject.h"
31 
32 // appleseed.renderer headers.
33 #include "renderer/kernel/shading/shadingray.h"
34 
35 // appleseed.foundation headers.
36 #include "foundation/math/intersection/rayparallelogram.h"
37 #include "foundation/math/ray.h"
38 #include "foundation/math/scalar.h"
39 #include "foundation/math/vector.h"
40 #include "foundation/platform/compiler.h"
41 #include "foundation/utility/api/specializedapiarrays.h"
42 #include "foundation/utility/containers/dictionary.h"
43 #include "foundation/utility/job/iabortswitch.h"
44 #include "foundation/utility/searchpaths.h"
45 #include "foundation/utility/string.h"
46 
47 using namespace foundation;
48 
49 namespace renderer
50 {
51 
52 //
53 // RectangleObject class implementation.
54 //
55 
56 namespace
57 {
58     const char* Model = "rectangle_object";
59 }
60 
61 struct RectangleObject::Impl
62 {
63     Vector3d    m_corner;
64     Vector3d    m_normal;
65     Vector3d    m_x;
66     Vector3d    m_y;
67     bool        m_skip_intersection;
68 };
69 
RectangleObject(const char * name,const ParamArray & params)70 RectangleObject::RectangleObject(
71     const char*            name,
72     const ParamArray&      params)
73   : ProceduralObject(name, params)
74   , impl(new Impl())
75 {
76 }
77 
release()78 void RectangleObject::release()
79 {
80     delete this;
81 }
82 
get_model() const83 const char* RectangleObject::get_model() const
84 {
85     return Model;
86 }
87 
on_frame_begin(const Project & project,const BaseGroup * parent,OnFrameBeginRecorder & recorder,IAbortSwitch * abort_switch)88 bool RectangleObject::on_frame_begin(
89     const Project&         project,
90     const BaseGroup*       parent,
91     OnFrameBeginRecorder&  recorder,
92     IAbortSwitch*          abort_switch)
93 {
94     if (!ProceduralObject::on_frame_begin(project, parent, recorder, abort_switch))
95         return false;
96 
97     const double width  = get_uncached_width();
98     const double height = get_uncached_height();
99 
100     const double half_width = width * 0.5;
101     const double half_height = height * 0.5;
102 
103     impl->m_x = Vector3d(width, 0.0, 0.0);
104     impl->m_y = Vector3d(0.0, 0.0, -height);
105 
106     impl->m_corner = Vector3d(-half_width, 0.0, half_height);
107     impl->m_normal = Vector3d(0.0, 1.0, 0.0);
108 
109     impl->m_skip_intersection = (half_width == 0.0f || half_height == 0.0f);
110 
111     return true;
112 }
113 
compute_local_bbox() const114 GAABB3 RectangleObject::compute_local_bbox() const
115 {
116     const float width  = static_cast<float>(get_uncached_width());
117     const float height = static_cast<float>(get_uncached_height());
118 
119     const GVector3 pmax(width * 0.5f, 0.0f, height * 0.5f);
120     const GVector3 pmin(-pmax.x, 0.0f, -pmax.z);
121 
122     GAABB3 bbox(pmin, pmax);
123     return bbox;
124 }
125 
get_material_slot_count() const126 size_t RectangleObject::get_material_slot_count() const
127 {
128     return 1;
129 }
130 
get_material_slot(const size_t index) const131 const char* RectangleObject::get_material_slot(const size_t index) const
132 {
133     return "default";
134 }
135 
get_uncached_width() const136 double RectangleObject::get_uncached_width() const
137 {
138     return m_params.get_optional<double>("width", 1.0);
139 }
140 
get_uncached_height() const141 double RectangleObject::get_uncached_height() const
142 {
143     return m_params.get_optional<double>("height", 1.0);
144 }
145 
get_origin_and_axes(Vector3d & origin,Vector3d & x,Vector3d & y,Vector3d & n) const146 void RectangleObject::get_origin_and_axes(
147     Vector3d&               origin,
148     Vector3d&               x,
149     Vector3d&               y,
150     Vector3d&               n) const
151 {
152     const double width = get_uncached_width();
153     const double height = get_uncached_height();
154 
155     origin = Vector3d(-width * 0.5, 0.0, height * 0.5);
156     x = Vector3d(width, 0.0, 0.0);
157     y = Vector3d(0.0, 0.0, -height);
158     n = Vector3d(0.0, 1.0, 0.0);
159 }
160 
intersect(const ShadingRay & ray,IntersectionResult & result) const161 void RectangleObject::intersect(
162     const ShadingRay&      ray,
163     IntersectionResult&    result) const
164 {
165     if APPLESEED_UNLIKELY(impl->m_skip_intersection)
166     {
167         result.m_hit = false;
168         return;
169     }
170 
171     double u, v;
172 
173     result.m_hit = intersect_parallelogram(
174         ray,
175         impl->m_corner,
176         impl->m_x,
177         impl->m_y,
178         impl->m_normal,
179         result.m_distance,
180         u,
181         v);
182 
183     if (result.m_hit)
184     {
185         result.m_geometric_normal = impl->m_normal;
186         result.m_shading_normal = impl->m_normal;
187 
188         result.m_uv[0] = static_cast<float>(u);
189         result.m_uv[1] = static_cast<float>(v);
190         result.m_material_slot = 0;
191     }
192 }
193 
intersect(const ShadingRay & ray) const194 bool RectangleObject::intersect(const ShadingRay& ray) const
195 {
196     if APPLESEED_UNLIKELY(impl->m_skip_intersection)
197         return false;
198 
199     return intersect_parallelogram(
200         ray,
201         impl->m_corner,
202         impl->m_x,
203         impl->m_y,
204         impl->m_normal);
205 }
206 
207 
208 //
209 // RectangleObjectFactory class implementation.
210 //
211 
release()212 void RectangleObjectFactory::release()
213 {
214     delete this;
215 }
216 
get_model() const217 const char* RectangleObjectFactory::get_model() const
218 {
219     return Model;
220 }
221 
get_model_metadata() const222 Dictionary RectangleObjectFactory::get_model_metadata() const
223 {
224     return
225         Dictionary()
226             .insert("name", Model)
227             .insert("label", "Rectangle Object");
228 }
229 
get_input_metadata() const230 DictionaryArray RectangleObjectFactory::get_input_metadata() const
231 {
232     DictionaryArray metadata;
233 
234     metadata.push_back(
235         Dictionary()
236             .insert("name", "width")
237             .insert("label", "Width")
238             .insert("type", "numeric")
239             .insert("min",
240                 Dictionary()
241                     .insert("value", "0.0")
242                     .insert("type", "hard"))
243             .insert("max",
244                 Dictionary()
245                     .insert("value", "10.0")
246                     .insert("type", "soft"))
247             .insert("use", "optional")
248             .insert("default", "1.0"));
249 
250     metadata.push_back(
251         Dictionary()
252             .insert("name", "height")
253             .insert("label", "Height")
254             .insert("type", "numeric")
255             .insert("min",
256                 Dictionary()
257                     .insert("value", "0.0")
258                     .insert("type", "hard"))
259             .insert("max",
260                 Dictionary()
261                     .insert("value", "10.0")
262                     .insert("type", "soft"))
263             .insert("use", "optional")
264             .insert("default", "1.0"));
265 
266     return metadata;
267 }
268 
create(const char * name,const ParamArray & params) const269 auto_release_ptr<Object> RectangleObjectFactory::create(
270     const char*            name,
271     const ParamArray&      params) const
272 {
273     return auto_release_ptr<Object>(new RectangleObject(name, params));
274 }
275 
create(const char * name,const ParamArray & params,const SearchPaths & search_paths,const bool omit_loading_assets,ObjectArray & objects) const276 bool RectangleObjectFactory::create(
277     const char*            name,
278     const ParamArray&      params,
279     const SearchPaths&     search_paths,
280     const bool             omit_loading_assets,
281     ObjectArray&           objects) const
282 {
283     objects.push_back(create(name, params).release());
284     return true;
285 }
286 
287 }   // namespace renderer
288