2 * Copyright 2011-2013 Blender Foundation
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "device/device.h"
20 #include "render/buffers.h"
21 #include "render/camera.h"
22 #include "render/integrator.h"
23 #include "render/scene.h"
24 #include "render/session.h"
26 #include "util/util_args.h"
27 #include "util/util_foreach.h"
28 #include "util/util_function.h"
29 #include "util/util_image.h"
30 #include "util/util_logging.h"
31 #include "util/util_path.h"
32 #include "util/util_progress.h"
33 #include "util/util_string.h"
34 #include "util/util_time.h"
35 #include "util/util_transform.h"
36 #include "util/util_unique_ptr.h"
37 #include "util/util_version.h"
39 #ifdef WITH_CYCLES_STANDALONE_GUI
40 # include "util/util_view.h"
43 #include "app/cycles_xml.h"
52 SceneParams scene_params;
53 SessionParams session_params;
55 bool show_help, interactive, pause;
59 static void session_print(const string &str)
61 /* print with carriage return to overwrite previous */
62 printf("\r%s", str.c_str());
64 /* add spaces to overwrite longer previous print */
65 static int maxlen = 0;
67 maxlen = max(len, maxlen);
69 for (int i = len; i < maxlen; i++)
72 /* flush because we don't write an end of line */
76 static void session_print_status()
78 string status, substatus;
81 float progress = options.session->progress.get_progress();
82 options.session->progress.get_status(status, substatus);
85 status += ": " + substatus;
88 status = string_printf("Progress %05.2f %s", (double)progress * 100, status.c_str());
89 session_print(status);
92 static bool write_render(const uchar *pixels, int w, int h, int channels)
94 string msg = string_printf("Writing image %s", options.output_path.c_str());
97 unique_ptr<ImageOutput> out = unique_ptr<ImageOutput>(ImageOutput::create(options.output_path));
102 ImageSpec spec(w, h, channels, TypeDesc::UINT8);
103 if (!out->open(options.output_path, spec)) {
107 /* conversion for different top/bottom convention */
109 TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride);
116 static BufferParams &session_buffer_params()
118 static BufferParams buffer_params;
119 buffer_params.width = options.width;
120 buffer_params.height = options.height;
121 buffer_params.full_width = options.width;
122 buffer_params.full_height = options.height;
124 return buffer_params;
127 static void scene_init()
129 options.scene = new Scene(options.scene_params, options.session->device);
132 xml_read_file(options.scene, options.filepath.c_str());
134 /* Camera width/height override? */
135 if (!(options.width == 0 || options.height == 0)) {
136 options.scene->camera->width = options.width;
137 options.scene->camera->height = options.height;
140 options.width = options.scene->camera->width;
141 options.height = options.scene->camera->height;
144 /* Calculate Viewplane */
145 options.scene->camera->compute_auto_viewplane();
148 static void session_init()
150 options.session_params.write_render_cb = write_render;
151 options.session = new Session(options.session_params);
153 if (options.session_params.background && !options.quiet)
154 options.session->progress.set_update_callback(function_bind(&session_print_status));
155 #ifdef WITH_CYCLES_STANDALONE_GUI
157 options.session->progress.set_update_callback(function_bind(&view_redraw));
162 options.session->scene = options.scene;
164 options.session->reset(session_buffer_params(), options.session_params.samples);
165 options.session->start();
168 static void session_exit()
170 if (options.session) {
171 delete options.session;
172 options.session = NULL;
175 if (options.session_params.background && !options.quiet) {
176 session_print("Finished Rendering.");
181 #ifdef WITH_CYCLES_STANDALONE_GUI
182 static void display_info(Progress &progress)
184 static double latency = 0.0;
185 static double last = 0;
186 double elapsed = time_dt();
187 string str, interactive;
189 latency = (elapsed - last);
192 double total_time, sample_time;
193 string status, substatus;
195 progress.get_time(total_time, sample_time);
196 progress.get_status(status, substatus);
197 float progress_val = progress.get_progress();
200 status += ": " + substatus;
202 interactive = options.interactive ? "On" : "Off";
214 (double)progress_val * 100,
216 interactive.c_str());
218 view_display_info(str.c_str());
220 if (options.show_help)
224 static void display()
226 static DeviceDrawParams draw_params = DeviceDrawParams();
228 options.session->draw(session_buffer_params(), draw_params);
230 display_info(options.session->progress);
233 static void motion(int x, int y, int button)
235 if (options.interactive) {
236 Transform matrix = options.session->scene->camera->matrix;
240 float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f);
241 matrix = matrix * transform_translate(translate);
245 else if (button == 2) {
246 float4 r1 = make_float4((float)x * 0.1f, 0.0f, 1.0f, 0.0f);
247 matrix = matrix * transform_rotate(DEG2RADF(r1.x), make_float3(r1.y, r1.z, r1.w));
249 float4 r2 = make_float4(y * 0.1f, 1.0f, 0.0f, 0.0f);
250 matrix = matrix * transform_rotate(DEG2RADF(r2.x), make_float3(r2.y, r2.z, r2.w));
253 /* Update and Reset */
254 options.session->scene->camera->matrix = matrix;
255 options.session->scene->camera->need_update = true;
256 options.session->scene->camera->need_device_update = true;
258 options.session->reset(session_buffer_params(), options.session_params.samples);
262 static void resize(int width, int height)
264 options.width = width;
265 options.height = height;
267 if (options.session) {
269 options.session->scene->camera->width = width;
270 options.session->scene->camera->height = height;
271 options.session->scene->camera->compute_auto_viewplane();
272 options.session->scene->camera->need_update = true;
273 options.session->scene->camera->need_device_update = true;
275 options.session->reset(session_buffer_params(), options.session_params.samples);
279 static void keyboard(unsigned char key)
283 options.show_help = !(options.show_help);
287 options.session->reset(session_buffer_params(), options.session_params.samples);
290 else if (key == 27) // escape
291 options.session->progress.set_cancel("Canceled");
294 else if (key == 'p') {
295 options.pause = !options.pause;
296 options.session->set_pause(options.pause);
299 /* Interactive Mode */
301 options.interactive = !(options.interactive);
304 else if (options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) {
305 Transform matrix = options.session->scene->camera->matrix;
309 translate = make_float3(0.0f, 0.0f, 0.1f);
311 translate = make_float3(0.0f, 0.0f, -0.1f);
313 translate = make_float3(-0.1f, 0.0f, 0.0f);
315 translate = make_float3(0.1f, 0.0f, 0.0f);
317 matrix = matrix * transform_translate(translate);
319 /* Update and Reset */
320 options.session->scene->camera->matrix = matrix;
321 options.session->scene->camera->need_update = true;
322 options.session->scene->camera->need_device_update = true;
324 options.session->reset(session_buffer_params(), options.session_params.samples);
327 /* Set Max Bounces */
328 else if (options.interactive && (key == '0' || key == '1' || key == '2' || key == '3')) {
348 options.session->scene->integrator->max_bounce = bounce;
350 /* Update and Reset */
351 options.session->scene->integrator->need_update = true;
353 options.session->reset(session_buffer_params(), options.session_params.samples);
358 static int files_parse(int argc, const char *argv[])
361 options.filepath = argv[0];
366 static void options_parse(int argc, const char **argv)
370 options.filepath = "";
371 options.session = NULL;
372 options.quiet = false;
375 string device_names = "";
376 string devicename = "CPU";
379 /* List devices for which support is compiled in. */
380 vector<DeviceType> types = Device::available_types();
381 foreach (DeviceType type, types) {
382 if (device_names != "")
383 device_names += ", ";
385 device_names += Device::string_from_type(type);
389 string ssname = "svm";
393 bool help = false, debug = false, version = false;
396 ap.options("Usage: cycles [options] file.xml",
402 ("Devices to use: " + device_names).c_str(),
406 "Shading system to use: svm, osl",
409 &options.session_params.background,
410 "Render in background, without user interface",
413 "In background mode, don't print progress messages",
415 &options.session_params.samples,
416 "Number of samples to render",
418 &options.output_path,
419 "File path to write output image",
421 &options.session_params.threads,
422 "CPU Rendering Threads",
425 "Window width in pixel",
428 "Window height in pixel",
430 &options.session_params.tile_size.x,
431 "Tile width in pixels",
433 &options.session_params.tile_size.y,
434 "Tile height in pixels",
437 "List information about all available devices",
438 #ifdef WITH_CYCLES_LOGGING
441 "Enable debug logging",
444 "Set verbosity of the logger",
448 "Print help message",
451 "Print version number",
454 if (ap.parse(argc, argv) < 0) {
455 fprintf(stderr, "%s\n", ap.geterror().c_str());
461 util_logging_start();
462 util_logging_verbosity_set(verbosity);
466 vector<DeviceInfo> devices = Device::available_devices();
467 printf("Devices:\n");
469 foreach (DeviceInfo &info, devices) {
470 printf(" %-10s%s%s\n",
471 Device::string_from_type(info.type).c_str(),
472 info.description.c_str(),
473 (info.display_device) ? " (display)" : "");
479 printf("%s\n", CYCLES_VERSION_STRING);
482 else if (help || options.filepath == "") {
488 options.scene_params.shadingsystem = SHADINGSYSTEM_OSL;
489 else if (ssname == "svm")
490 options.scene_params.shadingsystem = SHADINGSYSTEM_SVM;
492 #ifndef WITH_CYCLES_STANDALONE_GUI
493 options.session_params.background = true;
496 /* Use progressive rendering */
497 options.session_params.progressive = true;
499 /* find matching device */
500 DeviceType device_type = Device::type_from_string(devicename.c_str());
501 vector<DeviceInfo> devices = Device::available_devices(DEVICE_MASK(device_type));
503 bool device_available = false;
504 if (!devices.empty()) {
505 options.session_params.device = devices.front();
506 device_available = true;
509 /* handle invalid configurations */
510 if (options.session_params.device.type == DEVICE_NONE || !device_available) {
511 fprintf(stderr, "Unknown device: %s\n", devicename.c_str());
515 else if (!(ssname == "osl" || ssname == "svm")) {
516 fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str());
519 else if (options.scene_params.shadingsystem == SHADINGSYSTEM_OSL &&
520 options.session_params.device.type != DEVICE_CPU) {
521 fprintf(stderr, "OSL shading system only works with CPU device\n");
525 else if (options.session_params.samples < 0) {
526 fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples);
529 else if (options.filepath == "") {
530 fprintf(stderr, "No file path specified\n");
534 /* For smoother Viewport */
535 options.session_params.start_resolution = 64;
542 int main(int argc, const char **argv)
544 util_logging_init(argv[0]);
546 options_parse(argc, argv);
548 #ifdef WITH_CYCLES_STANDALONE_GUI
549 if (options.session_params.background) {
552 options.session->wait();
554 #ifdef WITH_CYCLES_STANDALONE_GUI
557 string title = "Cycles: " + path_filename(options.filepath);
559 /* init/exit are callback so they run while GL is initialized */
560 view_main_loop(title.c_str(),