/* * Copyright 2011-2013 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "device/device.h" #include "render/buffers.h" #include "render/camera.h" #include "render/integrator.h" #include "render/scene.h" #include "render/session.h" #include "util/util_args.h" #include "util/util_foreach.h" #include "util/util_function.h" #include "util/util_image.h" #include "util/util_logging.h" #include "util/util_path.h" #include "util/util_progress.h" #include "util/util_string.h" #include "util/util_time.h" #include "util/util_transform.h" #include "util/util_unique_ptr.h" #include "util/util_version.h" #ifdef WITH_CYCLES_STANDALONE_GUI # include "util/util_view.h" #endif #include "app/cycles_xml.h" CCL_NAMESPACE_BEGIN struct Options { Session *session; Scene *scene; string filepath; int width, height; SceneParams scene_params; SessionParams session_params; bool quiet; bool show_help, interactive, pause; string output_path; } options; static void session_print(const string &str) { /* print with carriage return to overwrite previous */ printf("\r%s", str.c_str()); /* add spaces to overwrite longer previous print */ static int maxlen = 0; int len = str.size(); maxlen = max(len, maxlen); for (int i = len; i < maxlen; i++) printf(" "); /* flush because we don't write an end of line */ fflush(stdout); } static void session_print_status() { string status, substatus; /* get status */ float progress = options.session->progress.get_progress(); options.session->progress.get_status(status, substatus); if (substatus != "") status += ": " + substatus; /* print status */ status = string_printf("Progress %05.2f %s", (double)progress * 100, status.c_str()); session_print(status); } static bool write_render(const uchar *pixels, int w, int h, int channels) { string msg = string_printf("Writing image %s", options.output_path.c_str()); session_print(msg); unique_ptr out = unique_ptr(ImageOutput::create(options.output_path)); if (!out) { return false; } ImageSpec spec(w, h, channels, TypeDesc::UINT8); if (!out->open(options.output_path, spec)) { return false; } /* conversion for different top/bottom convention */ out->write_image( TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride); out->close(); return true; } static BufferParams &session_buffer_params() { static BufferParams buffer_params; buffer_params.width = options.width; buffer_params.height = options.height; buffer_params.full_width = options.width; buffer_params.full_height = options.height; return buffer_params; } static void scene_init() { options.scene = new Scene(options.scene_params, options.session->device); /* Read XML */ xml_read_file(options.scene, options.filepath.c_str()); /* Camera width/height override? */ if (!(options.width == 0 || options.height == 0)) { options.scene->camera->width = options.width; options.scene->camera->height = options.height; } else { options.width = options.scene->camera->width; options.height = options.scene->camera->height; } /* Calculate Viewplane */ options.scene->camera->compute_auto_viewplane(); } static void session_init() { options.session_params.write_render_cb = write_render; options.session = new Session(options.session_params); if (options.session_params.denoising.type == ccl::DENOISER_OPENIMAGEDENOISE || options.session_params.denoising.type == ccl::DENOISER_OPTIX || options.session_params.denoising.type == ccl::DENOISER_NLM) { options.session_params.denoising.store_passes = true; options.session_params.denoising.input_passes = ccl::DENOISER_INPUT_RGB_ALBEDO_NORMAL; } if (options.session_params.background && !options.quiet) options.session->progress.set_update_callback(function_bind(&session_print_status)); #ifdef WITH_CYCLES_STANDALONE_GUI else options.session->progress.set_update_callback(function_bind(&view_redraw)); #endif /* load scene */ scene_init(); options.session->scene = options.scene; options.session->reset(session_buffer_params(), options.session_params.samples); options.session->start(); } static void session_exit() { if (options.session) { delete options.session; options.session = NULL; } if (options.session_params.background && !options.quiet) { session_print("Finished Rendering."); printf("\n"); } } #ifdef WITH_CYCLES_STANDALONE_GUI static void display_info(Progress &progress) { static double latency = 0.0; static double last = 0; double elapsed = time_dt(); string str, interactive; latency = (elapsed - last); last = elapsed; double total_time, sample_time; string status, substatus; progress.get_time(total_time, sample_time); progress.get_status(status, substatus); float progress_val = progress.get_progress(); if (substatus != "") status += ": " + substatus; interactive = options.interactive ? "On" : "Off"; str = string_printf( "%s" " Time: %.2f" " Latency: %.4f" " Progress: %05.2f" " Average: %.4f" " Interactive: %s", status.c_str(), total_time, latency, (double)progress_val * 100, sample_time, interactive.c_str()); view_display_info(str.c_str()); if (options.show_help) view_display_help(); } static void display() { static DeviceDrawParams draw_params = DeviceDrawParams(); options.session->draw(session_buffer_params(), draw_params); display_info(options.session->progress); } static void motion(int x, int y, int button) { if (options.interactive) { Transform matrix = options.session->scene->camera->matrix; /* Translate */ if (button == 0) { float3 translate = make_float3(x * 0.01f, -(y * 0.01f), 0.0f); matrix = matrix * transform_translate(translate); } /* Rotate */ else if (button == 2) { float4 r1 = make_float4((float)x * 0.1f, 0.0f, 1.0f, 0.0f); matrix = matrix * transform_rotate(DEG2RADF(r1.x), make_float3(r1.y, r1.z, r1.w)); float4 r2 = make_float4(y * 0.1f, 1.0f, 0.0f, 0.0f); matrix = matrix * transform_rotate(DEG2RADF(r2.x), make_float3(r2.y, r2.z, r2.w)); } /* Update and Reset */ options.session->scene->camera->matrix = matrix; options.session->scene->camera->need_update = true; options.session->scene->camera->need_device_update = true; options.session->reset(session_buffer_params(), options.session_params.samples); } } static void resize(int width, int height) { options.width = width; options.height = height; if (options.session) { /* Update camera */ options.session->scene->camera->width = width; options.session->scene->camera->height = height; options.session->scene->camera->compute_auto_viewplane(); options.session->scene->camera->need_update = true; options.session->scene->camera->need_device_update = true; options.session->reset(session_buffer_params(), options.session_params.samples); } } static void keyboard(unsigned char key) { /* Toggle help */ if (key == 'h') options.show_help = !(options.show_help); /* Reset */ else if (key == 'r') options.session->reset(session_buffer_params(), options.session_params.samples); /* Cancel */ else if (key == 27) // escape options.session->progress.set_cancel("Canceled"); /* Pause */ else if (key == 'p') { options.pause = !options.pause; options.session->set_pause(options.pause); } /* Interactive Mode */ else if (key == 'i') options.interactive = !(options.interactive); /* Navigation */ else if (options.interactive && (key == 'w' || key == 'a' || key == 's' || key == 'd')) { Transform matrix = options.session->scene->camera->matrix; float3 translate; if (key == 'w') translate = make_float3(0.0f, 0.0f, 0.1f); else if (key == 's') translate = make_float3(0.0f, 0.0f, -0.1f); else if (key == 'a') translate = make_float3(-0.1f, 0.0f, 0.0f); else if (key == 'd') translate = make_float3(0.1f, 0.0f, 0.0f); matrix = matrix * transform_translate(translate); /* Update and Reset */ options.session->scene->camera->matrix = matrix; options.session->scene->camera->need_update = true; options.session->scene->camera->need_device_update = true; options.session->reset(session_buffer_params(), options.session_params.samples); } /* Set Max Bounces */ else if (options.interactive && (key == '0' || key == '1' || key == '2' || key == '3')) { int bounce; switch (key) { case '0': bounce = 0; break; case '1': bounce = 1; break; case '2': bounce = 2; break; case '3': bounce = 3; break; default: bounce = 0; break; } options.session->scene->integrator->max_bounce = bounce; /* Update and Reset */ options.session->scene->integrator->need_update = true; options.session->reset(session_buffer_params(), options.session_params.samples); } } #endif static int files_parse(int argc, const char *argv[]) { if (argc > 0) options.filepath = argv[0]; return 0; } static void options_parse(int argc, const char **argv) { options.width = 0; options.height = 0; options.filepath = ""; options.session = NULL; options.quiet = false; /* device names */ string device_names = ""; string devicename = "CPU"; bool list = false; /* List devices for which support is compiled in. */ vector types = Device::available_types(); foreach (DeviceType type, types) { if (device_names != "") device_names += ", "; device_names += Device::string_from_type(type); } /* shading system */ string ssname = "svm"; /* parse options */ ArgParse ap; bool help = false, debug = false, version = false; string denoiser = ""; int verbosity = 1; ap.options("Usage: cycles [options] file.xml", "%*", files_parse, "", "--device %s", &devicename, ("Devices to use: " + device_names).c_str(), #ifdef WITH_OSL "--shadingsys %s", &ssname, "Shading system to use: svm, osl", #endif "--background", &options.session_params.background, "Render in background, without user interface", "--quiet", &options.quiet, "In background mode, don't print progress messages", "--samples %d", &options.session_params.samples, "Number of samples to render", "--output %s", &options.output_path, "File path to write output image", "--threads %d", &options.session_params.threads, "CPU Rendering Threads", "--width %d", &options.width, "Window width in pixel", "--height %d", &options.height, "Window height in pixel", "--tile-width %d", &options.session_params.tile_size.x, "Tile width in pixels", "--tile-height %d", &options.session_params.tile_size.y, "Tile height in pixels", "--list-devices", &list, "List information about all available devices", #ifdef WITH_CYCLES_LOGGING "--debug", &debug, "Enable debug logging", "--verbose %d", &verbosity, "Set verbosity of the logger", #endif "--help", &help, "Print help message", "--version", &version, "Print version number", "--denoiser %s", &denoiser, "Denoiser: NONE, NLM, OPTIX, INTEL", "--background", &options.session_params.background, "Render in background, without user interface", NULL); if (ap.parse(argc, argv) < 0) { fprintf(stderr, "%s\n", ap.geterror().c_str()); ap.usage(); exit(EXIT_FAILURE); } if (debug) { util_logging_start(); util_logging_verbosity_set(verbosity); } if (list) { vector devices = Device::available_devices(); printf("Devices:\n"); foreach (DeviceInfo &info, devices) { printf(" %-10s%s%s\n", Device::string_from_type(info.type).c_str(), info.description.c_str(), (info.display_device) ? " (display)" : ""); } exit(EXIT_SUCCESS); } else if (version) { printf("%s\n", CYCLES_VERSION_STRING); exit(EXIT_SUCCESS); } else if (help || options.filepath == "") { ap.usage(); exit(EXIT_SUCCESS); } if (ssname == "osl") options.scene_params.shadingsystem = SHADINGSYSTEM_OSL; else if (ssname == "svm") options.scene_params.shadingsystem = SHADINGSYSTEM_SVM; #ifndef WITH_CYCLES_STANDALONE_GUI options.session_params.background = true; #endif /* Use progressive rendering */ options.session_params.progressive = true; /* find matching device */ DeviceType device_type = Device::type_from_string(devicename.c_str()); vector devices = Device::available_devices(DEVICE_MASK(device_type)); bool device_available = false; if (!devices.empty()) { options.session_params.device = devices.front(); device_available = true; } /* handle invalid configurations */ if (options.session_params.device.type == DEVICE_NONE || !device_available) { fprintf(stderr, "Unknown device: %s\n", devicename.c_str()); exit(EXIT_FAILURE); } #ifdef WITH_OSL else if (!(ssname == "osl" || ssname == "svm")) { fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str()); exit(EXIT_FAILURE); } else if (options.scene_params.shadingsystem == SHADINGSYSTEM_OSL && options.session_params.device.type != DEVICE_CPU) { fprintf(stderr, "OSL shading system only works with CPU device\n"); exit(EXIT_FAILURE); } #endif else if (options.session_params.samples < 0) { fprintf(stderr, "Invalid number of samples: %d\n", options.session_params.samples); exit(EXIT_FAILURE); } else if (options.filepath == "") { fprintf(stderr, "No file path specified\n"); exit(EXIT_FAILURE); } if (denoiser == "INTEL") { printf("Using Intel Open Image Denoiser\n"); options.session_params.denoising.type = ccl::DENOISER_OPENIMAGEDENOISE; options.session_params.denoising.store_passes = true; options.session_params.denoising.input_passes = ccl::DENOISER_INPUT_RGB_ALBEDO_NORMAL; options.session_params.denoising.clamp_input = false; options.session_params.denoising.strength = 1.0; options.session_params.denoising.use = true; } else if (denoiser == "OPTIX") { printf("Using NVidia Optix Denoiser\n"); options.session_params.denoising.type = ccl::DENOISER_OPTIX; options.session_params.denoising.store_passes = true; options.session_params.denoising.input_passes = ccl::DENOISER_INPUT_RGB_ALBEDO_NORMAL; options.session_params.denoising.clamp_input = false; options.session_params.denoising.strength = 1.0; options.session_params.denoising.use = true; } else if (denoiser == "NLM") { printf("Using Cycles NLM Denoiser\n"); options.session_params.denoising.type = ccl::DENOISER_NLM; options.session_params.denoising.store_passes = true; options.session_params.denoising.input_passes = ccl::DENOISER_INPUT_RGB_ALBEDO_NORMAL; options.session_params.denoising.clamp_input = false; options.session_params.denoising.strength = 1.0; options.session_params.denoising.use = true; } /* For smoother Viewport */ options.session_params.start_resolution = 64; } CCL_NAMESPACE_END using namespace ccl; int main(int argc, const char **argv) { util_logging_init(argv[0]); path_init(); options_parse(argc, argv); #ifdef WITH_CYCLES_STANDALONE_GUI if (options.session_params.background) { #endif session_init(); options.session->wait(); session_exit(); #ifdef WITH_CYCLES_STANDALONE_GUI } else { string title = "Cycles: " + path_filename(options.filepath); /* init/exit are callback so they run while GL is initialized */ view_main_loop(title.c_str(), options.width, options.height, session_init, session_exit, resize, display, keyboard, motion); } #endif return 0; }