diff options
author | Ton Roosendaal <ton@blender.org> | 2011-04-27 15:58:34 +0400 |
---|---|---|
committer | Ton Roosendaal <ton@blender.org> | 2011-04-27 15:58:34 +0400 |
commit | da376e0237517543aa21740ee2363234ee1c20ae (patch) | |
tree | 014a513ed8d0eccc5e54fef42347781e85bae56a /intern/cycles/app | |
parent | 693780074388111e7b9ef1c3825e462f398dc6c4 (diff) |
Cycles render engine, initial commit. This is the engine itself, blender modifications and build instructions will follow later.
Cycles uses code from some great open source projects, many thanks them:
* BVH building and traversal code from NVidia's "Understanding the Efficiency of Ray Traversal on GPUs":
http://code.google.com/p/understanding-the-efficiency-of-ray-traversal-on-gpus/
* Open Shading Language for a large part of the shading system:
http://code.google.com/p/openshadinglanguage/
* Blender for procedural textures and a few other nodes.
* Approximate Catmull Clark subdivision from NVidia Mesh tools:
http://code.google.com/p/nvidia-mesh-tools/
* Sobol direction vectors from:
http://web.maths.unsw.edu.au/~fkuo/sobol/
* Film response functions from:
http://www.cs.columbia.edu/CAVE/software/softlib/dorf.php
Diffstat (limited to 'intern/cycles/app')
-rw-r--r-- | intern/cycles/app/CMakeLists.txt | 48 | ||||
-rw-r--r-- | intern/cycles/app/cycles_server.cpp | 71 | ||||
-rw-r--r-- | intern/cycles/app/cycles_test.cpp | 306 | ||||
-rw-r--r-- | intern/cycles/app/cycles_xml.cpp | 936 | ||||
-rw-r--r-- | intern/cycles/app/cycles_xml.h | 31 |
5 files changed, 1392 insertions, 0 deletions
diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt new file mode 100644 index 00000000000..b1f73ed74a3 --- /dev/null +++ b/intern/cycles/app/CMakeLists.txt @@ -0,0 +1,48 @@ + +INCLUDE_DIRECTORIES(. ../device ../kernel ../kernel/svm ../bvh ../util ../render ../subd) + +SET(LIBRARIES + device + kernel + render + bvh + subd + util + ${Boost_LIBRARIES} + ${OPENGL_LIBRARIES} + ${GLEW_LIBRARIES} + ${OPENIMAGEIO_LIBRARY} + ${GLUT_LIBRARIES}) + +IF(WITH_OSL) + LIST(APPEND LIBRARIES kernel_osl ${OSL_LIBRARIES}) +ENDIF(WITH_OSL) + +IF(WITH_PARTIO) + LIST(APPEND LIBRARIES ${PARTIO_LIBRARIES}) +ENDIF(WITH_PARTIO) + +IF(WITH_OPENCL) + LIST(APPEND LIBRARIES ${OPENCL_LIBRARIES}) +ENDIF(WITH_OPENCL) + +ADD_EXECUTABLE(cycles_test cycles_test.cpp cycles_xml.cpp cycles_xml.h) +TARGET_LINK_LIBRARIES(cycles_test ${LIBRARIES}) +INSTALL(TARGETS cycles_test DESTINATION ${INSTALL_PATH}/cycles) + +IF(UNIX AND NOT APPLE) + SET_TARGET_PROPERTIES(cycles_test PROPERTIES INSTALL_RPATH $ORIGIN/lib) +ENDIF() + +IF(WITH_NETWORK) + ADD_EXECUTABLE(cycles_server cycles_server.cpp) + TARGET_LINK_LIBRARIES(cycles_server ${LIBRARIES}) + INSTALL(TARGETS cycles_server DESTINATION ${INSTALL_PATH}/cycles) + + IF(UNIX AND NOT APPLE) + SET_TARGET_PROPERTIES(cycles_server PROPERTIES INSTALL_RPATH $ORIGIN/lib) + ENDIF() +ENDIF() + +INSTALL(CODE "FILE(MAKE_DIRECTORY ${INSTALL_PATH}/cycles/cache)") + diff --git a/intern/cycles/app/cycles_server.cpp b/intern/cycles/app/cycles_server.cpp new file mode 100644 index 00000000000..bcf4d3ea769 --- /dev/null +++ b/intern/cycles/app/cycles_server.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2011, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> + +#include "device.h" + +#include "util_args.h" +#include "util_foreach.h" +#include "util_path.h" +#include "util_string.h" + +using namespace ccl; + +int main(int argc, const char **argv) +{ + path_init(); + + /* device types */ + string devices = ""; + string devicename = "cpu"; + + vector<DeviceType> types = Device::available_types(); + + foreach(DeviceType type, types) { + if(devices != "") + devices += ", "; + + devices += Device::string_from_type(type); + } + + /* parse options */ + ArgParse ap; + + ap.options ("Usage: cycles_server [options]", + "--device %s", &devicename, ("Devices to use: " + devices).c_str(), + NULL); + + if(ap.parse(argc, argv) < 0) { + fprintf(stderr, "%s\n", ap.error_message().c_str()); + ap.usage(); + exit(EXIT_FAILURE); + } + + DeviceType dtype = Device::type_from_string(devicename.c_str()); + + while(1) { + Device *device = Device::create(dtype); + printf("Cycles Server with device: %s\n", device->description().c_str()); + device->server_run(); + delete device; + } + + return 0; +} + diff --git a/intern/cycles/app/cycles_test.cpp b/intern/cycles/app/cycles_test.cpp new file mode 100644 index 00000000000..96072ac3da6 --- /dev/null +++ b/intern/cycles/app/cycles_test.cpp @@ -0,0 +1,306 @@ +/* + * Copyright 2011, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> + +#include "buffers.h" +#include "camera.h" +#include "device.h" +#include "scene.h" +#include "session.h" + +#include "util_args.h" +#include "util_foreach.h" +#include "util_function.h" +#include "util_path.h" +#include "util_progress.h" +#include "util_string.h" +#include "util_time.h" +#include "util_view.h" + +#include "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; +} 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() +{ + int pass; + double total_time, pass_time; + string status, substatus; + + /* get status */ + options.session->progress.get_pass(pass, total_time, pass_time); + options.session->progress.get_status(status, substatus); + + if(substatus != "") + status += ": " + substatus; + + /* print status */ + status = string_printf("Pass %d %s", pass, status.c_str()); + session_print(status); +} + +static void session_init() +{ + options.session = new Session(options.session_params); + options.session->reset(options.width, options.height); + options.session->scene = options.scene; + + if(options.session_params.background && !options.quiet) + options.session->progress.set_update_callback(function_bind(&session_print_status)); + else + options.session->progress.set_update_callback(function_bind(&view_redraw)); + + options.session->start(); + + options.scene = NULL; +} + +static void scene_init() +{ + options.scene = new Scene(options.scene_params); + xml_read_file(options.scene, options.filepath.c_str()); + options.width = options.scene->camera->width; + options.height = options.scene->camera->height; +} + +static void session_exit() +{ + if(options.session) { + delete options.session; + options.session = NULL; + } + if(options.scene) { + delete options.scene; + options.scene = NULL; + } + + if(options.session_params.background && !options.quiet) { + session_print("Finished Rendering."); + printf("\n"); + } +} + +static void display_info(Progress& progress) +{ + static double latency = 0.0; + static double last = 0; + double elapsed = time_dt(); + string str; + + latency = (elapsed - last); + last = elapsed; + + int pass; + double total_time, pass_time; + string status, substatus; + + progress.get_pass(pass, total_time, pass_time); + progress.get_status(status, substatus); + + if(substatus != "") + status += ": " + substatus; + + str = string_printf("latency: %.4f pass: %d total: %.4f average: %.4f %s", + latency, pass, total_time, pass_time, status.c_str()); + + view_display_info(str.c_str()); +} + +static void display() +{ + options.session->draw(options.width, options.height); + + display_info(options.session->progress); +} + +static void resize(int width, int height) +{ + options.width= width; + options.height= height; + + if(options.session) + options.session->reset(options.width, options.height); +} + +void keyboard(unsigned char key) +{ + if(key == 'r') + options.session->reset(options.width, options.height); + else if(key == 27) // escape + options.session->progress.set_cancel("Cancelled"); +} + +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 = path_get("../../../test/models/elephants.xml"); + options.session = NULL; + options.quiet = false; + + /* devices */ + string devices = ""; + string devicename = "cpu"; + + vector<DeviceType> types = Device::available_types(); + + foreach(DeviceType type, types) { + if(devices != "") + devices += ", "; + + devices += Device::string_from_type(type); + } + + /* shading system */ + string ssname = "svm"; + string shadingsystems = "Shading system to use: svm"; + +#ifdef WITH_OSL + shadingsystems += ", osl"; +#endif + + /* parse options */ + ArgParse ap; + bool help = false; + + ap.options ("Usage: cycles_test [options] file.xml", + "%*", files_parse, "", + "--device %s", &devicename, ("Devices to use: " + devices).c_str(), + "--shadingsys %s", &ssname, "Shading system to use: svm, osl", + "--background", &options.session_params.background, "Render in background, without user interface", + "--quiet", &options.quiet, "In background mode, don't print progress messages", + "--passes %d", &options.session_params.passes, "Number of passes to render", + "--output %s", &options.session_params.output_path, "File path to write output image", + "--help", &help, "Print help message", + NULL); + + if(ap.parse(argc, argv) < 0) { + fprintf(stderr, "%s\n", ap.error_message().c_str()); + ap.usage(); + exit(EXIT_FAILURE); + } + else if(help || options.filepath == "") { + ap.usage(); + exit(EXIT_SUCCESS); + } + + options.session_params.device_type = Device::type_from_string(devicename.c_str()); + + if(ssname == "osl") + options.scene_params.shadingsystem = SceneParams::OSL; + else if(ssname == "svm") + options.scene_params.shadingsystem = SceneParams::SVM; + + /* handle invalid configurations */ + bool type_available = false; + + foreach(DeviceType dtype, types) + if(options.session_params.device_type == dtype) + type_available = true; + + if(options.session_params.device_type == DEVICE_NONE || !type_available) { + fprintf(stderr, "Unknown device: %s\n", devicename.c_str()); + exit(EXIT_FAILURE); + } +#ifdef WITH_OSL + else if(!(ssname == "osl" || ssname == "svm")) { +#else + else if(!(ssname == "svm")) { +#endif + fprintf(stderr, "Unknown shading system: %s\n", ssname.c_str()); + exit(EXIT_FAILURE); + } + else if(options.scene_params.shadingsystem == SceneParams::OSL && options.session_params.device_type != DEVICE_CPU) { + fprintf(stderr, "OSL shading system only works with CPU device\n"); + exit(EXIT_FAILURE); + } + else if(options.session_params.passes < 0) { + fprintf(stderr, "Invalid number of passes: %d\n", options.session_params.passes); + exit(EXIT_FAILURE); + } + else if(options.filepath == "") { + fprintf(stderr, "No file path specified\n"); + exit(EXIT_FAILURE); + } + + /* load scene */ + scene_init(); +} + +CCL_NAMESPACE_END + +using namespace ccl; + +int main(int argc, const char **argv) +{ + path_init(); + + options_parse(argc, argv); + + if(options.session_params.background) { + session_init(); + options.session->wait(); + session_exit(); + } + 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); + } + + return 0; +} + diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp new file mode 100644 index 00000000000..0e80cd80ece --- /dev/null +++ b/intern/cycles/app/cycles_xml.cpp @@ -0,0 +1,936 @@ +/* + * Copyright 2011, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> + +#include <sstream> +#include <algorithm> +#include <iterator> + +#include "camera.h" +#include "film.h" +#include "graph.h" +#include "integrator.h" +#include "light.h" +#include "mesh.h" +#include "nodes.h" +#include "object.h" +#include "shader.h" +#include "scene.h" + +#include "subd_mesh.h" +#include "subd_patch.h" +#include "subd_split.h" + +#include "util_debug.h" +#include "util_foreach.h" +#include "util_path.h" +#include "util_transform.h" +#include "util_xml.h" + +#include "cycles_xml.h" + +CCL_NAMESPACE_BEGIN + +/* XML reading state */ + +struct XMLReadState { + Scene *scene; /* scene pointer */ + Transform tfm; /* current transform state */ + bool smooth; /* smooth normal state */ + int shader; /* current shader */ + string base; /* base path to current file*/ + float dicing_rate; /* current dicing rate */ + Mesh::DisplacementMethod displacement_method; +}; + +/* Attribute Reading */ + +static bool xml_read_bool(bool *value, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + *value = (string_iequals(attr.value(), "true")) || (atoi(attr.value()) != 0); + return true; + } + + return false; +} + +static bool xml_read_int(int *value, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + *value = atoi(attr.value()); + return true; + } + + return false; +} + +static bool xml_read_int_array(vector<int>& value, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + vector<string> tokens; + string_split(tokens, attr.value()); + + foreach(const string& token, tokens) + value.push_back(atoi(token.c_str())); + + return true; + } + + return false; +} + +static bool xml_read_float(float *value, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + *value = atof(attr.value()); + return true; + } + + return false; +} + +static bool xml_read_float_array(vector<float>& value, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + vector<string> tokens; + string_split(tokens, attr.value()); + + foreach(const string& token, tokens) + value.push_back(atof(token.c_str())); + + return true; + } + + return false; +} + +static bool xml_read_float3(float3 *value, pugi::xml_node node, const char *name) +{ + vector<float> array; + + if(xml_read_float_array(array, node, name) && array.size() == 3) { + *value = make_float3(array[0], array[1], array[2]); + return true; + } + + return false; +} + +static bool xml_read_float3_array(vector<float3>& value, pugi::xml_node node, const char *name) +{ + vector<float> array; + + if(xml_read_float_array(array, node, name)) { + for(size_t i = 0; i < array.size(); i += 3) + value.push_back(make_float3(array[i+0], array[i+1], array[i+2])); + + return true; + } + + return false; +} + +static bool xml_read_float4(float4 *value, pugi::xml_node node, const char *name) +{ + vector<float> array; + + if(xml_read_float_array(array, node, name) && array.size() == 4) { + *value = make_float4(array[0], array[1], array[2], array[3]); + return true; + } + + return false; +} + +static bool xml_read_string(string *str, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + *str = attr.value(); + return true; + } + + return false; +} + +static bool xml_read_ustring(ustring *str, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + *str = ustring(attr.value()); + return true; + } + + return false; +} + +static bool xml_equal_string(pugi::xml_node node, const char *name, const char *value) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) + return string_iequals(attr.value(), value); + + return false; +} + +static bool xml_read_enum(ustring *str, ShaderEnum& enm, pugi::xml_node node, const char *name) +{ + pugi::xml_attribute attr = node.attribute(name); + + if(attr) { + ustring ustr(attr.value()); + + if(enm.exists(ustr)) { + *str = ustr; + return true; + } + else + fprintf(stderr, "Unknown value \"%s\" for attribute \"%s\".\n", ustr.c_str(), name); + } + + return false; +} + +/* Film */ + +static void xml_read_film(const XMLReadState& state, pugi::xml_node node) +{ + Camera *cam = state.scene->camera; + + xml_read_int(&cam->width, node, "width"); + xml_read_int(&cam->height, node, "height"); + + float aspect = (float)cam->width/(float)cam->height; + + if(cam->width >= cam->height) { + cam->left = -aspect; + cam->right = aspect; + cam->bottom = -1.0f; + cam->top = 1.0f; + } + else { + cam->left = -1.0f; + cam->right = 1.0f; + cam->bottom = -1.0f/aspect; + cam->top = 1.0f/aspect; + } + + cam->need_update = true; + cam->update(); +} + +/* Integrator */ + +static void xml_read_integrator(const XMLReadState& state, pugi::xml_node node) +{ + Integrator *integrator = state.scene->integrator; + + xml_read_int(&integrator->minbounce, node, "min_bounce"); + xml_read_int(&integrator->maxbounce, node, "max_bounce"); + xml_read_bool(&integrator->no_caustics, node, "no_caustics"); + xml_read_float(&integrator->blur_caustics, node, "blur_caustics"); +} + +/* Camera */ + +static void xml_read_camera(const XMLReadState& state, pugi::xml_node node) +{ + Camera *cam = state.scene->camera; + + if(xml_read_float(&cam->fov, node, "fov")) + cam->fov *= M_PI/180.0f; + + xml_read_float(&cam->nearclip, node, "nearclip"); + xml_read_float(&cam->farclip, node, "farclip"); + xml_read_float(&cam->lensradius, node, "lensradius"); // 0.5*focallength/fstop + xml_read_float(&cam->focaldistance, node, "focaldistance"); + xml_read_float(&cam->shutteropen, node, "shutteropen"); + xml_read_float(&cam->shutterclose, node, "shutterclose"); + + if(xml_equal_string(node, "type", "orthographic")) + cam->ortho = true; + else if(xml_equal_string(node, "type", "perspective")) + cam->ortho = false; + + cam->matrix = state.tfm; + + cam->need_update = true; + cam->update(); +} + +/* Shader */ + +static string xml_socket_name(const char *name) +{ + string sname = name; + size_t i; + + while((i = sname.find(" ")) != string::npos) + sname.replace(i, 1, ""); + + return sname; +} + +static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pugi::xml_node graph_node) +{ + ShaderGraph *graph = new ShaderGraph(); + + map<string, ShaderNode*> nodemap; + + nodemap["output"] = graph->output(); + + for(pugi::xml_node node = graph_node.first_child(); node; node = node.next_sibling()) { + ShaderNode *snode = NULL; + + if(string_iequals(node.name(), "image_texture")) { + ImageTextureNode *img = new ImageTextureNode(); + + xml_read_string(&img->filename, node, "src"); + img->filename = path_join(state.base, img->filename); + + snode = img; + } + else if(string_iequals(node.name(), "environment_texture")) { + EnvironmentTextureNode *env = new EnvironmentTextureNode(); + + xml_read_string(&env->filename, node, "src"); + env->filename = path_join(state.base, env->filename); + + snode = env; + } + else if(string_iequals(node.name(), "sky_texture")) { + SkyTextureNode *sky = new SkyTextureNode(); + + xml_read_float3(&sky->sun_direction, node, "sun_direction"); + xml_read_float(&sky->turbidity, node, "turbidity"); + + snode = sky; + } + else if(string_iequals(node.name(), "noise_texture")) { + snode = new NoiseTextureNode(); + } + else if(string_iequals(node.name(), "blend_texture")) { + BlendTextureNode *blend = new BlendTextureNode(); + xml_read_enum(&blend->progression, BlendTextureNode::progression_enum, node, "progression"); + xml_read_enum(&blend->axis, BlendTextureNode::axis_enum, node, "axis"); + snode = blend; + } + else if(string_iequals(node.name(), "clouds_texture")) { + CloudsTextureNode *clouds = new CloudsTextureNode(); + xml_read_bool(&clouds->hard, node, "hard"); + xml_read_int(&clouds->depth, node, "depth"); + xml_read_enum(&clouds->basis, CloudsTextureNode::basis_enum, node, "basis"); + snode = clouds; + } + else if(string_iequals(node.name(), "voronoi_texture")) { + VoronoiTextureNode *voronoi = new VoronoiTextureNode(); + xml_read_enum(&voronoi->distance_metric, VoronoiTextureNode::distance_metric_enum, node, "distance_metric"); + xml_read_enum(&voronoi->coloring, VoronoiTextureNode::coloring_enum, node, "coloring"); + snode = voronoi; + } + else if(string_iequals(node.name(), "musgrave_texture")) { + MusgraveTextureNode *musgrave = new MusgraveTextureNode(); + xml_read_enum(&musgrave->type, MusgraveTextureNode::type_enum, node, "type"); + xml_read_enum(&musgrave->basis, MusgraveTextureNode::basis_enum, node, "basis"); + snode = musgrave; + } + else if(string_iequals(node.name(), "marble_texture")) { + MarbleTextureNode *marble = new MarbleTextureNode(); + xml_read_enum(&marble->type, MarbleTextureNode::type_enum, node, "type"); + xml_read_enum(&marble->wave, MarbleTextureNode::wave_enum, node, "wave"); + xml_read_enum(&marble->basis, MarbleTextureNode::basis_enum, node, "basis"); + xml_read_bool(&marble->hard, node, "hard"); + xml_read_int(&marble->depth, node, "depth"); + snode = marble; + } + else if(string_iequals(node.name(), "magic_texture")) { + MagicTextureNode *magic = new MagicTextureNode(); + xml_read_int(&magic->depth, node, "depth"); + snode = magic; + } + else if(string_iequals(node.name(), "stucci_texture")) { + StucciTextureNode *stucci = new StucciTextureNode(); + xml_read_enum(&stucci->type, StucciTextureNode::type_enum, node, "type"); + xml_read_enum(&stucci->basis, StucciTextureNode::basis_enum, node, "basis"); + xml_read_bool(&stucci->hard, node, "hard"); + snode = stucci; + } + else if(string_iequals(node.name(), "distorted_noise_texture")) { + DistortedNoiseTextureNode *dist = new DistortedNoiseTextureNode(); + xml_read_enum(&dist->basis, DistortedNoiseTextureNode::basis_enum, node, "basis"); + xml_read_enum(&dist->distortion_basis, DistortedNoiseTextureNode::basis_enum, node, "distortion_basis"); + snode = dist; + } + else if(string_iequals(node.name(), "wood_texture")) { + WoodTextureNode *wood = new WoodTextureNode(); + xml_read_enum(&wood->type, WoodTextureNode::type_enum, node, "type"); + xml_read_enum(&wood->wave, WoodTextureNode::wave_enum, node, "wave"); + xml_read_enum(&wood->basis, WoodTextureNode::basis_enum, node, "basis"); + xml_read_bool(&wood->hard, node, "hard"); + snode = wood; + } + else if(string_iequals(node.name(), "mapping")) { + snode = new MappingNode(); + } + else if(string_iequals(node.name(), "ward_bsdf")) { + snode = new WardBsdfNode(); + } + else if(string_iequals(node.name(), "diffuse_bsdf")) { + snode = new DiffuseBsdfNode(); + } + else if(string_iequals(node.name(), "translucent_bsdf")) { + snode = new TranslucentBsdfNode(); + } + else if(string_iequals(node.name(), "transparent_bsdf")) { + snode = new TransparentBsdfNode(); + } + else if(string_iequals(node.name(), "velvet_bsdf")) { + snode = new VelvetBsdfNode(); + } + else if(string_iequals(node.name(), "glossy_bsdf")) { + GlossyBsdfNode *glossy = new GlossyBsdfNode(); + xml_read_enum(&glossy->distribution, GlossyBsdfNode::distribution_enum, node, "distribution"); + snode = glossy; + } + else if(string_iequals(node.name(), "glass_bsdf")) { + GlassBsdfNode *diel = new GlassBsdfNode(); + xml_read_enum(&diel->distribution, GlassBsdfNode::distribution_enum, node, "distribution"); + snode = diel; + } + else if(string_iequals(node.name(), "emission")) { + EmissionNode *emission = new EmissionNode(); + xml_read_bool(&emission->total_power, node, "total_power"); + snode = emission; + } + else if(string_iequals(node.name(), "background")) { + snode = new BackgroundNode(); + } + else if(string_iequals(node.name(), "geometry")) { + snode = new GeometryNode(); + } + else if(string_iequals(node.name(), "texture_coordinate")) { + snode = new TextureCoordinateNode(); + } + else if(string_iequals(node.name(), "lightPath")) { + snode = new LightPathNode(); + } + else if(string_iequals(node.name(), "value")) { + ValueNode *value = new ValueNode(); + xml_read_float(&value->value, node, "value"); + snode = value; + } + else if(string_iequals(node.name(), "color")) { + ColorNode *color = new ColorNode(); + xml_read_float3(&color->value, node, "value"); + snode = color; + } + else if(string_iequals(node.name(), "mix_closure")) { + snode = new MixClosureNode(); + } + else if(string_iequals(node.name(), "add_closure")) { + snode = new AddClosureNode(); + } + else if(string_iequals(node.name(), "mix")) { + MixNode *mix = new MixNode(); + xml_read_enum(&mix->type, MixNode::type_enum, node, "type"); + snode = mix; + } + else if(string_iequals(node.name(), "attribute")) { + AttributeNode *attr = new AttributeNode(); + xml_read_ustring(&attr->attribute, node, "attribute"); + snode = attr; + } + else if(string_iequals(node.name(), "fresnel")) { + snode = new FresnelNode(); + } + else if(string_iequals(node.name(), "math")) { + MathNode *math = new MathNode(); + xml_read_enum(&math->type, MathNode::type_enum, node, "type"); + snode = math; + } + else if(string_iequals(node.name(), "vector_math")) { + VectorMathNode *vmath = new VectorMathNode(); + xml_read_enum(&vmath->type, VectorMathNode::type_enum, node, "type"); + snode = vmath; + } + else if(string_iequals(node.name(), "connect")) { + /* connect nodes */ + vector<string> from_tokens, to_tokens; + + string_split(from_tokens, node.attribute("from").value()); + string_split(to_tokens, node.attribute("to").value()); + + if(from_tokens.size() == 2 && to_tokens.size() == 2) { + /* find nodes and sockets */ + ShaderOutput *output = NULL; + ShaderInput *input = NULL; + + if(nodemap.find(from_tokens[0]) != nodemap.end()) { + ShaderNode *fromnode = nodemap[from_tokens[0]]; + + foreach(ShaderOutput *out, fromnode->outputs) + if(string_iequals(xml_socket_name(out->name), from_tokens[1])) + output = out; + + if(!output) + fprintf(stderr, "Unknown output socket name \"%s\" on \"%s\".\n", from_tokens[1].c_str(), from_tokens[0].c_str()); + } + else + fprintf(stderr, "Unknown shader node name \"%s\".\n", from_tokens[0].c_str()); + + if(nodemap.find(to_tokens[0]) != nodemap.end()) { + ShaderNode *tonode = nodemap[to_tokens[0]]; + + foreach(ShaderInput *in, tonode->inputs) + if(string_iequals(xml_socket_name(in->name), to_tokens[1])) + input = in; + + if(!input) + fprintf(stderr, "Unknown input socket name \"%s\" on \"%s\".\n", to_tokens[1].c_str(), to_tokens[0].c_str()); + } + else + fprintf(stderr, "Unknown shader node name \"%s\".\n", to_tokens[0].c_str()); + + /* connect */ + if(output && input) + graph->connect(output, input); + } + else + fprintf(stderr, "Invalid from or to value for connect node.\n"); + } + else + fprintf(stderr, "Unknown shader node \"%s\".\n", node.name()); + + if(snode) { + /* add to graph */ + graph->add(snode); + + /* add to map for name lookups */ + string name = ""; + xml_read_string(&name, node, "name"); + + nodemap[name] = snode; + + /* read input values */ + for(pugi::xml_attribute attr = node.first_attribute(); attr; attr = attr.next_attribute()) { + foreach(ShaderInput *in, snode->inputs) { + if(string_iequals(in->name, attr.name())) { + switch(in->type) { + case SHADER_SOCKET_FLOAT: + xml_read_float(&in->value.x, node, attr.name()); + break; + case SHADER_SOCKET_COLOR: + case SHADER_SOCKET_VECTOR: + case SHADER_SOCKET_POINT: + case SHADER_SOCKET_NORMAL: + xml_read_float3(&in->value, node, attr.name()); + break; + default: + break; + } + } + } + } + } + } + + shader->set_graph(graph); + shader->tag_update(state.scene); +} + +static void xml_read_shader(const XMLReadState& state, pugi::xml_node node) +{ + Shader *shader = new Shader(); + xml_read_string(&shader->name, node, "name"); + xml_read_shader_graph(state, shader, node); + state.scene->shaders.push_back(shader); +} + +/* Background */ + +static void xml_read_background(const XMLReadState& state, pugi::xml_node node) +{ + Shader *shader = state.scene->shaders[state.scene->default_background]; + + xml_read_shader_graph(state, shader, node); +} + +/* Mesh */ + +static Mesh *xml_add_mesh(Scene *scene, const Transform& tfm) +{ + /* create mesh */ + Mesh *mesh = new Mesh(); + scene->meshes.push_back(mesh); + + /* create object*/ + Object *object = new Object(); + object->mesh = mesh; + object->tfm = tfm; + scene->objects.push_back(object); + + return mesh; +} + +static void xml_read_mesh(const XMLReadState& state, pugi::xml_node node) +{ + /* add mesh */ + Mesh *mesh = xml_add_mesh(state.scene, state.tfm); + mesh->used_shaders.push_back(state.shader); + + /* read state */ + int shader = state.shader; + bool smooth = state.smooth; + + mesh->displacement_method = state.displacement_method; + + /* read vertices and polygons, RIB style */ + vector<float3> P; + vector<int> verts, nverts; + + xml_read_float3_array(P, node, "P"); + xml_read_int_array(verts, node, "verts"); + xml_read_int_array(nverts, node, "nverts"); + + if(xml_equal_string(node, "subdivision", "catmull-clark")) { + /* create subd mesh */ + SubdMesh sdmesh; + + /* create subd vertices */ + for(size_t i = 0; i < P.size(); i++) + sdmesh.add_vert(P[i]); + + /* create subd faces */ + int index_offset = 0; + + for(size_t i = 0; i < nverts.size(); i++) { + if(nverts[i] == 4) { + int v0 = verts[index_offset + 0]; + int v1 = verts[index_offset + 1]; + int v2 = verts[index_offset + 2]; + int v3 = verts[index_offset + 3]; + + sdmesh.add_face(v0, v1, v2, v3); + } + else { + for(int j = 0; j < nverts[i]-2; j++) { + int v0 = verts[index_offset]; + int v1 = verts[index_offset + j + 1]; + int v2 = verts[index_offset + j + 2];; + + sdmesh.add_face(v0, v1, v2); + } + } + + index_offset += nverts[i]; + } + + /* finalize subd mesh */ + sdmesh.link_boundary(); + + /* subdivide */ + DiagSplit dsplit; + //dsplit.camera = state.scene->camera; + //dsplit.dicing_rate = 5.0f; + dsplit.dicing_rate = state.dicing_rate; + xml_read_float(&dsplit.dicing_rate, node, "dicing_rate"); + sdmesh.tesselate(&dsplit, false, mesh, shader, smooth); + } + else { + /* create vertices */ + mesh->verts = P; + + /* create triangles */ + int index_offset = 0; + + for(size_t i = 0; i < nverts.size(); i++) { + for(int j = 0; j < nverts[i]-2; j++) { + int v0 = verts[index_offset]; + int v1 = verts[index_offset + j + 1]; + int v2 = verts[index_offset + j + 2]; + + assert(v0 < (int)P.size()); + assert(v1 < (int)P.size()); + assert(v2 < (int)P.size()); + + mesh->add_triangle(v0, v1, v2, shader, smooth); + } + + index_offset += nverts[i]; + } + } + + /* temporary for test compatibility */ + mesh->attributes.remove(Attribute::STD_VERTEX_NORMAL); +} + +/* Patch */ + +static void xml_read_patch(const XMLReadState& state, pugi::xml_node node) +{ + /* read patch */ + Patch *patch = NULL; + + vector<float3> P; + xml_read_float3_array(P, node, "P"); + + if(xml_equal_string(node, "type", "bilinear")) { + /* bilinear patch */ + if(P.size() == 4) { + LinearQuadPatch *bpatch = new LinearQuadPatch(); + + for(int i = 0; i < 4; i++) + P[i] = transform(&state.tfm, P[i]); + memcpy(bpatch->hull, &P[0], sizeof(bpatch->hull)); + + patch = bpatch; + } + else + fprintf(stderr, "Invalid number of control points for bilinear patch.\n"); + } + else if(xml_equal_string(node, "type", "bicubic")) { + /* bicubic patch */ + if(P.size() == 16) { + BicubicPatch *bpatch = new BicubicPatch(); + + for(int i = 0; i < 16; i++) + P[i] = transform(&state.tfm, P[i]); + memcpy(bpatch->hull, &P[0], sizeof(bpatch->hull)); + + patch = bpatch; + } + else + fprintf(stderr, "Invalid number of control points for bicubic patch.\n"); + } + else + fprintf(stderr, "Unknown patch type.\n"); + + if(patch) { + /* add mesh */ + Mesh *mesh = xml_add_mesh(state.scene, transform_identity()); + + mesh->used_shaders.push_back(state.shader); + + /* split */ + DiagSplit dsplit; + //dsplit.camera = state.scene->camera; + //dsplit.dicing_rate = 5.0f; + dsplit.dicing_rate = state.dicing_rate; + xml_read_float(&dsplit.dicing_rate, node, "dicing_rate"); + dsplit.split_quad(mesh, patch, state.shader, state.smooth); + + delete patch; + + /* temporary for test compatibility */ + mesh->attributes.remove(Attribute::STD_VERTEX_NORMAL); + } +} + +/* Light */ + +static void xml_read_light(const XMLReadState& state, pugi::xml_node node) +{ + Light *light = new Light(); + light->shader = state.shader; + xml_read_float3(&light->co, node, "P"); + light->co = transform(&state.tfm, light->co); + + state.scene->lights.push_back(light); +} + +/* Transform */ + +static void xml_read_transform(pugi::xml_node node, Transform& tfm) +{ + if(node.attribute("matrix")) { + vector<float> matrix; + if(xml_read_float_array(matrix, node, "matrix") && matrix.size() == 16) + tfm = tfm * transform_transpose((*(Transform*)&matrix[0])); + } + + if(node.attribute("translate")) { + float3 translate = make_float3(0.0f, 0.0f, 0.0f); + xml_read_float3(&translate, node, "translate"); + tfm = tfm * transform_translate(translate); + } + + if(node.attribute("rotate")) { + float4 rotate = make_float4(0.0f, 0.0f, 0.0f, 0.0f); + xml_read_float4(&rotate, node, "rotate"); + tfm = tfm * transform_rotate(rotate.x*M_PI/180.0f, make_float3(rotate.y, rotate.z, rotate.w)); + } + + if(node.attribute("scale")) { + float3 scale = make_float3(0.0f, 0.0f, 0.0f); + xml_read_float3(&scale, node, "scale"); + tfm = tfm * transform_scale(scale); + } +} + +/* State */ + +static void xml_read_state(XMLReadState& state, pugi::xml_node node) +{ + /* read shader */ + string shadername; + + if(xml_read_string(&shadername, node, "shader")) { + int i = 0; + bool found = false; + + foreach(Shader *shader, state.scene->shaders) { + if(shader->name == shadername) { + state.shader = i; + found = true; + break; + } + + i++; + } + + if(!found) + fprintf(stderr, "Unknown shader \"%s\".\n", shadername.c_str()); + } + + xml_read_float(&state.dicing_rate, node, "dicing_rate"); + + /* read smooth/flat */ + if(xml_equal_string(node, "interpolation", "smooth")) + state.smooth = true; + else if(xml_equal_string(node, "interpolation", "flat")) + state.smooth = false; + + /* read displacement method */ + if(xml_equal_string(node, "displacement_method", "true")) + state.displacement_method = Mesh::DISPLACE_TRUE; + else if(xml_equal_string(node, "displacement_method", "bump")) + state.displacement_method = Mesh::DISPLACE_BUMP; + else if(xml_equal_string(node, "displacement_method", "both")) + state.displacement_method = Mesh::DISPLACE_BOTH; +} + +/* Scene */ + +static void xml_read_include(const XMLReadState& state, const string& src); + +static void xml_read_scene(const XMLReadState& state, pugi::xml_node scene_node) +{ + for(pugi::xml_node node = scene_node.first_child(); node; node = node.next_sibling()) { + if(string_iequals(node.name(), "film")) { + xml_read_film(state, node); + } + else if(string_iequals(node.name(), "integrator")) { + xml_read_integrator(state, node); + } + else if(string_iequals(node.name(), "camera")) { + xml_read_camera(state, node); + } + else if(string_iequals(node.name(), "shader")) { + xml_read_shader(state, node); + } + else if(string_iequals(node.name(), "background")) { + xml_read_background(state, node); + } + else if(string_iequals(node.name(), "mesh")) { + xml_read_mesh(state, node); + } + else if(string_iequals(node.name(), "patch")) { + xml_read_patch(state, node); + } + else if(string_iequals(node.name(), "light")) { + xml_read_light(state, node); + } + else if(string_iequals(node.name(), "transform")) { + XMLReadState substate = state; + + xml_read_transform(node, substate.tfm); + xml_read_scene(substate, node); + } + else if(string_iequals(node.name(), "state")) { + XMLReadState substate = state; + + xml_read_state(substate, node); + xml_read_scene(substate, node); + } + else if(string_iequals(node.name(), "include")) { + string src; + + if(xml_read_string(&src, node, "src")) + xml_read_include(state, src); + } + else + fprintf(stderr, "Unknown node \"%s\".\n", node.name()); + } +} + +/* Include */ + +static void xml_read_include(const XMLReadState& state, const string& src) +{ + /* open XML document */ + pugi::xml_document doc; + pugi::xml_parse_result parse_result; + + string path = path_join(state.base, src); + parse_result = doc.load_file(path.c_str()); + + if(parse_result) { + XMLReadState substate = state; + substate.base = path_dirname(path); + + xml_read_scene(substate, doc); + } + else + fprintf(stderr, "%s read error: %s\n", src.c_str(), parse_result.description()); +} + +/* File */ + +void xml_read_file(Scene *scene, const char *filepath) +{ + XMLReadState state; + + state.scene = scene; + state.tfm = transform_identity(); + state.shader = scene->default_surface; + state.smooth = false; + state.dicing_rate = 0.1f; + state.base = path_dirname(filepath); + + xml_read_include(state, path_filename(filepath)); + + scene->params.bvh_type = SceneParams::BVH_STATIC; +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/app/cycles_xml.h b/intern/cycles/app/cycles_xml.h new file mode 100644 index 00000000000..90ec52922e1 --- /dev/null +++ b/intern/cycles/app/cycles_xml.h @@ -0,0 +1,31 @@ +/* + * Copyright 2011, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __CYCLES_XML__ +#define __CYCLES_XML__ + +CCL_NAMESPACE_BEGIN + +class Scene; + +void xml_read_file(Scene *scene, const char *filepath); + +CCL_NAMESPACE_END + +#endif /* __CYCLES_XML__ */ + |