Long term, this should replace the XML format. This reuses the Hydra render delegate implementation, and so supports the same features. The same command line options and GUI work for both XML and USD also. The implementation of this is still disabled, waiting for some refactoring of USD library linking. However we want the Cycles code to be in sync between repositories for the 3.2 release. Ref T96731
565 lines
15 KiB
C++
565 lines
15 KiB
C++
/* SPDX-License-Identifier: Apache-2.0
|
|
* Copyright 2011-2022 Blender Foundation */
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "device/device.h"
|
|
#include "scene/camera.h"
|
|
#include "scene/integrator.h"
|
|
#include "scene/scene.h"
|
|
#include "session/buffers.h"
|
|
#include "session/session.h"
|
|
|
|
#include "util/args.h"
|
|
#include "util/foreach.h"
|
|
#include "util/function.h"
|
|
#include "util/image.h"
|
|
#include "util/log.h"
|
|
#include "util/path.h"
|
|
#include "util/progress.h"
|
|
#include "util/string.h"
|
|
#include "util/time.h"
|
|
#include "util/transform.h"
|
|
#include "util/unique_ptr.h"
|
|
#include "util/version.h"
|
|
|
|
#ifdef WITH_USD
|
|
# include "hydra/file_reader.h"
|
|
#endif
|
|
|
|
#include "app/cycles_xml.h"
|
|
#include "app/oiio_output_driver.h"
|
|
|
|
#ifdef WITH_CYCLES_STANDALONE_GUI
|
|
# include "opengl/display_driver.h"
|
|
# include "opengl/window.h"
|
|
#endif
|
|
|
|
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_filepath;
|
|
string output_pass;
|
|
} 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 */
|
|
double 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 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 = options.session->scene;
|
|
|
|
/* Read XML or USD */
|
|
#ifdef WITH_USD
|
|
if (!string_endswith(string_to_lower(options.filepath), ".xml")) {
|
|
HD_CYCLES_NS::HdCyclesFileReader::read(options.session, options.filepath.c_str());
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
xml_read_file(options.scene, options.filepath.c_str());
|
|
}
|
|
|
|
/* Camera width/height override? */
|
|
if (!(options.width == 0 || options.height == 0)) {
|
|
options.scene->camera->set_full_width(options.width);
|
|
options.scene->camera->set_full_height(options.height);
|
|
}
|
|
else {
|
|
options.width = options.scene->camera->get_full_width();
|
|
options.height = options.scene->camera->get_full_height();
|
|
}
|
|
|
|
/* Calculate Viewplane */
|
|
options.scene->camera->compute_auto_viewplane();
|
|
}
|
|
|
|
static void session_init()
|
|
{
|
|
options.output_pass = "combined";
|
|
options.session = new Session(options.session_params, options.scene_params);
|
|
|
|
#ifdef WITH_CYCLES_STANDALONE_GUI
|
|
if (!options.session_params.background) {
|
|
options.session->set_display_driver(make_unique<OpenGLDisplayDriver>(
|
|
window_opengl_context_enable, window_opengl_context_disable));
|
|
}
|
|
#endif
|
|
|
|
if (!options.output_filepath.empty()) {
|
|
options.session->set_output_driver(make_unique<OIIOOutputDriver>(
|
|
options.output_filepath, options.output_pass, session_print));
|
|
}
|
|
|
|
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(&window_redraw));
|
|
#endif
|
|
|
|
/* load scene */
|
|
scene_init();
|
|
|
|
/* add pass for output. */
|
|
Pass *pass = options.scene->create_node<Pass>();
|
|
pass->set_name(ustring(options.output_pass.c_str()));
|
|
pass->set_type(PASS_COMBINED);
|
|
|
|
options.session->reset(options.session_params, session_buffer_params());
|
|
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);
|
|
double 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());
|
|
|
|
window_display_info(str.c_str());
|
|
|
|
if (options.show_help)
|
|
window_display_help();
|
|
}
|
|
|
|
static void display()
|
|
{
|
|
options.session->draw();
|
|
|
|
display_info(options.session->progress);
|
|
}
|
|
|
|
static void motion(int x, int y, int button)
|
|
{
|
|
if (options.interactive) {
|
|
Transform matrix = options.session->scene->camera->get_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->set_matrix(matrix);
|
|
options.session->scene->camera->need_flags_update = true;
|
|
options.session->scene->camera->need_device_update = true;
|
|
|
|
options.session->reset(options.session_params, session_buffer_params());
|
|
}
|
|
}
|
|
|
|
static void resize(int width, int height)
|
|
{
|
|
options.width = width;
|
|
options.height = height;
|
|
|
|
if (options.session) {
|
|
/* Update camera */
|
|
options.session->scene->camera->set_full_width(options.width);
|
|
options.session->scene->camera->set_full_height(options.height);
|
|
options.session->scene->camera->compute_auto_viewplane();
|
|
options.session->scene->camera->need_flags_update = true;
|
|
options.session->scene->camera->need_device_update = true;
|
|
|
|
options.session->reset(options.session_params, session_buffer_params());
|
|
}
|
|
}
|
|
|
|
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(options.session_params, session_buffer_params());
|
|
|
|
/* 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->get_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->set_matrix(matrix);
|
|
options.session->scene->camera->need_flags_update = true;
|
|
options.session->scene->camera->need_device_update = true;
|
|
|
|
options.session->reset(options.session_params, session_buffer_params());
|
|
}
|
|
|
|
/* 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->set_max_bounce(bounce);
|
|
|
|
options.session->reset(options.session_params, session_buffer_params());
|
|
}
|
|
}
|
|
#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 = 1024;
|
|
options.height = 512;
|
|
options.filepath = "";
|
|
options.session = NULL;
|
|
options.quiet = false;
|
|
options.session_params.use_auto_tile = false;
|
|
options.session_params.tile_size = 0;
|
|
|
|
/* device names */
|
|
string device_names = "";
|
|
string devicename = "CPU";
|
|
bool list = false;
|
|
|
|
/* List devices for which support is compiled in. */
|
|
vector<DeviceType> 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, profile = false, debug = false, version = false;
|
|
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_filepath,
|
|
"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-size %d",
|
|
&options.session_params.tile_size,
|
|
"Tile size in pixels",
|
|
"--list-devices",
|
|
&list,
|
|
"List information about all available devices",
|
|
"--profile",
|
|
&profile,
|
|
"Enable profile logging",
|
|
#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",
|
|
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<DeviceInfo> 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);
|
|
}
|
|
|
|
options.session_params.use_profiling = profile;
|
|
|
|
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
|
|
|
|
if (options.session_params.tile_size > 0) {
|
|
options.session_params.use_auto_tile = true;
|
|
}
|
|
|
|
/* find matching device */
|
|
DeviceType device_type = Device::type_from_string(devicename.c_str());
|
|
vector<DeviceInfo> 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);
|
|
}
|
|
}
|
|
|
|
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 */
|
|
window_main_loop(title.c_str(),
|
|
options.width,
|
|
options.height,
|
|
session_init,
|
|
session_exit,
|
|
resize,
|
|
display,
|
|
keyboard,
|
|
motion);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|