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) 2014-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/bsdf.h"
31 #include "renderer/api/camera.h"
32 #include "renderer/api/color.h"
33 #include "renderer/api/environment.h"
34 #include "renderer/api/environmentedf.h"
35 #include "renderer/api/frame.h"
36 #include "renderer/api/light.h"
37 #include "renderer/api/log.h"
38 #include "renderer/api/material.h"
39 #include "renderer/api/object.h"
40 #include "renderer/api/project.h"
41 #include "renderer/api/scene.h"
42 #include "renderer/api/surfaceshader.h"
43 #include "renderer/api/texture.h"
44 #include "renderer/api/utility.h"
45 
46 // appleseed.foundation headers.
47 #include "foundation/image/color.h"
48 #include "foundation/image/canvasproperties.h"
49 #include "foundation/image/genericimagefilereader.h"
50 #include "foundation/image/image.h"
51 #include "foundation/math/rng/mersennetwister.h"
52 #include "foundation/math/matrix.h"
53 #include "foundation/math/scalar.h"
54 #include "foundation/math/transform.h"
55 #include "foundation/platform/compiler.h"
56 #include "foundation/platform/types.h"
57 #include "foundation/utility/containers/dictionary.h"
58 #include "foundation/utility/log/consolelogtarget.h"
59 #include "foundation/utility/autoreleaseptr.h"
60 #include "foundation/utility/searchpaths.h"
61 #include "foundation/utility/string.h"
62 
63 // Standard headers.
64 #include <algorithm>
65 #include <cassert>
66 #include <cmath>
67 #include <cstddef>
68 #include <iomanip>
69 #include <memory>
70 #include <sstream>
71 #include <string>
72 
73 using namespace foundation;
74 using namespace renderer;
75 using namespace std;
76 
77 class ProjectBuilder
78 {
79   public:
build_project()80     auto_release_ptr<Project> build_project()
81     {
82         GenericImageFileReader reader;
83         unique_ptr<Image> image(reader.read("data/heightfield.png"));
84         const size_t image_width = image->properties().m_canvas_width;
85         const size_t image_height = image->properties().m_canvas_height;
86 
87         auto_release_ptr<Project> project(ProjectFactory::create("heightfield"));
88         project->add_default_configurations();
89         project->search_paths().push_back_explicit_path("data");
90 
91         auto_release_ptr<Scene> scene(SceneFactory::create());
92         auto_release_ptr<Assembly> assembly(AssemblyFactory().create("assembly"));
93 
94         //------------------------------------------------------------------------
95         // Geometry
96         //------------------------------------------------------------------------
97 
98         initialize_assembly(
99             *project,
100             *assembly,
101             image_width,
102             image_height);
103 
104         const double ValueMultiplier = 1.0;
105         const double CubeSizeX = 0.002;
106         const double CubeScaleY = 0.02;
107         const double CubeSizeZ = 0.002;
108         const double CubeMinSizeY = 0.0001;
109         const double ScaleVariation = 0.6;
110         const double OrientationVariation = 0.2;
111         const double ShiftX = 0.1 * CubeSizeX;
112         const double ShiftZ = 0.1 * CubeSizeZ;
113 
114         const double total_size_x = CubeSizeX * image_width;
115         const double total_size_z = CubeSizeZ * image_height;
116 
117         MersenneTwister rng;
118 
119         for (size_t iy = 0; iy < image_height; ++iy)
120         {
121             for (size_t ix = 0; ix < image_width; ++ix)
122             {
123                 const double fx = (ix + 0.5) / image_width;
124                 const double fz = (iy + 0.5) / image_height;
125 
126                 // Read pixel color.
127                 Color3b color;
128                 image->get_pixel(ix, iy, color);
129 
130                 // Compute pixel value.
131                 const Color3f reflectance = Color3f(color) * (1.0f / 255);
132                 const double value = pow(reflectance.r, 3.0) * ValueMultiplier;
133 
134                 // Compute scaling.
135                 const double scale_multiplier = 1.0 + ScaleVariation * value;
136                 const double scale_x = CubeSizeX * scale_multiplier;
137                 const double scale_z = CubeSizeZ * scale_multiplier;
138                 const double scale_y = max(CubeScaleY * value, CubeMinSizeY);
139                 const Vector3d scaling(scale_x, scale_y, scale_z);
140 
141                 // Compute rotation.
142                 const double rotation = OrientationVariation * rand2(rng, -Pi<double>(), Pi<double>());
143 
144                 // Compute translation.
145                 const double translate_x = (fx - 0.5) * total_size_x;
146                 const double translate_z = (fz - 0.5) * total_size_z;
147                 const double shift_x = ShiftX * rand1(rng, -0.5, 0.5);
148                 const double shift_z = ShiftZ * rand1(rng, -0.5, 0.5);
149                 const Vector3d translation(translate_x + shift_x, 0.0, translate_z + shift_z);
150 
151                 // Compute cube transform.
152                 const Transformd transform =
153                     Transformd::from_local_to_parent(
154                           Matrix4d::make_translation(translation)
155                         * Matrix4d::make_rotation_y(rotation)
156                         * Matrix4d::make_scaling(scaling));
157 
158                 // Add cube to assembly.
159                 add_cube(*assembly, ix, iy, fx, fz, color, transform);
160             }
161 
162             RENDERER_LOG_INFO("%s completed...", pretty_percent(iy + 1, image_height).c_str());
163         }
164 
165         finalize_assembly(*assembly);
166 
167         //------------------------------------------------------------------------
168         // Light
169         //------------------------------------------------------------------------
170 
171         static const float LightIrradiance[] = { 1.0f, 1.0f, 1.0f };
172         assembly->colors().insert(
173             ColorEntityFactory::create(
174                 "light_irradiance",
175                 ParamArray()
176                     .insert("color_space", "srgb")
177                     .insert("multiplier", "6.0"),
178                 ColorValueArray(3, LightIrradiance)));
179 
180         auto_release_ptr<Light> light(
181             DirectionalLightFactory().create(
182                 "sun_light",
183                 ParamArray()
184                     .insert("irradiance", "light_irradiance")));
185         light->set_transform(
186             Transformd::from_local_to_parent(
187                 Matrix4d::make_rotation_x(deg_to_rad(-30.0))));
188         assembly->lights().insert(light);
189 
190         //------------------------------------------------------------------------
191         // Assembly instance
192         //------------------------------------------------------------------------
193 
194         auto_release_ptr<AssemblyInstance> assembly_instance(
195             AssemblyInstanceFactory::create(
196                 "assembly_inst",
197                 ParamArray(),
198                 "assembly"));
199         assembly_instance->transform_sequence().set_transform(0.0, Transformd::identity());
200         scene->assembly_instances().insert(assembly_instance);
201 
202         scene->assemblies().insert(assembly);
203 
204         //------------------------------------------------------------------------
205         // Environment
206         //------------------------------------------------------------------------
207 
208         scene->environment_edfs().insert(
209             HosekEnvironmentEDFFactory().create(
210                 "sky_edf",
211                 ParamArray()
212                     .insert("sun_theta", 30.0)
213                     .insert("sun_phi", 0.0)
214                     .insert("turbidity", 4.0)
215                     .insert("turbidity_multiplier", 1.0)
216                     .insert("luminance_multiplier", 2.0)));
217 
218         scene->set_environment(
219             EnvironmentFactory::create(
220                 "sky",
221                 ParamArray()
222                     .insert("environment_edf", "sky_edf")));
223 
224         //------------------------------------------------------------------------
225         // Camera
226         //------------------------------------------------------------------------
227 
228         auto_release_ptr<Camera> camera(
229             ThinLensCameraFactory().create(
230                 "camera",
231                 ParamArray()
232                     .insert("film_dimensions", "0.025 0.014")
233                     .insert("horizontal_fov", "70.0")
234                     .insert("f_stop", "2.0")
235                     .remove_path("focal_distance")
236                     .insert("autofocus_target", "0.5 0.5")
237                     .insert("controller_target", "0.00222523 -0.004497 0.00937141")));
238 
239         static const double CameraMatrix[16] =
240         {
241             0.898911352044089, -0.291992464000737, 0.326647795236774, 0.051430150409342,
242             0.000000000000000, 0.745548999631107, 0.666450815251250, 0.095894497845129,
243             -0.438130552650996, -0.599080203408387, 0.670182459273516, 0.110325032485670,
244             0.000000000000000, 0.000000000000000, 0.000000000000000, 1.000000000000000
245         };
246 
247         camera->transform_sequence().set_transform(
248             0.0,
249             Transformd::from_local_to_parent(
250                 Matrix4d::from_array(CameraMatrix)));
251 
252         scene->cameras().insert(camera);
253 
254         //------------------------------------------------------------------------
255         // Frame
256         //------------------------------------------------------------------------
257 
258         project->set_frame(
259             FrameFactory::create(
260                 "beauty",
261                 ParamArray()
262                     .insert("camera", "camera")
263                     .insert("resolution", "1280 720")
264                     .insert("color_space", "srgb")
265                     .insert("camera", "camera")));
266 
267         project->set_scene(scene);
268 
269         return project;
270     }
271 
272   protected:
273     virtual void initialize_assembly(
274         Project&            project,
275         Assembly&           assembly,
276         const size_t        image_width,
277         const size_t        image_height) = 0;
278 
279     virtual void add_cube(
280         Assembly&           assembly,
281         const size_t        ix,
282         const size_t        iy,
283         const double        fx,
284         const double        fz,
285         const Color3b&      color,
286         const Transformd&   transform) = 0;
287 
288     virtual void finalize_assembly(
289         Assembly&           assembly) = 0;
290 };
291 
292 class SingleBakedMeshProjectBuilder
293   : public ProjectBuilder
294 {
295   private:
296     auto_release_ptr<MeshObject>    m_cube;
297     auto_release_ptr<MeshObject>    m_mesh;
298 
initialize_assembly(Project & project,Assembly & assembly,const size_t image_width,const size_t image_height)299     void initialize_assembly(
300         Project&            project,
301         Assembly&           assembly,
302         const size_t        image_width,
303         const size_t        image_height) override
304     {
305         assembly.textures().insert(
306             DiskTexture2dFactory().create(
307                 "cubes_texture",
308                 ParamArray()
309                     .insert("filename", "heightfield.png")
310                     .insert("color_space", "srgb"),
311                 project.search_paths()));
312 
313         assembly.texture_instances().insert(
314             TextureInstanceFactory::create(
315                 "cubes_texture_instance",
316                 ParamArray()
317                     .insert("filtering_mode", "nearest")
318                     .insert("addressing_mode", "wrap"),
319                 "cubes_texture"));
320 
321         assembly.bsdfs().insert(
322             DisneyBRDFFactory().create(
323                 "cubes_brdf",
324                 ParamArray()
325                     .insert("anisotropic", 0.0)
326                     .insert("base_color", "cubes_texture_instance")
327                     .insert("clearcoat", 1.0)
328                     .insert("clearcoat_gloss", 0.9)
329                     .insert("metallic", 0.0)
330                     .insert("roughness", 0.3)
331                     .insert("sheen", 0.0)
332                     .insert("sheen_tint", 0.0)
333                     .insert("specular", 0.9)
334                     .insert("specular_tint", 1.0)
335                     .insert("subsurface", 1.0)));
336 
337         assembly.surface_shaders().insert(
338             PhysicalSurfaceShaderFactory().create(
339                 "physical_surface_shader",
340                 ParamArray()));
341 
342         assembly.materials().insert(
343             GenericMaterialFactory().create(
344                 "cubes_material",
345                 ParamArray()
346                     .insert("surface_shader", "physical_surface_shader")
347                     .insert("bsdf", "cubes_brdf")));
348 
349         // Load the cube mesh object from disk.
350         MeshObjectArray objects;
351         MeshObjectReader::read(
352             project.search_paths(),
353             "cube",
354             ParamArray()
355                 .insert("filename", "cube.obj"),
356             objects);
357         assert(objects.size() == 1);
358         m_cube.reset(objects[0]);
359 
360         // Create the baked mesh object.
361         m_mesh = MeshObjectFactory().create("cubes", ParamArray());
362 
363         // Reserve memory into the baked mesh object.
364         const size_t cube_count = image_width * image_height;
365         m_mesh->reserve_vertices(m_cube->get_vertex_count() * cube_count);
366         m_mesh->reserve_vertex_normals(m_cube->get_vertex_normal_count() * cube_count);
367         m_mesh->reserve_tex_coords(cube_count);
368         m_mesh->reserve_triangles(m_cube->get_triangle_count() * cube_count);
369     }
370 
add_cube(Assembly & assembly,const size_t ix,const size_t iy,const double fx,const double fz,const Color3b & color,const Transformd & transform)371     void add_cube(
372         Assembly&           assembly,
373         const size_t        ix,
374         const size_t        iy,
375         const double        fx,
376         const double        fz,
377         const Color3b&      color,
378         const Transformd&   transform) override
379     {
380         // Push vertices.
381         const size_t base_vertex_index = m_mesh->get_vertex_count();
382         for (size_t i = 0; i < m_cube->get_vertex_count(); ++i)
383         {
384             m_mesh->push_vertex(
385                 transform.point_to_parent(m_cube->get_vertex(i)));
386         }
387 
388         // Push normals.
389         const size_t base_vertex_normal_index = m_mesh->get_vertex_normal_count();
390         for (size_t i = 0; i < m_cube->get_vertex_normal_count(); ++i)
391         {
392             m_mesh->push_vertex_normal(
393                 normalize(
394                     transform.normal_to_parent(m_cube->get_vertex_normal(i))));
395         }
396 
397         // Push texture coordinates.
398         const size_t tex_coords_index =
399             m_mesh->push_tex_coords(
400                 GVector2(static_cast<float>(fx), static_cast<float>(1.0 - fz)));
401 
402         // Push triangles.
403         for (size_t i = 0; i < m_cube->get_triangle_count(); ++i)
404         {
405             Triangle triangle = m_cube->get_triangle(i);
406             triangle.m_v0 += static_cast<uint32>(base_vertex_index);
407             triangle.m_v1 += static_cast<uint32>(base_vertex_index);
408             triangle.m_v2 += static_cast<uint32>(base_vertex_index);
409             triangle.m_n0 += static_cast<uint32>(base_vertex_normal_index);
410             triangle.m_n1 += static_cast<uint32>(base_vertex_normal_index);
411             triangle.m_n2 += static_cast<uint32>(base_vertex_normal_index);
412             triangle.m_a0 = triangle.m_a1 = triangle.m_a2 = static_cast<uint32>(tex_coords_index);
413             m_mesh->push_triangle(triangle);
414         }
415     }
416 
finalize_assembly(Assembly & assembly)417     void finalize_assembly(Assembly& assembly) override
418     {
419         // Insert the baked mesh object into the assembly.
420         assembly.objects().insert(auto_release_ptr<Object>(m_mesh));
421 
422         // Instantiate the baked mesh object.
423         assembly.object_instances().insert(
424             ObjectInstanceFactory::create(
425                 "cubes_instance",
426                 ParamArray(),
427                 "cubes",
428                 Transformd::identity(),
429                 StringDictionary()
430                     .insert("default", "cubes_material")));
431     }
432 };
433 
434 class InstancesProjectBuilder
435   : public ProjectBuilder
436 {
437   private:
initialize_assembly(Project & project,Assembly & assembly,const size_t image_width,const size_t image_height)438     void initialize_assembly(
439         Project&            project,
440         Assembly&           assembly,
441         const size_t        image_width,
442         const size_t        image_height) override
443     {
444         assembly.surface_shaders().insert(
445             PhysicalSurfaceShaderFactory().create(
446                 "physical_surface_shader",
447                 ParamArray()));
448 
449         // Load the cube mesh object from disk.
450         MeshObjectArray objects;
451         MeshObjectReader::read(
452             project.search_paths(),
453             "cube",
454             ParamArray()
455                 .insert("filename", "smoothcube.obj"),
456             objects);
457 
458         // Insert the cube mesh into the assembly.
459         assert(objects.size() == 1);
460         assembly.objects().insert(auto_release_ptr<Object>(objects[0]));
461     }
462 
add_cube(Assembly & assembly,const size_t ix,const size_t iy,const double fx,const double fz,const Color3b & color,const Transformd & transform)463     void add_cube(
464         Assembly&           assembly,
465         const size_t        ix,
466         const size_t        iy,
467         const double        fx,
468         const double        fz,
469         const Color3b&      color,
470         const Transformd&   transform) override
471     {
472         const string color_suffix = color_to_string(color);
473         const string color_name = "color_" + color_suffix;
474         const string material_name = "material_" + color_suffix;
475 
476         if (assembly.colors().get_by_name(color_name.c_str()) == 0)
477         {
478             const Color3f reflectance = Color3f(color) * (1.0f / 255);
479             assembly.colors().insert(
480                 ColorEntityFactory::create(
481                     color_name.c_str(),
482                     ParamArray()
483                         .insert("color_space", "srgb"),
484                     ColorValueArray(3, &reflectance[0])));
485 
486             const string brdf_name = "brdf_" + color_suffix;
487             assembly.bsdfs().insert(
488                 DisneyBRDFFactory().create(
489                     brdf_name.c_str(),
490                     ParamArray()
491                         .insert("anisotropic", 0.0)
492                         .insert("base_color", color_name)
493                         .insert("clearcoat", 1.0)
494                         .insert("clearcoat_gloss", 0.9)
495                         .insert("metallic", 0.0)
496                         .insert("roughness", 0.3)
497                         .insert("sheen", 0.0)
498                         .insert("sheen_tint", 0.0)
499                         .insert("specular", 0.9)
500                         .insert("specular_tint", 1.0)
501                         .insert("subsurface", 1.0)));
502 
503             assembly.materials().insert(
504                 GenericMaterialFactory().create(
505                     material_name.c_str(),
506                     ParamArray()
507                         .insert("surface_shader", "physical_surface_shader")
508                         .insert("bsdf", brdf_name)));
509         }
510 
511         // Create an assembly for this cube.
512         const string coordinates_suffix = coordinates_to_string(ix, iy);
513         const string cube_assembly_name = "assembly_" + coordinates_suffix;
514         auto_release_ptr<Assembly> cube_assembly(
515             AssemblyFactory().create(cube_assembly_name.c_str()));
516 
517         // Instantiate the cube mesh object (from the parent assembly) into this assembly.
518         cube_assembly->object_instances().insert(
519             ObjectInstanceFactory::create(
520                 "cube.0_inst",
521                 ParamArray(),
522                 "cube.0",
523                 Transformd::identity(),
524                 StringDictionary()
525                     .insert("default", material_name)));
526 
527         // Create an instance of the cube assembly.
528         const string cube_assembly_inst_name = cube_assembly_name + "_inst";
529         auto_release_ptr<AssemblyInstance> cube_assembly_inst(
530             AssemblyInstanceFactory::create(
531                 cube_assembly_inst_name.c_str(),
532                 ParamArray(),
533                 cube_assembly_name.c_str()));
534         cube_assembly_inst->transform_sequence().set_transform(0.0, transform);
535 
536         // Insert both the cube assembly and its instance into the parent assembly.
537         assembly.assemblies().insert(cube_assembly);
538         assembly.assembly_instances().insert(cube_assembly_inst);
539     }
540 
finalize_assembly(Assembly & assembly)541     void finalize_assembly(Assembly& assembly) override
542     {
543     }
544 
color_to_string(const Color3b & c)545     static string color_to_string(const Color3b& c)
546     {
547         stringstream sstr;
548         sstr << '#' << hex << setfill('0')
549              << setw(2) << int(c.r)
550              << setw(2) << int(c.g)
551              << setw(2) << int(c.b);
552         return sstr.str();
553     }
554 
coordinates_to_string(const size_t x,const size_t y)555     static string coordinates_to_string(const size_t x, const size_t y)
556     {
557         stringstream sstr;
558         sstr << setfill('0')
559              << setw(4) << x << "_"
560              << setw(4) << y;
561         return sstr.str();
562     }
563 };
564 
main()565 int main()
566 {
567     // Create a log target that outputs to stderr, and binds it to the renderer's global logger.
568     unique_ptr<ILogTarget> log_target(create_console_log_target(stderr));
569     global_logger().add_target(log_target.get());
570 
571     //SingleBakedMeshProjectBuilder project_builder;
572     InstancesProjectBuilder project_builder;
573 
574     auto_release_ptr<Project> project(project_builder.build_project());
575     ProjectFileWriter::write(project.ref(), "output/heightfield.appleseed");
576 
577     return 0;
578 }
579