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