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