ClangFormat: apply to source, most of intern
[blender.git] / intern / cycles / app / cycles_standalone.cpp
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18
19 #include "render/buffers.h"
20 #include "render/camera.h"
21 #include "device/device.h"
22 #include "render/scene.h"
23 #include "render/session.h"
24 #include "render/integrator.h"
25
26 #include "util/util_args.h"
27 #include "util/util_foreach.h"
28 #include "util/util_function.h"
29 #include "util/util_logging.h"
30 #include "util/util_path.h"
31 #include "util/util_progress.h"
32 #include "util/util_string.h"
33 #include "util/util_time.h"
34 #include "util/util_transform.h"
35 #include "util/util_unique_ptr.h"
36 #include "util/util_version.h"
37
38 #ifdef WITH_CYCLES_STANDALONE_GUI
39 #  include "util/util_view.h"
40 #endif
41
42 #include "app/cycles_xml.h"
43
44 CCL_NAMESPACE_BEGIN
45
46 struct Options {
47   Session *session;
48   Scene *scene;
49   string filepath;
50   int width, height;
51   SceneParams scene_params;
52   SessionParams session_params;
53   bool quiet;
54   bool show_help, interactive, pause;
55   string output_path;
56 } options;
57
58 static void session_print(const string &str)
59 {
60   /* print with carriage return to overwrite previous */
61   printf("\r%s", str.c_str());
62
63   /* add spaces to overwrite longer previous print */
64   static int maxlen = 0;
65   int len = str.size();
66   maxlen = max(len, maxlen);
67
68   for (int i = len; i < maxlen; i++)
69     printf(" ");
70
71   /* flush because we don't write an end of line */
72   fflush(stdout);
73 }
74
75 static void session_print_status()
76 {
77   string status, substatus;
78
79   /* get status */
80   float progress = options.session->progress.get_progress();
81   options.session->progress.get_status(status, substatus);
82
83   if (substatus != "")
84     status += ": " + substatus;
85
86   /* print status */
87   status = string_printf("Progress %05.2f   %s", (double)progress * 100, status.c_str());
88   session_print(status);
89 }
90
91 static bool write_render(const uchar *pixels, int w, int h, int channels)
92 {
93   string msg = string_printf("Writing image %s", options.output_path.c_str());
94   session_print(msg);
95
96   unique_ptr<ImageOutput> out = unique_ptr<ImageOutput>(ImageOutput::create(options.output_path));
97   if (!out) {
98     return false;
99   }
100
101   ImageSpec spec(w, h, channels, TypeDesc::UINT8);
102   if (!out->open(options.output_path, spec)) {
103     return false;
104   }
105
106   /* conversion for different top/bottom convention */
107   out->write_image(
108       TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride);
109
110   out->close();
111
112   return true;
113 }
114
115 static BufferParams &session_buffer_params()
116 {
117   static BufferParams buffer_params;
118   buffer_params.width = options.width;
119   buffer_params.height = options.height;
120   buffer_params.full_width = options.width;
121   buffer_params.full_height = options.height;
122
123   return buffer_params;
124 }
125
126 static void scene_init()
127 {
128   options.scene = new Scene(options.scene_params, options.session->device);
129
130   /* Read XML */
131   xml_read_file(options.scene, options.filepath.c_str());
132
133   /* Camera width/height override? */
134   if (!(options.width == 0 || options.height == 0)) {
135     options.scene->camera->width = options.width;
136     options.scene->camera->height = options.height;
137   }
138   else {
139     options.width = options.scene->camera->width;
140     options.height = options.scene->camera->height;
141   }
142
143   /* Calculate Viewplane */
144   options.scene->camera->compute_auto_viewplane();
145 }
146
147 static void session_init()
148 {
149   options.session_params.write_render_cb = write_render;
150   options.session = new Session(options.session_params);
151
152   if (options.session_params.background && !options.quiet)
153     options.session->progress.set_update_callback(function_bind(&session_print_status));
154 #ifdef WITH_CYCLES_STANDALONE_GUI
155   else
156     options.session->progress.set_update_callback(function_bind(&view_redraw));
157 #endif
158
159   /* load scene */
160   scene_init();
161   options.session->scene = options.scene;
162
163   options.session->reset(session_buffer_params(), options.session_params.samples);
164   options.session->start();
165 }
166
167 static void session_exit()
168 {
169   if (options.session) {
170     delete options.session;
171     options.session = NULL;
172   }
173
174   if (options.session_params.background && !options.quiet) {
175     session_print("Finished Rendering.");
176     printf("\n");
177   }
178 }
179
180 #ifdef WITH_CYCLES_STANDALONE_GUI
181 static void display_info(Progress &progress)
182 {
183   static double latency = 0.0;
184   static double last = 0;
185   double elapsed = time_dt();
186   string str, interactive;
187
188   latency = (elapsed - last);
189   last = elapsed;
190
191   double total_time, sample_time;
192   string status, substatus;
193
194   progress.get_time(total_time, sample_time);
195   progress.get_status(status, substatus);
196   float progress_val = progress.get_progress();
197
198   if (substatus != "")
199     status += ": " + substatus;
200
201   interactive = options.interactive ? "On" : "Off";
202
203   str = string_printf(
204       "%s"
205       "        Time: %.2f"
206       "        Latency: %.4f"
207       "        Progress: %05.2f"
208       "        Average: %.4f"
209       "        Interactive: %s",
210       status.c_str(),
211       total_time,
212       latency,
213       (double)progress_val * 100,
214       sample_time,
215       interactive.c_str());
216
217   view_display_info(str.c_str());
218
219   if (options.show_help)
220     view_display_help();
221 }
222
223 static void display()
224 {
225   static DeviceDrawParams draw_params = DeviceDrawParams();
226
227   options.session->draw(session_buffer_params(), draw_params);
228
229   display_info(options.session->progress);
230 }
231
232 static void motion(int x, int y, int button)
233 {
234   if (options.interactive) {
235     Transform matrix = options.session->scene->camera->matrix;
236
237     /* Translate */
238     if (button == 0) {
239       float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f);
240       matrix = matrix * transform_translate(translate);
241     }
242
243     /* Rotate */
244     else if (button == 2) {
245       float4 r1 = make_float4((float)x * 0.1f, 0.0f, 1.0f, 0.0f);
246       matrix = matrix * transform_rotate(DEG2RADF(r1.x), make_float3(r1.y, r1.z, r1.w));
247
248       float4 r2 = make_float4(y * 0.1f, 1.0f, 0.0f, 0.0f);
249       matrix = matrix * transform_rotate(DEG2RADF(r2.x), make_float3(r2.y, r2.z, r2.w));
250     }
251
252     /* Update and Reset */
253     options.session->scene->camera->matrix = matrix;
254     options.session->scene->camera->need_update = true;
255     options.session->scene->camera->need_device_update = true;
256
257     options.session->reset(session_buffer_params(), options.session_params.samples);
258   }
259 }
260
261 static void resize(int width, int height)
262 {
263   options.width = width;
264   options.height = height;
265
266   if (options.session) {
267     /* Update camera */
268     options.session->scene->camera->width = width;
269     options.session->scene->camera->height = height;
270     options.session->scene->camera->compute_auto_viewplane();
271     options.session->scene->camera->need_update = true;
272     options.session->scene->camera->need_device_update = true;
273
274     options.session->reset(session_buffer_params(), options.session_params.samples);
275   }
276 }
277
278 static void keyboard(unsigned char key)
279 {
280   /* Toggle help */
281   if (key == 'h')
282     options.show_help = !(options.show_help);
283
284   /* Reset */
285   else if (key == 'r')
286     options.session->reset(session_buffer_params(), options.session_params.samples);
287
288   /* Cancel */
289   else if (key == 27)  // escape
290     options.session->progress.set_cancel("Canceled");
291
292   /* Pause */
293   else if (key == 'p') {
294     options.pause = !options.pause;
295     options.session->set_pause(options.pause);
296   }
297
298   /* Interactive Mode */
299   else if (key == 'i')
300     options.interactive = !(options.interactive);
301
302   /* Navigation */
303   else if (options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) {
304     Transform matrix = options.session->scene->camera->matrix;
305     float3 translate;
306
307     if (key == 'w')
308       translate = make_float3(0.0f, 0.0f, 0.1f);
309     else if (key == 's')
310       translate = make_float3(0.0f, 0.0f, -0.1f);
311     else if (key == 'a')
312       translate = make_float3(-0.1f, 0.0f, 0.0f);
313     else if (key == 'd')
314       translate = make_float3(0.1f, 0.0f, 0.0f);
315
316     matrix = matrix * transform_translate(translate);
317
318     /* Update and Reset */
319     options.session->scene->camera->matrix = matrix;
320     options.session->scene->camera->need_update = true;
321     options.session->scene->camera->need_device_update = true;
322
323     options.session->reset(session_buffer_params(), options.session_params.samples);
324   }
325
326   /* Set Max Bounces */
327   else if (options.interactive && (key == '0' || key == '1' || key == '2' || key == '3')) {
328     int bounce;
329     switch (key) {
330       case '0':
331         bounce = 0;
332         break;
333       case '1':
334         bounce = 1;
335         break;
336       case '2':
337         bounce = 2;
338         break;
339       case '3':
340         bounce = 3;
341         break;
342       default:
343         bounce = 0;
344         break;
345     }
346
347     options.session->scene->integrator->max_bounce = bounce;
348
349     /* Update and Reset */
350     options.session->scene->integrator->need_update = true;
351
352     options.session->reset(session_buffer_params(), options.session_params.samples);
353   }
354 }
355 #endif
356
357 static int files_parse(int argc, const char *argv[])
358 {
359   if (argc > 0)
360     options.filepath = argv[0];
361
362   return 0;
363 }
364
365 static void options_parse(int argc, const char **argv)
366 {
367   options.width = 0;
368   options.height = 0;
369   options.filepath = "";
370   options.session = NULL;
371   options.quiet = false;
372
373   /* device names */
374   string device_names = "";
375   string devicename = "CPU";
376   bool list = false;
377
378   /* List devices for which support is compiled in. */
379   vector<DeviceType> types = Device::available_types();
380   foreach (DeviceType type, types) {
381     if (device_names != "")
382       device_names += ", ";
383
384     device_names += Device::string_from_type(type);
385   }
386
387   /* shading system */
388   string ssname = "svm";
389
390   /* parse options */
391   ArgParse ap;
392   bool help = false, debug = false, version = false;
393   int verbosity = 1;
394
395   ap.options("Usage: cycles [options] file.xml",
396              "%*",
397              files_parse,
398              "",
399              "--device %s",
400              &devicename,
401              ("Devices to use: " + device_names).c_str(),
402 #ifdef WITH_OSL
403              "--shadingsys %s",
404              &ssname,
405              "Shading system to use: svm, osl",
406 #endif
407              "--background",
408              &options.session_params.background,
409              "Render in background, without user interface",
410              "--quiet",
411              &options.quiet,
412              "In background mode, don't print progress messages",
413              "--samples %d",
414              &options.session_params.samples,
415              "Number of samples to render",
416              "--output %s",
417              &options.output_path,
418              "File path to write output image",
419              "--threads %d",
420              &options.session_params.threads,
421              "CPU Rendering Threads",
422              "--width  %d",
423              &options.width,
424              "Window width in pixel",
425              "--height %d",
426              &options.height,
427              "Window height in pixel",
428              "--tile-width %d",
429              &options.session_params.tile_size.x,
430              "Tile width in pixels",
431              "--tile-height %d",
432              &options.session_params.tile_size.y,
433              "Tile height in pixels",
434              "--list-devices",
435              &list,
436              "List information about all available devices",
437 #ifdef WITH_CYCLES_LOGGING
438              "--debug",
439              &debug,
440              "Enable debug logging",
441              "--verbose %d",
442              &verbosity,
443              "Set verbosity of the logger",
444 #endif
445              "--help",
446              &help,
447              "Print help message",
448              "--version",
449              &version,
450              "Print version number",
451              NULL);
452
453   if (ap.parse(argc, argv) < 0) {
454     fprintf(stderr, "%s\n", ap.geterror().c_str());
455     ap.usage();
456     exit(EXIT_FAILURE);
457   }
458
459   if (debug) {
460     util_logging_start();
461     util_logging_verbosity_set(verbosity);
462   }
463
464   if (list) {
465     vector<DeviceInfo> devices = Device::available_devices();
466     printf("Devices:\n");
467
468     foreach (DeviceInfo &info, devices) {
469       printf("    %-10s%s%s\n",
470              Device::string_from_type(info.type).c_str(),
471              info.description.c_str(),
472              (info.display_device) ? " (display)" : "");
473     }
474
475     exit(EXIT_SUCCESS);
476   }
477   else if (version) {
478     printf("%s\n", CYCLES_VERSION_STRING);
479     exit(EXIT_SUCCESS);
480   }
481   else if (help || options.filepath == "") {
482     ap.usage();
483     exit(EXIT_SUCCESS);
484   }
485
486   if (ssname == "osl")
487     options.scene_params.shadingsystem = SHADINGSYSTEM_OSL;
488   else if (ssname == "svm")
489     options.scene_params.shadingsystem = SHADINGSYSTEM_SVM;
490
491 #ifndef WITH_CYCLES_STANDALONE_GUI
492   options.session_params.background = true;
493 #endif
494
495   /* Use progressive rendering */
496   options.session_params.progressive = true;
497
498   /* find matching device */
499   DeviceType device_type = Device::type_from_string(devicename.c_str());
500   vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK(device_type));
501
502   bool device_available = false;
503   if (!devices.empty()) {
504     options.session_params.device = devices.front();
505     device_available = true;
506   }
507
508   /* handle invalid configurations */
509   if (options.session_params.device.type == DEVICE_NONE || !device_available) {
510     fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
511     exit(EXIT_FAILURE);
512   }
513 #ifdef WITH_OSL
514   else if (!(ssname == "osl" || ssname == "svm")) {
515     fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
516     exit(EXIT_FAILURE);
517   }
518   else if (options.scene_params.shadingsystem == SHADINGSYSTEM_OSL &&
519            options.session_params.device.type != DEVICE_CPU) {
520     fprintf(stderr, "OSL shading system only works with CPU device\n");
521     exit(EXIT_FAILURE);
522   }
523 #endif
524   else if (options.session_params.samples < 0) {
525     fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
526     exit(EXIT_FAILURE);
527   }
528   else if (options.filepath == "") {
529     fprintf(stderr, "No file path specified\n");
530     exit(EXIT_FAILURE);
531   }
532
533   /* For smoother Viewport */
534   options.session_params.start_resolution = 64;
535 }
536
537 CCL_NAMESPACE_END
538
539 using namespace ccl;
540
541 int main(int argc, const char **argv)
542 {
543   util_logging_init(argv[0]);
544   path_init();
545   options_parse(argc, argv);
546
547 #ifdef WITH_CYCLES_STANDALONE_GUI
548   if (options.session_params.background) {
549 #endif
550     session_init();
551     options.session->wait();
552     session_exit();
553 #ifdef WITH_CYCLES_STANDALONE_GUI
554   }
555   else {
556     string title = "Cycles: " + path_filename(options.filepath);
557
558     /* init/exit are callback so they run while GL is initialized */
559     view_main_loop(title.c_str(),
560                    options.width,
561                    options.height,
562                    session_init,
563                    session_exit,
564                    resize,
565                    display,
566                    keyboard,
567                    motion);
568   }
569 #endif
570
571   return 0;
572 }