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