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
This commit is contained in:
Ton Roosendaal 2011-04-27 11:58:34 +00:00
parent 6937800743
commit da376e0237
291 changed files with 66987 additions and 0 deletions

@ -0,0 +1,59 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(cycles)
SET(CYCLES_VERSION_MAJOR 0)
SET(CYCLES_VERSION_MINOR 0)
SET(CYCLES_VERSION ${CYCLES_VERSION_MAJOR}.${CYCLES_VERSION_MINOR})
# Options
OPTION(WITH_OSL "Build with Open Shading Language support" OFF)
OPTION(WITH_CUDA "Build with CUDA support" OFF)
OPTION(WITH_OPENCL "Build with OpenCL support (not working)" OFF)
OPTION(WITH_BLENDER "Build Blender Python extension" OFF)
OPTION(WITH_PARTIO "Build with Partio point cloud support (unfinished)" OFF)
OPTION(WITH_NETWORK "Build with network rendering support (unfinished)" OFF)
OPTION(WITH_MULTI "Build with network rendering support (unfinished)" OFF)
OPTION(WITH_DOCS "Build html documentation" OFF)
# Flags
SET(CUDA_ARCH sm_10 sm_11 sm_12 sm_13 sm_20 sm_21 CACHE STRING "CUDA architectures to build for")
SET(CUDA_MAXREG 24 CACHE STRING "CUDA maximum number of register to use")
# Paths
SET(OSL_PATH "" CACHE PATH "Path to OpenShadingLanguage installation")
SET(OIIO_PATH "" CACHE PATH "Path to OpenImageIO installation")
SET(BOOST_PATH "/usr" CACHE PATH "Path to Boost installation")
SET(CUDA_PATH "/usr/local/cuda" CACHE PATH "Path to CUDA installation")
SET(OPENCL_PATH "" CACHE PATH "Path to OpenCL installation")
SET(PYTHON_PATH "" CACHE PATH "Path to Python installation")
SET(BLENDER_PATH "" CACHE PATH "Path to Blender installation")
SET(PARTIO_PATH "" CACHE PATH "Path to Partio installation")
SET(GLEW_PATH "" CACHE PATH "Path to GLEW installation")
SET(GLUT_PATH "" CACHE PATH "Path to GLUT installation")
SET(INSTALL_PATH "${CMAKE_BINARY_DIR}/install" CACHE PATH "Path to install to")
# External Libraries
INCLUDE(cmake/external_libs.cmake)
# Platforms
INCLUDE(cmake/platforms.cmake)
# Subdirectories
IF(WITH_BLENDER)
ADD_SUBDIRECTORY(blender)
ENDIF(WITH_BLENDER)
ADD_SUBDIRECTORY(app)
ADD_SUBDIRECTORY(bvh)
ADD_SUBDIRECTORY(device)
ADD_SUBDIRECTORY(doc)
ADD_SUBDIRECTORY(kernel)
ADD_SUBDIRECTORY(render)
ADD_SUBDIRECTORY(subd)
ADD_SUBDIRECTORY(util)

@ -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)")

@ -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;
}

@ -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;
}

@ -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

@ -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__ */

@ -0,0 +1,72 @@
SET(sources
blender_camera.cpp
blender_mesh.cpp
blender_object.cpp
blender_python.cpp
blender_session.cpp
blender_shader.cpp
blender_sync.cpp)
SET(headers
blender_sync.h
blender_session.h
blender_util.h)
SET(addonfiles
addon/__init__.py
addon/engine.py
addon/enums.py
addon/properties.py
addon/ui.py
addon/xml.py)
INCLUDE_DIRECTORIES(
../render
../device
../kernel
../kernel/svm
../util
../subd
${BLENDER_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS})
SET(LIBRARIES
render
bvh
device
kernel
util
subd
${Boost_LIBRARIES}
${OPENGL_LIBRARIES}
${OPENIMAGEIO_LIBRARY}
${PYTHON_LIBRARIES}
${GLUT_LIBRARIES}
${GLEW_LIBRARIES}
${BLENDER_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)
SET(CMAKE_MODULE_LINKER_FLAGS ${PYTHON_MODULE_FLAGS})
ADD_LIBRARY(cycles_blender MODULE ${sources} ${headers})
TARGET_LINK_LIBRARIES(cycles_blender ${LIBRARIES})
INSTALL(FILES ${addonfiles} DESTINATION ${INSTALL_PATH}/cycles)
INSTALL(TARGETS cycles_blender LIBRARY DESTINATION ${INSTALL_PATH}/cycles)
IF(UNIX AND NOT APPLE)
SET_TARGET_PROPERTIES(cycles_blender PROPERTIES INSTALL_RPATH $ORIGIN/lib)
ENDIF()

@ -0,0 +1,73 @@
#
# 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.
#
bl_info = {
"name": "Cycles Render Engine",
"author": "",
"version": (0,0),
"blender": (2, 5, 6),
"api": 34462,
"location": "Info header, render engine menu",
"description": "Cycles Render Engine integration.",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Render"}
import bpy
from cycles import ui
from cycles import properties
from cycles import xml
from cycles import engine
class CyclesRender(bpy.types.RenderEngine):
bl_idname = 'CYCLES'
bl_label = "Cycles"
def __init__(self):
engine.init()
self.session = None
def __del__(self):
engine.free(self)
def render(self, scene):
engine.create(self, scene, True)
engine.render(self, scene)
def draw(self, scene):
if not self.session:
engine.create(self, scene, False)
engine.draw(self, scene)
def update(self, scene):
engine.update(self, scene)
def register():
properties.register()
ui.register()
xml.register()
bpy.utils.register_module(__name__)
def unregister():
xml.unregister()
ui.unregister()
properties.unregister()
bpy.utils.unregister_module(__name__)

@ -0,0 +1,107 @@
#
# 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.
#
import bpy
def init():
from cycles import libcycles_blender as lib
import os.path
lib.init(os.path.dirname(__file__))
def create(engine, scene, offline):
from cycles import libcycles_blender as lib
data = bpy.data.as_pointer()
scene = scene.as_pointer()
if not offline and bpy.context.area.type == 'VIEW_3D':
region = bpy.context.region.as_pointer()
v3d = bpy.context.space_data.as_pointer()
rv3d = bpy.context.region_data.as_pointer()
else:
region = 0
v3d = 0
rv3d = 0
engine.session = lib.create(engine.as_pointer(), data, scene, region, v3d, rv3d)
def free(engine):
if "session" in dir(engine):
if engine.session:
from cycles import libcycles_blender as lib
lib.free(engine.session)
del engine.session
def render(engine, scene):
from cycles import libcycles_blender as lib
lib.render(engine.session)
def update(engine, scene):
from cycles import libcycles_blender as lib
lib.sync(engine.session)
def draw(engine, scene):
from cycles import libcycles_blender as lib
v3d = bpy.context.space_data.as_pointer()
rv3d = bpy.context.region_data.as_pointer()
region = bpy.context.region
# draw render image
status, substatus = lib.draw(engine.session, v3d, rv3d)
# draw text over image
if status != "":
import blf
import bgl
fontid = 0 # todo, find out how to set this
dim = blf.dimensions(fontid, status)
dim_sub = blf.dimensions(fontid, substatus)
padding = 5
x = (region.width - max(dim[0], dim_sub[0]))*0.5 - padding
y = (region.height - (dim[1] + dim_sub[1] + padding))*0.5 - padding
bgl.glColor4f(0.0, 0.0, 0.0, 0.5)
bgl.glEnable(bgl.GL_BLEND)
bgl.glBlendFunc(bgl.GL_SRC_ALPHA, bgl.GL_ONE_MINUS_SRC_ALPHA)
bgl.glRectf(x, y, x+max(dim[0], dim_sub[0])+padding+padding, y+dim[1]+dim_sub[1]+padding+padding+2)
bgl.glDisable(bgl.GL_BLEND)
x = (region.width - dim[0])*0.5
y = (region.height - (dim[1] + dim_sub[1] + padding))*0.5 + dim_sub[1] + padding
bgl.glColor3f(0.8, 0.8, 0.8)
blf.position(fontid, x, y, 0)
blf.draw(fontid, status)
x = (region.width - dim_sub[0])*0.5
y = (region.height - (dim[1] + dim_sub[1] + padding))*0.5
bgl.glColor3f(0.6, 0.6, 0.6)
blf.position(fontid, x, y, 0)
blf.draw(fontid, substatus)
def available_devices():
from cycles import libcycles_blender as lib
return lib.available_devices()
def with_osl():
from cycles import libcycles_blender as lib
return lib.with_osl()

@ -0,0 +1,113 @@
#
# 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.
#
devices = (
("CPU", "CPU", "Processor"),
("GPU", "GPU", "Graphics card (NVidia only)"))
shading_systems = (
("GPU_COMPATIBLE", "GPU Compatible", "Restricted shading system compatible with GPU rendering"),
("OSL", "Open Shading Language", "Open Shading Language shading system that only runs on the CPU"))
displacement_methods = (
("BUMP", "Bump", "Bump mapping to simulate the appearance of displacement"),
("TRUE", "True", "Use true displacement only, requires fine subdivision"),
("BOTH", "Both", "Combination of displacement and bump mapping"))
bvh_types = (
("DYNAMIC_BVH", "Dynamic BVH", "Objects can be individually updated, at the cost of slower render time"),
("STATIC_BVH", "Static BVH", "Any object modification requires a complete BVH rebuild, but renders faster"))
response_curves = (
("None", "None", ""),
("", "Agfa", ""),
("Agfacolor Futura 100", "Futura 100", ""),
("Agfacolor Futura 200", "Futura 200", ""),
("Agfacolor Futura 400", "Futura 400", ""),
("Agfacolor Futura II 100", "Futura II 100", ""),
("Agfacolor Futura II 200", "Futura II 200", ""),
("Agfacolor Futura II 400", "Futura II 400", ""),
("Agfacolor HDC 100 plus", "HDC 100 plus", ""),
("Agfacolor HDC 400 plus", "HDC 400 plus", ""),
("Agfacolor HDC 200 plus", "HDC 200 plus", ""),
("Agfacolor Optima II 100", "Optima II 100", ""),
("Agfacolor Optima II 200", "Optima II 200", ""),
("Agfacolor Ultra 050", "Ultra 050", ""),
("", "Agfa", ""),
("Agfacolor Vista 100", "Vista 100", ""),
("Agfacolor Vista 200", "Vista 200", ""),
("Agfacolor Vista 400", "Vista 400", ""),
("Agfacolor Vista 800", "Vista 800", ""),
("Agfachrome CT Precisa 100", "CT Precisa 100", ""),
("Agfachrome CT Precisa 200", "CT Precisa 200", ""),
("Agfachrome RSX2 050", "Agfachrome RSX2 050", ""),
("Agfachrome RSX2 100", "Agfachrome RSX2 100", ""),
("Agfachrome RSX2 200", "Agfachrome RSX2 200", ""),
("Advantix 100", "Advantix 100", ""),
("Advantix 200", "Advantix 200", ""),
("Advantix 400", "Advantix 400", ""),
("", "Kodak", ""),
("Gold 100", "Gold 100", ""),
("Gold 200", "Gold 200", ""),
("Max Zoom 800", "Max Zoom 800", ""),
("Portra 100T", "Portra 100T", ""),
("Portra 160NC", "Portra 160NC", ""),
("Portra 160VC", "Portra 160VC", ""),
("Portra 800", "Portra 800", ""),
("Portra 400VC", "Portra 400VC", ""),
("Portra 400NC", "Portra 400NC", ""),
("", "Kodak", ""),
("Ektachrome 100 plus", "Ektachrome 100 plus", ""),
("Ektachrome 320T", "Ektachrome 320T", ""),
("Ektachrome 400X", "Ektachrome 400X", ""),
("Ektachrome 64", "Ektachrome 64", ""),
("Ektachrome 64T", "Ektachrome 64T", ""),
("Ektachrome E100S", "Ektachrome E100S", ""),
("Ektachrome 100", "Ektachrome 100", ""),
("Kodachrome 200", "Kodachrome 200", ""),
("Kodachrome 25", "Kodachrome 25", ""),
("Kodachrome 64", "Kodachrome 64", ""),
#("DSCS 3151", "DSCS 3151", ""),
#("DSCS 3152", "DSCS 3152", ""),
#("DSCS 3153", "DSCS 3153", ""),
#("DSCS 3154", "DSCS 3154", ""),
#("DSCS 3155", "DSCS 3155", ""),
#("DSCS 3156", "DSCS 3156", ""),
#("KAI-0311", "KAI-0311", ""),
#("KAF-2001", "KAF-2001", ""),
#("KAF-3000", "KAF-3000", ""),
#("KAI-0372", "KAI-0372", ""),
#("KAI-1010", "KAI-1010", ""),
("", "Fujifilm", ""),
("F-125", "F-125", ""),
("F-250", "F-250", ""),
("F-400", "F-400", ""),
("FCI", "FCI", ""),
("FP2900Z", "FP2900Z", ""),
("", "Eastman", ""),
("Double X Neg 12min", "Double X Neg 12min", ""),
("Double X Neg 6min", "Double X Neg 6min", ""),
("Double X Neg 5min", "Double X Neg 5min", ""),
("Double X Neg 4min", "Double X Neg 4min", ""),
("", "Canon", ""),
("Optura 981111", "Optura 981111", ""),
("Optura 981113", "Optura 981113", ""),
("Optura 981114", "Optura 981114", ""),
("Optura 981111.SLRR", "Optura 981111.SLRR", "")
)

@ -0,0 +1,120 @@
#
# 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.
#
import bpy
from bpy.props import *
from cycles import enums
class CyclesRenderSettings(bpy.types.PropertyGroup):
@classmethod
def register(cls):
bpy.types.Scene.cycles = PointerProperty(type=cls, name="Cycles Render Settings", description="Cycles Render Settings")
cls.device = EnumProperty(name="Device", description="Device to use for rendering",
items=enums.devices, default="CPU")
cls.shading_system = EnumProperty(name="Shading System", description="Shading system to use for rendering",
items=enums.shading_systems, default="GPU_COMPATIBLE")
cls.passes = IntProperty(name="Passes", description="Number of passes to render",
default=10, min=1, max=2147483647)
cls.min_bounces = IntProperty(name="Min Bounces", description="Minimum number of bounces",
default=3, min=0, max=1024)
cls.max_bounces = IntProperty(name="Max Bounces", description="Maximum number of bounces",
default=8, min=0, max=1024)
cls.no_caustics = BoolProperty(name="No Caustics", description="Leave out caustics, resulting in a darker image with less noise",
default=False)
cls.blur_caustics = FloatProperty(name="Blur Caustics", description="Blur caustics to reduce noise",
default=0.0, min=0.0, max=1.0)
cls.exposure = FloatProperty(name="Exposure", description="Image brightness scale",
default=1.0, min=0.0, max=10.0)
cls.response_curve = EnumProperty(name="Response Curve", description="Measured camera film response",
items=enums.response_curves, default="Advantix 400")
cls.debug_tile_size = IntProperty(name="Tile Size", description="",
default=1024, min=1, max=4096)
cls.debug_min_size = IntProperty(name="Min Size", description="",
default=64, min=1, max=4096)
cls.debug_reset_timeout = FloatProperty(name="Reset timeout", description="",
default=0.1, min=0.01, max=10.0)
cls.debug_cancel_timeout = FloatProperty(name="Cancel timeout", description="",
default=0.1, min=0.01, max=10.0)
cls.debug_text_timeout = FloatProperty(name="Text timeout", description="",
default=1.0, min=0.01, max=10.0)
cls.debug_bvh_type = EnumProperty(name="BVH Type", description="Choose between faster updates, or faster render",
items=enums.bvh_types, default="DYNAMIC_BVH")
cls.debug_use_spatial_splits = BoolProperty(name="Use Spatial Splits", description="Use BVH spatial splits: longer builder time, faster render",
default=False)
@classmethod
def unregister(cls):
del bpy.types.Scene.cycles
class CyclesCameraSettings(bpy.types.PropertyGroup):
@classmethod
def register(cls):
bpy.types.Camera.cycles = PointerProperty(type=cls, name="Cycles Camera Settings", description="Cycles Camera Settings")
cls.lens_radius = FloatProperty(name="Lens radius", description="Lens radius for depth of field",
default=0.0, min=0.0, max=10.0)
@classmethod
def unregister(cls):
del bpy.types.Camera.cycles
class CyclesMaterialSettings(bpy.types.PropertyGroup):
@classmethod
def register(cls):
bpy.types.Material.cycles = PointerProperty(type=cls, name="Cycles Material Settings", description="Cycles Material Settings")
@classmethod
def unregister(cls):
del bpy.types.Material.cycles
class CyclesMeshSettings(bpy.types.PropertyGroup):
@classmethod
def register(cls):
bpy.types.Mesh.cycles = PointerProperty(type=cls, name="Cycles Mesh Settings", description="Cycles Mesh Settings")
bpy.types.Curve.cycles = PointerProperty(type=cls, name="Cycles Mesh Settings", description="Cycles Mesh Settings")
bpy.types.MetaBall.cycles = PointerProperty(type=cls, name="Cycles Mesh Settings", description="Cycles Mesh Settings")
cls.displacement_method = EnumProperty(name="Displacement Method", description="Method to use for the displacement",
items=enums.displacement_methods, default="BUMP")
cls.use_subdivision = BoolProperty(name="Use Subdivision", description="Subdivide mesh for rendering",
default=False)
cls.dicing_rate = FloatProperty(name="Dicing Rate", description="", default=1.0, min=0.001, max=1000.0)
@classmethod
def unregister(cls):
del bpy.types.Mesh.cycles
def register():
bpy.utils.register_class(CyclesRenderSettings)
bpy.utils.register_class(CyclesCameraSettings)
bpy.utils.register_class(CyclesMaterialSettings)
bpy.utils.register_class(CyclesMeshSettings)
def unregister():
bpy.utils.unregister_class(CyclesRenderSettings)
bpy.utils.unregister_class(CyclesCameraSettings)
bpy.utils.unregister_class(CyclesMaterialSettings)
bpy.utils.unregister_class(CyclesMeshSettings)

@ -0,0 +1,388 @@
#
# 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.
#
import bpy
from cycles import enums
from cycles import engine
class CyclesButtonsPanel():
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
@classmethod
def poll(cls, context):
rd = context.scene.render
return rd.engine == 'CYCLES'
class CyclesRender_PT_integrator(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Integrator"
def draw(self, context):
layout = self.layout
scene = context.scene
cycles = scene.cycles
split = layout.split()
col = split.column()
col.prop(cycles, "passes")
col.prop(cycles, "no_caustics")
col = split.column()
col = col.column(align=True)
col.prop(cycles, "max_bounces")
col.prop(cycles, "min_bounces")
#row = col.row()
#row.prop(cycles, "blur_caustics")
#row.active = not cycles.no_caustics
class CyclesRender_PT_film(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Film"
def draw(self, context):
layout = self.layout
scene = context.scene
cycles = scene.cycles
split = layout.split()
split.prop(cycles, "exposure")
split.prop(cycles, "response_curve", text="")
class CyclesRender_PT_debug(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Debug"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
scene = context.scene
cycles = scene.cycles
split = layout.split()
col = split.column()
sub = col.column(align=True)
sub.prop(cycles, "debug_bvh_type", text="")
sub.prop(cycles, "debug_use_spatial_splits")
sub = col.column(align=True)
sub.prop(cycles, "debug_tile_size")
sub.prop(cycles, "debug_min_size")
col = split.column(align=True)
col.prop(cycles, "debug_cancel_timeout")
col.prop(cycles, "debug_reset_timeout")
col.prop(cycles, "debug_text_timeout")
class Cycles_PT_post_processing(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Post Processing"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
rd = context.scene.render
split = layout.split()
col = split.column()
col.prop(rd, "use_compositing")
col.prop(rd, "use_sequencer")
col = split.column()
col.prop(rd, "dither_intensity", text="Dither", slider=True)
class Cycles_PT_camera(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Cycles"
bl_context = "data"
@classmethod
def poll(cls, context):
return context.camera
def draw(self, context):
layout = self.layout
camera = context.camera
cycles = camera.cycles
layout.prop(cycles, "lens_radius")
class Cycles_PT_context_material(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Surface"
bl_context = "material"
bl_options = {'HIDE_HEADER'}
@classmethod
def poll(cls, context):
return (context.material or context.object) and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
mat = context.material
ob = context.object
slot = context.material_slot
space = context.space_data
if ob:
row = layout.row()
row.template_list(ob, "material_slots", ob, "active_material_index", rows=2)
col = row.column(align=True)
col.operator("object.material_slot_add", icon='ZOOMIN', text="")
col.operator("object.material_slot_remove", icon='ZOOMOUT', text="")
col.menu("MATERIAL_MT_specials", icon='DOWNARROW_HLT', text="")
if ob.mode == 'EDIT':
row = layout.row(align=True)
row.operator("object.material_slot_assign", text="Assign")
row.operator("object.material_slot_select", text="Select")
row.operator("object.material_slot_deselect", text="Deselect")
split = layout.split(percentage=0.65)
if ob:
split.template_ID(ob, "active_material", new="material.new")
row = split.row()
if slot:
row.prop(slot, "link", text="")
else:
row.label()
elif mat:
split.template_ID(space, "pin_id")
split.separator()
class Cycles_PT_mesh_displacement(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Displacement"
bl_context = "data"
@classmethod
def poll(cls, context):
return context.mesh or context.curve or context.meta_ball
def draw(self, context):
layout = self.layout
mesh = context.mesh
curve = context.curve
mball = context.meta_ball
if mesh:
cycles = mesh.cycles
elif curve:
cycles = curve.cycles
elif mball:
cycles = mball.cycles
layout.prop(cycles, "displacement_method", text="Method")
layout.prop(cycles, "use_subdivision");
layout.prop(cycles, "dicing_rate");
def find_node(material, nodetype):
if material and material.node_tree:
ntree = material.node_tree
for node in ntree.nodes:
if type(node) is not bpy.types.NodeGroup and node.type == nodetype:
return node
return None
def find_node_input(node, name):
for input in node.inputs:
if input.name == name:
return input
return None
def panel_node_draw(layout, id, output_type, input_name):
if not id.node_tree:
layout.prop(id, "use_nodes")
return
ntree = id.node_tree
node = find_node(id, output_type)
if not node:
layout.label(text="No output node.")
input = find_node_input(node, input_name)
layout.template_node_view(id, ntree, node, input);
class CyclesLamp_PT_lamp(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Surface"
bl_context = "data"
@classmethod
def poll(cls, context):
return context.lamp and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
mat = context.lamp
panel_node_draw(layout, mat, 'OUTPUT_LAMP', 'Surface')
class CyclesWorld_PT_surface(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Surface"
bl_context = "world"
@classmethod
def poll(cls, context):
return context.world and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
mat = context.world
panel_node_draw(layout, mat, 'OUTPUT_WORLD', 'Surface')
class CyclesWorld_PT_volume(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Volume"
bl_context = "world"
@classmethod
def poll(cls, context):
return context.world and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
layout.active = False
mat = context.world
panel_node_draw(layout, mat, 'OUTPUT_WORLD', 'Volume')
class CyclesMaterial_PT_surface(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Surface"
bl_context = "material"
@classmethod
def poll(cls, context):
return context.material and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
mat = context.material
panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Surface')
class CyclesMaterial_PT_volume(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Volume"
bl_context = "material"
@classmethod
def poll(cls, context):
return context.material and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
layout.active = False
mat = context.material
panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Volume')
class CyclesMaterial_PT_displacement(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Displacement"
bl_context = "material"
@classmethod
def poll(cls, context):
return context.material and CyclesButtonsPanel.poll(context)
def draw(self, context):
layout = self.layout
mat = context.material
panel_node_draw(layout, mat, 'OUTPUT_MATERIAL', 'Displacement')
class CyclesMaterial_PT_settings(CyclesButtonsPanel, bpy.types.Panel):
bl_label = "Settings"
bl_context = "material"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
# return context.material and CyclesButtonsPanel.poll(context)
return False
def draw(self, context):
layout = self.layout
mat = context.material
row = layout.row()
row.label(text="Light Group:")
row.prop(mat, "light_group", text="")
def draw_device(self, context):
scene = context.scene
layout = self.layout
if scene.render.engine == "CYCLES":
cycles = scene.cycles
if 'cuda' in engine.available_devices():
layout.prop(cycles, "device")
if cycles.device == 'CPU' and engine.with_osl():
layout.prop(cycles, "shading_system")
def get_panels():
return [
bpy.types.RENDER_PT_render,
bpy.types.RENDER_PT_output,
bpy.types.RENDER_PT_encoding,
bpy.types.RENDER_PT_dimensions,
bpy.types.RENDER_PT_stamp,
bpy.types.WORLD_PT_context_world,
bpy.types.DATA_PT_context_mesh,
bpy.types.DATA_PT_vertex_groups,
bpy.types.DATA_PT_shape_keys,
bpy.types.DATA_PT_uv_texture,
bpy.types.DATA_PT_vertex_colors,
bpy.types.DATA_PT_custom_props_mesh,
bpy.types.DATA_PT_context_camera,
bpy.types.DATA_PT_camera,
bpy.types.DATA_PT_camera_display,
bpy.types.DATA_PT_custom_props_camera,
bpy.types.DATA_PT_context_lamp,
bpy.types.DATA_PT_custom_props_lamp,
bpy.types.TEXTURE_PT_context_texture]
def register():
bpy.types.RENDER_PT_render.append(draw_device)
for panel in get_panels():
panel.COMPAT_ENGINES.add('CYCLES')
def unregister():
bpy.types.RENDER_PT_render.remove(draw_device)
for panel in get_panels():
panel.COMPAT_ENGINES.remove('CYCLES')

@ -0,0 +1,99 @@
#
# 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.
#
# XML exporter for generating test files, not intended for end users
import os
import bpy
import io_utils
import xml.etree.ElementTree as etree
import xml.dom.minidom as dom
def strip(root):
root.text = None
root.tail = None
for elem in root:
strip(elem)
def write(node, fname):
strip(node)
s = etree.tostring(node)
s = dom.parseString(s).toprettyxml()
f = open(fname, "w")
f.write(s)
class ExportCyclesXML(bpy.types.Operator, io_utils.ExportHelper):
''''''
bl_idname = "export_mesh.cycles_xml"
bl_label = "Export Cycles XML"
filename_ext = ".xml"
@classmethod
def poll(cls, context):
return context.active_object != None
def execute(self, context):
filepath = bpy.path.ensure_ext(self.filepath, ".xml")
# get mesh
scene = context.scene
object = context.object
if not object:
raise Exception("No active object")
mesh = object.create_mesh(scene, True, 'PREVIEW')
if not mesh:
raise Exception("No mesh data in active object")
# generate mesh node
nverts = ""
verts = ""
P = ""
for v in mesh.vertices:
P += "%f %f %f " % (v.co[0], v.co[1], v.co[2])
for i, f in enumerate(mesh.faces):
nverts += str(len(f.vertices)) + " "
for v in f.vertices:
verts += str(v) + " "
verts += " "
node = etree.Element('mesh', attrib={'nverts': nverts, 'verts': verts, 'P': P})
# write to file
write(node, filepath)
return {'FINISHED'}
def register():
pass
def unregister():
pass
if __name__ == "__main__":
register()

@ -0,0 +1,249 @@
/*
* 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 "camera.h"
#include "scene.h"
#include "blender_sync.h"
#include "blender_util.h"
CCL_NAMESPACE_BEGIN
/* Blender Camera Intermediate: we first convert both the offline and 3d view
* render camera to this, and from there convert to our native camera format. */
struct BlenderCamera {
float nearclip;
float farclip;
bool ortho;
float ortho_scale;
float lens;
float lensradius;
float focaldistance;
float2 shift;
float2 offset;
float zoom;
float2 pixelaspect;
Transform matrix;
};
static void blender_camera_init(BlenderCamera *bcam)
{
memset(bcam, 0, sizeof(BlenderCamera));
bcam->zoom = 1.0f;
bcam->pixelaspect = make_float2(1.0f, 1.0f);
}
static float blender_camera_focal_distance(BL::Object b_ob, BL::Camera b_camera)
{
BL::Object b_dof_object = b_camera.dof_object();
if(!b_dof_object)
return b_camera.dof_distance();
/* for dof object, return distance along camera direction. this is
* compatible with blender, but does it fit our dof model? */
Transform obmat = get_transform(b_ob.matrix_world());
Transform dofmat = get_transform(b_dof_object.matrix_world());
float3 cam_p = transform_get_column(&obmat, 3);
float3 cam_dir = normalize(transform_get_column(&obmat, 2));
float3 dof_p = transform_get_column(&dofmat, 3);
float3 proj_p = dot(dof_p, cam_dir) * cam_dir;
return len(proj_p - cam_p);
}
static void blender_camera_from_object(BlenderCamera *bcam, BL::Object b_ob)
{
BL::ID b_ob_data = b_ob.data();
if(b_ob_data.is_a(&RNA_Camera)) {
BL::Camera b_camera(b_ob_data);
PointerRNA ccamera = RNA_pointer_get(&b_camera.ptr, "cycles");
bcam->nearclip = b_camera.clip_start();
bcam->farclip = b_camera.clip_end();
bcam->ortho = (b_camera.type() == BL::Camera::type_ORTHO);
bcam->ortho_scale = b_camera.ortho_scale();
bcam->lens = b_camera.lens();
bcam->lensradius = RNA_float_get(&ccamera, "lens_radius");
bcam->focaldistance = blender_camera_focal_distance(b_ob, b_camera);
bcam->shift.x = b_camera.shift_x();
bcam->shift.y = b_camera.shift_y();
}
else {
/* from lamp not implemented yet */
}
}
static void blender_camera_sync(Camera *cam, BlenderCamera *bcam, int width, int height)
{
/* copy camera to compare later */
Camera prevcam = *cam;
/* dimensions */
float xratio = width*bcam->pixelaspect.x;
float yratio = height*bcam->pixelaspect.y;
/* compute x/y aspect and ratio */
float aspectratio, xaspect, yaspect;
if(xratio > yratio) {
aspectratio= xratio/yratio;
xaspect= aspectratio;
yaspect= 1.0f;
}
else {
aspectratio= yratio/xratio;
xaspect= 1.0f;
yaspect= aspectratio;
}
/* modify aspect for orthographic scale */
if(bcam->ortho) {
xaspect = xaspect*bcam->ortho_scale/(aspectratio*2.0f);
yaspect = yaspect*bcam->ortho_scale/(aspectratio*2.0f);
aspectratio = bcam->ortho_scale/2.0f;
}
/* set viewplane */
cam->left = -xaspect;
cam->right = xaspect;
cam->bottom = -yaspect;
cam->top = yaspect;
/* zoom for 3d camera view */
cam->left *= bcam->zoom;
cam->right *= bcam->zoom;
cam->bottom *= bcam->zoom;
cam->top *= bcam->zoom;
/* modify viewplane with camera shift and 3d camera view offset */
float dx = 2.0f*(aspectratio*bcam->shift.x + bcam->offset.x*xaspect*2.0f);
float dy = 2.0f*(aspectratio*bcam->shift.y + bcam->offset.y*yaspect*2.0f);
cam->left += dx;
cam->right += dx;
cam->bottom += dy;
cam->top += dy;
/* clipping distances */
cam->nearclip = bcam->nearclip;
cam->farclip = bcam->farclip;
/* orthographic */
cam->ortho = bcam->ortho;
/* perspective */
cam->fov = 2.0f*atan(16.0f/bcam->lens/aspectratio);
cam->focaldistance = bcam->focaldistance;
cam->lensradius = bcam->lensradius;
/* transform, note the blender camera points along the negative z-axis */
cam->matrix = bcam->matrix * transform_scale(1.0f, 1.0f, -1.0f);
/* set update flag */
if(cam->modified(prevcam))
cam->tag_update();
}
/* Sync Render Camera */
void BlenderSync::sync_camera(int width, int height)
{
BlenderCamera bcam;
blender_camera_init(&bcam);
/* pixel aspect */
BL::RenderSettings r = b_scene.render();
bcam.pixelaspect.x = r.pixel_aspect_x();
bcam.pixelaspect.y = r.pixel_aspect_y();
/* camera object */
BL::Object b_ob = b_scene.camera();
if(b_ob) {
blender_camera_from_object(&bcam, b_ob);
bcam.matrix = get_transform(b_ob.matrix_world());
}
/* sync */
Camera *cam = scene->camera;
blender_camera_sync(cam, &bcam, width, height);
}
/* Sync 3D View Camera */
void BlenderSync::sync_view(BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height)
{
BlenderCamera bcam;
blender_camera_init(&bcam);
/* 3d view parameters */
bcam.nearclip = b_v3d.clip_start();
bcam.farclip = b_v3d.clip_end();
bcam.lens = b_v3d.lens();
if(b_rv3d.view_perspective() == BL::RegionView3D::view_perspective_CAMERA) {
/* camera view */
BL::Object b_ob = b_scene.camera();
if(b_ob) {
blender_camera_from_object(&bcam, b_ob);
/* magic zoom formula */
bcam.zoom = b_rv3d.view_camera_zoom();
bcam.zoom = (1.41421f + bcam.zoom/50.0f);
bcam.zoom *= bcam.zoom;
bcam.zoom = 2.0f/bcam.zoom;
/* offset */
bcam.offset = get_float2(b_rv3d.view_camera_offset());
}
}
else if(b_rv3d.view_perspective() == BL::RegionView3D::view_perspective_ORTHO) {
/* orthographic view */
bcam.farclip *= 0.5;
bcam.nearclip = -bcam.farclip;
bcam.ortho = true;
bcam.ortho_scale = b_rv3d.view_distance();
}
bcam.zoom *= 2.0f;
/* 3d view transform */
bcam.matrix = transform_inverse(get_transform(b_rv3d.view_matrix()));
/* sync */
blender_camera_sync(scene->camera, &bcam, width, height);
}
CCL_NAMESPACE_END

@ -0,0 +1,300 @@
/*
* 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 "mesh.h"
#include "object.h"
#include "scene.h"
#include "blender_sync.h"
#include "blender_util.h"
#include "subd_mesh.h"
#include "subd_patch.h"
#include "subd_split.h"
#include "util_foreach.h"
CCL_NAMESPACE_BEGIN
/* Find/Add */
static bool mesh_need_attribute(Scene *scene, Mesh *mesh, Attribute::Standard std)
{
if(std == Attribute::STD_NONE)
return false;
foreach(uint shader, mesh->used_shaders)
if(scene->shaders[shader]->attributes.find(std))
return true;
return false;
}
static bool mesh_need_attribute(Scene *scene, Mesh *mesh, ustring name)
{
if(name == ustring())
return false;
foreach(uint shader, mesh->used_shaders)
if(scene->shaders[shader]->attributes.find(name))
return true;
return false;
}
static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<uint>& used_shaders)
{
/* create vertices */
BL::Mesh::vertices_iterator v;
for(v = b_mesh.vertices.begin(); v != b_mesh.vertices.end(); ++v)
mesh->verts.push_back(get_float3(v->co()));
/* create faces */
BL::Mesh::faces_iterator f;
vector<int> nverts;
for(f = b_mesh.faces.begin(); f != b_mesh.faces.end(); ++f) {
int4 vi = get_int4(f->vertices_raw());
int n= (vi[3] == 0)? 3: 4;
int shader = used_shaders[f->material_index()];
bool smooth = f->use_smooth();
mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
if(n == 4)
mesh->add_triangle(vi[0], vi[2], vi[3], shader, smooth);
nverts.push_back(n);
}
/* create generated coordinates. todo: we should actually get the orco
coordinates from modifiers, for now we use texspace loc/size which
is available in the api. */
if(mesh_need_attribute(scene, mesh, Attribute::STD_GENERATED)) {
Attribute *attr = mesh->attributes.add(Attribute::STD_GENERATED);
float3 loc = get_float3(b_mesh.texspace_location());
float3 size = get_float3(b_mesh.texspace_size());
if(size.x != 0.0f) size.x = 0.5f/size.x;
if(size.y != 0.0f) size.y = 0.5f/size.y;
if(size.z != 0.0f) size.z = 0.5f/size.z;
loc = loc*size - make_float3(0.5f, 0.5f, 0.5f);
float3 *fdata = attr->data_float3();
BL::Mesh::vertices_iterator v;
size_t i = 0;
for(v = b_mesh.vertices.begin(); v != b_mesh.vertices.end(); ++v)
fdata[i++] = get_float3(v->co())*size - loc;
}
/* create vertex color attributes */
{
BL::Mesh::vertex_colors_iterator l;
for(l = b_mesh.vertex_colors.begin(); l != b_mesh.vertex_colors.end(); ++l) {
if(!mesh_need_attribute(scene, mesh, ustring(l->name())))
continue;
Attribute *attr = mesh->attributes.add(
ustring(l->name()), TypeDesc::TypeColor, Attribute::CORNER);
BL::MeshColorLayer::data_iterator c;
float3 *fdata = attr->data_float3();
size_t i = 0;
for(c = l->data.begin(); c != l->data.end(); ++c, ++i) {
fdata[0] = get_float3(c->color1());
fdata[1] = get_float3(c->color2());
fdata[2] = get_float3(c->color3());
fdata += 3;
if(nverts[i] == 4) {
fdata[0] = get_float3(c->color1());
fdata[1] = get_float3(c->color3());
fdata[2] = get_float3(c->color4());
fdata += 3;
}
}
}
}
/* create uv layer attributes */
{
BL::Mesh::uv_textures_iterator l;
for(l = b_mesh.uv_textures.begin(); l != b_mesh.uv_textures.end(); ++l) {
Attribute::Standard std = (l->active_render())? Attribute::STD_UV: Attribute::STD_NONE;
ustring name = ustring(l->name());
if(!(mesh_need_attribute(scene, mesh, name) || mesh_need_attribute(scene, mesh, std)))
continue;
Attribute *attr;
if(l->active_render())
attr = mesh->attributes.add(std, name);
else
attr = mesh->attributes.add(name, TypeDesc::TypePoint, Attribute::CORNER);
BL::MeshTextureFaceLayer::data_iterator t;
float3 *fdata = attr->data_float3();
size_t i = 0;
for(t = l->data.begin(); t != l->data.end(); ++t, ++i) {
fdata[0] = get_float3(t->uv1());
fdata[1] = get_float3(t->uv2());
fdata[2] = get_float3(t->uv3());
fdata += 3;
if(nverts[i] == 4) {
fdata[0] = get_float3(t->uv1());
fdata[1] = get_float3(t->uv3());
fdata[2] = get_float3(t->uv4());
fdata += 3;
}
}
}
}
}
static void create_subd_mesh(Mesh *mesh, BL::Mesh b_mesh, PointerRNA *cmesh, const vector<uint>& used_shaders)
{
/* create subd mesh */
SubdMesh sdmesh;
/* create vertices */
BL::Mesh::vertices_iterator v;
for(v = b_mesh.vertices.begin(); v != b_mesh.vertices.end(); ++v)
sdmesh.add_vert(get_float3(v->co()));
/* create faces */
BL::Mesh::faces_iterator f;
for(f = b_mesh.faces.begin(); f != b_mesh.faces.end(); ++f) {
int4 vi = get_int4(f->vertices_raw());
int n= (vi[3] == 0)? 3: 4;
//int shader = used_shaders[f->material_index()];
if(n == 4)
sdmesh.add_face(vi[0], vi[1], vi[2], vi[3]);
/*else
sdmesh.add_face(vi[0], vi[1], vi[2]);*/
}
/* finalize subd mesh */
sdmesh.link_boundary();
/* subdivide */
DiagSplit dsplit;
dsplit.camera = NULL;
dsplit.dicing_rate = RNA_float_get(cmesh, "dicing_rate");
sdmesh.tesselate(&dsplit, false, mesh, used_shaders[0], true);
}
/* Sync */
Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated)
{
/* test if we can instance or if the object is modified */
BL::ID b_ob_data = b_ob.data();
BL::ID key = (object_is_modified(b_ob))? b_ob: b_ob_data;
/* find shader indices */
vector<uint> used_shaders;
BL::Object::material_slots_iterator slot;
for(slot = b_ob.material_slots.begin(); slot != b_ob.material_slots.end(); ++slot)
find_shader(slot->material(), used_shaders);
if(used_shaders.size() == 0)
used_shaders.push_back(scene->default_surface);
/* test if we need to sync */
Mesh *mesh;
if(!mesh_map.sync(&mesh, key)) {
/* if transform was applied to mesh, need full update */
if(object_updated && mesh->transform_applied);
/* test if shaders changed, these can be object level so mesh
does not get tagged for recalc */
else if(mesh->used_shaders != used_shaders);
else {
/* even if not tagged for recalc, we may need to sync anyway
* because the shader needs different mesh attributes */
bool attribute_recalc = false;
foreach(uint shader, mesh->used_shaders)
if(scene->shaders[shader]->need_update_attributes)
attribute_recalc = true;
if(!attribute_recalc)
return mesh;
}
}
/* create derived mesh */
BL::Mesh b_mesh = object_to_mesh(b_ob, b_scene, true, !preview);
/* todo: this will crash on non-mesh types! */
PointerRNA cmesh = RNA_pointer_get(&b_ob_data.ptr, "cycles");
vector<Mesh::Triangle> oldtriangle = mesh->triangles;
mesh->clear();
mesh->used_shaders = used_shaders;
mesh->name = ustring(b_ob_data.name());
if(cmesh.data && RNA_boolean_get(&cmesh, "use_subdivision"))
create_subd_mesh(mesh, b_mesh, &cmesh, used_shaders);
else
create_mesh(scene, mesh, b_mesh, used_shaders);
/* free derived mesh */
object_remove_mesh(b_data, b_mesh);
/* displacement method */
if(cmesh.data) {
int method = RNA_enum_get(&cmesh, "displacement_method");
if(method == 0)
mesh->displacement_method = Mesh::DISPLACE_BUMP;
else if(method == 1)
mesh->displacement_method = Mesh::DISPLACE_TRUE;
else
mesh->displacement_method = Mesh::DISPLACE_BOTH;
}
/* tag update */
bool rebuild = false;
if(oldtriangle.size() != mesh->triangles.size())
rebuild = true;
else if(memcmp(&oldtriangle[0], &mesh->triangles[0], sizeof(Mesh::Triangle)*oldtriangle.size()) != 0)
rebuild = true;
mesh->tag_update(scene, rebuild);
return mesh;
}
CCL_NAMESPACE_END

@ -0,0 +1,183 @@
/*
* 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 "light.h"
#include "mesh.h"
#include "object.h"
#include "scene.h"
#include "blender_sync.h"
#include "blender_util.h"
#include "util_foreach.h"
CCL_NAMESPACE_BEGIN
/* Utilities */
bool BlenderSync::object_is_modified(BL::Object b_ob)
{
/* test if we can instance or if the object is modified */
if(ccl::object_is_modified(b_ob, b_scene, preview)) {
/* modifiers */
return true;
}
else {
/* object level material links */
BL::Object::material_slots_iterator slot;
for(slot = b_ob.material_slots.begin(); slot != b_ob.material_slots.end(); ++slot)
if(slot->link() == BL::MaterialSlot::link_OBJECT)
return true;
}
return false;
}
bool BlenderSync::object_is_mesh(BL::Object b_ob)
{
BL::ID b_ob_data = b_ob.data();
return (b_ob_data && (b_ob_data.is_a(&RNA_Mesh) ||
b_ob_data.is_a(&RNA_Curve) || b_ob_data.is_a(&RNA_MetaBall)));
}
bool BlenderSync::object_is_light(BL::Object b_ob)
{
BL::ID b_ob_data = b_ob.data();
return (b_ob_data && b_ob_data.is_a(&RNA_Lamp));
}
/* Light */
void BlenderSync::sync_light(BL::Object b_ob, Transform& tfm)
{
/* test if we need to sync */
Light *light;
/* todo: account for instancing */
if(!light_map.sync(&light, b_ob))
return;
/* location */
light->co = make_float3(tfm.x.w, tfm.y.w, tfm.z.w);
/* shader */
BL::Lamp b_lamp(b_ob.data());
vector<uint> used_shaders;
find_shader(b_lamp, used_shaders);
if(used_shaders.size() == 0)
used_shaders.push_back(scene->default_light);
light->shader = used_shaders[0];
/* tag */
light->tag_update(scene);
}
/* Object */
void BlenderSync::sync_object(BL::Object b_parent, int b_index, BL::Object b_ob, Transform& tfm)
{
/* light is handled separately */
if(object_is_light(b_ob)) {
sync_light(b_ob, tfm);
return;
}
/* only interested in object that we can create meshes from */
if(!object_is_mesh(b_ob))
return;
/* test if we need to sync */
ObjectKey key(b_parent, b_index, b_ob);
Object *object;
bool object_updated = false;
/* object sync */
if(object_map.sync(&object, b_ob, key)) {
object->name = b_ob.name();
object->tfm = tfm;
object->tag_update(scene);
object_updated = true;
}
/* mesh sync */
object->mesh = sync_mesh(b_ob, object_updated);
}
/* Object Loop */
void BlenderSync::sync_objects(BL::SpaceView3D b_v3d)
{
/* layer data */
uint layer;
if(b_v3d)
layer = get_layer(b_v3d.layers());
else
layer = get_layer(b_scene.layers());
/* prepare for sync */
light_map.pre_sync();
mesh_map.pre_sync();
object_map.pre_sync();
/* object loop */
BL::Scene::objects_iterator b_ob;
for(b_ob = b_scene.objects.begin(); b_ob != b_scene.objects.end(); ++b_ob) {
bool hide = (b_v3d)? b_ob->hide(): b_ob->hide_render();
if(!hide && get_layer(b_ob->layers()) & layer) {
if(b_ob->is_duplicator()) {
/* dupli objects */
object_create_duplilist(*b_ob, b_scene);
BL::Object::dupli_list_iterator b_dup;
int b_index = 0;
for(b_dup = b_ob->dupli_list.begin(); b_dup != b_ob->dupli_list.end(); ++b_dup) {
Transform tfm = get_transform(b_dup->matrix());
sync_object(*b_ob, b_index, b_dup->object(), tfm);
b_index++;
}
object_free_duplilist(*b_ob);
}
else {
/* object itself */
Transform tfm = get_transform(b_ob->matrix_world());
sync_object(*b_ob, 0, *b_ob, tfm);
}
}
}
/* handle removed data and modified pointers */
if(light_map.post_sync())
scene->light_manager->tag_update(scene);
if(mesh_map.post_sync())
scene->mesh_manager->tag_update(scene);
if(object_map.post_sync())
scene->object_manager->tag_update(scene);
}
CCL_NAMESPACE_END

@ -0,0 +1,230 @@
/*
* 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 <Python.h>
#include "blender_sync.h"
#include "blender_session.h"
#include "util_opengl.h"
#include "util_path.h"
CCL_NAMESPACE_BEGIN
static PyObject *init_func(PyObject *self, PyObject *args)
{
const char *path;
if(!PyArg_ParseTuple(args, "s", &path))
return NULL;
path_init(path);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *create_func(PyObject *self, PyObject *args)
{
Py_ssize_t pyengine, pydata, pyscene, pyregion, pyv3d, pyrv3d;
if(!PyArg_ParseTuple(args, "nnnnnn", &pyengine, &pydata, &pyscene, &pyregion, &pyv3d, &pyrv3d))
return NULL;
/* RNA */
PointerRNA engineptr;
RNA_pointer_create(NULL, &RNA_RenderEngine, (void*)pyengine, &engineptr);
BL::RenderEngine engine(engineptr);
PointerRNA dataptr;
RNA_id_pointer_create((ID*)pydata, &dataptr);
BL::BlendData data(dataptr);
PointerRNA sceneptr;
RNA_id_pointer_create((ID*)pyscene, &sceneptr);
BL::Scene scene(sceneptr);
PointerRNA regionptr;
RNA_id_pointer_create((ID*)pyregion, &regionptr);
BL::Region region(regionptr);
PointerRNA v3dptr;
RNA_id_pointer_create((ID*)pyv3d, &v3dptr);
BL::SpaceView3D v3d(v3dptr);
PointerRNA rv3dptr;
RNA_id_pointer_create((ID*)pyrv3d, &rv3dptr);
BL::RegionView3D rv3d(rv3dptr);
/* create session */
BlenderSession *session;
if(rv3d) {
/* interactive session */
int width = region.width();
int height = region.height();
session = new BlenderSession(engine, data, scene, v3d, rv3d, width, height);
}
else {
/* offline session */
session = new BlenderSession(engine, data, scene);
}
return PyLong_FromVoidPtr(session);
}
static PyObject *free_func(PyObject *self, PyObject *args)
{
Py_ssize_t pysession;
if(!PyArg_ParseTuple(args, "n", &pysession))
return NULL;
delete (BlenderSession*)pysession;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *render_func(PyObject *self, PyObject *args)
{
Py_ssize_t pysession;
if(!PyArg_ParseTuple(args, "n", &pysession))
return NULL;
Py_BEGIN_ALLOW_THREADS
BlenderSession *session = (BlenderSession*)pysession;
session->render();
Py_END_ALLOW_THREADS
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *draw_func(PyObject *self, PyObject *args)
{
Py_ssize_t pysession, pyv3d, pyrv3d;
if(!PyArg_ParseTuple(args, "nnn", &pysession, &pyv3d, &pyrv3d))
return NULL;
BlenderSession *session = (BlenderSession*)pysession;
bool draw_text = false;
if(pyrv3d) {
/* 3d view drawing */
int viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
draw_text = session->draw(viewport[2], viewport[3]);
}
else {
/* image editor drawing */
draw_text = session->draw();
}
/* draw */
PyObject *ret = PyTuple_New(2);
if(!draw_text) {
PyTuple_SetItem(ret, 0, PyUnicode_FromString(""));
PyTuple_SetItem(ret, 1, PyUnicode_FromString(""));
}
else {
string status, substatus;
session->get_status(status, substatus);
PyTuple_SetItem(ret, 0, PyUnicode_FromString(status.c_str()));
PyTuple_SetItem(ret, 1, PyUnicode_FromString(substatus.c_str()));
}
return ret;
}
static PyObject *sync_func(PyObject *self, PyObject *args)
{
Py_ssize_t pysession;
if(!PyArg_ParseTuple(args, "n", &pysession))
return NULL;
BlenderSession *session = (BlenderSession*)pysession;
session->synchronize();
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *available_devices_func(PyObject *self, PyObject *args)
{
vector<DeviceType> types = Device::available_types();
PyObject *ret = PyTuple_New(types.size());
for(size_t i = 0; i < types.size(); i++) {
string name = Device::string_from_type(types[i]);
PyTuple_SetItem(ret, i, PyUnicode_FromString(name.c_str()));
}
return ret;
}
static PyObject *with_osl_func(PyObject *self, PyObject *args)
{
#ifdef WITH_OSL
PyObject *ret = Py_True;
#else
PyObject *ret = Py_False;
#endif
return Py_INCREF(ret), ret;
}
static PyMethodDef methods[] = {
{"init", init_func, METH_VARARGS, ""},
{"create", create_func, METH_VARARGS, ""},
{"free", free_func, METH_VARARGS, ""},
{"render", render_func, METH_VARARGS, ""},
{"draw", draw_func, METH_VARARGS, ""},
{"sync", sync_func, METH_VARARGS, ""},
{"available_devices", available_devices_func, METH_NOARGS, ""},
{"with_osl", with_osl_func, METH_NOARGS, ""},
{NULL, NULL},
};
static struct PyModuleDef module = {
PyModuleDef_HEAD_INIT,
"libcycles_blender",
"Blender RNA to render exporter",
-1,
methods
};
CCL_NAMESPACE_END
PyMODINIT_FUNC PyInit_libcycles_blender()
{
return PyModule_Create(&ccl::module);
}

@ -0,0 +1,280 @@
/*
* 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 "background.h"
#include "buffers.h"
#include "camera.h"
#include "device.h"
#include "integrator.h"
#include "light.h"
#include "scene.h"
#include "session.h"
#include "shader.h"
#include "util_color.h"
#include "util_foreach.h"
#include "util_function.h"
#include "util_progress.h"
#include "util_time.h"
#include "blender_sync.h"
#include "blender_session.h"
#include "blender_util.h"
CCL_NAMESPACE_BEGIN
BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::BlendData b_data_, BL::Scene b_scene_)
: b_engine(b_engine_), b_data(b_data_), b_scene(b_scene_), b_v3d(PointerRNA_NULL), b_rv3d(PointerRNA_NULL)
{
/* offline render */
BL::RenderSettings r = b_scene.render();
width = (int)(r.resolution_x()*r.resolution_percentage()*0.01f);
height = (int)(r.resolution_y()*r.resolution_percentage()*0.01f);
background = true;
last_redraw_time = 0.0f;
create_session();
}
BlenderSession::BlenderSession(BL::RenderEngine b_engine_, BL::BlendData b_data_, BL::Scene b_scene_,
BL::SpaceView3D b_v3d_, BL::RegionView3D b_rv3d_, int width_, int height_)
: b_engine(b_engine_), b_data(b_data_), b_scene(b_scene_), b_v3d(b_v3d_), b_rv3d(b_rv3d_)
{
/* 3d view render */
width = width_;
height = height_;
background = false;
last_redraw_time = 0.0f;
create_session();
}
BlenderSession::~BlenderSession()
{
free_session();
}
void BlenderSession::create_session()
{
SceneParams scene_params = BlenderSync::get_scene_params(b_scene);
SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
/* create scene */
scene = new Scene(scene_params);
/* create sync */
sync = new BlenderSync(b_data, b_scene, scene, !background);
sync->sync_data(b_v3d);
if(b_rv3d)
sync->sync_view(b_v3d, b_rv3d, width, height);
else
sync->sync_camera(width, height);
/* create session */
session = new Session(session_params);
session->scene = scene;
session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this));
session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this));
/* start rendering */
session->reset(width, height);
session->start();
}
void BlenderSession::free_session()
{
delete sync;
delete session;
}
void BlenderSession::render()
{
session->wait();
if(session->progress.get_cancel())
return;
/* write result */
write_render_result();
}
void BlenderSession::write_render_result()
{
/* get result */
DisplayBuffer *display = session->display;
Device *device = session->device;
if(!display->rgba.device_pointer)
return;
/* todo: get float buffer */
device->pixels_copy_from(display->rgba, 0, width, height);
uchar4 *rgba = (uchar4*)display->rgba.data_pointer;
vector<float4> buffer(width*height);
float fac = 1.0f/255.0f;
/* normalize */
for(int i = width*height - 1; i >= 0; i--) {
uchar4 f = rgba[i];
float r = color_srgb_to_scene_linear(f.x*fac);
float g = color_srgb_to_scene_linear(f.y*fac);
float b = color_srgb_to_scene_linear(f.z*fac);
buffer[i] = make_float4(r, g, b, 1.0f);
}
struct RenderResult *rrp = RE_engine_begin_result((RenderEngine*)b_engine.ptr.data, 0, 0, width, height);
PointerRNA rrptr;
RNA_pointer_create(NULL, &RNA_RenderResult, rrp, &rrptr);
BL::RenderResult rr(rrptr);
rna_RenderLayer_rect_set(&rr.layers.begin()->ptr, (float*)&buffer[0]);
RE_engine_end_result((RenderEngine*)b_engine.ptr.data, rrp);
}
void BlenderSession::synchronize()
{
/* on session/scene parameter changes, we recreate session entirely */
SceneParams scene_params = BlenderSync::get_scene_params(b_scene);
SessionParams session_params = BlenderSync::get_session_params(b_scene, background);
if(session->params.modified(session_params) ||
scene->params.modified(scene_params)) {
free_session();
create_session();
return;
}
/* copy recalc flags, outside of mutex so we can decide to do the real
synchronization at a later time to not block on running updates */
sync->sync_recalc();
/* try to acquire mutex. if we don't want to or can't, come back later */
if(!session->ready_to_reset() || !session->scene->mutex.try_lock()) {
tag_update();
return;
}
/* data and camera synchronize */
sync->sync_data(b_v3d);
if(b_rv3d)
sync->sync_view(b_v3d, b_rv3d, width, height);
else
sync->sync_camera(width, height);
/* reset if needed */
if(scene->need_reset())
session->reset(width, height);
/* unlock */
session->scene->mutex.unlock();
}
bool BlenderSession::draw(int w, int h)
{
/* before drawing, we verify camera and viewport size changes, because
we do not get update callbacks for those, we must detect them here */
if(session->ready_to_reset()) {
bool reset = false;
/* try to acquire mutex. if we can't, come back later */
if(!session->scene->mutex.try_lock()) {
tag_update();
}
else {
/* update camera from 3d view */
bool need_update = scene->camera->need_update;
sync->sync_view(b_v3d, b_rv3d, w, h);
if(scene->camera->need_update && !need_update)
reset = true;
session->scene->mutex.unlock();
}
/* if dimensions changed, reset */
if(width != w || height != h) {
width = w;
height = h;
reset = true;
}
/* reset if requested */
if(reset)
session->reset(width, height);
}
/* draw */
return !session->draw(width, height);
}
bool BlenderSession::draw()
{
return !session->draw(width, height);
}
void BlenderSession::get_status(string& status, string& substatus)
{
session->progress.get_status(status, substatus);
}
void BlenderSession::tag_update()
{
/* tell blender that we want to get another update callback */
engine_tag_update((RenderEngine*)b_engine.ptr.data);
}
void BlenderSession::tag_redraw()
{
if(background) {
/* offline render, set stats and redraw if timeout passed */
string status, substatus;
get_status(status, substatus);
if(substatus.size() > 0)
status += " | " + substatus;
RE_engine_update_stats((RenderEngine*)b_engine.ptr.data, "", status.c_str());
if(time_dt() - last_redraw_time > 1.0f) {
write_render_result();
engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
last_redraw_time = time_dt();
}
}
else {
/* tell blender that we want to redraw */
engine_tag_redraw((RenderEngine*)b_engine.ptr.data);
}
}
void BlenderSession::test_cancel()
{
/* test if we need to cancel rendering */
if(background)
if(RE_engine_test_break((RenderEngine*)b_engine.ptr.data))
session->progress.set_cancel("Cancelled");
}
CCL_NAMESPACE_END

@ -0,0 +1,77 @@
/*
* 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 __BLENDER_SESSION_H__
#define __BLENDER_SESSION_H__
#include "device.h"
#include "scene.h"
#include "session.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
class Scene;
class Session;
class BlenderSession {
public:
BlenderSession(BL::RenderEngine b_engine, BL::BlendData b_data, BL::Scene b_scene);
BlenderSession(BL::RenderEngine b_engine, BL::BlendData b_data, BL::Scene b_scene,
BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height);
~BlenderSession();
/* session */
void create_session();
void free_session();
/* offline render */
void render();
void write_render_result();
/* interactive updates */
void synchronize();
/* drawing */
bool draw();
bool draw(int w, int h);
void tag_redraw();
void tag_update();
void get_status(string& status, string& substatus);
void test_cancel();
bool background;
Session *session;
Scene *scene;
BlenderSync *sync;
double last_redraw_time;
BL::RenderEngine b_engine;
BL::BlendData b_data;
BL::Scene b_scene;
BL::SpaceView3D b_v3d;
BL::RegionView3D b_rv3d;
int width, height;
};
CCL_NAMESPACE_END
#endif /* __BLENDER_SESSION_H__ */

@ -0,0 +1,629 @@
/*
* 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 "background.h"
#include "graph.h"
#include "light.h"
#include "nodes.h"
#include "scene.h"
#include "shader.h"
#include "blender_sync.h"
#include "blender_util.h"
#include "util_debug.h"
CCL_NAMESPACE_BEGIN
typedef map<void*, ShaderNode*> PtrNodeMap;
typedef pair<ShaderNode*, std::string> SocketPair;
typedef map<void*, SocketPair> PtrSockMap;
/* Find */
void BlenderSync::find_shader(BL::ID id, vector<uint>& used_shaders)
{
Shader *shader = shader_map.find(id);
for(size_t i = 0; i < scene->shaders.size(); i++) {
if(scene->shaders[i] == shader) {
used_shaders.push_back(i);
break;
}
}
}
/* Graph */
static BL::NodeSocket get_node_input(BL::Node *b_group_node, BL::Node b_node, const string& name)
{
BL::Node::inputs_iterator b_in;
for(b_in = b_node.inputs.begin(); b_in != b_node.inputs.end(); ++b_in) {
if(b_in->name() == name) {
if(b_group_node) {
BL::NodeTree b_ntree = BL::NodeGroup(*b_group_node).node_tree();
BL::NodeTree::links_iterator b_link;
for(b_link = b_ntree.links.begin(); b_link != b_ntree.links.end(); ++b_link) {
if(b_link->to_socket().ptr.data == b_in->ptr.data) {
BL::Node::inputs_iterator b_gin;
for(b_gin = b_group_node->inputs.begin(); b_gin != b_group_node->inputs.end(); ++b_gin)
if(b_gin->group_socket().ptr.data == b_link->from_socket().ptr.data)
return *b_gin;
}
}
}
return *b_in;
}
}
assert(0);
return *b_in;
}
static BL::NodeSocket get_node_output(BL::Node b_node, const string& name)
{
BL::Node::outputs_iterator b_out;
for(b_out = b_node.outputs.begin(); b_out != b_node.outputs.end(); ++b_out)
if(b_out->name() == name)
return *b_out;
assert(0);
return *b_out;
}
static float3 get_node_output_rgba(BL::Node b_node, const string& name)
{
BL::RGBANodeSocket sock(get_node_output(b_node, name));
return get_float3(sock.default_value());
}
static float get_node_output_value(BL::Node b_node, const string& name)
{
BL::ValueNodeSocket sock(get_node_output(b_node, name));
return sock.default_value()[0];
}
static ShaderNode *add_node(BL::BlendData b_data, ShaderGraph *graph, BL::Node *b_group_node, BL::ShaderNode b_node)
{
ShaderNode *node = NULL;
switch(b_node.type()) {
/* not supported */
case BL::ShaderNode::type_CAMERA: break;
case BL::ShaderNode::type_COMBRGB: break;
case BL::ShaderNode::type_CURVE_RGB: break;
case BL::ShaderNode::type_CURVE_VEC: break;
case BL::ShaderNode::type_GEOM: break;
case BL::ShaderNode::type_HUE_SAT: break;
case BL::ShaderNode::type_INVERT: break;
case BL::ShaderNode::type_MATERIAL: break;
case BL::ShaderNode::type_MATERIAL_EXT: break;
case BL::ShaderNode::type_NORMAL: break;
case BL::ShaderNode::type_OUTPUT: break;
case BL::ShaderNode::type_SCRIPT: break;
case BL::ShaderNode::type_SEPRGB: break;
case BL::ShaderNode::type_SQUEEZE: break;
case BL::ShaderNode::type_TEXTURE: break;
case BL::ShaderNode::type_VALTORGB: break;
/* handled outside this function */
case BL::ShaderNode::type_GROUP: break;
/* existing blender nodes */
case BL::ShaderNode::type_RGB: {
ColorNode *color = new ColorNode();
color->value = get_node_output_rgba(b_node, "Color");
node = color;
break;
}
case BL::ShaderNode::type_VALUE: {
ValueNode *value = new ValueNode();
value->value = get_node_output_value(b_node, "Value");
node = value;
break;
}
case BL::ShaderNode::type_MIX_RGB: {
BL::ShaderNodeMixRGB b_mix_node(b_node);
MixNode *mix = new MixNode();
mix->type = MixNode::type_enum[b_mix_node.blend_type()];
node = mix;
break;
}
case BL::ShaderNode::type_RGBTOBW: {
node = new ConvertNode(SHADER_SOCKET_COLOR, SHADER_SOCKET_FLOAT);
break;
}
case BL::ShaderNode::type_MATH: {
BL::ShaderNodeMath b_math_node(b_node);
MathNode *math = new MathNode();
math->type = MathNode::type_enum[b_math_node.operation()];
node = math;
break;
}
case BL::ShaderNode::type_VECT_MATH: {
BL::ShaderNodeVectorMath b_vector_math_node(b_node);
VectorMathNode *vmath = new VectorMathNode();
vmath->type = VectorMathNode::type_enum[b_vector_math_node.operation()];
node = vmath;
break;
}
case BL::ShaderNode::type_MAPPING: {
BL::ShaderNodeMapping b_mapping_node(b_node);
MappingNode *mapping = new MappingNode();
mapping->translation = get_float3(b_mapping_node.location());
mapping->rotation = get_float3(b_mapping_node.rotation());
mapping->scale = get_float3(b_mapping_node.scale());
node = mapping;
break;
}
/* new nodes */
case BL::ShaderNode::type_OUTPUT_MATERIAL:
case BL::ShaderNode::type_OUTPUT_WORLD:
case BL::ShaderNode::type_OUTPUT_LAMP: {
node = graph->output();
break;
}
case BL::ShaderNode::type_FRESNEL: {
node = new FresnelNode();
break;
}
case BL::ShaderNode::type_ADD_CLOSURE: {
node = new AddClosureNode();
break;
}
case BL::ShaderNode::type_MIX_CLOSURE: {
node = new MixClosureNode();
break;
}
case BL::ShaderNode::type_ATTRIBUTE: {
AttributeNode *attr = new AttributeNode();
attr->attribute = "";
node = attr;
break;
}
case BL::ShaderNode::type_BACKGROUND: {
node = new BackgroundNode();
break;
}
case BL::ShaderNode::type_BSDF_ANISOTROPIC: {
node = new WardBsdfNode();
break;
}
case BL::ShaderNode::type_BSDF_DIFFUSE: {
node = new DiffuseBsdfNode();
break;
}
case BL::ShaderNode::type_BSDF_GLOSSY: {
BL::ShaderNodeBsdfGlossy b_glossy_node(b_node);
GlossyBsdfNode *glossy = new GlossyBsdfNode();
switch(b_glossy_node.distribution()) {
case BL::ShaderNodeBsdfGlossy::distribution_SHARP:
glossy->distribution = ustring("Sharp");
break;
case BL::ShaderNodeBsdfGlossy::distribution_BECKMANN:
glossy->distribution = ustring("Beckmann");
break;
case BL::ShaderNodeBsdfGlossy::distribution_GGX:
glossy->distribution = ustring("GGX");
break;
}
node = glossy;
break;
}
case BL::ShaderNode::type_BSDF_GLASS: {
BL::ShaderNodeBsdfGlass b_glass_node(b_node);
GlassBsdfNode *glass = new GlassBsdfNode();
switch(b_glass_node.distribution()) {
case BL::ShaderNodeBsdfGlass::distribution_SHARP:
glass->distribution = ustring("Sharp");
break;
case BL::ShaderNodeBsdfGlass::distribution_BECKMANN:
glass->distribution = ustring("Beckmann");
break;
case BL::ShaderNodeBsdfGlass::distribution_GGX:
glass->distribution = ustring("GGX");
break;
}
node = glass;
break;
}
case BL::ShaderNode::type_BSDF_TRANSLUCENT: {
node = new TranslucentBsdfNode();
break;
}
case BL::ShaderNode::type_BSDF_TRANSPARENT: {
node = new TransparentBsdfNode();
break;
}
case BL::ShaderNode::type_BSDF_VELVET: {
node = new VelvetBsdfNode();
break;
}
case BL::ShaderNode::type_EMISSION: {
node = new EmissionNode();
break;
}
case BL::ShaderNode::type_GEOMETRY: {
node = new GeometryNode();
break;
}
case BL::ShaderNode::type_LIGHT_PATH: {
node = new LightPathNode();
break;
}
case BL::ShaderNode::type_TEX_IMAGE: {
BL::ShaderNodeTexImage b_image_node(b_node);
BL::Image b_image(b_image_node.image());
ImageTextureNode *image = new ImageTextureNode();
/* todo: handle generated/builtin images */
if(b_image)
image->filename = blender_absolute_path(b_data, b_image, b_image.filepath());
node = image;
break;
}
case BL::ShaderNode::type_TEX_ENVIRONMENT: {
BL::ShaderNodeTexEnvironment b_environment_node(b_node);
BL::Image b_image(b_environment_node.image());
EnvironmentTextureNode *env = new EnvironmentTextureNode();
if(b_image)
env->filename = blender_absolute_path(b_data, b_image, b_image.filepath());
node = env;
break;
}
case BL::ShaderNode::type_TEX_NOISE: {
node = new NoiseTextureNode();
break;
}
case BL::ShaderNode::type_TEX_BLEND: {
BL::ShaderNodeTexBlend b_blend_node(b_node);
BlendTextureNode *blend = new BlendTextureNode();
blend->progression = BlendTextureNode::progression_enum[(int)b_blend_node.progression()];
blend->axis = BlendTextureNode::axis_enum[(int)b_blend_node.axis()];
node = blend;
break;
}
case BL::ShaderNode::type_TEX_VORONOI: {
BL::ShaderNodeTexVoronoi b_voronoi_node(b_node);
VoronoiTextureNode *voronoi = new VoronoiTextureNode();
voronoi->distance_metric = VoronoiTextureNode::distance_metric_enum[(int)b_voronoi_node.distance_metric()];
voronoi->coloring = VoronoiTextureNode::coloring_enum[(int)b_voronoi_node.coloring()];
node = voronoi;
break;
}
case BL::ShaderNode::type_TEX_MAGIC: {
BL::ShaderNodeTexMagic b_magic_node(b_node);
MagicTextureNode *magic = new MagicTextureNode();
magic->depth = b_magic_node.turbulence_depth();
node = magic;
break;
}
case BL::ShaderNode::type_TEX_MARBLE: {
BL::ShaderNodeTexMarble b_marble_node(b_node);
MarbleTextureNode *marble = new MarbleTextureNode();
marble->depth = b_marble_node.turbulence_depth();
marble->basis = MarbleTextureNode::basis_enum[(int)b_marble_node.noise_basis()];
marble->type = MarbleTextureNode::type_enum[(int)b_marble_node.marble_type()];
marble->wave = MarbleTextureNode::wave_enum[(int)b_marble_node.wave_type()];
marble->hard = b_marble_node.noise_type() == BL::ShaderNodeTexMarble::noise_type_HARD;
node = marble;
break;
}
case BL::ShaderNode::type_TEX_CLOUDS: {
BL::ShaderNodeTexClouds b_clouds_node(b_node);
CloudsTextureNode *clouds = new CloudsTextureNode();
clouds->depth = b_clouds_node.turbulence_depth();
clouds->basis = CloudsTextureNode::basis_enum[(int)b_clouds_node.noise_basis()];
clouds->hard = b_clouds_node.noise_type() == BL::ShaderNodeTexClouds::noise_type_HARD;
node = clouds;
break;
}
case BL::ShaderNode::type_TEX_WOOD: {
BL::ShaderNodeTexWood b_wood_node(b_node);
WoodTextureNode *wood = new WoodTextureNode();
wood->type = WoodTextureNode::type_enum[(int)b_wood_node.wood_type()];
wood->basis = WoodTextureNode::basis_enum[(int)b_wood_node.noise_basis()];
wood->hard = b_wood_node.noise_type() == BL::ShaderNodeTexWood::noise_type_HARD;
wood->wave = WoodTextureNode::wave_enum[(int)b_wood_node.wave_type()];
node = wood;
break;
}
case BL::ShaderNode::type_TEX_MUSGRAVE: {
BL::ShaderNodeTexMusgrave b_musgrave_node(b_node);
MusgraveTextureNode *musgrave = new MusgraveTextureNode();
musgrave->type = MusgraveTextureNode::type_enum[(int)b_musgrave_node.musgrave_type()];
musgrave->basis = MusgraveTextureNode::basis_enum[(int)b_musgrave_node.noise_basis()];
node = musgrave;
break;
}
case BL::ShaderNode::type_TEX_STUCCI: {
BL::ShaderNodeTexStucci b_stucci_node(b_node);
StucciTextureNode *stucci = new StucciTextureNode();
stucci->type = StucciTextureNode::type_enum[(int)b_stucci_node.stucci_type()];
stucci->basis = StucciTextureNode::basis_enum[(int)b_stucci_node.noise_basis()];
stucci->hard = b_stucci_node.noise_type() == BL::ShaderNodeTexStucci::noise_type_HARD;
node = stucci;
break;
}
case BL::ShaderNode::type_TEX_DISTORTED_NOISE: {
BL::ShaderNodeTexDistortedNoise b_distnoise_node(b_node);
DistortedNoiseTextureNode *distnoise = new DistortedNoiseTextureNode();
distnoise->basis = DistortedNoiseTextureNode::basis_enum[(int)b_distnoise_node.noise_basis()];
distnoise->distortion_basis = DistortedNoiseTextureNode::basis_enum[(int)b_distnoise_node.noise_distortion()];
node = distnoise;
break;
}
case BL::ShaderNode::type_TEX_COORD: {
node = new TextureCoordinateNode();;
break;
}
case BL::ShaderNode::type_TEX_SKY: {
BL::ShaderNodeTexSky b_sky_node(b_node);
SkyTextureNode *sky = new SkyTextureNode();
sky->sun_direction = get_float3(b_sky_node.sun_direction());
sky->turbidity = b_sky_node.turbidity();
node = sky;
break;
}
}
if(node != graph->output())
graph->add(node);
return node;
}
static void add_nodes(BL::BlendData b_data, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, BL::Node *b_group_node, PtrSockMap& sockets_map)
{
/* add nodes */
BL::ShaderNodeTree::nodes_iterator b_node;
PtrNodeMap node_map;
map<void*, PtrSockMap> node_groups;
for(b_node = b_ntree.nodes.begin(); b_node != b_ntree.nodes.end(); ++b_node) {
if(b_node->is_a(&RNA_NodeGroup)) {
BL::NodeGroup b_gnode(*b_node);
BL::ShaderNodeTree b_group_ntree(b_gnode.node_tree());
node_groups[b_node->ptr.data] = PtrSockMap();
add_nodes(b_data, graph, b_group_ntree, &b_gnode, node_groups[b_node->ptr.data]);
}
else {
ShaderNode *node = add_node(b_data, graph, b_group_node, BL::ShaderNode(*b_node));
if(node) {
BL::Node::inputs_iterator b_input;
BL::Node::outputs_iterator b_output;
node_map[b_node->ptr.data] = node;
for(b_input = b_node->inputs.begin(); b_input != b_node->inputs.end(); ++b_input) {
ShaderInput *input = node->input(b_input->name().c_str());
BL::NodeSocket sock(get_node_input(b_group_node, *b_node, b_input->name()));
assert(input);
/* copy values for non linked inputs */
switch(input->type) {
case SHADER_SOCKET_FLOAT: {
BL::ValueNodeSocket value_sock(sock);
input->set(value_sock.default_value()[0]);
break;
}
case SHADER_SOCKET_COLOR: {
BL::RGBANodeSocket rgba_sock(sock);
input->set(get_float3(rgba_sock.default_value()));
break;
}
case SHADER_SOCKET_NORMAL:
case SHADER_SOCKET_POINT:
case SHADER_SOCKET_VECTOR: {
BL::VectorNodeSocket vec_sock(sock);
input->set(get_float3(vec_sock.default_value()));
break;
}
case SHADER_SOCKET_CLOSURE:
break;
}
}
}
}
}
/* connect nodes */
BL::NodeTree::links_iterator b_link;
for(b_link = b_ntree.links.begin(); b_link != b_ntree.links.end(); ++b_link) {
/* get blender link data */
BL::Node b_from_node = b_link->from_node();
BL::Node b_to_node = b_link->to_node();
BL::NodeSocket b_from_sock = b_link->from_socket();
BL::NodeSocket b_to_sock = b_link->to_socket();
/* if link with group socket, add to map so we can connect it later */
if(b_group_node) {
if(!b_from_node) {
sockets_map[b_from_sock.ptr.data] =
SocketPair(node_map[b_to_node.ptr.data], b_to_sock.name());
continue;
}
else if(!b_to_node) {
sockets_map[b_to_sock.ptr.data] =
SocketPair(node_map[b_from_node.ptr.data], b_from_sock.name());
continue;
}
}
ShaderNode *from_node, *to_node;
string from_name, to_name;
/* from sock */
if(b_from_node.is_a(&RNA_NodeGroup)) {
/* group node */
BL::NodeSocket group_sock = b_from_sock.group_socket();
SocketPair& pair = node_groups[b_from_node.ptr.data][group_sock.ptr.data];
from_node = pair.first;
from_name = pair.second;
}
else {
/* regular node */
from_node = node_map[b_from_node.ptr.data];
from_name = b_from_sock.name();
}
/* to sock */
if(b_to_node.is_a(&RNA_NodeGroup)) {
/* group node */
BL::NodeSocket group_sock = b_to_sock.group_socket();
SocketPair& pair = node_groups[b_to_node.ptr.data][group_sock.ptr.data];
to_node = pair.first;
to_name = pair.second;
}
else {
/* regular node */
to_node = node_map[b_to_node.ptr.data];
to_name = b_to_sock.name();
}
graph->connect(from_node->output(from_name.c_str()), to_node->input(to_name.c_str()));
}
}
/* Sync Materials */
void BlenderSync::sync_materials()
{
shader_map.set_default(scene->shaders[scene->default_surface]);
/* material loop */
BL::BlendData::materials_iterator b_mat;
for(b_mat = b_data.materials.begin(); b_mat != b_data.materials.end(); ++b_mat) {
Shader *shader;
/* test if we need to sync */
if(shader_map.sync(&shader, *b_mat)) {
ShaderGraph *graph = new ShaderGraph();
/* create nodes */
if(b_mat && b_mat->node_tree()) {
shader->name = b_mat->name();
PtrSockMap sock_to_node;
BL::ShaderNodeTree b_ntree(b_mat->node_tree());
add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
}
shader->set_graph(graph);
shader->tag_update(scene);
}
}
}
/* Sync World */
void BlenderSync::sync_world()
{
Background *background = scene->background;
Background prevbackground = *background;
BL::World b_world = b_scene.world();
if(world_recalc || b_world.ptr.data != world_map) {
Shader *shader = scene->shaders[scene->default_background];
ShaderGraph *graph = new ShaderGraph();
/* create nodes */
if(b_world && b_world.node_tree()) {
PtrSockMap sock_to_node;
BL::ShaderNodeTree b_ntree(b_world.node_tree());
add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
}
shader->set_graph(graph);
shader->tag_update(scene);
}
if(background->modified(prevbackground))
background->tag_update(scene);
world_map = b_world.ptr.data;
world_recalc = false;
}
/* Sync Lamps */
void BlenderSync::sync_lamps()
{
shader_map.set_default(scene->shaders[scene->default_light]);
/* lamp loop */
BL::BlendData::lamps_iterator b_lamp;
for(b_lamp = b_data.lamps.begin(); b_lamp != b_data.lamps.end(); ++b_lamp) {
Shader *shader;
/* test if we need to sync */
if(shader_map.sync(&shader, *b_lamp)) {
ShaderGraph *graph = new ShaderGraph();
/* create nodes */
if(b_lamp && b_lamp->node_tree()) {
shader->name = b_lamp->name();
PtrSockMap sock_to_node;
BL::ShaderNodeTree b_ntree(b_lamp->node_tree());
add_nodes(b_data, graph, b_ntree, NULL, sock_to_node);
}
shader->set_graph(graph);
shader->tag_update(scene);
}
}
}
void BlenderSync::sync_shaders()
{
shader_map.pre_sync();
sync_world();
sync_lamps();
sync_materials();
/* false = don't delete unused shaders, not supported */
shader_map.post_sync(false);
}
CCL_NAMESPACE_END

@ -0,0 +1,210 @@
/*
* 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 "background.h"
#include "film.h"
#include "graph.h"
#include "integrator.h"
#include "light.h"
#include "mesh.h"
#include "nodes.h"
#include "object.h"
#include "scene.h"
#include "shader.h"
#include "device.h"
#include "blender_sync.h"
#include "blender_util.h"
#include "util_debug.h"
#include "util_foreach.h"
CCL_NAMESPACE_BEGIN
/* Constructor */
BlenderSync::BlenderSync(BL::BlendData b_data_, BL::Scene b_scene_, Scene *scene_, bool preview_)
: b_data(b_data_), b_scene(b_scene_),
shader_map(&scene_->shaders),
object_map(&scene_->objects),
mesh_map(&scene_->meshes),
light_map(&scene_->lights),
world_map(NULL),
world_recalc(false)
{
scene = scene_;
preview = preview_;
}
BlenderSync::~BlenderSync()
{
}
/* Sync */
bool BlenderSync::sync_recalc()
{
BL::BlendData::materials_iterator b_mat;
for(b_mat = b_data.materials.begin(); b_mat != b_data.materials.end(); ++b_mat)
if(b_mat->recalc())
shader_map.set_recalc(*b_mat);
BL::BlendData::lamps_iterator b_lamp;
for(b_lamp = b_data.lamps.begin(); b_lamp != b_data.lamps.end(); ++b_lamp)
if(b_lamp->recalc())
shader_map.set_recalc(*b_lamp);
BL::BlendData::objects_iterator b_ob;
for(b_ob = b_data.objects.begin(); b_ob != b_data.objects.end(); ++b_ob) {
if(b_ob->recalc()) {
object_map.set_recalc(*b_ob);
light_map.set_recalc(*b_ob);
}
if(object_is_mesh(*b_ob)) {
if(b_ob->recalc_data() || b_ob->data().recalc()) {
BL::ID key = object_is_modified(*b_ob)? *b_ob: b_ob->data();
mesh_map.set_recalc(key);
}
}
}
BL::BlendData::meshes_iterator b_mesh;
for(b_mesh = b_data.meshes.begin(); b_mesh != b_data.meshes.end(); ++b_mesh)
if(b_mesh->recalc())
mesh_map.set_recalc(*b_mesh);
BL::BlendData::worlds_iterator b_world;
for(b_world = b_data.worlds.begin(); b_world != b_data.worlds.end(); ++b_world)
if(world_map == b_world->ptr.data && b_world->recalc())
world_recalc = true;
bool recalc =
shader_map.has_recalc() ||
object_map.has_recalc() ||
light_map.has_recalc() ||
mesh_map.has_recalc() ||
world_recalc;
return recalc;
}
void BlenderSync::sync_data(BL::SpaceView3D b_v3d)
{
sync_integrator();
sync_film();
sync_shaders();
sync_objects(b_v3d);
}
/* Integrator */
void BlenderSync::sync_integrator()
{
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
Integrator *integrator = scene->integrator;
Integrator previntegrator = *integrator;
integrator->minbounce = get_int(cscene, "min_bounces");
integrator->maxbounce = get_int(cscene, "max_bounces");
integrator->no_caustics = get_boolean(cscene, "no_caustics");
integrator->blur_caustics = get_float(cscene, "blur_caustics");
if(integrator->modified(previntegrator))
integrator->tag_update(scene);
}
/* Film */
void BlenderSync::sync_film()
{
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
Film *film = scene->film;
Film prevfilm = *film;
film->exposure = get_float(cscene, "exposure");
film->response = get_enum_identifier(cscene, "response_curve");
if(film->modified(prevfilm))
film->tag_update(scene);
}
/* Scene Parameters */
SceneParams BlenderSync::get_scene_params(BL::Scene b_scene)
{
SceneParams params;
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
int shadingsystem = RNA_enum_get(&cscene, "shading_system");
if(shadingsystem == 0)
params.shadingsystem = SceneParams::SVM;
else if(shadingsystem == 1)
params.shadingsystem = SceneParams::OSL;
params.bvh_type = (SceneParams::BVHType)RNA_enum_get(&cscene, "debug_bvh_type");
params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits");
return params;
}
/* Session Parameters */
SessionParams BlenderSync::get_session_params(BL::Scene b_scene, bool background)
{
SessionParams params;
PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles");
/* device type */
DeviceType dtype = (RNA_enum_get(&cscene, "device") == 1)? DEVICE_CUDA: DEVICE_CPU;
params.device_type = DEVICE_CPU;
vector<DeviceType> types = Device::available_types();
foreach(DeviceType dt, types)
if(dt == dtype)
params.device_type = dtype;
/* other parameters */
params.background = background;
params.passes = (background)? get_int(cscene, "passes"): INT_MAX;
params.tile_size = get_int(cscene, "debug_tile_size");
params.min_size = get_int(cscene, "debug_min_size");
params.cancel_timeout = get_float(cscene, "debug_cancel_timeout");
params.reset_timeout = get_float(cscene, "debug_reset_timeout");
params.text_timeout = get_float(cscene, "debug_text_timeout");
if(background) {
params.progressive = true;
params.min_size = INT_MAX;
}
else
params.progressive = true;
return params;
}
CCL_NAMESPACE_END

@ -0,0 +1,105 @@
/*
* 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 __BLENDER_SYNC_H__
#define __BLENDER_SYNC_H__
#include "MEM_guardedalloc.h"
#include "RNA_types.h"
#include "RNA_access.h"
#include "RNA_blender_cpp.h"
#include "blender_util.h"
#include "scene.h"
#include "session.h"
#include "util_map.h"
#include "util_set.h"
#include "util_transform.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
class Background;
class Camera;
class Film;
class Light;
class Mesh;
class Object;
class Scene;
class Shader;
class ShaderGraph;
class ShaderNode;
class BlenderSync {
public:
BlenderSync(BL::BlendData b_data, BL::Scene b_scene, Scene *scene_, bool preview_);
~BlenderSync();
/* sync */
bool sync_recalc();
void sync_data(BL::SpaceView3D b_v3d);
void sync_camera(int width, int height);
void sync_view(BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height);
/* get parameters */
static SceneParams get_scene_params(BL::Scene b_scene);
static SessionParams get_session_params(BL::Scene b_scene, bool background);
private:
/* sync */
void sync_lamps();
void sync_materials();
void sync_objects(BL::SpaceView3D b_v3d);
void sync_film();
void sync_integrator();
void sync_view();
void sync_world();
void sync_shaders();
void sync_nodes(Shader *shader, BL::ShaderNodeTree b_ntree);
Mesh *sync_mesh(BL::Object b_ob, bool object_updated);
void sync_object(BL::Object b_parent, int b_index, BL::Object b_object, Transform& tfm);
void sync_light(BL::Object b_ob, Transform& tfm);
/* util */
void find_shader(BL::ID id, vector<uint>& used_shaders);
bool object_is_modified(BL::Object b_ob);
bool object_is_mesh(BL::Object b_ob);
bool object_is_light(BL::Object b_ob);
/* variables */
BL::BlendData b_data;
BL::Scene b_scene;
id_map<void*, Shader> shader_map;
id_map<ObjectKey, Object> object_map;
id_map<void*, Mesh> mesh_map;
id_map<void*, Light> light_map;
void *world_map;
bool world_recalc;
Scene *scene;
bool preview;
};
CCL_NAMESPACE_END
#endif /* __BLENDER_SYNC_H__ */

@ -0,0 +1,326 @@
/*
* 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 __BLENDER_UTIL_H__
#define __BLENDER_UTIL_H__
#include "util_map.h"
#include "util_path.h"
#include "util_set.h"
#include "util_transform.h"
#include "util_types.h"
#include "util_vector.h"
/* Hacks to hook into Blender API */
extern "C" {
struct RenderEngine;
struct RenderResult;
ID *rna_Object_to_mesh(void *_self, void *reports, void *scene, int apply_modifiers, int settings);
void rna_Main_meshes_remove(void *bmain, void *reports, void *mesh);
void rna_Object_create_duplilist(void *ob, void *reports, void *sce);
void rna_Object_free_duplilist(void *ob, void *reports);
void rna_RenderLayer_rect_set(PointerRNA *ptr, const float *values);
void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values);
struct RenderResult *RE_engine_begin_result(struct RenderEngine *engine, int x, int y, int w, int h);
void RE_engine_update_result(struct RenderEngine *engine, struct RenderResult *result);
void RE_engine_end_result(struct RenderEngine *engine, struct RenderResult *result);
int RE_engine_test_break(struct RenderEngine *engine);
void RE_engine_update_stats(struct RenderEngine *engine, const char *stats, const char *info);
void engine_tag_redraw(void *engine);
void engine_tag_update(void *engine);
int rna_Object_is_modified(void *ob, void *scene, int settings);
}
CCL_NAMESPACE_BEGIN
static inline BL::Mesh object_to_mesh(BL::Object self, BL::Scene scene, bool apply_modifiers, bool render)
{
ID *data = rna_Object_to_mesh(self.ptr.data, NULL, scene.ptr.data, apply_modifiers, render);
PointerRNA ptr;
RNA_id_pointer_create(data, &ptr);
return BL::Mesh(ptr);
}
static inline void object_remove_mesh(BL::BlendData data, BL::Mesh mesh)
{
rna_Main_meshes_remove(data.ptr.data, NULL, mesh.ptr.data);
}
static inline void object_create_duplilist(BL::Object self, BL::Scene scene)
{
rna_Object_create_duplilist(self.ptr.data, NULL, scene.ptr.data);
}
static inline void object_free_duplilist(BL::Object self)
{
rna_Object_free_duplilist(self.ptr.data, NULL);
}
static inline bool object_is_modified(BL::Object self, BL::Scene scene, bool preview)
{
return rna_Object_is_modified(self.ptr.data, scene.ptr.data, (preview)? (1<<0): (1<<1));
}
/* Utilities */
static inline Transform get_transform(BL::Array<float, 16> array)
{
Transform tfm;
/* we assume both types to be just 16 floats, and transpose because blender
use column major matrix order while we use row major */
memcpy(&tfm, &array, sizeof(float)*16);
tfm = transform_transpose(tfm);
return tfm;
}
static inline float2 get_float2(BL::Array<float, 2> array)
{
return make_float2(array[0], array[1]);
}
static inline float3 get_float3(BL::Array<float, 2> array)
{
return make_float3(array[0], array[1], 0.0f);
}
static inline float3 get_float3(BL::Array<float, 3> array)
{
return make_float3(array[0], array[1], array[2]);
}
static inline float3 get_float3(BL::Array<float, 4> array)
{
return make_float3(array[0], array[1], array[2]);
}
static inline int4 get_int4(BL::Array<int, 4> array)
{
return make_int4(array[0], array[1], array[2], array[3]);
}
static inline uint get_layer(BL::Array<int, 20> array)
{
uint layer = 0;
for(uint i = 0; i < 20; i++)
if(array[i])
layer |= (1 << i);
return layer;
}
/*static inline float3 get_float3(PointerRNA& ptr, const char *name)
{
float3 f;
RNA_float_get_array(&ptr, name, &f.x);
return f;
}*/
static inline bool get_boolean(PointerRNA& ptr, const char *name)
{
return RNA_boolean_get(&ptr, name);
}
static inline float get_float(PointerRNA& ptr, const char *name)
{
return RNA_float_get(&ptr, name);
}
static inline int get_int(PointerRNA& ptr, const char *name)
{
return RNA_int_get(&ptr, name);
}
static inline int get_enum(PointerRNA& ptr, const char *name)
{
return RNA_enum_get(&ptr, name);
}
static inline string get_enum_identifier(PointerRNA& ptr, const char *name)
{
PropertyRNA *prop = RNA_struct_find_property(&ptr, name);
const char *identifier = "";
int value = RNA_property_enum_get(&ptr, prop);
RNA_property_enum_identifier(NULL, &ptr, prop, value, &identifier);
return string(identifier);
}
/* Relative Paths */
static inline string blender_absolute_path(BL::BlendData b_data, BL::ID b_id, const string& path)
{
if(path.size() >= 2 && path[0] == '/' && path[1] == '/') {
string dirname = (b_id.library())? b_id.library().filepath(): b_data.filepath();
return path_join(path_dirname(dirname), path.substr(2));
}
return path;
}
/* ID Map
*
* Utility class to keep in sync with blender data.
* Used for objects, meshes, lights and shaders. */
template<typename K, typename T>
class id_map {
public:
id_map(vector<T*> *scene_data_)
{
scene_data = scene_data_;
}
T *find(BL::ID id)
{
return find(id.ptr.id.data);
}
T *find(const K& key)
{
if(b_map.find(key) != b_map.end()) {
T *data = b_map[key];
return data;
}
return NULL;
}
void set_recalc(BL::ID id)
{
b_recalc.insert(id.ptr.data);
}
bool has_recalc()
{
return !(b_recalc.empty());
}
void pre_sync()
{
used_set.clear();
}
bool sync(T **r_data, BL::ID id)
{
return sync(r_data, id, id.ptr.id.data);
}
bool sync(T **r_data, BL::ID id, const K& key)
{
T *data = find(key);
bool recalc;
if(!data) {
/* add data if it didn't exist yet */
data = new T();
scene_data->push_back(data);
b_map[key] = data;
recalc = true;
}
else
recalc = (b_recalc.find(id.ptr.data) != b_recalc.end());
used(data);
*r_data = data;
return recalc;
}
void used(T *data)
{
/* tag data as still in use */
used_set.insert(data);
}
void set_default(T *data)
{
b_map[NULL] = data;
}
bool post_sync(bool do_delete = true)
{
/* remove unused data */
vector<T*> new_scene_data;
typename vector<T*>::iterator it;
bool deleted = false;
for(it = scene_data->begin(); it != scene_data->end(); it++) {
T *data = *it;
if(do_delete && used_set.find(data) == used_set.end()) {
delete data;
deleted = true;
}
else
new_scene_data.push_back(data);
}
*scene_data = new_scene_data;
/* update mapping */
map<K, T*> new_map;
typedef pair<const K, T*> TMapPair;
typename map<K, T*>::iterator jt;
for(jt = b_map.begin(); jt != b_map.end(); jt++) {
TMapPair& pair = *jt;
if(used_set.find(pair.second) != used_set.end())
new_map[pair.first] = pair.second;
}
used_set.clear();
b_recalc.clear();
b_map = new_map;
return deleted;
}
protected:
vector<T*> *scene_data;
map<K, T*> b_map;
set<T*> used_set;
set<void*> b_recalc;
};
/* Object Key */
struct ObjectKey {
void *parent;
int index;
void *ob;
ObjectKey(void *parent_, int index_, void *ob_)
: parent(parent_), index(index_), ob(ob_) {}
bool operator<(const ObjectKey& k) const
{ return (parent < k.parent || (parent == k.parent && (index < k.index || (index == k.index && ob < k.ob)))); }
};
CCL_NAMESPACE_END
#endif /* __BLENDER_UTIL_H__ */

@ -0,0 +1,18 @@
INCLUDE_DIRECTORIES(. ../kernel ../kernel/svm ../render ../util ../device)
SET(sources
bvh.cpp
bvh_build.cpp
bvh_node.cpp
bvh_sort.cpp)
SET(headers
bvh.h
bvh_build.h
bvh_node.h
bvh_params.h
bvh_sort.h)
ADD_LIBRARY(bvh ${sources} ${headers})

661
intern/cycles/bvh/bvh.cpp Normal file

@ -0,0 +1,661 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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 "mesh.h"
#include "object.h"
#include "scene.h"
#include "bvh.h"
#include "bvh_build.h"
#include "bvh_node.h"
#include "bvh_params.h"
#include "util_cache.h"
#include "util_debug.h"
#include "util_foreach.h"
#include "util_progress.h"
#include "util_types.h"
CCL_NAMESPACE_BEGIN
/* Pack Utility */
struct BVHStackEntry
{
const BVHNode *node;
int idx;
BVHStackEntry(const BVHNode* n = 0, int i = 0)
: node(n), idx(i)
{
}
int encodeIdx() const
{
return (node->is_leaf())? ~idx: idx;
}
};
/* BVH */
BVH::BVH(const BVHParams& params_, const vector<Object*>& objects_)
: params(params_), objects(objects_)
{
}
BVH *BVH::create(const BVHParams& params, const vector<Object*>& objects)
{
if(params.use_qbvh)
return new QBVH(params, objects);
else
return new RegularBVH(params, objects);
}
/* Cache */
bool BVH::cache_read(CacheData& key)
{
key.add(&params, sizeof(params));
foreach(Object *ob, objects) {
key.add(ob->mesh->verts);
key.add(ob->mesh->triangles);
}
CacheData value;
if(Cache::global.lookup(key, value)) {
value.read(pack.root_index);
value.read(pack.nodes);
value.read(pack.object_node);
value.read(pack.tri_woop);
value.read(pack.prim_index);
value.read(pack.prim_object);
value.read(pack.is_leaf);
return true;
}
return false;
}
void BVH::cache_write(CacheData& key)
{
CacheData value;
value.add(pack.root_index);
value.add(pack.nodes);
value.add(pack.object_node);
value.add(pack.tri_woop);
value.add(pack.prim_index);
value.add(pack.prim_object);
value.add(pack.is_leaf);
Cache::global.insert(key, value);
}
/* Building */
void BVH::build(Progress& progress)
{
progress.set_substatus("Building BVH");
/* cache read */
CacheData key("bvh");
if(params.use_cache) {
progress.set_substatus("Looking in BVH cache");
if(cache_read(key))
return;
}
/* build nodes */
vector<int> prim_index;
vector<int> prim_object;
BVHBuild bvh_build(objects, prim_index, prim_object, params, progress);
BVHNode *root = bvh_build.run();
if(progress.get_cancel()) {
if(root) root->deleteSubtree();
return;
}
/* todo: get rid of this copy */
pack.prim_index = prim_index;
pack.prim_object = prim_object;
/* compute SAH */
if(!params.top_level)
pack.SAH = root->computeSubtreeSAHCost(params);
if(progress.get_cancel()) {
root->deleteSubtree();
return;
}
/* pack triangles */
progress.set_substatus("Packing BVH triangles");
pack_triangles();
if(progress.get_cancel()) {
root->deleteSubtree();
return;
}
/* pack nodes */
progress.set_substatus("Packing BVH nodes");
array<int> tmp_prim_object = pack.prim_object;
pack_nodes(tmp_prim_object, root);
/* free build nodes */
root->deleteSubtree();
if(progress.get_cancel()) return;
/* cache write */
if(params.use_cache) {
progress.set_substatus("Writing BVH cache");
cache_write(key);
}
}
/* Refitting */
void BVH::refit(Progress& progress)
{
progress.set_substatus("Packing BVH triangles");
pack_triangles();
if(progress.get_cancel()) return;
progress.set_substatus("Refitting BVH nodes");
refit_nodes();
}
/* Triangles */
void BVH::pack_triangle(int idx, float4 woop[3])
{
/* create Woop triangle */
int tob = pack.prim_object[idx];
const Mesh *mesh = objects[tob]->mesh;
int tidx = pack.prim_index[idx];
const int *vidx = mesh->triangles[tidx].v;
const float3* vpos = &mesh->verts[0];
float3 v0 = vpos[vidx[0]];
float3 v1 = vpos[vidx[1]];
float3 v2 = vpos[vidx[2]];
float3 r0 = v0 - v2;
float3 r1 = v1 - v2;
float3 r2 = cross(r0, r1);
if(dot(r0, r0) == 0.0f || dot(r1, r1) == 0.0f || dot(r2, r2) == 0.0f) {
/* degenerate */
woop[0] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
woop[1] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
woop[2] = make_float4(0.0f, 0.0f, 0.0f, 0.0f);
}
else {
Transform t = make_transform(
r0.x, r1.x, r2.x, v2.x,
r0.y, r1.y, r2.y, v2.y,
r0.z, r1.z, r2.z, v2.z,
0.0f, 0.0f, 0.0f, 1.0f);
t = transform_inverse(t);
woop[0] = make_float4(t.z.x, t.z.y, t.z.z, -t.z.w);
woop[1] = make_float4(t.x.x, t.x.y, t.x.z, t.x.w);
woop[2] = make_float4(t.y.x, t.y.y, t.y.z, t.y.w);
}
}
void BVH::pack_triangles()
{
int nsize = TRI_NODE_SIZE;
size_t tidx_size = pack.prim_index.size();
pack.tri_woop.clear();
pack.tri_woop.resize(tidx_size * nsize);
for(unsigned int i = 0; i < tidx_size; i++) {
if(pack.prim_index[i] != -1) {
float4 woop[3];
pack_triangle(i, woop);
memcpy(&pack.tri_woop[i * nsize], woop, sizeof(float4)*3);
}
}
}
/* Pack Instances */
void BVH::pack_instances(size_t nodes_size)
{
/* The BVH's for instances are built separately, but for traversal all
BVH's are stored in global arrays. This function merges them into the
top level BVH, adjusting indexes and offsets where appropriate. */
bool use_qbvh = params.use_qbvh;
size_t nsize = (use_qbvh)? BVH_QNODE_SIZE: BVH_NODE_SIZE;
/* adjust primitive index to point to the triangle in the global array, for
meshes with transform applied and already in the top level BVH */
for(size_t i = 0; i < pack.prim_index.size(); i++)
if(pack.prim_index[i] != -1)
pack.prim_index[i] += objects[pack.prim_object[i]]->mesh->tri_offset;
/* track offsets of instanced BVH data in global array */
size_t tri_offset = pack.prim_index.size();
size_t nodes_offset = nodes_size;
/* clear array that gives the node indexes for instanced objects */
pack.object_node.clear();
/* reserve */
size_t prim_index_size = pack.prim_index.size();
size_t tri_woop_size = pack.tri_woop.size();
size_t pack_prim_index_offset = prim_index_size;
size_t pack_tri_woop_offset = tri_woop_size;
size_t pack_nodes_offset = nodes_size;
size_t object_offset = 0;
foreach(Object *ob, objects) {
Mesh *mesh = ob->mesh;
BVH *bvh = mesh->bvh;
if(!mesh->transform_applied) {
prim_index_size += bvh->pack.prim_index.size();
tri_woop_size += bvh->pack.tri_woop.size();
nodes_size += bvh->pack.nodes.size()*nsize;
}
}
pack.prim_index.resize(prim_index_size);
pack.prim_object.resize(prim_index_size);
pack.tri_woop.resize(tri_woop_size);
pack.nodes.resize(nodes_size);
pack.object_node.resize(objects.size());
int *pack_prim_index = &pack.prim_index[0];
int *pack_prim_object = &pack.prim_object[0];
float4 *pack_tri_woop = &pack.tri_woop[0];
int4 *pack_nodes = &pack.nodes[0];
/* merge */
foreach(Object *ob, objects) {
Mesh *mesh = ob->mesh;
/* if mesh transform is applied, that means it's already in the top
level BVH, and we don't need to merge it in */
if(mesh->transform_applied) {
pack.object_node[object_offset++] = 0;
continue;
}
BVH *bvh = mesh->bvh;
int noffset = nodes_offset/nsize;
int mesh_tri_offset = mesh->tri_offset;
/* fill in node indexes for instances */
if(bvh->pack.is_leaf[0])
pack.object_node[object_offset++] = -noffset-1;
else
pack.object_node[object_offset++] = noffset;
/* merge primitive and object indexes */
{
size_t bvh_prim_index_size = bvh->pack.prim_index.size();
int *bvh_prim_index = &bvh->pack.prim_index[0];
for(size_t i = 0; i < bvh_prim_index_size; i++) {
pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + mesh_tri_offset;
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
pack_prim_index_offset++;
}
}
/* merge triangle intersection data */
{
memcpy(pack_tri_woop+pack_tri_woop_offset, &bvh->pack.tri_woop[0],
bvh->pack.tri_woop.size()*sizeof(float4));
pack_tri_woop_offset += bvh->pack.tri_woop.size();
}
/* merge nodes */
{
size_t nsize_bbox = (use_qbvh)? nsize-2: nsize-1;
int4 *bvh_nodes = &bvh->pack.nodes[0];
size_t bvh_nodes_size = bvh->pack.nodes.size();
int *bvh_is_leaf = &bvh->pack.is_leaf[0];
for(size_t i = 0, j = 0; i < bvh_nodes_size; i+=nsize, j++) {
memcpy(pack_nodes + pack_nodes_offset, bvh_nodes + i, nsize_bbox*sizeof(int4));
/* modify offsets into arrays */
int4 data = bvh_nodes[i + nsize_bbox];
if(bvh_is_leaf[j]) {
data.x += tri_offset;
data.y += tri_offset;
}
else {
data.x += (data.x < 0)? -noffset: noffset;
data.y += (data.y < 0)? -noffset: noffset;
if(use_qbvh) {
data.z += (data.z < 0)? -noffset: noffset;
data.w += (data.w < 0)? -noffset: noffset;
}
}
pack_nodes[pack_nodes_offset + nsize_bbox] = data;
if(use_qbvh)
pack_nodes[pack_nodes_offset + nsize_bbox+1] = bvh_nodes[i + nsize_bbox+1];
pack_nodes_offset += nsize;
}
}
nodes_offset += bvh->pack.nodes.size();
tri_offset += bvh->pack.prim_index.size();
}
}
/* Regular BVH */
RegularBVH::RegularBVH(const BVHParams& params_, const vector<Object*>& objects_)
: BVH(params_, objects_)
{
}
void RegularBVH::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf)
{
if(leaf->num_triangles() == 1 && pack.prim_index[leaf->m_lo] == -1)
/* object */
pack_node(e.idx, leaf->m_bounds, leaf->m_bounds, ~(leaf->m_lo), 0);
else
/* triangle */
pack_node(e.idx, leaf->m_bounds, leaf->m_bounds, leaf->m_lo, leaf->m_hi);
}
void RegularBVH::pack_inner(const BVHStackEntry& e, const BVHStackEntry& e0, const BVHStackEntry& e1)
{
pack_node(e.idx, e0.node->m_bounds, e1.node->m_bounds, e0.encodeIdx(), e1.encodeIdx());
}
void RegularBVH::pack_node(int idx, const BoundBox& b0, const BoundBox& b1, int c0, int c1)
{
int4 data[BVH_NODE_SIZE] =
{
make_int4(__float_as_int(b0.min.x), __float_as_int(b0.max.x), __float_as_int(b0.min.y), __float_as_int(b0.max.y)),
make_int4(__float_as_int(b1.min.x), __float_as_int(b1.max.x), __float_as_int(b1.min.y), __float_as_int(b1.max.y)),
make_int4(__float_as_int(b0.min.z), __float_as_int(b0.max.z), __float_as_int(b1.min.z), __float_as_int(b1.max.z)),
make_int4(c0, c1, 0, 0)
};
memcpy(&pack.nodes[idx * BVH_NODE_SIZE], data, sizeof(int4)*BVH_NODE_SIZE);
}
void RegularBVH::pack_nodes(const array<int>& prims, const BVHNode *root)
{
size_t node_size = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
/* resize arrays */
pack.nodes.clear();
pack.is_leaf.clear();
pack.is_leaf.resize(node_size);
/* for top level BVH, first merge existing BVH's so we know the offsets */
if(params.top_level)
pack_instances(node_size*BVH_NODE_SIZE);
else
pack.nodes.resize(node_size*BVH_NODE_SIZE);
int nextNodeIdx = 0;
vector<BVHStackEntry> stack;
stack.push_back(BVHStackEntry(root, nextNodeIdx++));
while(stack.size()) {
BVHStackEntry e = stack.back();
stack.pop_back();
pack.is_leaf[e.idx] = e.node->is_leaf();
if(e.node->is_leaf()) {
/* leaf node */
const LeafNode* leaf = reinterpret_cast<const LeafNode*>(e.node);
pack_leaf(e, leaf);
}
else {
/* innner node */
stack.push_back(BVHStackEntry(e.node->get_child(0), nextNodeIdx++));
stack.push_back(BVHStackEntry(e.node->get_child(1), nextNodeIdx++));
pack_inner(e, stack[stack.size()-2], stack[stack.size()-1]);
}
}
/* root index to start traversal at, to handle case of single leaf node */
pack.root_index = (pack.is_leaf[0])? -1: 0;
}
void RegularBVH::refit_nodes()
{
assert(!params.top_level);
BoundBox bbox;
refit_node(0, pack.is_leaf[0], bbox);
}
void RegularBVH::refit_node(int idx, bool leaf, BoundBox& bbox)
{
int4 *data = &pack.nodes[idx*4];
int c0 = data[3].x;
int c1 = data[3].y;
if(leaf) {
/* refit leaf node */
for(int tri = c0; tri < c1; tri++) {
int tidx = pack.prim_index[tri];
int tob = pack.prim_object[tri];
Object *ob = objects[tob];
if(tidx == -1) {
/* object instance */
bbox.grow(ob->bounds);
}
else {
/* triangles */
const Mesh *mesh = ob->mesh;
int tri_offset = (params.top_level)? mesh->tri_offset: 0;
const int *vidx = mesh->triangles[tidx - tri_offset].v;
const float3 *vpos = &mesh->verts[0];
bbox.grow(vpos[vidx[0]]);
bbox.grow(vpos[vidx[1]]);
bbox.grow(vpos[vidx[2]]);
}
}
pack_node(idx, bbox, bbox, c0, c1);
}
else {
/* refit inner node, set bbox from children */
BoundBox bbox0, bbox1;
refit_node((c0 < 0)? -c0-1: c0, (c0 < 0), bbox0);
refit_node((c1 < 0)? -c1-1: c1, (c1 < 0), bbox1);
bbox.grow(bbox0);
bbox.grow(bbox1);
pack_node(idx, bbox0, bbox1, c0, c1);
}
}
/* QBVH */
QBVH::QBVH(const BVHParams& params_, const vector<Object*>& objects_)
: BVH(params_, objects_)
{
params.use_qbvh = true;
}
void QBVH::pack_leaf(const BVHStackEntry& e, const LeafNode *leaf)
{
float4 data[BVH_QNODE_SIZE];
memset(data, 0, sizeof(data));
if(leaf->num_triangles() == 1 && pack.prim_index[leaf->m_lo] == -1) {
/* object */
data[6].x = __int_as_float(~(leaf->m_lo));
data[6].y = __int_as_float(0);
}
else {
/* triangle */
data[6].x = __int_as_float(leaf->m_lo);
data[6].y = __int_as_float(leaf->m_hi);
}
memcpy(&pack.nodes[e.idx * BVH_QNODE_SIZE], data, sizeof(float4)*BVH_QNODE_SIZE);
}
void QBVH::pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num)
{
float4 data[BVH_QNODE_SIZE];
for(int i = 0; i < num; i++) {
float3 bb_min = en[i].node->m_bounds.min;
float3 bb_max = en[i].node->m_bounds.max;
data[0][i] = bb_min.x;
data[1][i] = bb_max.x;
data[2][i] = bb_min.y;
data[3][i] = bb_max.y;
data[4][i] = bb_min.z;
data[5][i] = bb_max.z;
data[6][i] = __int_as_float(en[i].encodeIdx());
data[7][i] = 0.0f;
}
for(int i = num; i < 4; i++) {
data[0][i] = 0.0f;
data[1][i] = 0.0f;
data[2][i] = 0.0f;
data[3][i] = 0.0f;
data[4][i] = 0.0f;
data[5][i] = 0.0f;
data[6][i] = __int_as_float(0);
data[7][i] = 0.0f;
}
memcpy(&pack.nodes[e.idx * BVH_QNODE_SIZE], data, sizeof(float4)*BVH_QNODE_SIZE);
}
/* Quad SIMD Nodes */
void QBVH::pack_nodes(const array<int>& prims, const BVHNode *root)
{
size_t node_size = root->getSubtreeSize(BVH_STAT_NODE_COUNT);
/* resize arrays */
pack.nodes.clear();
pack.is_leaf.clear();
pack.is_leaf.resize(node_size);
/* for top level BVH, first merge existing BVH's so we know the offsets */
if(params.top_level)
pack_instances(node_size*BVH_QNODE_SIZE);
else
pack.nodes.resize(node_size*BVH_QNODE_SIZE);
int nextNodeIdx = 0;
vector<BVHStackEntry> stack;
stack.push_back(BVHStackEntry(root, nextNodeIdx++));
while(stack.size()) {
BVHStackEntry e = stack.back();
stack.pop_back();
pack.is_leaf[e.idx] = e.node->is_leaf();
if(e.node->is_leaf()) {
/* leaf node */
const LeafNode* leaf = reinterpret_cast<const LeafNode*>(e.node);
pack_leaf(e, leaf);
}
else {
/* inner node */
const BVHNode *node = e.node;
const BVHNode *node0 = node->get_child(0);
const BVHNode *node1 = node->get_child(1);
/* collect nodes */
const BVHNode *nodes[4];
int numnodes = 0;
if(node0->is_leaf()) {
nodes[numnodes++] = node0;
}
else {
nodes[numnodes++] = node0->get_child(0);
nodes[numnodes++] = node0->get_child(1);
}
if(node1->is_leaf()) {
nodes[numnodes++] = node1;
}
else {
nodes[numnodes++] = node1->get_child(0);
nodes[numnodes++] = node1->get_child(1);
}
/* push entries on the stack */
for(int i = 0; i < numnodes; i++)
stack.push_back(BVHStackEntry(nodes[i], nextNodeIdx++));
/* set node */
pack_inner(e, &stack[stack.size()-numnodes], numnodes);
}
}
/* root index to start traversal at, to handle case of single leaf node */
pack.root_index = (pack.is_leaf[0])? -1: 0;
}
void QBVH::refit_nodes()
{
assert(0); /* todo */
}
CCL_NAMESPACE_END

152
intern/cycles/bvh/bvh.h Normal file

@ -0,0 +1,152 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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.
*/
#ifndef __BVH_H__
#define __BVH_H__
#include "bvh_params.h"
#include "util_types.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
class BVHNode;
class BVHStackEntry;
class BVHParams;
class BoundBox;
class CacheData;
class LeafNode;
class Object;
class Progress;
#define BVH_NODE_SIZE 4
#define BVH_QNODE_SIZE 8
#define BVH_ALIGN 4096
#define TRI_NODE_SIZE 3
/* Packed BVH
*
* BVH stored as it will be used for traversal on the rendering device. */
struct PackedBVH {
/* BVH nodes storage, one node is 4x int4, and contains two bounding boxes,
and child, triangle or object indexes dependening on the node type */
array<int4> nodes;
/* object index to BVH node index mapping for instances */
array<int> object_node;
/* precomputed triangle intersection data, one triangle is 4x float4 */
array<float4> tri_woop;
/* mapping from BVH primitive index to true primitive index, as primitives
may be duplicated due to spatial splits. -1 for instances. */
array<int> prim_index;
/* mapping from BVH primitive index, to the object id of that primitive. */
array<int> prim_object;
/* quick array to lookup if a node is a leaf, not used for traversal, only
for instance BVH merging */
array<int> is_leaf;
/* index of the root node. */
int root_index;
/* surface area heuristic, for building top level BVH */
float SAH;
PackedBVH()
{
root_index = 0;
SAH = 0.0f;
}
};
/* BVH */
class BVH
{
public:
PackedBVH pack;
BVHParams params;
vector<Object*> objects;
static BVH *create(const BVHParams& params, const vector<Object*>& objects);
void build(Progress& progress);
void refit(Progress& progress);
protected:
BVH(const BVHParams& params, const vector<Object*>& objects);
/* cache */
bool cache_read(CacheData& key);
void cache_write(CacheData& key);
/* triangles */
void pack_triangles();
void pack_triangle(int idx, float4 woop[3]);
/* merge instance BVH's */
void pack_instances(size_t nodes_size);
/* for subclasses to implement */
virtual void pack_nodes(const array<int>& prims, const BVHNode *root) = 0;
virtual void refit_nodes() = 0;
};
/* Regular BVH
*
* Typical BVH with each node having two children. */
class RegularBVH : public BVH {
protected:
/* constructor */
friend class BVH;
RegularBVH(const BVHParams& params, const vector<Object*>& objects);
/* pack */
void pack_nodes(const array<int>& prims, const BVHNode *root);
void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf);
void pack_inner(const BVHStackEntry& e, const BVHStackEntry& e0, const BVHStackEntry& e1);
void pack_node(int idx, const BoundBox& b0, const BoundBox& b1, int c0, int c1);
/* refit */
void refit_nodes();
void refit_node(int idx, bool leaf, BoundBox& bbox);
};
/* QBVH
*
* Quad BVH, with each node having four children, to use with SIMD instructions. */
class QBVH : public BVH {
protected:
/* constructor */
friend class BVH;
QBVH(const BVHParams& params, const vector<Object*>& objects);
/* pack */
void pack_nodes(const array<int>& prims, const BVHNode *root);
void pack_leaf(const BVHStackEntry& e, const LeafNode *leaf);
void pack_inner(const BVHStackEntry& e, const BVHStackEntry *en, int num);
/* refit */
void refit_nodes();
};
CCL_NAMESPACE_END
#endif /* __BVH_H__ */

@ -0,0 +1,545 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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 "bvh_build.h"
#include "bvh_node.h"
#include "bvh_params.h"
#include "bvh_sort.h"
#include "mesh.h"
#include "object.h"
#include "scene.h"
#include "util_algorithm.h"
#include "util_foreach.h"
#include "util_progress.h"
#include "util_time.h"
CCL_NAMESPACE_BEGIN
/* Constructor / Destructor */
BVHBuild::BVHBuild(const vector<Object*>& objects_,
vector<int>& prim_index_, vector<int>& prim_object_,
const BVHParams& params_, Progress& progress_)
: objects(objects_),
prim_index(prim_index_),
prim_object(prim_object_),
params(params_),
progress(progress_),
progress_start_time(0.0)
{
spatial_min_overlap = 0.0f;
progress_num_duplicates = 0;
}
BVHBuild::~BVHBuild()
{
}
/* Adding References */
void BVHBuild::add_reference_mesh(NodeSpec& root, Mesh *mesh, int i)
{
for(uint j = 0; j < mesh->triangles.size(); j++) {
Mesh::Triangle t = mesh->triangles[j];
Reference ref;
ref.prim_index = j;
ref.prim_object = i;
for(int k = 0; k < 3; k++) {
float3 pt = mesh->verts[t.v[k]];
ref.bounds.grow(pt);
}
references.push_back(ref);
root.bounds.grow(ref.bounds);
}
}
void BVHBuild::add_reference_object(NodeSpec& root, Object *ob, int i)
{
Reference ref;
ref.prim_index = -1;
ref.prim_object = i;
ref.bounds = ob->bounds;
references.push_back(ref);
root.bounds.grow(ref.bounds);
}
void BVHBuild::add_references(NodeSpec& root)
{
/* init root spec */
root.num = 0;
root.bounds = BoundBox();
/* add objects */
int i = 0;
foreach(Object *ob, objects) {
if(params.top_level) {
if(ob->mesh->transform_applied)
add_reference_mesh(root, ob->mesh, i);
else
add_reference_object(root, ob, i);
}
else
add_reference_mesh(root, ob->mesh, i);
i++;
if(progress.get_cancel()) return;
}
/* happens mostly on empty meshes */
if(!root.bounds.valid())
root.bounds.grow(make_float3(0.0f, 0.0f, 0.0f));
root.num = references.size();
}
/* Build */
BVHNode* BVHBuild::run()
{
NodeSpec root;
/* add references */
add_references(root);
if(progress.get_cancel()) return NULL;
/* init spatial splits */
if(params.top_level) /* todo: get rid of this */
params.use_spatial_split = false;
spatial_min_overlap = root.bounds.area() * params.spatial_split_alpha;
spatial_right_bounds.clear();
spatial_right_bounds.resize(max(root.num, (int)BVHParams::NUM_SPATIAL_BINS) - 1);
/* init progress updates */
progress_num_duplicates = 0;
progress_start_time = time_dt();
/* build recursively */
return build_node(root, 0, 0.0f, 1.0f);
}
void BVHBuild::progress_update(float progress_start, float progress_end)
{
if(time_dt() - progress_start_time < 0.25f)
return;
float duplicates = (float)progress_num_duplicates/(float)references.size();
string msg = string_printf("Building BVH %.0f%%, duplicates %.0f%%",
progress_start*100.0f, duplicates*100.0f);
progress.set_substatus(msg);
progress_start_time = time_dt();
}
BVHNode* BVHBuild::build_node(const NodeSpec& spec, int level, float progress_start, float progress_end)
{
/* progress update */
progress_update(progress_start, progress_end);
if(progress.get_cancel()) return NULL;
/* small enough or too deep => create leaf. */
if(spec.num <= params.min_leaf_size || level >= BVHParams::MAX_DEPTH)
return create_leaf_node(spec);
/* find split candidates. */
float area = spec.bounds.area();
float leafSAH = area * params.triangle_cost(spec.num);
float nodeSAH = area * params.node_cost(2);
ObjectSplit object = find_object_split(spec, nodeSAH);
SpatialSplit spatial;
if(params.use_spatial_split && level < BVHParams::MAX_SPATIAL_DEPTH) {
BoundBox overlap = object.left_bounds;
overlap.intersect(object.right_bounds);
if(overlap.area() >= spatial_min_overlap)
spatial = find_spatial_split(spec, nodeSAH);
}
/* leaf SAH is the lowest => create leaf. */
float minSAH = min(min(leafSAH, object.sah), spatial.sah);
if(minSAH == leafSAH && spec.num <= params.max_leaf_size)
return create_leaf_node(spec);
/* perform split. */
NodeSpec left, right;
if(params.use_spatial_split && minSAH == spatial.sah)
do_spatial_split(left, right, spec, spatial);
if(!left.num || !right.num)
do_object_split(left, right, spec, object);
/* create inner node. */
progress_num_duplicates += left.num + right.num - spec.num;
float progress_mid = lerp(progress_start, progress_end, (float)right.num / (float)(left.num + right.num));
BVHNode* rightNode = build_node(right, level + 1, progress_start, progress_mid);
if(progress.get_cancel()) {
if(rightNode) rightNode->deleteSubtree();
return NULL;
}
BVHNode* leftNode = build_node(left, level + 1, progress_mid, progress_end);
if(progress.get_cancel()) {
if(leftNode) leftNode->deleteSubtree();
return NULL;
}
return new InnerNode(spec.bounds, leftNode, rightNode);
}
BVHNode *BVHBuild::create_object_leaf_nodes(const Reference *ref, int num)
{
if(num == 0) {
BoundBox bounds;
return new LeafNode(bounds, 0, 0);
}
else if(num == 1) {
prim_index.push_back(ref[0].prim_index);
prim_object.push_back(ref[0].prim_object);
return new LeafNode(ref[0].bounds, prim_index.size()-1, prim_index.size());
}
else {
int mid = num/2;
BVHNode *leaf0 = create_object_leaf_nodes(ref, mid);
BVHNode *leaf1 = create_object_leaf_nodes(ref+mid, num-mid);
BoundBox bounds;
bounds.grow(leaf0->m_bounds);
bounds.grow(leaf1->m_bounds);
return new InnerNode(bounds, leaf0, leaf1);
}
}
BVHNode* BVHBuild::create_leaf_node(const NodeSpec& spec)
{
vector<int>& p_index = prim_index;
vector<int>& p_object = prim_object;
BoundBox bounds;
int num = 0;
for(int i = 0; i < spec.num; i++) {
if(references.back().prim_index != -1) {
p_index.push_back(references.back().prim_index);
p_object.push_back(references.back().prim_object);
bounds.grow(references.back().bounds);
references.pop_back();
num++;
}
}
BVHNode *leaf = NULL;
if(num > 0) {
leaf = new LeafNode(bounds, p_index.size() - num, p_index.size());
if(num == spec.num)
return leaf;
}
/* while there may be multiple triangles in a leaf, for object primitives
* we want them to be the only one, so we */
int ob_num = spec.num - num;
BVHNode *oleaf = create_object_leaf_nodes(&references.back() - (ob_num - 1), ob_num);
for(int i = 0; i < ob_num; i++)
references.pop_back();
if(leaf)
return new InnerNode(spec.bounds, leaf, oleaf);
else
return oleaf;
}
/* Object Split */
BVHBuild::ObjectSplit BVHBuild::find_object_split(const NodeSpec& spec, float nodeSAH)
{
ObjectSplit split;
const Reference *ref_ptr = &references[references.size() - spec.num];
for(int dim = 0; dim < 3; dim++) {
/* sort references */
bvh_reference_sort(references.size() - spec.num, references.size(), &references[0], dim);
/* sweep right to left and determine bounds. */
BoundBox right_bounds;
for(int i = spec.num - 1; i > 0; i--) {
right_bounds.grow(ref_ptr[i].bounds);
spatial_right_bounds[i - 1] = right_bounds;
}
/* sweep left to right and select lowest SAH. */
BoundBox left_bounds;
for(int i = 1; i < spec.num; i++) {
left_bounds.grow(ref_ptr[i - 1].bounds);
right_bounds = spatial_right_bounds[i - 1];
float sah = nodeSAH +
left_bounds.area() * params.triangle_cost(i) +
right_bounds.area() * params.triangle_cost(spec.num - i);
if(sah < split.sah) {
split.sah = sah;
split.dim = dim;
split.num_left = i;
split.left_bounds = left_bounds;
split.right_bounds = right_bounds;
}
}
}
return split;
}
void BVHBuild::do_object_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const ObjectSplit& split)
{
/* sort references according to split */
int start = references.size() - spec.num;
int end = references.size(); /* todo: is this right? */
bvh_reference_sort(start, end, &references[0], split.dim);
/* split node specs */
left.num = split.num_left;
left.bounds = split.left_bounds;
right.num = spec.num - split.num_left;
right.bounds = split.right_bounds;
}
/* Spatial Split */
BVHBuild::SpatialSplit BVHBuild::find_spatial_split(const NodeSpec& spec, float nodeSAH)
{
/* initialize bins. */
float3 origin = spec.bounds.min;
float3 binSize = (spec.bounds.max - origin) * (1.0f / (float)BVHParams::NUM_SPATIAL_BINS);
float3 invBinSize = 1.0f / binSize;
for(int dim = 0; dim < 3; dim++) {
for(int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) {
SpatialBin& bin = spatial_bins[dim][i];
bin.bounds = BoundBox();
bin.enter = 0;
bin.exit = 0;
}
}
/* chop references into bins. */
for(unsigned int refIdx = references.size() - spec.num; refIdx < references.size(); refIdx++) {
const Reference& ref = references[refIdx];
float3 firstBinf = (ref.bounds.min - origin) * invBinSize;
float3 lastBinf = (ref.bounds.max - origin) * invBinSize;
int3 firstBin = make_int3(firstBinf.x, firstBinf.y, firstBinf.z);
int3 lastBin = make_int3(lastBinf.x, lastBinf.y, lastBinf.z);
firstBin = clamp(firstBin, 0, BVHParams::NUM_SPATIAL_BINS - 1);
lastBin = clamp(lastBin, firstBin, BVHParams::NUM_SPATIAL_BINS - 1);
for(int dim = 0; dim < 3; dim++) {
Reference currRef = ref;
for(int i = firstBin[dim]; i < lastBin[dim]; i++) {
Reference leftRef, rightRef;
split_reference(leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1));
spatial_bins[dim][i].bounds.grow(leftRef.bounds);
currRef = rightRef;
}
spatial_bins[dim][lastBin[dim]].bounds.grow(currRef.bounds);
spatial_bins[dim][firstBin[dim]].enter++;
spatial_bins[dim][lastBin[dim]].exit++;
}
}
/* select best split plane. */
SpatialSplit split;
for(int dim = 0; dim < 3; dim++) {
/* sweep right to left and determine bounds. */
BoundBox right_bounds;
for(int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) {
right_bounds.grow(spatial_bins[dim][i].bounds);
spatial_right_bounds[i - 1] = right_bounds;
}
/* sweep left to right and select lowest SAH. */
BoundBox left_bounds;
int leftNum = 0;
int rightNum = spec.num;
for(int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) {
left_bounds.grow(spatial_bins[dim][i - 1].bounds);
leftNum += spatial_bins[dim][i - 1].enter;
rightNum -= spatial_bins[dim][i - 1].exit;
float sah = nodeSAH +
left_bounds.area() * params.triangle_cost(leftNum) +
spatial_right_bounds[i - 1].area() * params.triangle_cost(rightNum);
if(sah < split.sah) {
split.sah = sah;
split.dim = dim;
split.pos = origin[dim] + binSize[dim] * (float)i;
}
}
}
return split;
}
void BVHBuild::do_spatial_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const SpatialSplit& split)
{
/* Categorize references and compute bounds.
*
* Left-hand side: [left_start, left_end[
* Uncategorized/split: [left_end, right_start[
* Right-hand side: [right_start, refs.size()[ */
vector<Reference>& refs = references;
int left_start = refs.size() - spec.num;
int left_end = left_start;
int right_start = refs.size();
left.bounds = right.bounds = BoundBox();
for(int i = left_end; i < right_start; i++) {
if(refs[i].bounds.max[split.dim] <= split.pos) {
/* entirely on the left-hand side */
left.bounds.grow(refs[i].bounds);
swap(refs[i], refs[left_end++]);
}
else if(refs[i].bounds.min[split.dim] >= split.pos) {
/* entirely on the right-hand side */
right.bounds.grow(refs[i].bounds);
swap(refs[i--], refs[--right_start]);
}
}
/* duplicate or unsplit references intersecting both sides. */
while(left_end < right_start) {
/* split reference. */
Reference lref, rref;
split_reference(lref, rref, refs[left_end], split.dim, split.pos);
/* compute SAH for duplicate/unsplit candidates. */
BoundBox lub = left.bounds; // Unsplit to left: new left-hand bounds.
BoundBox rub = right.bounds; // Unsplit to right: new right-hand bounds.
BoundBox ldb = left.bounds; // Duplicate: new left-hand bounds.
BoundBox rdb = right.bounds; // Duplicate: new right-hand bounds.
lub.grow(refs[left_end].bounds);
rub.grow(refs[left_end].bounds);
ldb.grow(lref.bounds);
rdb.grow(rref.bounds);
float lac = params.triangle_cost(left_end - left_start);
float rac = params.triangle_cost(refs.size() - right_start);
float lbc = params.triangle_cost(left_end - left_start + 1);
float rbc = params.triangle_cost(refs.size() - right_start + 1);
float unsplitLeftSAH = lub.area() * lbc + right.bounds.area() * rac;
float unsplitRightSAH = left.bounds.area() * lac + rub.area() * rbc;
float duplicateSAH = ldb.area() * lbc + rdb.area() * rbc;
float minSAH = min(min(unsplitLeftSAH, unsplitRightSAH), duplicateSAH);
if(minSAH == unsplitLeftSAH) {
/* unsplit to left */
left.bounds = lub;
left_end++;
}
else if(minSAH == unsplitRightSAH) {
/* unsplit to right */
right.bounds = rub;
swap(refs[left_end], refs[--right_start]);
}
else {
/* duplicate */
left.bounds = ldb;
right.bounds = rdb;
refs[left_end++] = lref;
refs.push_back(rref);
}
}
left.num = left_end - left_start;
right.num = refs.size() - right_start;
}
void BVHBuild::split_reference(Reference& left, Reference& right, const Reference& ref, int dim, float pos)
{
/* initialize references. */
left.prim_index = right.prim_index = ref.prim_index;
left.prim_object = right.prim_object = ref.prim_object;
left.bounds = right.bounds = BoundBox();
/* loop over vertices/edges. */
Object *ob = objects[ref.prim_object];
const Mesh *mesh = ob->mesh;
const int *inds = mesh->triangles[ref.prim_index].v;
const float3 *verts = &mesh->verts[0];
const float3* v1 = &verts[inds[2]];
for(int i = 0; i < 3; i++) {
const float3* v0 = v1;
int vindex = inds[i];
v1 = &verts[vindex];
float v0p = (*v0)[dim];
float v1p = (*v1)[dim];
/* insert vertex to the boxes it belongs to. */
if(v0p <= pos)
left.bounds.grow(*v0);
if(v0p >= pos)
right.bounds.grow(*v0);
/* edge intersects the plane => insert intersection to both boxes. */
if((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
float3 t = lerp(*v0, *v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
left.bounds.grow(t);
right.bounds.grow(t);
}
}
/* intersect with original bounds. */
left.bounds.max[dim] = pos;
right.bounds.min[dim] = pos;
left.bounds.intersect(ref.bounds);
right.bounds.intersect(ref.bounds);
}
CCL_NAMESPACE_END

@ -0,0 +1,152 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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.
*/
#ifndef __BVH_BUILD_H__
#define __BVH_BUILD_H__
#include <float.h>
#include "bvh.h"
#include "util_boundbox.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
class BVHParams;
class Mesh;
class Object;
class Progress;
/* BVH Builder */
class BVHBuild
{
public:
struct Reference
{
int prim_index;
int prim_object;
BoundBox bounds;
Reference()
{
}
};
struct NodeSpec
{
int num;
BoundBox bounds;
NodeSpec()
{
num = 0;
}
};
BVHBuild(
const vector<Object*>& objects,
vector<int>& prim_index,
vector<int>& prim_object,
const BVHParams& params,
Progress& progress);
~BVHBuild();
BVHNode *run();
protected:
/* adding references */
void add_reference_mesh(NodeSpec& root, Mesh *mesh, int i);
void add_reference_object(NodeSpec& root, Object *ob, int i);
void add_references(NodeSpec& root);
/* building */
BVHNode *build_node(const NodeSpec& spec, int level, float progress_start, float progress_end);
BVHNode *create_leaf_node(const NodeSpec& spec);
BVHNode *create_object_leaf_nodes(const Reference *ref, int num);
void progress_update(float progress_start, float progress_end);
/* object splits */
struct ObjectSplit
{
float sah;
int dim;
int num_left;
BoundBox left_bounds;
BoundBox right_bounds;
ObjectSplit()
: sah(FLT_MAX), dim(0), num_left(0)
{
}
};
ObjectSplit find_object_split(const NodeSpec& spec, float nodeSAH);
void do_object_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const ObjectSplit& split);
/* spatial splits */
struct SpatialSplit
{
float sah;
int dim;
float pos;
SpatialSplit()
: sah(FLT_MAX), dim(0), pos(0.0f)
{
}
};
struct SpatialBin
{
BoundBox bounds;
int enter;
int exit;
};
SpatialSplit find_spatial_split(const NodeSpec& spec, float nodeSAH);
void do_spatial_split(NodeSpec& left, NodeSpec& right, const NodeSpec& spec, const SpatialSplit& split);
void split_reference(Reference& left, Reference& right, const Reference& ref, int dim, float pos);
/* objects and primitive references */
vector<Object*> objects;
vector<Reference> references;
/* output primitive indexes and objects */
vector<int>& prim_index;
vector<int>& prim_object;
/* build parameters */
BVHParams params;
/* progress reporting */
Progress& progress;
double progress_start_time;
int progress_num_duplicates;
/* spatial splitting */
float spatial_min_overlap;
vector<BoundBox> spatial_right_bounds;
SpatialBin spatial_bins[3][BVHParams::NUM_SPATIAL_BINS];
};
CCL_NAMESPACE_END
#endif /* __BVH_BUILD_H__ */

@ -0,0 +1,101 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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 "bvh.h"
#include "bvh_build.h"
#include "bvh_node.h"
#include "util_debug.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
int BVHNode::getSubtreeSize(BVH_STAT stat) const
{
int cnt = 0;
switch(stat)
{
case BVH_STAT_NODE_COUNT:
cnt = 1;
break;
case BVH_STAT_LEAF_COUNT:
cnt = is_leaf() ? 1 : 0;
break;
case BVH_STAT_INNER_COUNT:
cnt = is_leaf() ? 0 : 1;
break;
case BVH_STAT_TRIANGLE_COUNT:
cnt = is_leaf() ? reinterpret_cast<const LeafNode*>(this)->num_triangles() : 0;
break;
case BVH_STAT_CHILDNODE_COUNT:
cnt = num_children();
break;
default:
assert(0); /* unknown mode */
}
if(!is_leaf())
for(int i=0;i<num_children();i++)
cnt += get_child(i)->getSubtreeSize(stat);
return cnt;
}
void BVHNode::deleteSubtree()
{
for(int i=0;i<num_children();i++)
get_child(i)->deleteSubtree();
delete this;
}
float BVHNode::computeSubtreeSAHCost(const BVHParams& p, float probability) const
{
float SAH = probability * p.cost(num_children(), num_triangles());
for(int i=0;i<num_children();i++) {
BVHNode *child = get_child(i);
SAH += child->computeSubtreeSAHCost(p, probability * child->m_bounds.area()/m_bounds.area());
}
return SAH;
}
void InnerNode::print(int depth) const
{
for(int i = 0; i < depth; i++)
printf(" ");
printf("inner node %p\n", (void*)this);
if(children[0])
children[0]->print(depth+1);
if(children[1])
children[1]->print(depth+1);
}
void LeafNode::print(int depth) const
{
for(int i = 0; i < depth; i++)
printf(" ");
printf("leaf node %d to %d\n", m_lo, m_hi);
}
CCL_NAMESPACE_END

@ -0,0 +1,108 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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.
*/
#ifndef __BVH_NODE_H__
#define __BVH_NODE_H__
#include "util_boundbox.h"
#include "util_debug.h"
#include "util_types.h"
CCL_NAMESPACE_BEGIN
enum BVH_STAT
{
BVH_STAT_NODE_COUNT,
BVH_STAT_INNER_COUNT,
BVH_STAT_LEAF_COUNT,
BVH_STAT_TRIANGLE_COUNT,
BVH_STAT_CHILDNODE_COUNT
};
class BVHParams;
class BVHNode
{
public:
BVHNode()
{
}
virtual bool is_leaf() const = 0;
virtual int num_children() const = 0;
virtual BVHNode *get_child(int i) const = 0;
virtual int num_triangles() const { return 0; }
virtual void print(int depth = 0) const = 0;
float getArea() const { return m_bounds.area(); }
BoundBox m_bounds;
// Subtree functions
int getSubtreeSize(BVH_STAT stat=BVH_STAT_NODE_COUNT) const;
float computeSubtreeSAHCost(const BVHParams& p, float probability = 1.0f) const;
void deleteSubtree();
};
class InnerNode : public BVHNode
{
public:
InnerNode(const BoundBox& bounds, BVHNode* child0, BVHNode* child1)
{
m_bounds = bounds;
children[0] = child0;
children[1] = child1;
}
bool is_leaf() const { return false; }
int num_children() const { return 2; }
BVHNode *get_child(int i) const{ assert(i>=0 && i<2); return children[i]; }
void print(int depth) const;
BVHNode *children[2];
};
class LeafNode : public BVHNode
{
public:
LeafNode(const BoundBox& bounds, int lo, int hi)
{
m_bounds = bounds;
m_lo = lo;
m_hi = hi;
}
LeafNode(const LeafNode& s)
: BVHNode()
{
*this = s;
}
bool is_leaf() const { return true; }
int num_children() const { return 0; }
BVHNode *get_child(int) const { return NULL; }
int num_triangles() const { return m_hi - m_lo; }
void print(int depth) const;
int m_lo;
int m_hi;
};
CCL_NAMESPACE_END
#endif /* __BVH_NODE_H__ */

@ -0,0 +1,86 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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.
*/
#ifndef __BVH_PARAMS_H__
#define __BVH_PARAMS_H__
CCL_NAMESPACE_BEGIN
/* BVH Parameters */
class BVHParams
{
public:
/* spatial split area threshold */
bool use_spatial_split;
float spatial_split_alpha;
/* SAH costs */
float sah_node_cost;
float sah_triangle_cost;
/* number of triangles in leaf */
int min_leaf_size;
int max_leaf_size;
/* object or mesh level bvh */
bool top_level;
/* disk cache */
bool use_cache;
/* QBVH */
bool use_qbvh;
/* fixed parameters */
enum {
MAX_DEPTH = 64,
MAX_SPATIAL_DEPTH = 48,
NUM_SPATIAL_BINS = 32
};
BVHParams()
{
use_spatial_split = true;
spatial_split_alpha = 1e-5f;
sah_node_cost = 1.0f;
sah_triangle_cost = 1.0f;
min_leaf_size = 1;
max_leaf_size = 0x7FFFFFF;
top_level = false;
use_cache = false;
use_qbvh = false;
}
/* SAH costs */
float cost(int num_nodes, int num_tris) const
{ return node_cost(num_nodes) + triangle_cost(num_tris); }
float triangle_cost(int n) const
{ return n*sah_triangle_cost; }
float node_cost(int n) const
{ return n*sah_node_cost; }
};
CCL_NAMESPACE_END
#endif /* __BVH_PARAMS_H__ */

@ -0,0 +1,57 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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 "bvh_build.h"
#include "bvh_sort.h"
#include "util_algorithm.h"
#include "util_debug.h"
CCL_NAMESPACE_BEGIN
struct BVHReferenceCompare {
public:
int dim;
BVHReferenceCompare(int dim_)
{
dim = dim_;
}
bool operator()(const BVHBuild::Reference& ra, const BVHBuild::Reference& rb)
{
float ca = ra.bounds.min[dim] + ra.bounds.max[dim];
float cb = rb.bounds.min[dim] + rb.bounds.max[dim];
if(ca < cb) return true;
else if(ca > cb) return false;
else if(ra.prim_object < rb.prim_object) return true;
else if(ra.prim_object > rb.prim_object) return false;
else if(ra.prim_index < rb.prim_index) return true;
else if(ra.prim_index > rb.prim_index) return false;
return false;
}
};
void bvh_reference_sort(int start, int end, BVHBuild::Reference *data, int dim)
{
sort(data+start, data+end, BVHReferenceCompare(dim));
}
CCL_NAMESPACE_END

@ -0,0 +1,28 @@
/*
* Adapted from code copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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.
*/
#ifndef __BVH_SORT_H__
#define __BVH_SORT_H__
CCL_NAMESPACE_BEGIN
void bvh_reference_sort(int start, int end, BVHBuild::Reference *data, int dim);
CCL_NAMESPACE_END
#endif /* __BVH_SORT_H__ */

@ -0,0 +1,14 @@
#!/usr/bin/python
import os
import string
import sys
name = string.replace(sys.argv[1], ".zip", "")
os.system("rm -f %s.dmg" % (name))
os.system("mkdir -p /tmp/cycles_dmg")
os.system("rm /tmp/cycles_dmg/*")
os.system("cp %s.zip /tmp/cycles_dmg/" % (name))
os.system("/usr/bin/hdiutil create -fs HFS+ -srcfolder /tmp/cycles_dmg -volname %s %s.dmg" % (name, name))

@ -0,0 +1,210 @@
###########################################################################
# Boost setup
MESSAGE(STATUS "BOOST_PATH ${BOOST_PATH}")
SET(BOOST_ROOT ${BOOST_PATH})
SET(Boost_ADDITIONAL_VERSIONS "1.45" "1.44"
"1.43" "1.43.0" "1.42" "1.42.0"
"1.41" "1.41.0" "1.40" "1.40.0"
"1.39" "1.39.0" "1.38" "1.38.0"
"1.37" "1.37.0" "1.34.1" "1_34_1")
IF(LINKSTATIC)
SET(Boost_USE_STATIC_LIBS ON)
ENDIF()
SET(Boost_USE_MULTITHREADED ON)
FIND_PACKAGE(Boost 1.34 REQUIRED COMPONENTS filesystem regex system serialization thread)
MESSAGE(STATUS "Boost found ${Boost_FOUND}")
MESSAGE(STATUS "Boost version ${Boost_VERSION}")
MESSAGE(STATUS "Boost include dirs ${Boost_INCLUDE_DIRS}")
MESSAGE(STATUS "Boost library dirs ${Boost_LIBRARY_DIRS}")
MESSAGE(STATUS "Boost libraries ${Boost_LIBRARIES}")
INCLUDE_DIRECTORIES("${Boost_INCLUDE_DIRS}")
LINK_DIRECTORIES("${Boost_LIBRARY_DIRS}")
IF(WITH_NETWORK)
ADD_DEFINITIONS(-DWITH_NETWORK)
ENDIF()
IF(WITH_MULTI)
ADD_DEFINITIONS(-DWITH_MULTI)
ENDIF()
###########################################################################
# OpenImageIO
MESSAGE(STATUS "OIIO_PATH = ${OIIO_PATH}")
FIND_LIBRARY(OPENIMAGEIO_LIBRARY NAMES OpenImageIO PATHS ${OIIO_PATH}/lib)
FIND_PATH(OPENIMAGEIO_INCLUDES OpenImageIO/imageio.h ${OIIO_PATH}/include)
FIND_PROGRAM(OPENIMAGEIO_IDIFF NAMES idiff PATHS ${OIIO_PATH}/bin)
IF(OPENIMAGEIO_INCLUDES AND OPENIMAGEIO_LIBRARY)
SET(OPENIMAGEIO_FOUND TRUE)
MESSAGE(STATUS "OpenImageIO includes = ${OPENIMAGEIO_INCLUDES}")
MESSAGE(STATUS "OpenImageIO library = ${OPENIMAGEIO_LIBRARY}")
ELSE()
MESSAGE(STATUS "OpenImageIO not found")
ENDIF()
ADD_DEFINITIONS(-DWITH_OIIO)
INCLUDE_DIRECTORIES(${OPENIMAGEIO_INCLUDES} ${OPENIMAGEIO_INCLUDES}/OpenImageIO)
###########################################################################
# OpenGL
FIND_PACKAGE(OpenGL)
MESSAGE(STATUS "OPENGL_FOUND=${OPENGL_FOUND}")
INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR})
###########################################################################
# GLUT
SET(GLUT_ROOT_PATH ${GLUT_PATH})
FIND_PACKAGE(GLUT)
MESSAGE(STATUS "GLUT_FOUND=${GLUT_FOUND}")
INCLUDE_DIRECTORIES(${GLUT_INCLUDE_DIR})
###########################################################################
# GLEW
SET(GLEW_VERSION 1.5.1)
FIND_LIBRARY(GLEW_LIBRARIES NAMES GLEW PATHS ${GLEW_PATH}/lib)
FIND_PATH(GLEW_INCLUDES NAMES glew.h PATH_SUFFIXES GL PATHS ${GLEW_PATH}/include)
IF(GLEW_INCLUDES AND GLEW_LIBRARIES)
MESSAGE(STATUS "GLEW includes = ${GLEW_INCLUDES}")
MESSAGE(STATUS "GLEW library = ${GLEW_LIBRARIES}")
ELSE()
MESSAGE(STATUS "GLEW not found")
ENDIF()
INCLUDE_DIRECTORIES("${GLEW_INCLUDES}")
###########################################################################
# OpenShadingLanguage
IF(WITH_OSL)
MESSAGE(STATUS "OSL_PATH = ${OSL_PATH}")
FIND_LIBRARY(OSL_LIBRARIES NAMES oslexec oslcomp oslquery PATHS ${OSL_PATH}/lib)
FIND_PATH(OSL_INCLUDES OSL/oslclosure.h ${OSL_PATH}/include)
FIND_PROGRAM(OSL_COMPILER NAMES oslc PATHS ${OSL_PATH}/bin)
IF(OSL_INCLUDES AND OSL_LIBRARIES AND OSL_COMPILER)
SET(OSL_FOUND TRUE)
MESSAGE(STATUS "OSL includes = ${OSL_INCLUDES}")
MESSAGE(STATUS "OSL library = ${OSL_LIBRARIES}")
MESSAGE(STATUS "OSL compiler = ${OSL_COMPILER}")
ELSE()
MESSAGE(STATUS "OSL not found")
ENDIF()
ADD_DEFINITIONS(-DWITH_OSL)
INCLUDE_DIRECTORIES(${OSL_INCLUDES} ${OSL_INCLUDES}/OSL ${OSL_INCLUDES}/../../../src/liboslexec)
ENDIF()
###########################################################################
# Partio
IF(WITH_PARTIO)
MESSAGE(STATUS "PARTIO_PATH = ${PARTIO_PATH}")
FIND_LIBRARY(PARTIO_LIBRARIES NAMES partio PATHS ${PARTIO_PATH}/lib)
FIND_PATH(PARTIO_INCLUDES Partio.h ${PARTIO_PATH}/include)
FIND_PACKAGE(ZLIB)
IF(PARTIO_INCLUDES AND PARTIO_LIBRARIES AND ZLIB_LIBRARIES)
LIST(APPEND PARTIO_LIBRARIES ${ZLIB_LIBRARIES})
SET(PARTIO_FOUND TRUE)
MESSAGE(STATUS "PARTIO includes = ${PARTIO_INCLUDES}")
MESSAGE(STATUS "PARTIO library = ${PARTIO_LIBRARIES}")
ELSE()
MESSAGE(STATUS "PARTIO not found")
ENDIF()
ADD_DEFINITIONS(-DWITH_PARTIO)
INCLUDE_DIRECTORIES(${PARTIO_INCLUDES})
ENDIF()
###########################################################################
# Python
IF(WITH_BLENDER)
FIND_PATH(PYTHON_INCLUDE_DIRS Python.h PATHS ${PYTHON_PATH} ${PYTHON_PATH}/include ${PYTHON_PATH}/include/python3.1 ${PYTHON_PATH}/include/python3.2 NO_DEFAULT_PATH)
IF(WIN32)
FIND_LIBRARY(PYTHON_LIBRARIES NAMES python31 PATHS ${PYTHON_PATH}/lib)
ENDIF()
ENDIF()
###########################################################################
# Blender
IF(WITH_BLENDER)
FIND_PATH(BLENDER_INCLUDE_DIRS RNA_blender.h PATHS ${BLENDER_PATH}/include)
IF(WIN32)
SET(BLENDER_LIBRARIES ${BLENDER_PATH}/bin/Release/blender.lib)
ENDIF()
ENDIF()
###########################################################################
# CUDA
IF(WITH_CUDA)
FIND_LIBRARY(CUDA_LIBRARIES NAMES cuda PATHS ${CUDA_PATH}/lib ${CUDA_PATH}/lib/Win32 NO_DEFAULT_PATH)
FIND_PATH(CUDA_INCLUDES cuda.h ${CUDA_PATH}/include NO_DEFAULT_PATH)
FIND_PROGRAM(CUDA_NVCC NAMES nvcc PATHS ${CUDA_PATH}/bin NO_DEFAULT_PATH)
IF(CUDA_INCLUDES AND CUDA_LIBRARIES AND CUDA_NVCC)
MESSAGE(STATUS "CUDA includes = ${CUDA_INCLUDES}")
MESSAGE(STATUS "CUDA library = ${CUDA_LIBRARIES}")
MESSAGE(STATUS "CUDA nvcc = ${CUDA_NVCC}")
ELSE()
MESSAGE(STATUS "CUDA not found")
ENDIF()
ADD_DEFINITIONS(-DWITH_CUDA)
INCLUDE_DIRECTORIES(${CUDA_INCLUDES})
ENDIF()
###########################################################################
# OpenCL
IF(WITH_OPENCL)
IF(APPLE)
SET(OPENCL_INCLUDES "/System/Library/Frameworks/OpenCL.framework/Headers")
SET(OPENCL_LIBRARIES "-framework OpenCL")
ENDIF()
IF(WIN32)
SET(OPENCL_INCLUDES "")
SET(OPENCL_LIRBARIES "OpenCL")
ENDIF()
IF(UNIX AND NOT APPLE)
SET(OPENCL_INCLUDES ${OPENCL_PATH})
SET(OPENCL_LIRBARIES "OpenCL")
ENDIF()
ADD_DEFINITIONS(-DWITH_OPENCL)
INCLUDE_DIRECTORIES(${OPENCL_INCLUDES})
ENDIF()

@ -0,0 +1,27 @@
# Platform specific build flags
SET(GCC_WARNING_FLAGS "-Wall -Wextra -Wno-unused-parameter -Wno-long-long") # -pedantic
SET(GCC_OPTIM_FLAGS "-ffast-math -fPIC -msse -msse2 -msse3 -mtune=native")
IF(APPLE)
SET(CMAKE_CXX_FLAGS "${GCC_WARNING_FLAGS} ${GCC_OPTIM_FLAGS}")
SET(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
SET(PYTHON_MODULE_FLAGS "-undefined dynamic_lookup")
ENDIF(APPLE)
IF(WIN32)
SET(CMAKE_CXX_FLAGS "-D_CRT_SECURE_NO_WARNINGS /EHsc /fp:fast")
SET(RTTI_DISABLE_FLAGS "/GR- -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
SET(PYTHON_MODULE_FLAGS "-DLL")
ENDIF(WIN32)
IF(UNIX AND NOT APPLE)
SET(CMAKE_CXX_FLAGS "${GCC_WARNING_FLAGS} ${GCC_OPTIM_FLAGS}")
SET(RTTI_DISABLE_FLAGS "-fno-rtti -DBOOST_NO_RTTI -DBOOST_NO_TYPEID")
SET(PYTHON_MODULE_FLAGS "-fPIC")
ENDIF(UNIX AND NOT APPLE)
ADD_DEFINITIONS(-DCCL_NAMESPACE_BEGIN=namespace\ ccl\ {)
ADD_DEFINITIONS(-DCCL_NAMESPACE_END=})

@ -0,0 +1,18 @@
INCLUDE_DIRECTORIES(. ../kernel ../kernel/svm ../kernel/osl ../util ../render)
SET(sources
device.cpp
device_cpu.cpp
device_cuda.cpp
device_multi.cpp
device_network.cpp
device_opencl.cpp)
SET(headers
device.h
device_intern.h
device_network.h)
ADD_LIBRARY(device ${sources} ${headers})

@ -0,0 +1,198 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include "device.h"
#include "device_intern.h"
#include "util_cuda.h"
#include "util_debug.h"
#include "util_opengl.h"
#include "util_types.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
/* Device Task */
DeviceTask::DeviceTask(Type type_)
: type(type_), x(0), y(0), w(0), h(0), rng_state(0), rgba(0), buffer(0),
pass(0), resolution(0),
displace_input(0), displace_offset(0), displace_x(0), displace_w(0)
{
}
void DeviceTask::split(ThreadQueue<DeviceTask>& tasks, int num)
{
if(type == DISPLACE) {
for(int i = 0; i < num; i++) {
int tx = displace_x + (displace_w/num)*i;
int tw = (i == num-1)? displace_w - i*(displace_w/num): displace_w/num;
DeviceTask task = *this;
task.displace_x = tx;
task.displace_w = tw;
tasks.push(task);
}
}
else {
for(int i = 0; i < num; i++) {
int ty = y + (h/num)*i;
int th = (i == num-1)? h - i*(h/num): h/num;
DeviceTask task = *this;
task.y = ty;
task.h = th;
tasks.push(task);
}
}
}
/* Device */
void Device::pixels_alloc(device_memory& mem)
{
mem_alloc(mem, MEM_READ_WRITE);
}
void Device::pixels_copy_from(device_memory& mem, int y, int w, int h)
{
mem_copy_from(mem, sizeof(uchar)*4*y*w, sizeof(uchar)*4*w*h);
}
void Device::pixels_free(device_memory& mem)
{
mem_free(mem);
}
void Device::draw_pixels(device_memory& rgba, int y, int w, int h, int width, int height)
{
pixels_copy_from(rgba, y, w, h);
glPixelZoom((float)width/(float)w, (float)height/(float)h);
glRasterPos2f(0, y);
glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void*)rgba.data_pointer);
glRasterPos2f(0.0f, 0.0f);
glPixelZoom(1.0f, 1.0f);
}
Device *Device::create(DeviceType type, bool background)
{
Device *device;
switch(type) {
case DEVICE_CPU:
device = device_cpu_create();
break;
#ifdef WITH_CUDA
case DEVICE_CUDA:
if(cuLibraryInit())
device = device_cuda_create(background);
else
device = NULL;
break;
#endif
#ifdef WITH_MULTI
case DEVICE_MULTI:
device = device_multi_create(background);
break;
#endif
#ifdef WITH_NETWORK
case DEVICE_NETWORK:
device = device_network_create("127.0.0.1");
break;
#endif
#ifdef WITH_OPENCL
case DEVICE_OPENCL:
device = device_opencl_create(background);
break;
#endif
default:
return NULL;
}
return device;
}
DeviceType Device::type_from_string(const char *name)
{
if(strcmp(name, "cpu") == 0)
return DEVICE_CPU;
else if(strcmp(name, "cuda") == 0)
return DEVICE_CUDA;
else if(strcmp(name, "opencl") == 0)
return DEVICE_OPENCL;
else if(strcmp(name, "network") == 0)
return DEVICE_NETWORK;
else if(strcmp(name, "multi") == 0)
return DEVICE_MULTI;
return DEVICE_NONE;
}
string Device::string_from_type(DeviceType type)
{
if(type == DEVICE_CPU)
return "cpu";
else if(type == DEVICE_CUDA)
return "cuda";
else if(type == DEVICE_OPENCL)
return "opencl";
else if(type == DEVICE_NETWORK)
return "network";
else if(type == DEVICE_MULTI)
return "multi";
return "";
}
vector<DeviceType> Device::available_types()
{
vector<DeviceType> types;
types.push_back(DEVICE_CPU);
#ifdef WITH_CUDA
if(cuLibraryInit())
types.push_back(DEVICE_CUDA);
#endif
#ifdef WITH_OPENCL
types.push_back(DEVICE_OPENCL);
#endif
#ifdef WITH_NETWORK
types.push_back(DEVICE_NETWORK);
#endif
#ifdef WITH_MULTI
types.push_back(DEVICE_MULTI);
#endif
return types;
}
CCL_NAMESPACE_END

@ -0,0 +1,136 @@
/*
* 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 __DEVICE_H__
#define __DEVICE_H__
#include <stdlib.h>
#include "device_memory.h"
#include "util_string.h"
#include "util_thread.h"
#include "util_types.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
class Progress;
enum DeviceType {
DEVICE_NONE,
DEVICE_CPU,
DEVICE_OPENCL,
DEVICE_CUDA,
DEVICE_NETWORK,
DEVICE_MULTI
};
enum MemoryType {
MEM_READ_ONLY,
MEM_WRITE_ONLY,
MEM_READ_WRITE
};
/* Device Task */
class DeviceTask {
public:
typedef enum { PATH_TRACE, TONEMAP, DISPLACE } Type;
Type type;
int x, y, w, h;
device_ptr rng_state;
device_ptr rgba;
device_ptr buffer;
int pass;
int resolution;
device_ptr displace_input;
device_ptr displace_offset;
int displace_x, displace_w;
DeviceTask(Type type = PATH_TRACE);
void split(ThreadQueue<DeviceTask>& tasks, int num);
};
/* Device */
class Device {
protected:
Device() {}
DeviceType type;
bool background;
public:
virtual ~Device() {}
/* info */
virtual string description() = 0;
/* regular memory */
virtual void mem_alloc(device_memory& mem, MemoryType type) = 0;
virtual void mem_copy_to(device_memory& mem) = 0;
virtual void mem_copy_from(device_memory& mem,
size_t offset, size_t size) = 0;
virtual void mem_zero(device_memory& mem) = 0;
virtual void mem_free(device_memory& mem) = 0;
/* constant memory */
virtual void const_copy_to(const char *name, void *host, size_t size) = 0;
/* texture memory */
virtual void tex_alloc(const char *name, device_memory& mem,
bool interpolation = false, bool periodic = false) {};
virtual void tex_free(device_memory& mem) {};
/* pixel memory */
virtual void pixels_alloc(device_memory& mem);
virtual void pixels_copy_from(device_memory& mem, int y, int w, int h);
virtual void pixels_free(device_memory& mem);
/* open shading language, only for CPU device */
virtual void *osl_memory() { return NULL; }
/* tasks */
virtual void task_add(DeviceTask& task) = 0;
virtual void task_wait() = 0;
virtual void task_cancel() = 0;
/* opengl drawing */
virtual void draw_pixels(device_memory& mem, int y, int w, int h,
int width, int height);
#ifdef WITH_NETWORK
/* networking */
void server_run();
#endif
/* static */
static Device *create(DeviceType type, bool background = true);
static DeviceType type_from_string(const char *name);
static string string_from_type(DeviceType type);
static vector<DeviceType> available_types();
};
CCL_NAMESPACE_END
#endif /* __DEVICE_H__ */

@ -0,0 +1,216 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include "device.h"
#include "device_intern.h"
#include "kernel.h"
#include "kernel_types.h"
#include "osl_shader.h"
#include "util_debug.h"
#include "util_foreach.h"
#include "util_function.h"
#include "util_opengl.h"
#include "util_progress.h"
#include "util_system.h"
#include "util_thread.h"
CCL_NAMESPACE_BEGIN
class CPUDevice : public Device
{
public:
vector<thread*> threads;
ThreadQueue<DeviceTask> tasks;
KernelGlobals *kg;
CPUDevice()
{
kg = kernel_globals_create();
threads.resize(system_cpu_thread_count());
for(size_t i = 0; i < threads.size(); i++)
threads[i] = new thread(function_bind(&CPUDevice::thread_run, this, i));
}
~CPUDevice()
{
tasks.stop();
foreach(thread *t, threads) {
t->join();
delete t;
}
kernel_globals_free(kg);
}
string description()
{
return system_cpu_brand_string();
}
void mem_alloc(device_memory& mem, MemoryType type)
{
mem.device_pointer = mem.data_pointer;
}
void mem_copy_to(device_memory& mem)
{
/* no-op */
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
/* no-op */
}
void mem_zero(device_memory& mem)
{
memset((void*)mem.device_pointer, 0, mem.memory_size());
}
void mem_free(device_memory& mem)
{
mem.device_pointer = 0;
}
void const_copy_to(const char *name, void *host, size_t size)
{
kernel_const_copy(kg, name, host, size);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
kernel_tex_copy(kg, name, mem.data_pointer, mem.data_width, mem.data_height);
mem.device_pointer = mem.data_pointer;
}
void tex_free(device_memory& mem)
{
mem.device_pointer = 0;
}
void *osl_memory()
{
#ifdef WITH_OSL
return kernel_osl_memory(kg);
#else
return NULL;
#endif
}
void thread_run(int t)
{
DeviceTask task;
while(tasks.worker_wait_pop(task)) {
if(task.type == DeviceTask::PATH_TRACE)
thread_path_trace(task);
else if(task.type == DeviceTask::TONEMAP)
thread_tonemap(task);
else if(task.type == DeviceTask::DISPLACE)
thread_displace(task);
tasks.worker_done();
}
}
void thread_path_trace(DeviceTask& task)
{
if(tasks.worker_cancel())
return;
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_init(kg);
#endif
for(int y = task.y; y < task.y + task.h; y++) {
for(int x = task.x; x < task.x + task.w; x++)
kernel_cpu_path_trace(kg, (float4*)task.buffer, (unsigned int*)task.rng_state, task.pass, x, y);
if(tasks.worker_cancel())
break;
}
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_free(kg);
#endif
}
void thread_tonemap(DeviceTask& task)
{
for(int y = task.y; y < task.y + task.h; y++) {
for(int x = task.x; x < task.x + task.w; x++)
kernel_cpu_tonemap(kg, (uchar4*)task.rgba, (float4*)task.buffer, task.pass, task.resolution, x, y);
}
}
void thread_displace(DeviceTask& task)
{
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_init(kg);
#endif
for(int x = task.displace_x; x < task.displace_x + task.displace_w; x++) {
kernel_cpu_displace(kg, (uint4*)task.displace_input, (float3*)task.displace_offset, x);
if(tasks.worker_cancel())
break;
}
#ifdef WITH_OSL
if(kernel_osl_use(kg))
OSLShader::thread_free(kg);
#endif
}
void task_add(DeviceTask& task)
{
if(task.type == DeviceTask::TONEMAP)
tasks.push(task);
else
task.split(tasks, threads.size());
}
void task_wait()
{
tasks.wait_done();
}
void task_cancel()
{
tasks.cancel();
}
};
Device *device_cpu_create()
{
return new CPUDevice();
}
CCL_NAMESPACE_END

@ -0,0 +1,682 @@
/*
* 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 <stdlib.h>
#include <string.h>
#include "device.h"
#include "device_intern.h"
#include "util_cuda.h"
#include "util_debug.h"
#include "util_map.h"
#include "util_opengl.h"
#include "util_path.h"
#include "util_types.h"
CCL_NAMESPACE_BEGIN
class CUDADevice : public Device
{
public:
CUdevice cuDevice;
CUcontext cuContext;
CUmodule cuModule;
map<device_ptr, bool> tex_interp_map;
int cuDevId;
struct PixelMem {
GLuint cuPBO;
CUgraphicsResource cuPBOresource;
GLuint cuTexId;
int w, h;
};
map<device_ptr, PixelMem> pixel_mem_map;
CUdeviceptr cuda_device_ptr(device_ptr mem)
{
return (CUdeviceptr)mem;
}
const char *cuda_error_string(CUresult result)
{
switch(result) {
case CUDA_SUCCESS: return "No errors";
case CUDA_ERROR_INVALID_VALUE: return "Invalid value";
case CUDA_ERROR_OUT_OF_MEMORY: return "Out of memory";
case CUDA_ERROR_NOT_INITIALIZED: return "Driver not initialized";
case CUDA_ERROR_DEINITIALIZED: return "Driver deinitialized";
case CUDA_ERROR_NO_DEVICE: return "No CUDA-capable device available";
case CUDA_ERROR_INVALID_DEVICE: return "Invalid device";
case CUDA_ERROR_INVALID_IMAGE: return "Invalid kernel image";
case CUDA_ERROR_INVALID_CONTEXT: return "Invalid context";
case CUDA_ERROR_CONTEXT_ALREADY_CURRENT: return "Context already current";
case CUDA_ERROR_MAP_FAILED: return "Map failed";
case CUDA_ERROR_UNMAP_FAILED: return "Unmap failed";
case CUDA_ERROR_ARRAY_IS_MAPPED: return "Array is mapped";
case CUDA_ERROR_ALREADY_MAPPED: return "Already mapped";
case CUDA_ERROR_NO_BINARY_FOR_GPU: return "No binary for GPU";
case CUDA_ERROR_ALREADY_ACQUIRED: return "Already acquired";
case CUDA_ERROR_NOT_MAPPED: return "Not mapped";
case CUDA_ERROR_NOT_MAPPED_AS_ARRAY: return "Mapped resource not available for access as an array";
case CUDA_ERROR_NOT_MAPPED_AS_POINTER: return "Mapped resource not available for access as a pointer";
case CUDA_ERROR_ECC_UNCORRECTABLE: return "Uncorrectable ECC error detected";
case CUDA_ERROR_UNSUPPORTED_LIMIT: return "CUlimit not supported by device";
case CUDA_ERROR_INVALID_SOURCE: return "Invalid source";
case CUDA_ERROR_FILE_NOT_FOUND: return "File not found";
case CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND: return "Link to a shared object failed to resolve";
case CUDA_ERROR_SHARED_OBJECT_INIT_FAILED: return "Shared object initialization failed";
case CUDA_ERROR_INVALID_HANDLE: return "Invalid handle";
case CUDA_ERROR_NOT_FOUND: return "Not found";
case CUDA_ERROR_NOT_READY: return "CUDA not ready";
case CUDA_ERROR_LAUNCH_FAILED: return "Launch failed";
case CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES: return "Launch exceeded resources";
case CUDA_ERROR_LAUNCH_TIMEOUT: return "Launch exceeded timeout";
case CUDA_ERROR_LAUNCH_INCOMPATIBLE_TEXTURING: return "Launch with incompatible texturing";
case CUDA_ERROR_UNKNOWN: return "Unknown error";
default: return "Unknown CUDA error value";
}
}
static int cuda_align_up(int& offset, int alignment)
{
return (offset + alignment - 1) & ~(alignment - 1);
}
#ifdef NDEBUG
#define cuda_abort()
#else
#define cuda_abort() abort()
#endif
#define cuda_assert(stmt) \
{ \
CUresult result = stmt; \
\
if(result != CUDA_SUCCESS) { \
fprintf(stderr, "CUDA error: %s in %s\n", cuda_error_string(result), #stmt); \
cuda_abort(); \
} \
}
void cuda_push_context()
{
cuda_assert(cuCtxSetCurrent(cuContext))
}
void cuda_pop_context()
{
cuda_assert(cuCtxSetCurrent(NULL));
}
CUDADevice(bool background_)
{
int major, minor;
background = background_;
cuDevId = 0;
/* intialize */
cuda_assert(cuInit(0))
/* setup device and context */
cuda_assert(cuDeviceGet(&cuDevice, cuDevId))
if(background)
cuda_assert(cuCtxCreate(&cuContext, 0, cuDevice))
else
cuda_assert(cuGLCtxCreate(&cuContext, 0, cuDevice))
/* open module */
cuDeviceComputeCapability(&major, &minor, cuDevId);
string cubin = string_printf("lib/kernel_sm_%d%d.cubin", major, minor);
cuda_assert(cuModuleLoad(&cuModule, path_get(cubin).c_str()))
cuda_pop_context();
}
~CUDADevice()
{
cuda_push_context();
cuda_assert(cuCtxDetach(cuContext))
}
string description()
{
/* print device information */
char deviceName[100];
cuda_push_context();
cuDeviceGetName(deviceName, 256, cuDevId);
cuda_pop_context();
return string("CUDA ") + deviceName;
}
void mem_alloc(device_memory& mem, MemoryType type)
{
cuda_push_context();
CUdeviceptr device_pointer;
cuda_assert(cuMemAlloc(&device_pointer, mem.memory_size()))
mem.device_pointer = (device_ptr)device_pointer;
cuda_pop_context();
}
void mem_copy_to(device_memory& mem)
{
cuda_push_context();
cuda_assert(cuMemcpyHtoD(cuda_device_ptr(mem.device_pointer), (void*)mem.data_pointer, mem.memory_size()))
cuda_pop_context();
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
/* todo: offset is ignored */
cuda_push_context();
cuda_assert(cuMemcpyDtoH((uchar*)mem.data_pointer + offset,
(CUdeviceptr)((uchar*)mem.device_pointer + offset), size))
cuda_pop_context();
}
void mem_zero(device_memory& mem)
{
memset((void*)mem.data_pointer, 0, mem.memory_size());
cuda_push_context();
cuda_assert(cuMemsetD8(cuda_device_ptr(mem.device_pointer), 0, mem.memory_size()))
cuda_pop_context();
}
void mem_free(device_memory& mem)
{
if(mem.device_pointer) {
cuda_push_context();
cuda_assert(cuMemFree(cuda_device_ptr(mem.device_pointer)))
cuda_pop_context();
mem.device_pointer = 0;
}
}
void const_copy_to(const char *name, void *host, size_t size)
{
CUdeviceptr mem;
size_t bytes;
cuda_push_context();
cuda_assert(cuModuleGetGlobal(&mem, &bytes, cuModule, name))
assert(bytes == size);
cuda_assert(cuMemcpyHtoD(mem, host, size))
cuda_pop_context();
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
/* determine format */
CUarray_format_enum format;
size_t dsize = datatype_size(mem.data_type);
size_t size = mem.memory_size();
switch(mem.data_type) {
case TYPE_UCHAR: format = CU_AD_FORMAT_UNSIGNED_INT8; break;
case TYPE_UINT: format = CU_AD_FORMAT_UNSIGNED_INT32; break;
case TYPE_INT: format = CU_AD_FORMAT_SIGNED_INT32; break;
case TYPE_FLOAT: format = CU_AD_FORMAT_FLOAT; break;
default: assert(0); return;
}
CUtexref texref;
cuda_push_context();
cuda_assert(cuModuleGetTexRef(&texref, cuModule, name))
if(interpolation) {
CUarray handle;
CUDA_ARRAY_DESCRIPTOR desc;
desc.Width = mem.data_width;
desc.Height = mem.data_height;
desc.Format = format;
desc.NumChannels = mem.data_elements;
cuda_assert(cuArrayCreate(&handle, &desc))
if(mem.data_height > 1) {
CUDA_MEMCPY2D param;
memset(&param, 0, sizeof(param));
param.dstMemoryType = CU_MEMORYTYPE_ARRAY;
param.dstArray = handle;
param.srcMemoryType = CU_MEMORYTYPE_HOST;
param.srcHost = (void*)mem.data_pointer;
param.srcPitch = mem.data_width*dsize*mem.data_elements;
param.WidthInBytes = param.srcPitch;
param.Height = mem.data_height;
cuda_assert(cuMemcpy2D(&param))
}
else
cuda_assert(cuMemcpyHtoA(handle, 0, (void*)mem.data_pointer, size))
cuda_assert(cuTexRefSetArray(texref, handle, CU_TRSA_OVERRIDE_FORMAT))
cuda_assert(cuTexRefSetFilterMode(texref, CU_TR_FILTER_MODE_LINEAR))
cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_NORMALIZED_COORDINATES))
mem.device_pointer = (device_ptr)handle;
}
else {
cuda_pop_context();
mem_alloc(mem, MEM_READ_ONLY);
mem_copy_to(mem);
cuda_push_context();
cuda_assert(cuTexRefSetAddress(NULL, texref, cuda_device_ptr(mem.device_pointer), size))
cuda_assert(cuTexRefSetFilterMode(texref, CU_TR_FILTER_MODE_POINT))
cuda_assert(cuTexRefSetFlags(texref, CU_TRSF_READ_AS_INTEGER))
}
if(periodic) {
cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_WRAP))
cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_WRAP))
}
else {
cuda_assert(cuTexRefSetAddressMode(texref, 0, CU_TR_ADDRESS_MODE_CLAMP))
cuda_assert(cuTexRefSetAddressMode(texref, 1, CU_TR_ADDRESS_MODE_CLAMP))
}
cuda_assert(cuTexRefSetFormat(texref, format, mem.data_elements))
cuda_pop_context();
tex_interp_map[mem.device_pointer] = interpolation;
}
void tex_free(device_memory& mem)
{
if(mem.device_pointer) {
if(tex_interp_map[mem.device_pointer]) {
cuda_push_context();
cuArrayDestroy((CUarray)mem.device_pointer);
cuda_pop_context();
tex_interp_map.erase(tex_interp_map.find(mem.device_pointer));
mem.device_pointer = 0;
}
else {
tex_interp_map.erase(tex_interp_map.find(mem.device_pointer));
mem_free(mem);
}
}
}
void path_trace(DeviceTask& task)
{
cuda_push_context();
CUfunction cuPathTrace;
CUdeviceptr d_buffer = cuda_device_ptr(task.buffer);
CUdeviceptr d_rng_state = cuda_device_ptr(task.rng_state);
/* get kernel function */
cuda_assert(cuModuleGetFunction(&cuPathTrace, cuModule, "kernel_cuda_path_trace"))
/* pass in parameters */
int offset = 0;
cuda_assert(cuParamSetv(cuPathTrace, offset, &d_buffer, sizeof(d_buffer)))
offset += sizeof(d_buffer);
cuda_assert(cuParamSetv(cuPathTrace, offset, &d_rng_state, sizeof(d_rng_state)))
offset += sizeof(d_rng_state);
offset = cuda_align_up(offset, __alignof(task.pass));
cuda_assert(cuParamSeti(cuPathTrace, offset, task.pass))
offset += sizeof(task.pass);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.x))
offset += sizeof(task.x);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.y))
offset += sizeof(task.y);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.w))
offset += sizeof(task.w);
cuda_assert(cuParamSeti(cuPathTrace, offset, task.h))
offset += sizeof(task.h);
cuda_assert(cuParamSetSize(cuPathTrace, offset))
/* launch kernel: todo find optimal size, cache config for fermi */
#ifndef __APPLE__
int xthreads = 16;
int ythreads = 16;
#else
int xthreads = 8;
int ythreads = 8;
#endif
int xblocks = (task.w + xthreads - 1)/xthreads;
int yblocks = (task.h + ythreads - 1)/ythreads;
cuda_assert(cuFuncSetCacheConfig(cuPathTrace, CU_FUNC_CACHE_PREFER_L1))
cuda_assert(cuFuncSetBlockShape(cuPathTrace, xthreads, ythreads, 1))
cuda_assert(cuLaunchGrid(cuPathTrace, xblocks, yblocks))
cuda_pop_context();
}
void tonemap(DeviceTask& task)
{
cuda_push_context();
CUfunction cuFilmConvert;
CUdeviceptr d_rgba = map_pixels(task.rgba);
CUdeviceptr d_buffer = cuda_device_ptr(task.buffer);
/* get kernel function */
cuda_assert(cuModuleGetFunction(&cuFilmConvert, cuModule, "kernel_cuda_tonemap"))
/* pass in parameters */
int offset = 0;
cuda_assert(cuParamSetv(cuFilmConvert, offset, &d_rgba, sizeof(d_rgba)))
offset += sizeof(d_rgba);
cuda_assert(cuParamSetv(cuFilmConvert, offset, &d_buffer, sizeof(d_buffer)))
offset += sizeof(d_buffer);
offset = cuda_align_up(offset, __alignof(task.pass));
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.pass))
offset += sizeof(task.pass);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.resolution))
offset += sizeof(task.resolution);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.x))
offset += sizeof(task.x);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.y))
offset += sizeof(task.y);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.w))
offset += sizeof(task.w);
cuda_assert(cuParamSeti(cuFilmConvert, offset, task.h))
offset += sizeof(task.h);
cuda_assert(cuParamSetSize(cuFilmConvert, offset))
/* launch kernel: todo find optimal size, cache config for fermi */
#ifndef __APPLE__
int xthreads = 16;
int ythreads = 16;
#else
int xthreads = 8;
int ythreads = 8;
#endif
int xblocks = (task.w + xthreads - 1)/xthreads;
int yblocks = (task.h + ythreads - 1)/ythreads;
cuda_assert(cuFuncSetCacheConfig(cuFilmConvert, CU_FUNC_CACHE_PREFER_L1))
cuda_assert(cuFuncSetBlockShape(cuFilmConvert, xthreads, ythreads, 1))
cuda_assert(cuLaunchGrid(cuFilmConvert, xblocks, yblocks))
unmap_pixels(task.rgba);
cuda_pop_context();
}
void displace(DeviceTask& task)
{
cuda_push_context();
CUfunction cuDisplace;
CUdeviceptr d_input = cuda_device_ptr(task.displace_input);
CUdeviceptr d_offset = cuda_device_ptr(task.displace_offset);
/* get kernel function */
cuda_assert(cuModuleGetFunction(&cuDisplace, cuModule, "kernel_cuda_displace"))
/* pass in parameters */
int offset = 0;
cuda_assert(cuParamSetv(cuDisplace, offset, &d_input, sizeof(d_input)))
offset += sizeof(d_input);
cuda_assert(cuParamSetv(cuDisplace, offset, &d_offset, sizeof(d_offset)))
offset += sizeof(d_offset);
offset = cuda_align_up(offset, __alignof(task.displace_x));
cuda_assert(cuParamSeti(cuDisplace, offset, task.displace_x))
offset += sizeof(task.displace_x);
cuda_assert(cuParamSetSize(cuDisplace, offset))
/* launch kernel: todo find optimal size, cache config for fermi */
#ifndef __APPLE__
int xthreads = 16;
#else
int xthreads = 8;
#endif
int xblocks = (task.displace_w + xthreads - 1)/xthreads;
cuda_assert(cuFuncSetCacheConfig(cuDisplace, CU_FUNC_CACHE_PREFER_L1))
cuda_assert(cuFuncSetBlockShape(cuDisplace, xthreads, 1, 1))
cuda_assert(cuLaunchGrid(cuDisplace, xblocks, 1))
cuda_pop_context();
}
CUdeviceptr map_pixels(device_ptr mem)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem];
CUdeviceptr buffer;
size_t bytes;
cuda_assert(cuGraphicsMapResources(1, &pmem.cuPBOresource, 0))
cuda_assert(cuGraphicsResourceGetMappedPointer(&buffer, &bytes, pmem.cuPBOresource))
return buffer;
}
return cuda_device_ptr(mem);
}
void unmap_pixels(device_ptr mem)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem];
cuda_assert(cuGraphicsUnmapResources(1, &pmem.cuPBOresource, 0))
}
}
void pixels_alloc(device_memory& mem)
{
if(!background) {
PixelMem pmem;
pmem.w = mem.data_width;
pmem.h = mem.data_height;
cuda_push_context();
glGenBuffers(1, &pmem.cuPBO);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO);
glBufferData(GL_PIXEL_UNPACK_BUFFER, pmem.w*pmem.h*sizeof(GLfloat)*3, NULL, GL_DYNAMIC_DRAW);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
glGenTextures(1, &pmem.cuTexId);
glBindTexture(GL_TEXTURE_2D, pmem.cuTexId);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pmem.w, pmem.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
cuda_assert(cuGraphicsGLRegisterBuffer(&pmem.cuPBOresource, pmem.cuPBO, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE))
cuda_pop_context();
mem.device_pointer = pmem.cuTexId;
pixel_mem_map[mem.device_pointer] = pmem;
return;
}
Device::pixels_alloc(mem);
}
void pixels_copy_from(device_memory& mem, int y, int w, int h)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem.device_pointer];
cuda_push_context();
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO);
uchar *pixels = (uchar*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_ONLY);
size_t offset = sizeof(uchar)*4*y*w;
memcpy((uchar*)mem.data_pointer + offset, pixels + offset, sizeof(uchar)*4*w*h);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
cuda_pop_context();
return;
}
Device::pixels_copy_from(mem, y, w, h);
}
void pixels_free(device_memory& mem)
{
if(mem.device_pointer) {
if(!background) {
PixelMem pmem = pixel_mem_map[mem.device_pointer];
cuda_push_context();
cuda_assert(cuGraphicsUnregisterResource(pmem.cuPBOresource))
glDeleteBuffers(1, &pmem.cuPBO);
glDeleteTextures(1, &pmem.cuTexId);
cuda_pop_context();
pixel_mem_map.erase(pixel_mem_map.find(mem.device_pointer));
mem.device_pointer = 0;
return;
}
Device::pixels_free(mem);
}
}
void draw_pixels(device_memory& mem, int y, int w, int h, int width, int height)
{
if(!background) {
PixelMem pmem = pixel_mem_map[mem.device_pointer];
cuda_push_context();
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, pmem.cuPBO);
glBindTexture(GL_TEXTURE_2D, pmem.cuTexId);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
glEnable(GL_TEXTURE_2D);
glColor3f(1.0f, 1.0f, 1.0f);
glPushMatrix();
glTranslatef(0, y, 0.0f);
glBegin(GL_QUADS);
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f((float)w/(float)width, 0);
glVertex2f(width, 0);
glTexCoord2f((float)w/(float)width, (float)h/(float)height);
glVertex2f(width, height);
glTexCoord2f(0, (float)h/(float)height);
glVertex2f(0, height);
glEnd();
glPopMatrix();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
cuda_pop_context();
return;
}
Device::draw_pixels(mem, y, w, h, width, height);
}
void task_add(DeviceTask& task)
{
if(task.type == DeviceTask::TONEMAP)
tonemap(task);
else if(task.type == DeviceTask::PATH_TRACE)
path_trace(task);
else if(task.type == DeviceTask::DISPLACE)
displace(task);
}
void task_wait()
{
cuda_push_context();
cuda_assert(cuCtxSynchronize())
cuda_pop_context();
}
void task_cancel()
{
}
};
Device *device_cuda_create(bool background)
{
return new CUDADevice(background);
}
CCL_NAMESPACE_END

@ -0,0 +1,35 @@
/*
* 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 __DEVICE_INTERN_H__
#define __DEVICE_INTERN_H__
CCL_NAMESPACE_BEGIN
class Device;
Device *device_cpu_create();
Device *device_opencl_create(bool background);
Device *device_cuda_create(bool background);
Device *device_network_create(const char *address);
Device *device_multi_create(bool background);
CCL_NAMESPACE_END
#endif /* __DEVICE_INTERN_H__ */

@ -0,0 +1,244 @@
/*
* 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 __DEVICE_MEMORY_H__
#define __DEVICE_MEMORY_H__
/* Device Memory
*
* This file defines data types that can be used in device memory arrays, and
* a device_vector<T> type to store such arrays.
*
* device_vector<T> contains an STL vector, metadata about the data type,
* dimensions, elements, and a device pointer. For the CPU device this is just
* a pointer to the STL vector data, as no copying needs to take place. For
* other devices this is a pointer to device memory, where we will copy memory
* to and from. */
#include "util_debug.h"
#include "util_types.h"
#include "util_vector.h"
CCL_NAMESPACE_BEGIN
/* Supported Data Types */
enum DataType {
TYPE_UCHAR,
TYPE_UINT,
TYPE_INT,
TYPE_FLOAT
};
static inline size_t datatype_size(DataType datatype)
{
switch(datatype) {
case TYPE_UCHAR: return sizeof(uchar);
case TYPE_FLOAT: return sizeof(float);
case TYPE_UINT: return sizeof(uint);
case TYPE_INT: return sizeof(int);
default: return 0;
}
}
/* Traits for data types */
template<typename T> struct device_type_traits {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 0;
};
template<> struct device_type_traits<uchar> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 1;
};
template<> struct device_type_traits<uchar2> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 2;
};
template<> struct device_type_traits<uchar3> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 3;
};
template<> struct device_type_traits<uchar4> {
static const DataType data_type = TYPE_UCHAR;
static const int num_elements = 4;
};
template<> struct device_type_traits<uint> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 1;
};
template<> struct device_type_traits<uint2> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 2;
};
template<> struct device_type_traits<uint3> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 3;
};
template<> struct device_type_traits<uint4> {
static const DataType data_type = TYPE_UINT;
static const int num_elements = 4;
};
template<> struct device_type_traits<int> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 1;
};
template<> struct device_type_traits<int2> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 2;
};
template<> struct device_type_traits<int3> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 3;
};
template<> struct device_type_traits<int4> {
static const DataType data_type = TYPE_INT;
static const int num_elements = 4;
};
template<> struct device_type_traits<float> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 1;
};
template<> struct device_type_traits<float2> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 2;
};
template<> struct device_type_traits<float3> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 3;
};
template<> struct device_type_traits<float4> {
static const DataType data_type = TYPE_FLOAT;
static const int num_elements = 4;
};
/* Device Memory */
class device_memory
{
public:
size_t memory_size() { return data_size*data_elements*datatype_size(data_type); }
/* data information */
DataType data_type;
int data_elements;
device_ptr data_pointer;
size_t data_size;
size_t data_width;
size_t data_height;
/* device pointer */
device_ptr device_pointer;
protected:
device_memory() {}
virtual ~device_memory() { assert(!device_pointer); }
/* no copying */
device_memory(const device_memory&);
device_memory& operator = (const device_memory&);
};
/* Device Vector */
template<typename T> class device_vector : public device_memory
{
public:
device_vector()
{
data_type = device_type_traits<T>::data_type;
data_elements = device_type_traits<T>::num_elements;
data_pointer = 0;
data_size = 0;
data_width = 0;
data_height = 0;
assert(data_elements > 0);
device_pointer = 0;
}
virtual ~device_vector() {}
/* vector functions */
T *resize(size_t width, size_t height = 0)
{
data_size = (height == 0)? width: width*height;
data.resize(data_size);
data_pointer = (device_ptr)&data[0];
data_width = width;
data_height = height;
return &data[0];
}
T *copy(T *ptr, size_t width, size_t height = 0)
{
T *mem = resize(width, height);
memcpy(mem, ptr, memory_size());
return mem;
}
void reference(T *ptr, size_t width, size_t height = 0)
{
data.clear();
data_size = (height == 0)? width: width*height;
data_pointer = (device_ptr)ptr;
data_width = width;
data_height = height;
}
void clear()
{
data.clear();
data_pointer = 0;
data_width = 0;
data_height = 0;
data_size = 0;
}
size_t size()
{
return data.size();
}
private:
array<T> data;
bool referenced;
};
CCL_NAMESPACE_END
#endif /* __DEVICE_MEMORY_H__ */

@ -0,0 +1,304 @@
/*
* 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 <stdlib.h>
#include <sstream>
#include "device.h"
#include "device_intern.h"
#include "device_network.h"
#include "util_foreach.h"
#include "util_list.h"
#include "util_map.h"
#include "util_time.h"
CCL_NAMESPACE_BEGIN
class MultiDevice : public Device
{
public:
struct SubDevice {
SubDevice(Device *device_)
: device(device_) {}
Device *device;
map<device_ptr, device_ptr> ptr_map;
};
list<SubDevice> devices;
device_ptr unique_ptr;
MultiDevice(bool background_)
: unique_ptr(1)
{
/* enforce background for now */
background = true;
Device *device;
/* add CPU device */
device = Device::create(DEVICE_CPU, background);
devices.push_back(SubDevice(device));
#ifdef WITH_CUDA
/* try to add GPU device */
device = Device::create(DEVICE_CUDA, background);
if(device) {
devices.push_back(SubDevice(device));
}
else
#endif
{
#ifdef WITH_OPENCL
device = Device::create(DEVICE_OPENCL, background);
if(device)
devices.push_back(SubDevice(device));
#endif
}
#ifdef WITH_NETWORK
/* try to add network devices */
ServerDiscovery discovery(true);
time_sleep(1.0);
list<string> servers = discovery.get_server_list();
foreach(string& server, servers) {
device = device_network_create(server.c_str());
if(device)
devices.push_back(SubDevice(device));
}
#endif
}
~MultiDevice()
{
foreach(SubDevice& sub, devices)
delete sub.device;
}
string description()
{
/* create map to find duplicate descriptions */
map<string, int> dupli_map;
map<string, int>::iterator dt;
foreach(SubDevice& sub, devices) {
string key = sub.device->description();
if(dupli_map.find(key) == dupli_map.end())
dupli_map[key] = 1;
else
dupli_map[key]++;
}
/* generate string */
stringstream desc;
bool first = true;
for(dt = dupli_map.begin(); dt != dupli_map.end(); dt++) {
if(!first) desc << ", ";
first = false;
if(dt->second > 1)
desc << dt->second << "x " << dt->first;
else
desc << dt->first;
}
return desc.str();
}
void mem_alloc(device_memory& mem, MemoryType type)
{
foreach(SubDevice& sub, devices) {
mem.device_pointer = 0;
sub.device->mem_alloc(mem, type);
sub.ptr_map[unique_ptr] = mem.device_pointer;
}
mem.device_pointer = unique_ptr++;
}
void mem_copy_to(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_copy_to(mem);
}
mem.device_pointer = tmp;
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
device_ptr tmp = mem.device_pointer;
/* todo: how does this work? */
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_copy_from(mem, offset, size);
break;
}
mem.device_pointer = tmp;
}
void mem_zero(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_zero(mem);
}
mem.device_pointer = tmp;
}
void mem_free(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->mem_free(mem);
sub.ptr_map.erase(sub.ptr_map.find(tmp));
}
mem.device_pointer = 0;
}
void const_copy_to(const char *name, void *host, size_t size)
{
foreach(SubDevice& sub, devices)
sub.device->const_copy_to(name, host, size);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
foreach(SubDevice& sub, devices) {
mem.device_pointer = 0;
sub.device->tex_alloc(name, mem, interpolation, periodic);
sub.ptr_map[unique_ptr] = mem.device_pointer;
}
mem.device_pointer = unique_ptr++;
}
void tex_free(device_memory& mem)
{
device_ptr tmp = mem.device_pointer;
foreach(SubDevice& sub, devices) {
mem.device_pointer = sub.ptr_map[tmp];
sub.device->tex_free(mem);
sub.ptr_map.erase(sub.ptr_map.find(tmp));
}
mem.device_pointer = 0;
}
void pixels_alloc(device_memory& mem)
{
Device::pixels_alloc(mem);
}
void pixels_free(device_memory& mem)
{
Device::pixels_free(mem);
}
void pixels_copy_from(device_memory& mem, int y, int w, int h)
{
device_ptr tmp = mem.device_pointer;
int i = 0, sub_h = h/devices.size();
foreach(SubDevice& sub, devices) {
int sy = y + i*sub_h;
int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
mem.device_pointer = sub.ptr_map[tmp];
sub.device->pixels_copy_from(mem, sy, w, sh);
i++;
}
mem.device_pointer = tmp;
}
void draw_pixels(device_memory& rgba, int x, int y, int w, int h, int width, int height)
{
device_ptr tmp = rgba.device_pointer;
int i = 0, sub_h = h/devices.size();
foreach(SubDevice& sub, devices) {
int sy = y + i*sub_h;
int sh = (i == (int)devices.size() - 1)? h - sub_h*i: sub_h;
/* adjust math for w/width */
rgba.device_pointer = sub.ptr_map[tmp];
sub.device->draw_pixels(rgba, sy, w, sh, width, height);
i++;
}
rgba.device_pointer = tmp;
}
void task_add(DeviceTask& task)
{
ThreadQueue<DeviceTask> tasks;
task.split(tasks, devices.size());
foreach(SubDevice& sub, devices) {
DeviceTask subtask;
if(tasks.worker_wait_pop(subtask)) {
if(task.buffer) subtask.buffer = sub.ptr_map[task.buffer];
if(task.rng_state) subtask.rng_state = sub.ptr_map[task.rng_state];
if(task.rgba) subtask.rgba = sub.ptr_map[task.rgba];
if(task.displace_input) subtask.displace_input = sub.ptr_map[task.displace_input];
if(task.displace_offset) subtask.displace_offset = sub.ptr_map[task.displace_offset];
sub.device->task_add(subtask);
}
}
}
void task_wait()
{
foreach(SubDevice& sub, devices)
sub.device->task_wait();
}
void task_cancel()
{
foreach(SubDevice& sub, devices)
sub.device->task_cancel();
}
};
Device *device_multi_create(bool background)
{
return new MultiDevice(background);
}
CCL_NAMESPACE_END

@ -0,0 +1,382 @@
/*
* 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 "device.h"
#include "device_intern.h"
#include "device_network.h"
#include "util_foreach.h"
CCL_NAMESPACE_BEGIN
class NetworkDevice : public Device
{
public:
boost::asio::io_service io_service;
tcp::socket socket;
NetworkDevice(const char *address)
: socket(io_service)
{
stringstream portstr;
portstr << SERVER_PORT;
tcp::resolver resolver(io_service);
tcp::resolver::query query(address, portstr.str());
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::resolver::iterator end;
boost::system::error_code error = boost::asio::error::host_not_found;
while(error && endpoint_iterator != end)
{
socket.close();
socket.connect(*endpoint_iterator++, error);
}
if(error)
throw boost::system::system_error(error);
}
~NetworkDevice()
{
}
string description()
{
RPCSend snd(socket, "description");
snd.write();
RPCReceive rcv(socket);
string desc_string;
*rcv.archive & desc_string;
return desc_string + " (remote)";
}
void mem_alloc(device_memory& mem, MemoryType type)
{
#if 0
RPCSend snd(socket, "mem_alloc");
snd.archive & size & type;
snd.write();
RPCReceive rcv(socket);
device_ptr mem;
*rcv.archive & mem;
return mem;
#endif
}
void mem_copy_to(device_memory& mem)
{
#if 0
RPCSend snd(socket, "mem_copy_to");
snd.archive & mem & size;
snd.write();
snd.write_buffer(host, size);
#endif
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
#if 0
RPCSend snd(socket, "mem_copy_from");
snd.archive & mem & offset & size;
snd.write();
RPCReceive rcv(socket);
rcv.read_buffer(host, size);
#endif
}
void mem_zero(device_memory& mem)
{
#if 0
RPCSend snd(socket, "mem_zero");
snd.archive & mem & size;
snd.write();
#endif
}
void mem_free(device_memory& mem)
{
#if 0
if(mem) {
RPCSend snd(socket, "mem_free");
snd.archive & mem;
snd.write();
}
#endif
}
void const_copy_to(const char *name, void *host, size_t size)
{
RPCSend snd(socket, "const_copy_to");
string name_string(name);
snd.archive & name_string & size;
snd.write();
snd.write_buffer(host, size);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
#if 0
RPCSend snd(socket, "tex_alloc");
string name_string(name);
snd.archive & name_string & width & height & datatype & components & interpolation;
snd.write();
size_t size = width*height*components*datatype_size(datatype);
snd.write_buffer(host, size);
RPCReceive rcv(socket);
device_ptr mem;
*rcv.archive & mem;
return mem;
#endif
}
void tex_free(device_memory& mem)
{
#if 0
if(mem) {
RPCSend snd(socket, "tex_free");
snd.archive & mem;
snd.write();
}
#endif
}
void path_trace(int x, int y, int w, int h, device_ptr buffer, device_ptr rng_state, int pass)
{
#if 0
RPCSend snd(socket, "path_trace");
snd.archive & x & y & w & h & buffer & rng_state & pass;
snd.write();
#endif
}
void tonemap(int x, int y, int w, int h, device_ptr rgba, device_ptr buffer, int pass, int resolution)
{
#if 0
RPCSend snd(socket, "tonemap");
snd.archive & x & y & w & h & rgba & buffer & pass & resolution;
snd.write();
#endif
}
void task_add(DeviceTask& task)
{
if(task.type == DeviceTask::TONEMAP)
tonemap(task.x, task.y, task.w, task.h, task.rgba, task.buffer, task.pass, task.resolution);
else if(task.type == DeviceTask::PATH_TRACE)
path_trace(task.x, task.y, task.w, task.h, task.buffer, task.rng_state, task.pass);
}
void task_wait()
{
}
void task_cancel()
{
}
};
Device *device_network_create(const char *address)
{
return new NetworkDevice(address);
}
void Device::server_run()
{
try
{
/* starts thread that responds to discovery requests */
ServerDiscovery discovery;
for(;;)
{
/* accept connection */
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), SERVER_PORT));
tcp::socket socket(io_service);
acceptor.accept(socket);
/* receive remote function calls */
for(;;) {
RPCReceive rcv(socket);
if(rcv.name == "description") {
string desc = description();
RPCSend snd(socket);
snd.archive & desc;
snd.write();
}
else if(rcv.name == "mem_alloc") {
#if 0
MemoryType type;
size_t size;
device_ptr mem;
*rcv.archive & size & type;
mem = mem_alloc(size, type);
RPCSend snd(socket);
snd.archive & mem;
snd.write();
#endif
}
else if(rcv.name == "mem_copy_to") {
#if 0
device_ptr mem;
size_t size;
*rcv.archive & mem & size;
vector<char> host_vector(size);
rcv.read_buffer(&host_vector[0], size);
mem_copy_to(mem, &host_vector[0], size);
#endif
}
else if(rcv.name == "mem_copy_from") {
#if 0
device_ptr mem;
size_t offset, size;
*rcv.archive & mem & offset & size;
vector<char> host_vector(size);
mem_copy_from(&host_vector[0], mem, offset, size);
RPCSend snd(socket);
snd.write();
snd.write_buffer(&host_vector[0], size);
#endif
}
else if(rcv.name == "mem_zero") {
#if 0
device_ptr mem;
size_t size;
*rcv.archive & mem & size;
mem_zero(mem, size);
#endif
}
else if(rcv.name == "mem_free") {
#if 0
device_ptr mem;
*rcv.archive & mem;
mem_free(mem);
#endif
}
else if(rcv.name == "const_copy_to") {
string name_string;
size_t size;
*rcv.archive & name_string & size;
vector<char> host_vector(size);
rcv.read_buffer(&host_vector[0], size);
const_copy_to(name_string.c_str(), &host_vector[0], size);
}
else if(rcv.name == "tex_alloc") {
#if 0
string name_string;
DataType datatype;
device_ptr mem;
size_t width, height;
int components;
bool interpolation;
*rcv.archive & name_string & width & height & datatype & components & interpolation;
size_t size = width*height*components*datatype_size(datatype);
vector<char> host_vector(size);
rcv.read_buffer(&host_vector[0], size);
mem = tex_alloc(name_string.c_str(), &host_vector[0], width, height, datatype, components, interpolation);
RPCSend snd(socket);
snd.archive & mem;
snd.write();
#endif
}
else if(rcv.name == "tex_free") {
#if 0
device_ptr mem;
*rcv.archive & mem;
tex_free(mem);
#endif
}
else if(rcv.name == "path_trace") {
#if 0
device_ptr buffer, rng_state;
int x, y, w, h;
int pass;
*rcv.archive & x & y & w & h & buffer & rng_state & pass;
path_trace(x, y, w, h, buffer, rng_state, pass);
#endif
}
else if(rcv.name == "tonemap") {
#if 0
device_ptr rgba, buffer;
int x, y, w, h;
int pass, resolution;
*rcv.archive & x & y & w & h & rgba & buffer & pass & resolution;
tonemap(x, y, w, h, rgba, buffer, pass, resolution);
#endif
}
}
}
}
catch(exception& e)
{
cerr << "Network server exception: " << e.what() << endl;
}
}
CCL_NAMESPACE_END

@ -0,0 +1,304 @@
/*
* 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 __DEVICE_NETWORK_H__
#define __DEVICE_NETWORK_H__
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/thread.hpp>
#include <iostream>
#include "util_foreach.h"
#include "util_list.h"
#include "util_string.h"
CCL_NAMESPACE_BEGIN
using std::cout;
using std::cerr;
using std::endl;
using std::hex;
using std::setw;
using std::exception;
using boost::asio::ip::tcp;
static const int SERVER_PORT = 5120;
static const int DISCOVER_PORT = 5121;
static const string DISCOVER_REQUEST_MSG = "REQUEST_RENDER_SERVER_IP";
static const string DISCOVER_REPLY_MSG = "REPLY_RENDER_SERVER_IP";
typedef struct RPCSend {
RPCSend(tcp::socket& socket_, const string& name_ = "")
: name(name_), socket(socket_), archive(archive_stream)
{
archive & name_;
}
void write()
{
boost::system::error_code error;
/* get string from stream */
string archive_str = archive_stream.str();
/* first send fixed size header with size of following data */
ostringstream header_stream;
header_stream << setw(8) << hex << archive_str.size();
string header_str = header_stream.str();
boost::asio::write(socket,
boost::asio::buffer(header_str),
boost::asio::transfer_all(), error);
if(error.value())
cout << "Network send error: " << error.message() << "\n";
/* then send actual data */
boost::asio::write(socket,
boost::asio::buffer(archive_str),
boost::asio::transfer_all(), error);
if(error.value())
cout << "Network send error: " << error.message() << "\n";
}
void write_buffer(void *buffer, size_t size)
{
boost::system::error_code error;
boost::asio::write(socket,
boost::asio::buffer(buffer, size),
boost::asio::transfer_all(), error);
if(error.value())
cout << "Network send error: " << error.message() << "\n";
}
string name;
tcp::socket& socket;
ostringstream archive_stream;
boost::archive::text_oarchive archive;
} RPCSend;
typedef struct RPCReceive {
RPCReceive(tcp::socket& socket_)
: socket(socket_), archive_stream(NULL), archive(NULL)
{
/* read head with fixed size */
vector<char> header(8);
size_t len = boost::asio::read(socket, boost::asio::buffer(header));
/* verify if we got something */
if(len == header.size()) {
/* decode header */
string header_str(&header[0], header.size());
istringstream header_stream(header_str);
size_t data_size;
if((header_stream >> hex >> data_size)) {
vector<char> data(data_size);
size_t len = boost::asio::read(socket, boost::asio::buffer(data));
if(len == data_size) {
archive_str = string(&data[0], data.size());
/*istringstream archive_stream(archive_str);
boost::archive::text_iarchive archive(archive_stream);*/
archive_stream = new istringstream(archive_str);
archive = new boost::archive::text_iarchive(*archive_stream);
*archive & name;
}
else
cout << "Network receive error: data size doens't match header\n";
}
else
cout << "Network receive error: can't decode data size from header\n";
}
else
cout << "Network receive error: invalid header size\n";
}
~RPCReceive()
{
delete archive;
delete archive_stream;
}
void read_buffer(void *buffer, size_t size)
{
size_t len = boost::asio::read(socket, boost::asio::buffer(buffer, size));
if(len != size)
cout << "Network receive error: buffer size doesn't match expected size\n";
}
string name;
tcp::socket& socket;
string archive_str;
istringstream *archive_stream;
boost::archive::text_iarchive *archive;
} RPCReceive;
class ServerDiscovery {
public:
ServerDiscovery(bool discover = false)
: listen_socket(io_service), collect_servers(false)
{
/* setup listen socket */
listen_endpoint.address(boost::asio::ip::address_v4::any());
listen_endpoint.port(DISCOVER_PORT);
listen_socket.open(listen_endpoint.protocol());
boost::asio::socket_base::reuse_address option(true);
listen_socket.set_option(option);
listen_socket.bind(listen_endpoint);
/* setup receive callback */
async_receive();
/* start server discovery */
if(discover) {
collect_servers = true;
servers.clear();
broadcast_message(DISCOVER_REQUEST_MSG);
}
/* start thread */
work = new boost::asio::io_service::work(io_service);
thread = new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
}
~ServerDiscovery()
{
io_service.stop();
thread->join();
delete thread;
delete work;
}
list<string> get_server_list()
{
list<string> result;
mutex.lock();
result = servers;
mutex.unlock();
return result;
}
private:
void handle_receive_from(const boost::system::error_code& error, size_t size)
{
if(error) {
cout << "Server discovery receive error: " << error.message() << "\n";
return;
}
if(size > 0) {
string msg = string(receive_buffer, size);
/* handle incoming message */
if(collect_servers) {
if(msg == DISCOVER_REPLY_MSG) {
string address = receive_endpoint.address().to_string();
mutex.lock();
/* add address if it's not already in the list */
bool found = false;
foreach(string& server, servers)
if(server == address)
found = true;
if(!found)
servers.push_back(address);
mutex.unlock();
}
}
else {
/* reply to request */
if(msg == DISCOVER_REQUEST_MSG)
broadcast_message(DISCOVER_REPLY_MSG);
}
}
async_receive();
}
void async_receive()
{
listen_socket.async_receive_from(
boost::asio::buffer(receive_buffer), receive_endpoint,
boost::bind(&ServerDiscovery::handle_receive_from, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void broadcast_message(const string& msg)
{
/* setup broadcast socket */
boost::asio::ip::udp::socket socket(io_service);
socket.open(boost::asio::ip::udp::v4());
boost::asio::socket_base::broadcast option(true);
socket.set_option(option);
boost::asio::ip::udp::endpoint broadcast_endpoint(
boost::asio::ip::address::from_string("255.255.255.255"), DISCOVER_PORT);
/* broadcast message */
socket.send_to(boost::asio::buffer(msg), broadcast_endpoint);
}
/* network service and socket */
boost::asio::io_service io_service;
boost::asio::ip::udp::endpoint listen_endpoint;
boost::asio::ip::udp::socket listen_socket;
/* threading */
boost::thread *thread;
boost::asio::io_service::work *work;
boost::mutex mutex;
/* buffer and endpoint for receiving messages */
char receive_buffer[256];
boost::asio::ip::udp::endpoint receive_endpoint;
/* collection of server addresses in list */
bool collect_servers;
list<string> servers;
};
CCL_NAMESPACE_END
#endif /* __DEVICE_NETWORK_H__ */

@ -0,0 +1,396 @@
/*
* 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.
*/
#ifdef WITH_OPENCL
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
#include <cl.h>
#include <cl_ext.h>
#else
#include <CL/cl.h>
#include <CL/cl_ext.h>
#endif
#include "device.h"
#include "device_intern.h"
#include "util_map.h"
#include "util_opengl.h"
#include "util_path.h"
#include "util_time.h"
CCL_NAMESPACE_BEGIN
#define CL_MEM_PTR(p) ((cl_mem)(unsigned long)(p))
class OpenCLDevice : public Device
{
public:
cl_context cxGPUContext;
cl_command_queue cqCommandQueue;
cl_platform_id cpPlatform;
cl_device_id cdDevice;
cl_program cpProgram;
cl_kernel ckPathTraceKernel;
cl_kernel ckFilmConvertKernel;
cl_int ciErr;
map<string, device_vector<uchar>*> const_mem_map;
map<string, device_memory*> mem_map;
const char *opencl_error_string(cl_int err)
{
switch (err) {
case CL_SUCCESS: return "Success!";
case CL_DEVICE_NOT_FOUND: return "Device not found.";
case CL_DEVICE_NOT_AVAILABLE: return "Device not available";
case CL_COMPILER_NOT_AVAILABLE: return "Compiler not available";
case CL_MEM_OBJECT_ALLOCATION_FAILURE: return "Memory object allocation failure";
case CL_OUT_OF_RESOURCES: return "Out of resources";
case CL_OUT_OF_HOST_MEMORY: return "Out of host memory";
case CL_PROFILING_INFO_NOT_AVAILABLE: return "Profiling information not available";
case CL_MEM_COPY_OVERLAP: return "Memory copy overlap";
case CL_IMAGE_FORMAT_MISMATCH: return "Image format mismatch";
case CL_IMAGE_FORMAT_NOT_SUPPORTED: return "Image format not supported";
case CL_BUILD_PROGRAM_FAILURE: return "Program build failure";
case CL_MAP_FAILURE: return "Map failure";
case CL_INVALID_VALUE: return "Invalid value";
case CL_INVALID_DEVICE_TYPE: return "Invalid device type";
case CL_INVALID_PLATFORM: return "Invalid platform";
case CL_INVALID_DEVICE: return "Invalid device";
case CL_INVALID_CONTEXT: return "Invalid context";
case CL_INVALID_QUEUE_PROPERTIES: return "Invalid queue properties";
case CL_INVALID_COMMAND_QUEUE: return "Invalid command queue";
case CL_INVALID_HOST_PTR: return "Invalid host pointer";
case CL_INVALID_MEM_OBJECT: return "Invalid memory object";
case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: return "Invalid image format descriptor";
case CL_INVALID_IMAGE_SIZE: return "Invalid image size";
case CL_INVALID_SAMPLER: return "Invalid sampler";
case CL_INVALID_BINARY: return "Invalid binary";
case CL_INVALID_BUILD_OPTIONS: return "Invalid build options";
case CL_INVALID_PROGRAM: return "Invalid program";
case CL_INVALID_PROGRAM_EXECUTABLE: return "Invalid program executable";
case CL_INVALID_KERNEL_NAME: return "Invalid kernel name";
case CL_INVALID_KERNEL_DEFINITION: return "Invalid kernel definition";
case CL_INVALID_KERNEL: return "Invalid kernel";
case CL_INVALID_ARG_INDEX: return "Invalid argument index";
case CL_INVALID_ARG_VALUE: return "Invalid argument value";
case CL_INVALID_ARG_SIZE: return "Invalid argument size";
case CL_INVALID_KERNEL_ARGS: return "Invalid kernel arguments";
case CL_INVALID_WORK_DIMENSION: return "Invalid work dimension";
case CL_INVALID_WORK_GROUP_SIZE: return "Invalid work group size";
case CL_INVALID_WORK_ITEM_SIZE: return "Invalid work item size";
case CL_INVALID_GLOBAL_OFFSET: return "Invalid global offset";
case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list";
case CL_INVALID_EVENT: return "Invalid event";
case CL_INVALID_OPERATION: return "Invalid operation";
case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object";
case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size";
case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level";
default: return "Unknown";
}
}
void opencl_assert(cl_int err)
{
if(err != CL_SUCCESS) {
printf("error (%d): %s\n", err, opencl_error_string(err));
abort();
}
}
OpenCLDevice(bool background_)
{
background = background_;
/* setup device */
ciErr = clGetPlatformIDs(1, &cpPlatform, NULL);
opencl_assert(ciErr);
ciErr = clGetDeviceIDs(cpPlatform, CL_DEVICE_TYPE_GPU, 1, &cdDevice, NULL);
opencl_assert(ciErr);
cxGPUContext = clCreateContext(0, 1, &cdDevice, NULL /*clLogMessagesToStdoutAPPLE */, NULL, &ciErr);
opencl_assert(ciErr);
cqCommandQueue = clCreateCommandQueue(cxGPUContext, cdDevice, 0, &ciErr);
opencl_assert(ciErr);
/* compile kernel */
string source = string_printf("#include \"kernel.cl\" // %lf\n", time_dt());
size_t source_len = source.size();
string build_options = "-I ../kernel -I ../util -Werror -DCCL_NAMESPACE_BEGIN= -DCCL_NAMESPACE_END="; //" + path_get("kernel") + " -Werror";
//printf("path %s\n", path_get("kernel").c_str());
//clUnloadCompiler();
cpProgram = clCreateProgramWithSource(cxGPUContext, 1, (const char **)&source, &source_len, &ciErr);
opencl_assert(ciErr);
ciErr = clBuildProgram(cpProgram, 0, NULL, build_options.c_str(), NULL, NULL);
if(ciErr != CL_SUCCESS) {
char *build_log;
size_t ret_val_size;
clGetProgramBuildInfo(cpProgram, cdDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size);
build_log = new char[ret_val_size+1];
clGetProgramBuildInfo(cpProgram, cdDevice, CL_PROGRAM_BUILD_LOG, ret_val_size, build_log, NULL);
build_log[ret_val_size] = '\0';
printf("OpenCL build failed:\n %s\n", build_log);
delete[] build_log;
opencl_assert(ciErr);
return;
}
ckPathTraceKernel = clCreateKernel(cpProgram, "kernel_ocl_path_trace", &ciErr);
opencl_assert(ciErr);
ckFilmConvertKernel = clCreateKernel(cpProgram, "kernel_ocl_tonemap", &ciErr);
opencl_assert(ciErr);
}
~OpenCLDevice()
{
map<string, device_vector<uchar>*>::iterator mt;
for(mt = const_mem_map.begin(); mt != const_mem_map.end(); mt++) {
mem_free(*(mt->second));
delete mt->second;
}
clReleaseKernel(ckPathTraceKernel);
clReleaseKernel(ckFilmConvertKernel);
clReleaseProgram(cpProgram);
clReleaseCommandQueue(cqCommandQueue);
clReleaseContext(cxGPUContext);
}
string description()
{
char name[1024];
clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(name), &name, NULL);
return string("OpenCL ") + name;
}
void mem_alloc(device_memory& mem, MemoryType type)
{
size_t size = mem.memory_size();
if(type == MEM_READ_ONLY)
mem.device_pointer = (device_ptr)clCreateBuffer(cxGPUContext, CL_MEM_READ_ONLY, size, NULL, &ciErr);
else if(type == MEM_WRITE_ONLY)
mem.device_pointer = (device_ptr)clCreateBuffer(cxGPUContext, CL_MEM_WRITE_ONLY, size, NULL, &ciErr);
else
mem.device_pointer = (device_ptr)clCreateBuffer(cxGPUContext, CL_MEM_READ_WRITE, size, NULL, &ciErr);
opencl_assert(ciErr);
}
void mem_copy_to(device_memory& mem)
{
/* this is blocking */
size_t size = mem.memory_size();
ciErr = clEnqueueWriteBuffer(cqCommandQueue, CL_MEM_PTR(mem.device_pointer), CL_TRUE, 0, size, (void*)mem.data_pointer, 0, NULL, NULL);
opencl_assert(ciErr);
}
void mem_copy_from(device_memory& mem, size_t offset, size_t size)
{
ciErr = clEnqueueReadBuffer(cqCommandQueue, CL_MEM_PTR(mem.device_pointer), CL_TRUE, offset, size, (uchar*)mem.data_pointer + offset, 0, NULL, NULL);
opencl_assert(ciErr);
}
void mem_zero(device_memory& mem)
{
if(mem.device_pointer) {
memset((void*)mem.data_pointer, 0, mem.memory_size());
mem_copy_to(mem);
}
}
void mem_free(device_memory& mem)
{
if(mem.device_pointer) {
ciErr = clReleaseMemObject(CL_MEM_PTR(mem.device_pointer));
mem.device_pointer = 0;
opencl_assert(ciErr);
}
}
void const_copy_to(const char *name, void *host, size_t size)
{
if(const_mem_map.find(name) == const_mem_map.end()) {
device_vector<uchar> *data = new device_vector<uchar>();
data->copy((uchar*)host, size);
mem_alloc(*data, MEM_READ_ONLY);
const_mem_map[name] = data;
}
else {
device_vector<uchar> *data = const_mem_map[name];
data->copy((uchar*)host, size);
}
mem_copy_to(*const_mem_map[name]);
}
void tex_alloc(const char *name, device_memory& mem, bool interpolation, bool periodic)
{
mem_alloc(mem, MEM_READ_ONLY);
mem_map[name] = &mem;
}
void tex_free(device_memory& mem)
{
if(mem.data_pointer)
mem_free(mem);
}
size_t global_size_round_up(int group_size, int global_size)
{
int r = global_size % group_size;
return global_size + ((r == 0)? 0: group_size - r);
}
void path_trace(DeviceTask& task)
{
/* cast arguments to cl types */
cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer);
cl_mem d_buffer = CL_MEM_PTR(task.buffer);
cl_mem d_rng_state = CL_MEM_PTR(task.rng_state);
cl_int d_x = task.x;
cl_int d_y = task.y;
cl_int d_w = task.w;
cl_int d_h = task.h;
cl_int d_pass = task.pass;
/* pass arguments */
int narg = 0;
ciErr = 0;
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_data), (void*)&d_data);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_buffer), (void*)&d_buffer);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_rng_state), (void*)&d_rng_state);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_pass), (void*)&d_pass);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_x), (void*)&d_x);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_y), (void*)&d_y);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_w), (void*)&d_w);
ciErr |= clSetKernelArg(ckPathTraceKernel, narg++, sizeof(d_h), (void*)&d_h);
opencl_assert(ciErr);
size_t local_size[2] = {1, 1};
size_t global_size[2] = {global_size_round_up(local_size[0], d_w), global_size_round_up(local_size[1], d_h)};
/* run kernel */
ciErr = clEnqueueNDRangeKernel(cqCommandQueue, ckPathTraceKernel, 2, NULL, global_size, local_size, 0, NULL, NULL);
opencl_assert(ciErr);
opencl_assert(clFinish(cqCommandQueue));
}
cl_int set_kernel_arg_mem(cl_kernel kernel, int *narg, const char *name)
{
device_memory *mem = mem_map[name];
cl_mem ptr = CL_MEM_PTR(mem->device_pointer);
cl_int size = mem->data_width;
cl_int err = 0;
err |= clSetKernelArg(kernel, (*narg)++, sizeof(ptr), (void*)&ptr);
opencl_assert(err);
err |= clSetKernelArg(kernel, (*narg)++, sizeof(size), (void*)&size);
opencl_assert(err);
return err;
}
void tonemap(DeviceTask& task)
{
/* cast arguments to cl types */
cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer);
cl_mem d_rgba = CL_MEM_PTR(task.rgba);
cl_mem d_buffer = CL_MEM_PTR(task.buffer);
cl_int d_x = task.x;
cl_int d_y = task.y;
cl_int d_w = task.w;
cl_int d_h = task.h;
cl_int d_pass = task.pass;
cl_int d_resolution = task.resolution;
/* pass arguments */
int narg = 0;
ciErr = 0;
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_data), (void*)&d_data);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_rgba), (void*)&d_rgba);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_buffer), (void*)&d_buffer);
ciErr |= set_kernel_arg_mem(ckFilmConvertKernel, &narg, "__response_curve_R");
ciErr |= set_kernel_arg_mem(ckFilmConvertKernel, &narg, "__response_curve_G");
ciErr |= set_kernel_arg_mem(ckFilmConvertKernel, &narg, "__response_curve_B");
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_pass), (void*)&d_pass);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_resolution), (void*)&d_resolution);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_x), (void*)&d_x);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_y), (void*)&d_y);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_w), (void*)&d_w);
ciErr |= clSetKernelArg(ckFilmConvertKernel, narg++, sizeof(d_h), (void*)&d_h);
opencl_assert(ciErr);
size_t local_size[2] = {1, 1};
size_t global_size[2] = {global_size_round_up(local_size[0], d_w), global_size_round_up(local_size[1], d_h)};
/* run kernel */
ciErr = clEnqueueNDRangeKernel(cqCommandQueue, ckFilmConvertKernel, 2, NULL, global_size, local_size, 0, NULL, NULL);
opencl_assert(ciErr);
opencl_assert(clFinish(cqCommandQueue));
}
void task_add(DeviceTask& task)
{
if(task.type == DeviceTask::TONEMAP)
tonemap(task);
else if(task.type == DeviceTask::PATH_TRACE)
path_trace(task);
}
void task_wait()
{
}
void task_cancel()
{
}
};
Device *device_opencl_create(bool background)
{
return new OpenCLDevice(background);
}
CCL_NAMESPACE_END
#endif /* WITH_OPENCL */

@ -0,0 +1,105 @@
INSTALL(DIRECTORY license DESTINATION ${INSTALL_PATH}/cycles)
SET(doc_sources
index.html
development/build.html
development/bvh.html
development/design.html
development/device_abstraction.html
development/displacement.html
development/feature_todo.html
development/known_issues.html
development/geometric_issues.html
development/ideas.html
development/index.html
development/kernel.html
development/license.html
development/node_guidelines.html
development/optimization.html
development/osl_gpu.html
development/papers.html
development/sobol.html
development/source.html
development/subdivision.html
development/threads.html
development/units_colors.html
reference/camera.html
reference/curve.html
reference/devices.html
reference/film.html
reference/index.html
reference/integrator.html
reference/interactive.html
reference/lamp.html
reference/mesh.html
reference/motion_blur.html
reference/particle.html
reference/subdivision.html
reference/world.html
reference/material/displacement.html
reference/material/index.html
reference/material/surface.html
reference/material/volume.html
reference/shader/background.html
reference/shader/bsdf.html
reference/shader/color_operations.html
reference/shader/custom.html
reference/shader/emission.html
reference/shader/image_textures.html
reference/shader/index.html
reference/shader/input.html
reference/shader/output.html
reference/shader/procedural_textures.html
reference/shader/vector_operations.html
reference/shader/volume.html
reference/shader/volume_textures.html)
SET(doc_extra
development/images/rng_lcg_50_pass.png
development/images/rng_sobol_50_pass.png
development/threads.svg
reference/camera_ortho.svg
reference/camera_persp.svg
reference/material/material.svg
reference/shader/bsdf.svg
style/style.css)
SET(doc_templates
templates/development.html
templates/footer.html
templates/header.html
templates/nodes.html
templates/reference.html)
IF(WITH_DOCS)
MACRO(install_doc_file source_file html_file)
GET_FILENAME_COMPONENT(subdir ${source_file} PATH)
INSTALL(
FILES ${html_file}
DESTINATION ${INSTALL_PATH}/cycles/doc/${subdir})
ENDMACRO()
FOREACH(_file ${doc_sources})
SET(source_file ${_file})
SET(html_file ${CMAKE_CURRENT_BINARY_DIR}/${_file})
ADD_CUSTOM_COMMAND(
OUTPUT ${html_file}
COMMAND python generate.py ${source_file} ${html_file} ${CYCLES_VERSION}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${source_file} ${doc_templates} generate.py)
LIST(APPEND html_files ${html_file})
install_doc_file(${source_file} ${html_file})
ENDFOREACH()
FOREACH(_file ${doc_extra})
install_doc_file(${_file} ${_file})
ENDFOREACH()
ADD_CUSTOM_TARGET(doc ALL DEPENDS ${html_files})
ENDIF()

@ -0,0 +1,203 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

@ -0,0 +1,17 @@
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.
Contributor(s): Alfredo de Greef, Blender Foundation

@ -0,0 +1,342 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

@ -0,0 +1,29 @@
Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
Digital Ltd. LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Industrial Light & Magic nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,36 @@
Copyright 2006, NVIDIA Corporation Ignacio Castano <icastano@nvidia.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Copyright 2009-2010 NVIDIA Corporation
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.

@ -0,0 +1,28 @@
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,29 @@
Copyright (c) 2008, Frances Y. Kuo and Stephen Joe
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the names of the copyright holders nor the names of the
University of New South Wales and the University of Waikato
and its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,11 @@
This program uses code from various sources. These are the licenses:
* New code is licensed under the GPL license v2 or later.
* BVH building and traversal code is licensed under Apache License v2.
* Approximate Catmull Clark subdivision code is licensed under the MIT license.
* Open Shading Language code on GPU is licensed under the Modified BSD license.
* Sobol direction vectors are licensed under the Modified BSD license.
* Matrix code adapted from OpenEXR under the Modified BSD license.
* Procedural texture functions from Blender are licensed under GPL v2 or later.

@ -0,0 +1,104 @@
SET(sources
kernel.cpp
kernel.cl
kernel.cu)
SET(headers
kernel.h
kernel_bvh.h
kernel_camera.h
kernel_compat_cpu.h
kernel_compat_cuda.h
kernel_compat_opencl.h
kernel_differential.h
kernel_displace.h
kernel_emission.h
kernel_film.h
kernel_globals.h
kernel_light.h
kernel_math.h
kernel_mbvh.h
kernel_montecarlo.h
kernel_object.h
kernel_path.h
kernel_qbvh.h
kernel_random.h
kernel_shader.h
kernel_triangle.h
kernel_types.h
svm/bsdf.h
svm/bsdf_ashikhmin_velvet.h
svm/bsdf_diffuse.h
svm/bsdf_microfacet.h
svm/bsdf_reflection.h
svm/bsdf_refraction.h
svm/bsdf_transparent.h
svm/bsdf_ward.h
svm/bsdf_westin.h
svm/emissive.h
svm/svm.h
svm/svm_attribute.h
svm/svm_blend.h
svm/svm_bsdf.h
svm/svm_closure.h
svm/svm_clouds.h
svm/svm_convert.h
svm/svm_displace.h
svm/svm_distorted_noise.h
svm/svm_fresnel.h
svm/svm_geometry.h
svm/svm_image.h
svm/svm_light_path.h
svm/svm_magic.h
svm/svm_mapping.h
svm/svm_marble.h
svm/svm_math.h
svm/svm_mix.h
svm/svm_musgrave.h
svm/svm_noise.h
svm/svm_noisetex.h
svm/svm_sky.h
svm/svm_stucci.h
svm/svm_tex_coord.h
svm/svm_texture.h
svm/svm_types.h
svm/svm_value.h
svm/svm_voronoi.h
svm/svm_wood.h
svm/volume.h
)
# CUDA module
IF(WITH_CUDA)
SET(cuda_sources kernel.cu ${headers})
SET(cuda_cubins)
FOREACH(arch ${CUDA_ARCH})
SET(cuda_cubin kernel_${arch}.cubin)
ADD_CUSTOM_COMMAND(
OUTPUT ${cuda_cubin}
COMMAND ${CUDA_NVCC} -arch=${arch} -m64 --cubin ${CMAKE_CURRENT_SOURCE_DIR}/kernel.cu --use_fast_math -o ${CMAKE_CURRENT_BINARY_DIR}/${cuda_cubin} --ptxas-options="-v" --maxrregcount=${CUDA_MAXREG} --opencc-options -OPT:Olimit=0 -I${CMAKE_CURRENT_SOURCE_DIR}/../util -I${CMAKE_CURRENT_SOURCE_DIR}/svm -DCCL_NAMESPACE_BEGIN= -DCCL_NAMESPACE_END=
DEPENDS ${cuda_sources})
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${cuda_cubin} DESTINATION ${INSTALL_PATH}/cycles/lib)
LIST(APPEND cuda_cubins ${cuda_cubin})
ENDFOREACH()
ADD_CUSTOM_TARGET(kernel_cuda ALL DEPENDS ${cuda_cubins})
ENDIF(WITH_CUDA)
# OSL module
IF(WITH_OSL)
ADD_SUBDIRECTORY(osl)
ENDIF(WITH_OSL)
# CPU module
INCLUDE_DIRECTORIES(. ../util osl svm)
ADD_LIBRARY(kernel ${sources} ${headers})

@ -0,0 +1,105 @@
/*
* 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.
*/
/* OpenCL kernel entry points - unfinished */
#include "kernel_compat_opencl.h"
#include "kernel_math.h"
#include "kernel_types.h"
#include "kernel_globals.h"
typedef struct KernelGlobals {
__constant KernelData *data;
__global float *__response_curve_R;
int __response_curve_R_width;
__global float *__response_curve_G;
int __response_curve_G_width;
__global float *__response_curve_B;
int __response_curve_B_width;
} KernelGlobals;
#include "kernel_film.h"
//#include "kernel_path.h"
//#include "kernel_displace.h"
__kernel void kernel_ocl_path_trace(__constant KernelData *data, __global float4 *buffer, __global uint *rng_state, int pass, int sx, int sy, int sw, int sh)
{
KernelGlobals kglobals, *kg = &kglobals;
kg->data = data;
int x = get_global_id(0);
int y = get_global_id(1);
int w = kernel_data.cam.width;
if(x < sx + sw && y < sy + sh) {
if(pass == 0) {
buffer[x + w*y].x = 0.5f;
buffer[x + w*y].y = 0.5f;
buffer[x + w*y].z = 0.5f;
}
else {
buffer[x + w*y].x += 0.5f;
buffer[x + w*y].y += 0.5f;
buffer[x + w*y].z += 0.5f;
}
//= make_float3(1.0f, 0.9f, 0.0f);
//kernel_path_trace(buffer, rng_state, pass, x, y);
}
}
__kernel void kernel_ocl_tonemap(
__constant KernelData *data,
__global uchar4 *rgba,
__global float4 *buffer,
__global float *__response_curve_R,
int __response_curve_R_width,
__global float *__response_curve_G,
int __response_curve_G_width,
__global float *__response_curve_B,
int __response_curve_B_width,
int pass, int resolution,
int sx, int sy, int sw, int sh)
{
KernelGlobals kglobals, *kg = &kglobals;
kg->data = data;
kg->__response_curve_R = __response_curve_R;
kg->__response_curve_R_width = __response_curve_R_width;
kg->__response_curve_G = __response_curve_G;
kg->__response_curve_G_width = __response_curve_G_width;
kg->__response_curve_B = __response_curve_B;
kg->__response_curve_B_width = __response_curve_B_width;
int x = sx + get_global_id(0);
int y = sy + get_global_id(1);
if(x < sx + sw && y < sy + sh)
kernel_film_tonemap(kg, rgba, buffer, pass, resolution, x, y);
}
__kernel void kernel_ocl_displace(__global uint4 *input, __global float3 *offset, int sx)
{
int x = sx + get_global_id(0);
kernel_displace(input, offset, x);
}

@ -0,0 +1,300 @@
/*
* 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.
*/
/* CPU kernel entry points */
#include "kernel.h"
#include "kernel_compat_cpu.h"
#include "kernel_math.h"
#include "kernel_types.h"
#include "kernel_globals.h"
#include "kernel_film.h"
#include "kernel_path.h"
#include "kernel_displace.h"
CCL_NAMESPACE_BEGIN
/* Globals */
KernelGlobals *kernel_globals_create()
{
KernelGlobals *kg = new KernelGlobals();
#ifdef WITH_OSL
kg->osl.use = false;
#endif
return kg;
}
void kernel_globals_free(KernelGlobals *kg)
{
delete kg;
}
/* OSL */
#ifdef WITH_OSL
void *kernel_osl_memory(KernelGlobals *kg)
{
return (void*)&kg->osl;
}
bool kernel_osl_use(KernelGlobals *kg)
{
return kg->osl.use;
}
#endif
/* Memory Copy */
void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size)
{
if(strcmp(name, "__data") == 0)
memcpy(&kg->__data, host, size);
else
assert(0);
}
void kernel_tex_copy(KernelGlobals *kg, const char *name, device_ptr mem, size_t width, size_t height)
{
if(strcmp(name, "__bvh_nodes") == 0) {
kg->__bvh_nodes.data = (float4*)mem;
kg->__bvh_nodes.width = width;
}
else if(strcmp(name, "__objects") == 0) {
kg->__objects.data = (float4*)mem;
kg->__objects.width = width;
}
else if(strcmp(name, "__tri_normal") == 0) {
kg->__tri_normal.data = (float4*)mem;
kg->__tri_normal.width = width;
}
else if(strcmp(name, "__tri_woop") == 0) {
kg->__tri_woop.data = (float4*)mem;
kg->__tri_woop.width = width;
}
else if(strcmp(name, "__prim_index") == 0) {
kg->__prim_index.data = (uint*)mem;
kg->__prim_index.width = width;
}
else if(strcmp(name, "__prim_object") == 0) {
kg->__prim_object.data = (uint*)mem;
kg->__prim_object.width = width;
}
else if(strcmp(name, "__object_node") == 0) {
kg->__object_node.data = (uint*)mem;
kg->__object_node.width = width;
}
else if(strcmp(name, "__tri_vnormal") == 0) {
kg->__tri_vnormal.data = (float4*)mem;
kg->__tri_vnormal.width = width;
}
else if(strcmp(name, "__tri_vindex") == 0) {
kg->__tri_vindex.data = (float4*)mem;
kg->__tri_vindex.width = width;
}
else if(strcmp(name, "__tri_verts") == 0) {
kg->__tri_verts.data = (float4*)mem;
kg->__tri_verts.width = width;
}
else if(strcmp(name, "__light_distribution") == 0) {
kg->__light_distribution.data = (float4*)mem;
kg->__light_distribution.width = width;
}
else if(strcmp(name, "__light_point") == 0) {
kg->__light_point.data = (float4*)mem;
kg->__light_point.width = width;
}
else if(strcmp(name, "__svm_nodes") == 0) {
kg->__svm_nodes.data = (uint4*)mem;
kg->__svm_nodes.width = width;
}
else if(strcmp(name, "__filter_table") == 0) {
kg->__filter_table.data = (float*)mem;
kg->__filter_table.width = width;
}
else if(strcmp(name, "__response_curve_R") == 0) {
kg->__response_curve_R.data = (float*)mem;
kg->__response_curve_R.width = width;
}
else if(strcmp(name, "__response_curve_B") == 0) {
kg->__response_curve_B.data = (float*)mem;
kg->__response_curve_B.width = width;
}
else if(strcmp(name, "__response_curve_G") == 0) {
kg->__response_curve_G.data = (float*)mem;
kg->__response_curve_G.width = width;
}
else if(strcmp(name, "__sobol_directions") == 0) {
kg->__sobol_directions.data = (uint*)mem;
kg->__sobol_directions.width = width;
}
else if(strcmp(name, "__attributes_map") == 0) {
kg->__attributes_map.data = (uint4*)mem;
kg->__attributes_map.width = width;
}
else if(strcmp(name, "__attributes_float") == 0) {
kg->__attributes_float.data = (float*)mem;
kg->__attributes_float.width = width;
}
else if(strcmp(name, "__attributes_float3") == 0) {
kg->__attributes_float3.data = (float4*)mem;
kg->__attributes_float3.width = width;
}
else if(strstr(name, "__tex_image")) {
texture_image_uchar4 *tex = NULL;
int id = atoi(name + strlen("__tex_image_"));
switch(id) {
case 0: tex = &kg->__tex_image_000; break;
case 1: tex = &kg->__tex_image_001; break;
case 2: tex = &kg->__tex_image_002; break;
case 3: tex = &kg->__tex_image_003; break;
case 4: tex = &kg->__tex_image_004; break;
case 5: tex = &kg->__tex_image_005; break;
case 6: tex = &kg->__tex_image_006; break;
case 7: tex = &kg->__tex_image_007; break;
case 8: tex = &kg->__tex_image_008; break;
case 9: tex = &kg->__tex_image_009; break;
case 10: tex = &kg->__tex_image_010; break;
case 11: tex = &kg->__tex_image_011; break;
case 12: tex = &kg->__tex_image_012; break;
case 13: tex = &kg->__tex_image_013; break;
case 14: tex = &kg->__tex_image_014; break;
case 15: tex = &kg->__tex_image_015; break;
case 16: tex = &kg->__tex_image_016; break;
case 17: tex = &kg->__tex_image_017; break;
case 18: tex = &kg->__tex_image_018; break;
case 19: tex = &kg->__tex_image_019; break;
case 20: tex = &kg->__tex_image_020; break;
case 21: tex = &kg->__tex_image_021; break;
case 22: tex = &kg->__tex_image_022; break;
case 23: tex = &kg->__tex_image_023; break;
case 24: tex = &kg->__tex_image_024; break;
case 25: tex = &kg->__tex_image_025; break;
case 26: tex = &kg->__tex_image_026; break;
case 27: tex = &kg->__tex_image_027; break;
case 28: tex = &kg->__tex_image_028; break;
case 29: tex = &kg->__tex_image_029; break;
case 30: tex = &kg->__tex_image_030; break;
case 31: tex = &kg->__tex_image_031; break;
case 32: tex = &kg->__tex_image_032; break;
case 33: tex = &kg->__tex_image_033; break;
case 34: tex = &kg->__tex_image_034; break;
case 35: tex = &kg->__tex_image_035; break;
case 36: tex = &kg->__tex_image_036; break;
case 37: tex = &kg->__tex_image_037; break;
case 38: tex = &kg->__tex_image_038; break;
case 39: tex = &kg->__tex_image_039; break;
case 40: tex = &kg->__tex_image_040; break;
case 41: tex = &kg->__tex_image_041; break;
case 42: tex = &kg->__tex_image_042; break;
case 43: tex = &kg->__tex_image_043; break;
case 44: tex = &kg->__tex_image_044; break;
case 45: tex = &kg->__tex_image_045; break;
case 46: tex = &kg->__tex_image_046; break;
case 47: tex = &kg->__tex_image_047; break;
case 48: tex = &kg->__tex_image_048; break;
case 49: tex = &kg->__tex_image_049; break;
case 50: tex = &kg->__tex_image_050; break;
case 51: tex = &kg->__tex_image_051; break;
case 52: tex = &kg->__tex_image_052; break;
case 53: tex = &kg->__tex_image_053; break;
case 54: tex = &kg->__tex_image_054; break;
case 55: tex = &kg->__tex_image_055; break;
case 56: tex = &kg->__tex_image_056; break;
case 57: tex = &kg->__tex_image_057; break;
case 58: tex = &kg->__tex_image_058; break;
case 59: tex = &kg->__tex_image_059; break;
case 60: tex = &kg->__tex_image_060; break;
case 61: tex = &kg->__tex_image_061; break;
case 62: tex = &kg->__tex_image_062; break;
case 63: tex = &kg->__tex_image_063; break;
case 64: tex = &kg->__tex_image_064; break;
case 65: tex = &kg->__tex_image_065; break;
case 66: tex = &kg->__tex_image_066; break;
case 67: tex = &kg->__tex_image_067; break;
case 68: tex = &kg->__tex_image_068; break;
case 69: tex = &kg->__tex_image_069; break;
case 70: tex = &kg->__tex_image_070; break;
case 71: tex = &kg->__tex_image_071; break;
case 72: tex = &kg->__tex_image_072; break;
case 73: tex = &kg->__tex_image_073; break;
case 74: tex = &kg->__tex_image_074; break;
case 75: tex = &kg->__tex_image_075; break;
case 76: tex = &kg->__tex_image_076; break;
case 77: tex = &kg->__tex_image_077; break;
case 78: tex = &kg->__tex_image_078; break;
case 79: tex = &kg->__tex_image_079; break;
case 80: tex = &kg->__tex_image_080; break;
case 81: tex = &kg->__tex_image_081; break;
case 82: tex = &kg->__tex_image_082; break;
case 83: tex = &kg->__tex_image_083; break;
case 84: tex = &kg->__tex_image_084; break;
case 85: tex = &kg->__tex_image_085; break;
case 86: tex = &kg->__tex_image_086; break;
case 87: tex = &kg->__tex_image_087; break;
case 88: tex = &kg->__tex_image_088; break;
case 89: tex = &kg->__tex_image_089; break;
case 90: tex = &kg->__tex_image_090; break;
case 91: tex = &kg->__tex_image_091; break;
case 92: tex = &kg->__tex_image_092; break;
case 93: tex = &kg->__tex_image_093; break;
case 94: tex = &kg->__tex_image_094; break;
case 95: tex = &kg->__tex_image_095; break;
case 96: tex = &kg->__tex_image_096; break;
case 97: tex = &kg->__tex_image_097; break;
case 98: tex = &kg->__tex_image_098; break;
case 99: tex = &kg->__tex_image_099; break;
default: break;
}
if(tex) {
tex->data = (uchar4*)mem;
tex->width = width;
tex->height = height;
}
}
else
assert(0);
}
/* Path Tracing */
void kernel_cpu_path_trace(KernelGlobals *kg, float4 *buffer, unsigned int *rng_state, int pass, int x, int y)
{
kernel_path_trace(kg, buffer, rng_state, pass, x, y);
}
/* Tonemapping */
void kernel_cpu_tonemap(KernelGlobals *kg, uchar4 *rgba, float4 *buffer, int pass, int resolution, int x, int y)
{
kernel_film_tonemap(kg, rgba, buffer, pass, resolution, x, y);
}
/* Displacement */
void kernel_cpu_displace(KernelGlobals *kg, uint4 *input, float3 *offset, int i)
{
kernel_displace(kg, input, offset, i);
}
CCL_NAMESPACE_END

@ -0,0 +1,53 @@
/*
* 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.
*/
/* CUDA kernel entry points */
#include "kernel_compat_cuda.h"
#include "kernel_math.h"
#include "kernel_types.h"
#include "kernel_globals.h"
#include "kernel_film.h"
#include "kernel_path.h"
#include "kernel_displace.h"
extern "C" __global__ void kernel_cuda_path_trace(float4 *buffer, uint *rng_state, int pass, int sx, int sy, int sw, int sh)
{
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
if(x < sx + sw && y < sy + sh)
kernel_path_trace(NULL, buffer, rng_state, pass, x, y);
}
extern "C" __global__ void kernel_cuda_tonemap(uchar4 *rgba, float4 *buffer, int pass, int resolution, int sx, int sy, int sw, int sh)
{
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
if(x < sx + sw && y < sy + sh)
kernel_film_tonemap(NULL, rgba, buffer, pass, resolution, x, y);
}
extern "C" __global__ void kernel_cuda_displace(uint4 *input, float3 *offset, int sx)
{
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
kernel_displace(NULL, input, offset, x);
}

@ -0,0 +1,47 @@
/*
* 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 __KERNEL_H__
#define __KERNEL_H__
/* CPU Kernel Interfae */
#include "util_types.h"
CCL_NAMESPACE_BEGIN
struct KernelGlobals;
KernelGlobals *kernel_globals_create();
void kernel_globals_free(KernelGlobals *kg);
void *kernel_osl_memory(KernelGlobals *kg);
bool kernel_osl_use(KernelGlobals *kg);
void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size);
void kernel_tex_copy(KernelGlobals *kg, const char *name, device_ptr mem, size_t width, size_t height);
void kernel_cpu_path_trace(KernelGlobals *kg, float4 *buffer, unsigned int *rng_state, int pass, int x, int y);
void kernel_cpu_tonemap(KernelGlobals *kg, uchar4 *rgba, float4 *buffer, int pass, int resolution, int x, int y);
void kernel_cpu_displace(KernelGlobals *kg, uint4 *input, float3 *offset, int i);
CCL_NAMESPACE_END
#endif /* __KERNEL_H__ */

@ -0,0 +1,361 @@
/*
* Adapted from code Copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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.
*/
CCL_NAMESPACE_BEGIN
/*
* "Persistent while-while kernel" used in:
*
* "Understanding the Efficiency of Ray Traversal on GPUs",
* Timo Aila and Samuli Laine,
* Proc. High-Performance Graphics 2009
*/
/* bottom-most stack entry, indicating the end of traversal */
#define ENTRYPOINT_SENTINEL 0x76543210
/* 64 object BVH + 64 mesh BVH + 64 object node splitting */
#define BVH_STACK_SIZE 192
#define BVH_NODE_SIZE 4
#define TRI_NODE_SIZE 3
__device_inline float3 bvh_inverse_direction(float3 dir)
{
/* avoid divide by zero (ooeps = exp2f(-80.0f)) */
float ooeps = 0.00000000000000000000000082718061255302767487140869206996285356581211090087890625f;
float3 idir;
idir.x = 1.0f/((fabsf(dir.x) > ooeps)? dir.x: copysignf(ooeps, dir.x));
idir.y = 1.0f/((fabsf(dir.y) > ooeps)? dir.y: copysignf(ooeps, dir.y));
idir.z = 1.0f/((fabsf(dir.z) > ooeps)? dir.z: copysignf(ooeps, dir.z));
return idir;
}
__device_inline void bvh_instance_push(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *idir, float *t, const float tmax)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
*P = transform(&tfm, ray->P);
float3 dir = transform_direction(&tfm, ray->D);
float len;
dir = normalize_len(dir, &len);
*idir = bvh_inverse_direction(dir);
if(*t != FLT_MAX)
*t *= len;
}
__device_inline void bvh_instance_pop(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *idir, float *t, const float tmax)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
if(*t != FLT_MAX)
*t *= len(transform_direction(&tfm, 1.0f/(*idir)));
*P = ray->P;
*idir = bvh_inverse_direction(ray->D);
}
/* intersect two bounding boxes */
__device_inline void bvh_node_intersect(KernelGlobals *kg,
bool *traverseChild0, bool *traverseChild1,
bool *closestChild1, int *nodeAddr0, int *nodeAddr1,
float3 P, float3 idir, float t, int nodeAddr)
{
/* fetch node data */
float4 n0xy = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+0);
float4 n1xy = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+1);
float4 nz = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+2);
float4 cnodes = kernel_tex_fetch(__bvh_nodes, nodeAddr*BVH_NODE_SIZE+3);
/* intersect ray against child nodes */
float3 ood = P * idir;
float c0lox = n0xy.x * idir.x - ood.x;
float c0hix = n0xy.y * idir.x - ood.x;
float c0loy = n0xy.z * idir.y - ood.y;
float c0hiy = n0xy.w * idir.y - ood.y;
float c0loz = nz.x * idir.z - ood.z;
float c0hiz = nz.y * idir.z - ood.z;
float c1loz = nz.z * idir.z - ood.z;
float c1hiz = nz.w * idir.z - ood.z;
float c0min_x = min(c0lox, c0hix);
float c0min_y = min(c0loy, c0hiy);
float c0min_z = min(c0loz, c0hiz);
float c0min = max4(c0min_x, c0min_y, c0min_z, 0.0f);
float c0max = min4(max(c0lox, c0hix), max(c0loy, c0hiy), max(c0loz, c0hiz), t);
float c1lox = n1xy.x * idir.x - ood.x;
float c1hix = n1xy.y * idir.x - ood.x;
float c1loy = n1xy.z * idir.y - ood.y;
float c1hiy = n1xy.w * idir.y - ood.y;
float c1min = max4(min(c1lox, c1hix), min(c1loy, c1hiy), min(c1loz, c1hiz), 0.0f);
float c1max = min4(max(c1lox, c1hix), max(c1loy, c1hiy), max(c1loz, c1hiz), t);
/* decide which nodes to traverse next */
*traverseChild0 = (c0max >= c0min);
*traverseChild1 = (c1max >= c1min);
*nodeAddr0 = __float_as_int(cnodes.x);
*nodeAddr1 = __float_as_int(cnodes.y);
*closestChild1 = (c1min < c0min);
}
/* Sven Woop's algorithm */
__device_inline void bvh_triangle_intersect(KernelGlobals *kg, Intersection *isect, float3 P, float3 idir, int object, int triAddr)
{
/* compute and check intersection t-value */
float4 v00 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0);
float4 v11 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1);
float3 dir = 1.0f/idir;
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(dir.x*v00.x + dir.y*v00.y + dir.z*v00.z);
float t = Oz * invDz;
if(t > 0.0f && t < isect->t) {
/* compute and check barycentric u */
float Ox = v11.w + P.x*v11.x + P.y*v11.y + P.z*v11.z;
float Dx = dir.x*v11.x + dir.y*v11.y + dir.z*v11.z;
float u = Ox + t*Dx;
if(u >= 0.0f) {
/* compute and check barycentric v */
float4 v22 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
float Oy = v22.w + P.x*v22.x + P.y*v22.y + P.z*v22.z;
float Dy = dir.x*v22.x + dir.y*v22.y + dir.z*v22.z;
float v = Oy + t*Dy;
if(v >= 0.0f && u + v <= 1.0f) {
/* record intersection */
isect->prim = triAddr;
isect->object = object;
isect->u = u;
isect->v = v;
isect->t = t;
}
}
}
}
__device_inline bool scene_intersect(KernelGlobals *kg, const Ray *ray, const bool isshadowray, Intersection *isect)
{
/* traversal stack in CUDA thread-local memory */
int traversalStack[BVH_STACK_SIZE];
traversalStack[0] = ENTRYPOINT_SENTINEL;
/* traversal variables in registers */
int stackPtr = 0;
int nodeAddr = kernel_data.bvh.root;
/* ray parameters in registers */
const float tmax = ray->t;
float3 P = ray->P;
float3 idir = bvh_inverse_direction(ray->D);
int object = ~0;
isect->t = tmax;
isect->object = ~0;
isect->prim = ~0;
isect->u = 0.0f;
isect->v = 0.0f;
/* traversal loop */
do {
do
{
/* traverse internal nodes */
while(nodeAddr >= 0 && nodeAddr != ENTRYPOINT_SENTINEL)
{
bool traverseChild0, traverseChild1, closestChild1;
int nodeAddrChild1;
bvh_node_intersect(kg, &traverseChild0, &traverseChild1,
&closestChild1, &nodeAddr, &nodeAddrChild1,
P, idir, isect->t, nodeAddr);
if(traverseChild0 != traverseChild1) {
/* one child was intersected */
if(traverseChild1) {
nodeAddr = nodeAddrChild1;
}
}
else {
if(!traverseChild0) {
/* neither child was intersected */
nodeAddr = traversalStack[stackPtr];
--stackPtr;
}
else {
/* both children were intersected, push the farther one */
if(closestChild1) {
int tmp = nodeAddr;
nodeAddr = nodeAddrChild1;
nodeAddrChild1 = tmp;
}
++stackPtr;
traversalStack[stackPtr] = nodeAddrChild1;
}
}
}
/* if node is leaf, fetch triangle list */
if(nodeAddr < 0) {
float4 leaf = kernel_tex_fetch(__bvh_nodes, (-nodeAddr-1)*BVH_NODE_SIZE+(BVH_NODE_SIZE-1));
int primAddr = __float_as_int(leaf.x);
#ifdef __INSTANCING__
if(primAddr >= 0) {
#endif
int primAddr2 = __float_as_int(leaf.y);
/* pop */
nodeAddr = traversalStack[stackPtr];
--stackPtr;
/* triangle intersection */
while(primAddr < primAddr2) {
/* intersect ray against triangle */
bvh_triangle_intersect(kg, isect, P, idir, object, primAddr);
/* shadow ray early termination */
if(isshadowray && isect->prim != ~0)
return true;
primAddr++;
}
#ifdef __INSTANCING__
}
else {
/* instance push */
object = kernel_tex_fetch(__prim_object, -primAddr-1);
bvh_instance_push(kg, object, ray, &P, &idir, &isect->t, tmax);
++stackPtr;
traversalStack[stackPtr] = ENTRYPOINT_SENTINEL;
nodeAddr = kernel_tex_fetch(__object_node, object);
}
#endif
}
} while(nodeAddr != ENTRYPOINT_SENTINEL);
#ifdef __INSTANCING__
if(stackPtr >= 0) {
kernel_assert(object != ~0);
/* instance pop */
bvh_instance_pop(kg, object, ray, &P, &idir, &isect->t, tmax);
object = ~0;
nodeAddr = traversalStack[stackPtr];
--stackPtr;
}
#endif
} while(nodeAddr != ENTRYPOINT_SENTINEL);
return (isect->prim != ~0);
}
__device_inline float3 ray_offset(float3 P, float3 Ng)
{
#ifdef __INTERSECTION_REFINE__
const float epsilon_f = 1e-5f;
const int epsilon_i = 32;
float3 res;
/* x component */
if(fabsf(P.x) < epsilon_f) {
res.x = P.x + Ng.x*epsilon_f;
}
else {
uint ix = __float_as_uint(P.x);
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31)? -epsilon_i: epsilon_i;
res.x = __uint_as_float(ix);
}
/* y component */
if(fabsf(P.y) < epsilon_f) {
res.y = P.y + Ng.y*epsilon_f;
}
else {
uint iy = __float_as_uint(P.y);
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31)? -epsilon_i: epsilon_i;
res.y = __uint_as_float(iy);
}
/* z component */
if(fabsf(P.z) < epsilon_f) {
res.z = P.z + Ng.z*epsilon_f;
}
else {
uint iz = __float_as_uint(P.z);
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31)? -epsilon_i: epsilon_i;
res.z = __uint_as_float(iz);
}
return res;
#else
const float epsilon_f = 1e-4f;
return P + epsilon_f*Ng;
#endif
}
__device_inline float3 bvh_triangle_refine(KernelGlobals *kg, const Intersection *isect, const Ray *ray)
{
float3 P = ray->P;
float3 D = ray->D;
float t = isect->t;
#ifdef __INTERSECTION_REFINE__
if(isect->object != ~0) {
Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM);
P = transform(&tfm, P);
D = transform_direction(&tfm, D*t);
D = normalize_len(D, &t);
}
P = P + D*t;
float4 v00 = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0);
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(D.x*v00.x + D.y*v00.y + D.z*v00.z);
float rt = Oz * invDz;
P = P + D*rt;
if(isect->object != ~0) {
Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM);
P = transform(&tfm, P);
}
return P;
#else
return P + D*t;
#endif
}
CCL_NAMESPACE_END

@ -0,0 +1,132 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
/* Perspective Camera */
__device void camera_sample_perspective(KernelGlobals *kg, float raster_x, float raster_y, float lens_u, float lens_v, Ray *ray)
{
/* create ray form raster position */
Transform rastertocamera = kernel_data.cam.rastertocamera;
float3 Pcamera = transform(&rastertocamera, make_float3(raster_x, raster_y, 0.0f));
ray->P = make_float3(0.0f, 0.0f, 0.0f);
ray->D = Pcamera;
/* modify ray for depth of field */
float lensradius = kernel_data.cam.lensradius;
if(lensradius > 0.0f) {
/* sample point on lens */
float2 lensuv;
lensuv = concentric_sample_disk(lens_u, lens_v);
lensuv *= lensradius;
/* compute point on plane of focus */
float ft = kernel_data.cam.focaldistance/ray->D.z;
float3 Pfocus = ray->P + ray->D*ft;
/* update ray for effect of lens */
ray->P = make_float3(lensuv.x, lensuv.y, 0.0f);
ray->D = normalize(Pfocus - ray->P);
}
/* transform ray from camera to world */
Transform cameratoworld = kernel_data.cam.cameratoworld;
ray->P = transform(&cameratoworld, ray->P);
ray->D = transform_direction(&cameratoworld, ray->D);
ray->D = normalize(ray->D);
#ifdef __RAY_DIFFERENTIALS__
/* ray differential */
float3 Ddiff = transform_direction(&cameratoworld, Pcamera);
ray->dP.dx = make_float3(0.0f, 0.0f, 0.0f);
ray->dP.dy = make_float3(0.0f, 0.0f, 0.0f);
ray->dD.dx = normalize(Ddiff + kernel_data.cam.dx) - normalize(Ddiff);
ray->dD.dy = normalize(Ddiff + kernel_data.cam.dy) - normalize(Ddiff);
#endif
#ifdef __CAMERA_CLIPPING__
/* clipping */
ray->P += kernel_data.cam.nearclip*ray->D;
ray->t = kernel_data.cam.cliplength;
#else
ray->t = FLT_MAX;
#endif
}
/* Orthographic Camera */
__device void camera_sample_orthographic(KernelGlobals *kg, float raster_x, float raster_y, Ray *ray)
{
/* create ray form raster position */
Transform rastertocamera = kernel_data.cam.rastertocamera;
float3 Pcamera = transform(&rastertocamera, make_float3(raster_x, raster_y, 0.0f));
ray->P = Pcamera;
ray->D = make_float3(0.0f, 0.0f, 1.0f);
/* transform ray from camera to world */
Transform cameratoworld = kernel_data.cam.cameratoworld;
ray->P = transform(&cameratoworld, ray->P);
ray->D = transform_direction(&cameratoworld, ray->D);
ray->D = normalize(ray->D);
#ifdef __RAY_DIFFERENTIALS__
/* ray differential */
ray->dP.dx = kernel_data.cam.dx;
ray->dP.dy = kernel_data.cam.dy;
ray->dD.dx = make_float3(0.0f, 0.0f, 0.0f);
ray->dD.dy = make_float3(0.0f, 0.0f, 0.0f);
#endif
#ifdef __CAMERA_CLIPPING__
/* clipping */
ray->t = kernel_data.cam.cliplength;
#else
ray->t = FLT_MAX;
#endif
}
/* Common */
__device void camera_sample(KernelGlobals *kg, int x, int y, float filter_u, float filter_v, float lens_u, float lens_v, Ray *ray)
{
/* pixel filter */
float raster_x = x + kernel_tex_interp(__filter_table, filter_u);
float raster_y = y + kernel_tex_interp(__filter_table, filter_v);
/* motion blur */
//ray->time = lerp(time_t, kernel_data.cam.shutter_open, kernel_data.cam.shutter_close);
/* sample */
if(kernel_data.cam.ortho)
camera_sample_orthographic(kg, raster_x, raster_y, ray);
else
camera_sample_perspective(kg, raster_x, raster_y, lens_u, lens_v, ray);
}
CCL_NAMESPACE_END

@ -0,0 +1,162 @@
/*
* 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 __KERNEL_COMPAT_CPU_H__
#define __KERNEL_COMPAT_CPU_H__
#define __KERNEL_CPU__
#include "util_debug.h"
#include "util_math.h"
#include "util_types.h"
CCL_NAMESPACE_BEGIN
/* Assertions inside the kernel only work for the CPU device, so we wrap it in
a macro which is empty for other devices */
#define kernel_assert(cond) assert(cond)
/* Texture types to be compatible with CUDA textures. These are really just
simple arrays and after inlining fetch hopefully revert to being a simple
pointer lookup. */
template<typename T> struct texture {
T fetch(int index)
{
kernel_assert(index >= 0 && index < width);
return data[index];
}
__m128 fetch_m128(int index)
{
kernel_assert(index >= 0 && index < width);
return ((__m128*)data)[index];
}
__m128i fetch_m128i(int index)
{
kernel_assert(index >= 0 && index < width);
return ((__m128i*)data)[index];
}
float interp(float x)
{
x = clamp(x, 0.0f, 1.0f)*width;
int index = min((int)x, width-1);
int nindex = min(index+1, width-1);
float t = x - index;
return (1.0f - t)*data[index] + t*data[nindex];
}
T *data;
int width;
};
template<typename T> struct texture_image {
float4 read(float4 r)
{
return r;
}
float4 read(uchar4 r)
{
float f = 1.0f/255.0f;
return make_float4(r.x*f, r.y*f, r.z*f, r.w*f);
}
int wrap_periodic(int x, int width)
{
x %= width;
if(x < 0)
x += width;
return x;
}
int wrap_clamp(int x, int width)
{
return clamp(x, 0, width-1);
}
float frac(float x, int *ix)
{
int i = (int)x - ((x < 0.0f)? 1: 0);
*ix = i;
return x - (float)i;
}
float4 interp(float x, float y, bool periodic = true)
{
if(!data)
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
int ix, iy, nix, niy;
float tx = frac(x*width, &ix);
float ty = frac(y*height, &iy);
if(periodic) {
ix = wrap_periodic(ix, width);
iy = wrap_periodic(iy, height);
nix = wrap_periodic(ix+1, width);
niy = wrap_periodic(iy+1, height);
}
else {
ix = wrap_clamp(ix, width);
iy = wrap_clamp(iy, height);
nix = wrap_clamp(ix+1, width);
niy = wrap_clamp(iy+1, height);
}
float4 r = (1.0f - ty)*(1.0f - tx)*read(data[ix + iy*width]);
r += (1.0f - ty)*tx*read(data[nix + iy*width]);
r += ty*(1.0f - tx)*read(data[ix + niy*width]);
r += ty*tx*read(data[nix + niy*width]);
return r;
}
T *data;
int width, height;
};
typedef texture<float4> texture_float4;
typedef texture<float> texture_float;
typedef texture<uint> texture_uint;
typedef texture<int> texture_int;
typedef texture<uint4> texture_uint4;
typedef texture_image<float4> texture_image_float4;
typedef texture_image<uchar4> texture_image_uchar4;
/* Macros to handle different memory storage on different devices */
#define kernel_tex_fetch(tex, index) (kg->tex.fetch(index))
#define kernel_tex_fetch_m128(tex, index) (kg->tex.fetch_m128(index))
#define kernel_tex_fetch_m128i(tex, index) (kg->tex.fetch_m128i(index))
#define kernel_tex_interp(tex, t) (kg->tex.interp(t))
#define kernel_tex_image_interp(tex, x, y) (kg->tex.interp(x, y))
#define kernel_data (kg->__data)
CCL_NAMESPACE_END
#endif /* __KERNEL_COMPAT_CPU_H__ */

@ -0,0 +1,65 @@
/*
* 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 __KERNEL_COMPAT_CUDA_H__
#define __KERNEL_COMPAT_CUDA_H__
#define __KERNEL_GPU__
#define __KERNEL_CUDA__
#include <cuda.h>
#include <float.h>
#include "util_types.h"
CCL_NAMESPACE_BEGIN
/* Qualifier wrappers for different names on different devices */
#define __device __device__ __inline__
#define __device_inline __device__ __inline__
#define __global
#define __shared __shared__
#define __constant __constant__
/* No assert supported for CUDA */
#define kernel_assert(cond)
/* Textures */
typedef texture<float4, 1> texture_float4;
typedef texture<float, 1> texture_float;
typedef texture<uint, 1> texture_uint;
typedef texture<int, 1> texture_int;
typedef texture<uint4, 1> texture_uint4;
typedef texture<float4, 2> texture_image_float4;
typedef texture<uchar4, 2, cudaReadModeNormalizedFloat> texture_image_uchar4;
/* Macros to handle different memory storage on different devices */
#define kernel_tex_fetch(t, index) tex1Dfetch(t, index)
#define kernel_tex_interp(t, x) tex1D(t, x)
#define kernel_tex_image_interp(t, x, y) tex2D(t, x, y)
#define kernel_data __data
CCL_NAMESPACE_END
#endif /* __KERNEL_COMPAT_CUDA_H__ */

@ -0,0 +1,50 @@
/*
* 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 __KERNEL_COMPAT_OPENCL_H__
#define __KERNEL_COMPAT_OPENCL_H__
#define __KERNEL_GPU__
#define __KERNEL_OPENCL__
#include "util_types.h"
CCL_NAMESPACE_BEGIN
#define __device
#define __device_inline
__device float kernel_tex_interp_(__global float *data, int width, float x)
{
x = clamp(x, 0.0f, 1.0f)*width;
int index = min((int)x, width-1);
int nindex = min(index+1, width-1);
float t = x - index;
return (1.0f - t)*data[index] + t*data[nindex];
}
#define kernel_data (*kg->data)
#define kernel_tex_interp(t, x) \
kernel_tex_interp_(kg->t, kg->t##_width, x);
CCL_NAMESPACE_END
#endif /* __KERNEL_COMPAT_OPENCL_H__ */

@ -0,0 +1,90 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
/* See "Tracing Ray Differentials", Homan Igehy, 1999. */
__device void differential_transfer(differential3 *dP_, const differential3 dP, float3 D, const differential3 dD, float3 Ng, float t)
{
/* ray differential transfer through homogenous medium, to
* compute dPdx/dy at a shading point from the incoming ray */
float3 tmp = D/dot(D, Ng);
float3 tmpx = dP.dx + t*dD.dx;
float3 tmpy = dP.dy + t*dD.dy;
dP_->dx = tmpx - dot(tmpx, Ng)*tmp;
dP_->dy = tmpy - dot(tmpy, Ng)*tmp;
}
__device void differential_incoming(differential3 *dI, const differential3 dD)
{
/* compute dIdx/dy at a shading point, we just need to negate the
* differential of the ray direction */
dI->dx = -dD.dx;
dI->dy = -dD.dy;
}
__device void differential_dudv(differential *du, differential *dv, float3 dPdu, float3 dPdv, differential3 dP, float3 Ng)
{
/* now we have dPdx/dy from the ray differential transfer, and dPdu/dv
* from the primitive, we can compute dudx/dy and dvdx/dy. these are
* mainly used for differentials of arbitrary mesh attributes. */
/* find most stable axis to project to 2D */
float xn= fabsf(Ng.x);
float yn= fabsf(Ng.y);
float zn= fabsf(Ng.z);
if(zn < xn || zn < yn) {
if(yn < xn || yn < zn) {
dPdu.x = dPdu.y;
dPdv.x = dPdv.y;
dP.dx.x = dP.dx.y;
dP.dy.x = dP.dy.y;
}
dPdu.y = dPdu.z;
dPdv.y = dPdv.z;
dP.dx.y = dP.dx.z;
dP.dy.y = dP.dy.z;
}
/* using Cramer's rule, we solve for dudx and dvdx in a 2x2 linear system,
* and the same for dudy and dvdy. the denominator is the same for both
* solutions, so we compute it only once.
*
* dP.dx = dPdu * dudx + dPdv * dvdx;
* dP.dy = dPdu * dudy + dPdv * dvdy; */
float det = (dPdu.x*dPdv.y - dPdv.x*dPdu.y);
if(det != 0.0f)
det = 1.0f/det;
du->dx = (dP.dx.x*dPdv.y - dP.dx.y*dPdv.x)*det;
dv->dx = (dP.dx.y*dPdu.x - dP.dx.x*dPdu.y)*det;
du->dy = (dP.dy.x*dPdv.y - dP.dy.y*dPdv.x)*det;
dv->dy = (dP.dy.y*dPdu.x - dP.dy.x*dPdu.y)*det;
}
CCL_NAMESPACE_END

@ -0,0 +1,35 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
__device void kernel_displace(KernelGlobals *kg, uint4 *input, float3 *offset, int i)
{
/* setup shader data */
ShaderData sd;
uint4 in = input[i];
shader_setup_from_displace(kg, &sd, in.x, in.y, __int_as_float(in.z), __int_as_float(in.w));
/* evaluate */
float3 P = sd.P;
shader_eval_displacement(kg, &sd);
offset[i] = sd.P - P;
}
CCL_NAMESPACE_END

@ -0,0 +1,118 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
/* Direction Emission */
__device float3 direct_emissive_eval(KernelGlobals *kg, float rando,
LightSample *ls, float u, float v, float3 I)
{
/* setup shading at emitter */
ShaderData sd;
shader_setup_from_sample(kg, &sd, ls->P, ls->Ng, I, ls->shader, ls->object, ls->prim, u, v);
ls->Ng = sd.Ng;
/* no path flag, we're evaluating this for all closures. that's weak but
we'd have to do multiple evaluations otherwise */
shader_eval_surface(kg, &sd, rando, 0);
float3 eval;
/* evaluate emissive closure */
if(sd.flag & SD_EMISSION)
eval = shader_emissive_eval(kg, &sd);
else
eval = make_float3(0.0f, 0.0f, 0.0f);
shader_release(kg, &sd);
return eval;
}
__device bool direct_emission(KernelGlobals *kg, ShaderData *sd, float randt, float rando,
float randu, float randv, Ray *ray, float3 *eval)
{
/* sample a position on a light */
LightSample ls;
light_sample(kg, randt, randu, randv, sd->P, &ls);
/* compute incoming direction and distance */
float t;
float3 omega_in = normalize_len(ls.P - sd->P, &t);
/* compute pdf */
float pdf = light_pdf(kg, &ls, -omega_in, t);
/* evaluate closure */
*eval = direct_emissive_eval(kg, rando, &ls, randu, randv, -omega_in);
if(is_zero(*eval) || pdf == 0.0f)
return false;
/* evaluate BSDF at shading point */
float bsdf_pdf;
float3 bsdf_eval = shader_bsdf_eval(kg, sd, omega_in, &bsdf_pdf);
*eval *= bsdf_eval/pdf;
if(is_zero(*eval))
return false;
if(ls.prim != ~0) {
/* multiple importance sampling */
float mis_weight = power_heuristic(pdf, bsdf_pdf);
*eval *= mis_weight;
}
else {
/* ensure point light works in Watts, this should be handled
* elsewhere but for now together with the diffuse emission
* closure it works out to the right value */
*eval *= 0.25f;
}
/* setup ray */
ray->P = ray_offset(sd->P, sd->Ng);
ray->D = ray_offset(ls.P, ls.Ng) - ray->P;
ray->D = normalize_len(ray->D, &ray->t);
return true;
}
/* Indirect Emission */
__device float3 indirect_emission(KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf)
{
/* evaluate emissive closure */
float3 L = shader_emissive_eval(kg, sd);
if(!(path_flag & PATH_RAY_SINGULAR)) {
/* multiple importance sampling */
float pdf = triangle_light_pdf(kg, sd->Ng, sd->I, t);
float mis_weight = power_heuristic(bsdf_pdf, pdf);
return L*mis_weight;
}
return L;
}
CCL_NAMESPACE_END

@ -0,0 +1,68 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
__device float4 film_map(KernelGlobals *kg, float4 irradiance, int pass)
{
float scale = kernel_data.film.exposure*(1.0f/(pass+1));
float4 result = irradiance*scale;
if(kernel_data.film.use_response_curve) {
/* camera response curve */
result.x = kernel_tex_interp(__response_curve_R, result.x);
result.y = kernel_tex_interp(__response_curve_G, result.y);
result.z = kernel_tex_interp(__response_curve_B, result.z);
}
else {
/* conversion to srgb */
result.x = color_scene_linear_to_srgb(result.x);
result.y = color_scene_linear_to_srgb(result.y);
result.z = color_scene_linear_to_srgb(result.z);
}
return result;
}
__device uchar4 film_float_to_byte(float4 color)
{
uchar4 result;
/* simple float to byte conversion */
result.x = clamp(color.x*255.0f, 0.0f, 255.0f);
result.y = clamp(color.y*255.0f, 0.0f, 255.0f);
result.z = clamp(color.z*255.0f, 0.0f, 255.0f);
result.w = 255;
return result;
}
__device void kernel_film_tonemap(KernelGlobals *kg, __global uchar4 *rgba, __global float4 *buffer, int pass, int resolution, int x, int y)
{
int w = kernel_data.cam.width;
int index = x + y*w;
float4 irradiance = buffer[index];
float4 float_result = film_map(kg, irradiance, pass);
uchar4 byte_result = film_float_to_byte(float_result);
rgba[index] = byte_result;
}
CCL_NAMESPACE_END

@ -0,0 +1,208 @@
/*
* 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.
*/
/* Constant Globals */
#ifdef __KERNEL_CPU__
#ifdef WITH_OSL
#include "osl_globals.h"
#endif
CCL_NAMESPACE_BEGIN
/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in
the kernel, to access constant data. These are all stored as "textures", but
these are really just standard arrays. We can't use actually globals because
multiple renders may be running inside the same process. */
typedef struct KernelGlobals {
#else
/* On the GPU, constant memory textures must be globals, so we can't put them
into a struct. As a result we don't actually use this struct and use actual
globals and simply pass along a NULL pointer everywhere, which we hope gets
optimized out. */
#ifdef __KERNEL_CUDA__
typedef struct KernelGlobals {} KernelGlobals;
#endif
#endif
/* globals */
__constant KernelData __data;
#ifndef __KERNEL_OPENCL__
/* bvh */
texture_float4 __bvh_nodes;
texture_float4 __tri_woop;
texture_uint __prim_index;
texture_uint __prim_object;
texture_uint __object_node;
/* objects */
texture_float4 __objects;
/* triangles */
texture_float4 __tri_normal;
texture_float4 __tri_vnormal;
texture_float4 __tri_vindex;
texture_float4 __tri_verts;
/* attributes */
texture_uint4 __attributes_map;
texture_float __attributes_float;
texture_float4 __attributes_float3;
/* lights */
texture_float4 __light_distribution;
texture_float4 __light_point;
/* shaders */
texture_uint4 __svm_nodes;
/* camera/film */
texture_float __filter_table;
texture_float __response_curve_R;
texture_float __response_curve_G;
texture_float __response_curve_B;
/* sobol */
texture_uint __sobol_directions;
/* image */
texture_image_uchar4 __tex_image_000;
texture_image_uchar4 __tex_image_001;
texture_image_uchar4 __tex_image_002;
texture_image_uchar4 __tex_image_003;
texture_image_uchar4 __tex_image_004;
texture_image_uchar4 __tex_image_005;
texture_image_uchar4 __tex_image_006;
texture_image_uchar4 __tex_image_007;
texture_image_uchar4 __tex_image_008;
texture_image_uchar4 __tex_image_009;
texture_image_uchar4 __tex_image_010;
texture_image_uchar4 __tex_image_011;
texture_image_uchar4 __tex_image_012;
texture_image_uchar4 __tex_image_013;
texture_image_uchar4 __tex_image_014;
texture_image_uchar4 __tex_image_015;
texture_image_uchar4 __tex_image_016;
texture_image_uchar4 __tex_image_017;
texture_image_uchar4 __tex_image_018;
texture_image_uchar4 __tex_image_019;
texture_image_uchar4 __tex_image_020;
texture_image_uchar4 __tex_image_021;
texture_image_uchar4 __tex_image_022;
texture_image_uchar4 __tex_image_023;
texture_image_uchar4 __tex_image_024;
texture_image_uchar4 __tex_image_025;
texture_image_uchar4 __tex_image_026;
texture_image_uchar4 __tex_image_027;
texture_image_uchar4 __tex_image_028;
texture_image_uchar4 __tex_image_029;
texture_image_uchar4 __tex_image_030;
texture_image_uchar4 __tex_image_031;
texture_image_uchar4 __tex_image_032;
texture_image_uchar4 __tex_image_033;
texture_image_uchar4 __tex_image_034;
texture_image_uchar4 __tex_image_035;
texture_image_uchar4 __tex_image_036;
texture_image_uchar4 __tex_image_037;
texture_image_uchar4 __tex_image_038;
texture_image_uchar4 __tex_image_039;
texture_image_uchar4 __tex_image_040;
texture_image_uchar4 __tex_image_041;
texture_image_uchar4 __tex_image_042;
texture_image_uchar4 __tex_image_043;
texture_image_uchar4 __tex_image_044;
texture_image_uchar4 __tex_image_045;
texture_image_uchar4 __tex_image_046;
texture_image_uchar4 __tex_image_047;
texture_image_uchar4 __tex_image_048;
texture_image_uchar4 __tex_image_049;
texture_image_uchar4 __tex_image_050;
texture_image_uchar4 __tex_image_051;
texture_image_uchar4 __tex_image_052;
texture_image_uchar4 __tex_image_053;
texture_image_uchar4 __tex_image_054;
texture_image_uchar4 __tex_image_055;
texture_image_uchar4 __tex_image_056;
texture_image_uchar4 __tex_image_057;
texture_image_uchar4 __tex_image_058;
texture_image_uchar4 __tex_image_059;
texture_image_uchar4 __tex_image_060;
texture_image_uchar4 __tex_image_061;
texture_image_uchar4 __tex_image_062;
texture_image_uchar4 __tex_image_063;
texture_image_uchar4 __tex_image_064;
texture_image_uchar4 __tex_image_065;
texture_image_uchar4 __tex_image_066;
texture_image_uchar4 __tex_image_067;
texture_image_uchar4 __tex_image_068;
texture_image_uchar4 __tex_image_069;
texture_image_uchar4 __tex_image_070;
texture_image_uchar4 __tex_image_071;
texture_image_uchar4 __tex_image_072;
texture_image_uchar4 __tex_image_073;
texture_image_uchar4 __tex_image_074;
texture_image_uchar4 __tex_image_075;
texture_image_uchar4 __tex_image_076;
texture_image_uchar4 __tex_image_077;
texture_image_uchar4 __tex_image_078;
texture_image_uchar4 __tex_image_079;
texture_image_uchar4 __tex_image_080;
texture_image_uchar4 __tex_image_081;
texture_image_uchar4 __tex_image_082;
texture_image_uchar4 __tex_image_083;
texture_image_uchar4 __tex_image_084;
texture_image_uchar4 __tex_image_085;
texture_image_uchar4 __tex_image_086;
texture_image_uchar4 __tex_image_087;
texture_image_uchar4 __tex_image_088;
texture_image_uchar4 __tex_image_089;
texture_image_uchar4 __tex_image_090;
texture_image_uchar4 __tex_image_091;
texture_image_uchar4 __tex_image_092;
texture_image_uchar4 __tex_image_093;
texture_image_uchar4 __tex_image_094;
texture_image_uchar4 __tex_image_095;
texture_image_uchar4 __tex_image_096;
texture_image_uchar4 __tex_image_097;
texture_image_uchar4 __tex_image_098;
texture_image_uchar4 __tex_image_099;
#endif
#ifdef __KERNEL_CPU__
#ifdef WITH_OSL
/* On the CPU, we also have the OSL globals here. Most data structures are shared
with SVM, the difference is in the shaders and object/mesh attributes. */
OSLGlobals osl;
#endif
} KernelGlobals;
#endif
CCL_NAMESPACE_END

@ -0,0 +1,145 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
struct LightSample {
float3 P;
float3 Ng;
int object;
int prim;
int shader;
float weight;
};
/* Point Light */
__device void point_light_sample(KernelGlobals *kg, int point,
float randu, float randv, float3 P, LightSample *ls)
{
float4 f = kernel_tex_fetch(__light_point, point);
ls->P = make_float3(f.x, f.y, f.z);
ls->Ng = normalize(ls->P - P);
ls->shader = __float_as_int(f.w);
ls->object = ~0;
ls->prim = ~0;
}
__device float point_light_pdf(KernelGlobals *kg, float t)
{
return t*t*kernel_data.integrator.pdf_lights;
}
/* Triangle Light */
__device void triangle_light_sample(KernelGlobals *kg, int prim, int object,
float randu, float randv, LightSample *ls)
{
/* triangle, so get position, normal, shader */
ls->P = triangle_sample_MT(kg, prim, randu, randv);
ls->Ng = triangle_normal_MT(kg, prim, &ls->shader);
ls->object = object;
ls->prim = prim;
#ifdef __INSTANCING__
/* instance transform */
if(ls->object >= 0) {
object_position_transform(kg, ls->object, &ls->P);
object_normal_transform(kg, ls->object, &ls->Ng);
}
#endif
}
__device float triangle_light_pdf(KernelGlobals *kg,
const float3 Ng, const float3 I, float t)
{
float cos_pi = fabsf(dot(Ng, I));
if(cos_pi == 0.0f)
return 0.0f;
return (t*t*kernel_data.integrator.pdf_triangles)/cos_pi;
}
/* Light Distribution */
__device int light_distribution_sample(KernelGlobals *kg, float randt)
{
/* this is basically std::upper_bound as used by pbrt, to find a point light or
triangle to emit from, proportional to area. a good improvement would be to
also sample proportional to power, though it's not so well defined with
OSL shaders. */
int first = 0;
int len = kernel_data.integrator.num_distribution + 1;
while(len > 0) {
int half_len = len >> 1;
int middle = first + half_len;
if(randt < kernel_tex_fetch(__light_distribution, middle).x) {
len = half_len;
}
else {
first = middle + 1;
len = len - half_len - 1;
}
}
first = max(0, first-1);
kernel_assert(first >= 0 && first < kernel_data.integrator.num_distribution);
return first;
}
/* Generic Light */
__device void light_sample(KernelGlobals *kg, float randt, float randu, float randv, float3 P, LightSample *ls)
{
/* sample index */
int index = light_distribution_sample(kg, randt);
/* fetch light data */
float4 l = kernel_tex_fetch(__light_distribution, index);
int prim = __float_as_int(l.y);
ls->weight = l.z;
if(prim >= 0) {
int object = __float_as_int(l.w);
triangle_light_sample(kg, prim, object, randu, randv, ls);
}
else {
int point = -prim-1;
point_light_sample(kg, point, randu, randv, P, ls);
}
}
__device float light_pdf(KernelGlobals *kg, LightSample *ls, float3 I, float t)
{
float pdf;
if(ls->prim != ~0)
pdf = triangle_light_pdf(kg, ls->Ng, I, t);
else
pdf = point_light_pdf(kg, t);
return pdf;
}
CCL_NAMESPACE_END

@ -0,0 +1,27 @@
/*
* 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 __KERNEL_MATH_H__
#define __KERNEL_MATH_H__
#include "util_color.h"
#include "util_math.h"
#include "util_transform.h"
#endif /* __KERNEL_MATH_H__ */

@ -0,0 +1,394 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
#define MBVH_OBJECT_SENTINEL 0x76543210
#define MBVH_NODE_SIZE 8
#define MBVH_STACK_SIZE 1024
#define MBVH_RAY_STACK_SIZE 10000
typedef struct MBVHTask {
int node;
int index;
int num;
int object;
} MBVHTask;
typedef struct MVBHRay {
float3 P;
float u;
float3 idir;
float v;
float t;
int index;
int object;
float3 origP;
float3 origD;
float tmax;
} MBVHRay;
__device float3 mbvh_inverse_direction(float3 dir)
{
// Avoid divide by zero (ooeps = exp2f(-80.0f))
float ooeps = 0.00000000000000000000000082718061255302767487140869206996285356581211090087890625f;
float3 idir;
idir.x = 1.0f / (fabsf(dir.x) > ooeps ? dir.x : copysignf(ooeps, dir.x));
idir.y = 1.0f / (fabsf(dir.y) > ooeps ? dir.y : copysignf(ooeps, dir.y));
idir.z = 1.0f / (fabsf(dir.z) > ooeps ? dir.z : copysignf(ooeps, dir.z));
return idir;
}
__device void mbvh_instance_push(KernelGlobals *kg, int object, MBVHRay *ray)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
ray->P = transform(&tfm, ray->origP);
float3 dir = ray->origD;
if(ray->t != ray->tmax) dir *= ray->t;
dir = transform_direction(&tfm, dir);
ray->idir = mbvh_inverse_direction(normalize(dir));
if(ray->t != ray->tmax) ray->t = len(dir);
}
__device void mbvh_instance_pop(KernelGlobals *kg, int object, MBVHRay *ray)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
if(ray->t != ray->tmax)
ray->t = len(transform_direction(&tfm, (1.0f/(ray->idir)) * (ray->t)));
ray->P = ray->origP;
ray->idir = mbvh_inverse_direction(ray->origD);
}
/* Sven Woop's algorithm */
__device void mbvh_triangle_intersect(KernelGlobals *kg, MBVHRay *ray, int object, int triAddr)
{
float3 P = ray->P;
float3 idir = ray->idir;
/* compute and check intersection t-value */
float4 v00 = kernel_tex_fetch(__tri_woop, triAddr*MBVH_NODE_SIZE+0);
float4 v11 = kernel_tex_fetch(__tri_woop, triAddr*MBVH_NODE_SIZE+1);
float3 dir = 1.0f/idir;
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(dir.x*v00.x + dir.y*v00.y + dir.z*v00.z);
float t = Oz * invDz;
if(t > 0.0f && t < ray->t) {
/* compute and check barycentric u */
float Ox = v11.w + P.x*v11.x + P.y*v11.y + P.z*v11.z;
float Dx = dir.x*v11.x + dir.y*v11.y + dir.z*v11.z;
float u = Ox + t*Dx;
if(u >= 0.0f) {
/* compute and check barycentric v */
float4 v22 = kernel_tex_fetch(__tri_woop, triAddr*MBVH_NODE_SIZE+2);
float Oy = v22.w + P.x*v22.x + P.y*v22.y + P.z*v22.z;
float Dy = dir.x*v22.x + dir.y*v22.y + dir.z*v22.z;
float v = Oy + t*Dy;
if(v >= 0.0f && u + v <= 1.0f) {
/* record intersection */
ray->index = triAddr;
ray->object = object;
ray->u = u;
ray->v = v;
ray->t = t;
}
}
}
}
__device void mbvh_node_intersect(KernelGlobals *kg, __m128 *traverseChild,
__m128 *tHit, float3 P, float3 idir, float t, int nodeAddr)
{
/* X axis */
const __m128 bminx = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+0);
const __m128 t0x = _mm_mul_ps(_mm_sub_ps(bminx, _mm_set_ps1(P.x)), _mm_set_ps1(idir.x));
const __m128 bmaxx = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+1);
const __m128 t1x = _mm_mul_ps(_mm_sub_ps(bmaxx, _mm_set_ps1(P.x)), _mm_set_ps1(idir.x));
__m128 tmin = _mm_max_ps(_mm_min_ps(t0x, t1x), _mm_setzero_ps());
__m128 tmax = _mm_min_ps(_mm_max_ps(t0x, t1x), _mm_set_ps1(t));
/* Y axis */
const __m128 bminy = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+2);
const __m128 t0y = _mm_mul_ps(_mm_sub_ps(bminy, _mm_set_ps1(P.y)), _mm_set_ps1(idir.y));
const __m128 bmaxy = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+3);
const __m128 t1y = _mm_mul_ps(_mm_sub_ps(bmaxy, _mm_set_ps1(P.y)), _mm_set_ps1(idir.y));
tmin = _mm_max_ps(_mm_min_ps(t0y, t1y), tmin);
tmax = _mm_min_ps(_mm_max_ps(t0y, t1y), tmax);
/* Z axis */
const __m128 bminz = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+4);
const __m128 t0z = _mm_mul_ps(_mm_sub_ps(bminz, _mm_set_ps1(P.z)), _mm_set_ps1(idir.z));
const __m128 bmaxz = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*MBVH_NODE_SIZE+5);
const __m128 t1z = _mm_mul_ps(_mm_sub_ps(bmaxz, _mm_set_ps1(P.z)), _mm_set_ps1(idir.z));
tmin = _mm_max_ps(_mm_min_ps(t0z, t1z), tmin);
tmax = _mm_min_ps(_mm_max_ps(t0z, t1z), tmax);
/* compare and get mask */
*traverseChild = _mm_cmple_ps(tmin, tmax);
/* get distance XXX probably wrong */
*tHit = tmin;
}
static void mbvh_sort_by_length(int id[4], float len[4])
{
for(int i = 1; i < 4; i++) {
int j = i - 1;
while(j >= 0 && len[j] > len[j+1]) {
swap(len[j], len[j+1]);
swap(id[j], id[j+1]);
j--;
}
}
}
__device void scene_intersect(KernelGlobals *kg, MBVHRay *rays, int numrays)
{
/* traversal stacks */
MBVHTask task_stack[MBVH_STACK_SIZE];
int active_ray_stacks[4][MBVH_RAY_STACK_SIZE];
int num_task, num_active[4] = {0, 0, 0, 0};
__m128i one_mm = _mm_set1_epi32(1);
/* push root node task on stack */
task_stack[0].node = kernel_data.bvh.root;
task_stack[0].index = 0;
task_stack[0].num = numrays;
task_stack[0].object = ~0;
num_task = 1;
/* push all rays in first SIMD lane */
for(int i = 0; i < numrays; i++)
active_ray_stacks[0][i] = i;
num_active[0] = numrays;
while(num_task >= 1) {
/* pop task */
MBVHTask task = task_stack[--num_task];
if(task.node == MBVH_OBJECT_SENTINEL) {
/* instance pop */
/* pop rays from stack */
num_active[task.index] -= task.num;
int ray_offset = num_active[task.index];
/* transform rays */
for(int i = 0; i < task.num; i++) {
MBVHRay *ray = &rays[active_ray_stacks[task.index][ray_offset + i]];
mbvh_instance_pop(kg, task.object, ray);
}
}
else if(task.node >= 0) {
/* inner node? */
/* pop rays from stack*/
num_active[task.index] -= task.num;
int ray_offset = num_active[task.index];
/* initialze simd values */
__m128i num_active_mm = _mm_load_si128((__m128i*)num_active);
__m128 len_mm = _mm_set_ps1(0.0f);
for(int i = 0; i < task.num; i++) {
int rayid = active_ray_stacks[task.index][ray_offset + i];
MVBHRay *ray = rays + rayid;
/* intersect 4 QBVH node children */
__m128 result;
__m128 thit;
mbvh_node_intersect(kg, &result, &thit, ray->P, ray->idir, ray->t, task.node);
/* update length for sorting */
len_mm = _mm_add_ps(len_mm, _mm_and_ps(thit, result));
/* push rays on stack */
for(int j = 0; j < 4; j++)
active_ray_stacks[j][num_active[j]] = rayid;
/* update num active */
__m128i resulti = _mm_and_si128(*((__m128i*)&result), one_mm);
num_active_mm = _mm_add_epi32(resulti, num_active_mm);
_mm_store_si128((__m128i*)num_active, num_active_mm);
}
if(num_active[0] || num_active[1] || num_active[2] || num_active[3]) {
/* load child node addresses */
float4 cnodes = kernel_tex_fetch(__bvh_nodes, task.node);
int child[4] = {
__float_as_int(cnodes.x),
__float_as_int(cnodes.y),
__float_as_int(cnodes.z),
__float_as_int(cnodes.w)};
/* sort nodes by average intersection distance */
int ids[4] = {0, 1, 2, 3};
float len[4];
_mm_store_ps(len, len_mm);
mbvh_sort_by_length(ids, len);
/* push new tasks on stack */
for(int j = 0; j < 4; j++) {
if(num_active[j]) {
int id = ids[j];
task_stack[num_task].node = child[id];
task_stack[num_task].index = id;
task_stack[num_task].num = num_active[id];
task_stack[num_task].object = task.object;
num_task++;
}
}
}
}
else {
/* fetch leaf node data */
float4 leaf = kernel_tex_fetch(__bvh_nodes, (-task.node-1)*MBVH_NODE_SIZE+(MBVH_NODE_SIZE-2));
int triAddr = __float_as_int(leaf.x);
int triAddr2 = __float_as_int(leaf.y);
/* pop rays from stack*/
num_active[task.index] -= task.num;
int ray_offset = num_active[task.index];
/* triangles */
if(triAddr >= 0) {
int i, numq = (task.num >> 2) << 2;
/* SIMD ray leaf intersection */
for(i = 0; i < numq; i += 4) {
MBVHRay *ray4[4] = {
&rays[active_ray_stacks[task.index][ray_offset + i + 0]],
&rays[active_ray_stacks[task.index][ray_offset + i + 1]],
&rays[active_ray_stacks[task.index][ray_offset + i + 2]],
&rays[active_ray_stacks[task.index][ray_offset + i + 3]]};
/* load SoA */
while(triAddr < triAddr2) {
mbvh_triangle_intersect(ray4[0], task.object, task.node);
mbvh_triangle_intersect(ray4[1], task.object, task.node);
mbvh_triangle_intersect(ray4[2], task.object, task.node);
mbvh_triangle_intersect(ray4[3], task.object, task.node);
triAddr++;
/* some shadow ray optim could be done by setting t=0 */
}
/* store AoS */
}
/* mono ray leaf intersection */
for(; i < task.num; i++) {
MBVHRay *ray = &rays[active_ray_stacks[task.index][ray_offset + i]];
while(triAddr < triAddr2) {
mbvh_triangle_intersect(kg, ray, task.object, task.node);
triAddr++;
}
}
}
else {
/* instance push */
int object = -triAddr-1;
int node = triAddr;
/* push instance pop task */
task_stack[num_task].node = MBVH_OBJECT_SENTINEL;
task_stack[num_task].index = task.index;
task_stack[num_task].num = task.num;
task_stack[num_task].object = object;
num_task++;
num_active[task.index] += task.num;
/* push node task */
task_stack[num_task].node = node;
task_stack[num_task].index = task.index;
task_stack[num_task].num = task.num;
task_stack[num_task].object = object;
num_task++;
for(int i = 0; i < task.num; i++) {
int rayid = active_ray_stacks[task.index][ray_offset + i];
/* push on stack for last task */
active_ray_stacks[task.index][num_active[task.index]] = rayid;
num_active[task.index]++;
/* transform ray */
MBVHRay *ray = &rays[rayid];
mbvh_instance_push(kg, object, ray);
}
}
}
}
}
__device void mbvh_set_ray(MBVHRay *rays, int i, Ray *ray, float tmax)
{
MBVHRay *mray = &rays[i];
/* ray parameters in registers */
mray->P = ray->P;
mray->idir = mbvh_inverse_direction(ray->D);
mray->t = tmax;
}
__device bool mbvh_get_intersection(MVBHRay *rays, int i, Intersection *isect, float tmax)
{
MBVHRay *mray = &rays[i];
if(mray->t == tmax)
return false;
isect->t = mray->t;
isect->u = mray->u;
isect->v = mray->v;
isect->index = mray->index;
isect->object = mray->object;
return true;
}
__device bool mbvh_get_shadow(MBVHRay *rays, int i, float tmax)
{
return (rays[i].t == tmax);
}
CCL_NAMESPACE_END

@ -0,0 +1,196 @@
/*
* Parts adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __KERNEL_MONTECARLO_CL__
#define __KERNEL_MONTECARLO_CL__
CCL_NAMESPACE_BEGIN
/// Given values x and y on [0,1], convert them in place to values on
/// [-1,1] uniformly distributed over a unit sphere. This code is
/// derived from Peter Shirley, "Realistic Ray Tracing", p. 103.
__device void to_unit_disk(float *x, float *y)
{
float r, phi;
float a = 2.0f * (*x) - 1.0f;
float b = 2.0f * (*y) - 1.0f;
if(a > -b) {
if(a > b) {
r = a;
phi = M_PI_4_F *(b/a);
} else {
r = b;
phi = M_PI_4_F *(2.0f - a/b);
}
} else {
if(a < b) {
r = -a;
phi = M_PI_4_F *(4.0f + b/a);
} else {
r = -b;
if(b != 0.0f)
phi = M_PI_4_F *(6.0f - a/b);
else
phi = 0.0f;
}
}
*x = r * cosf(phi);
*y = r * sinf(phi);
}
__device_inline void make_orthonormals(const float3 N, float3 *a, float3 *b)
{
if(N.x != N.y || N.x != N.z)
*a = make_float3(N.z-N.y, N.x-N.z, N.y-N.x); //(1,1,1)x N
else
*a = make_float3(N.z-N.y, N.x+N.z, -N.y-N.x); //(-1,1,1)x N
*a = normalize(*a);
*b = cross(N, *a);
}
__device void make_orthonormals_tangent(const float3 N, const float3 T, float3 *a, float3 *b)
{
*b = cross(N, T);
*a = cross(*b, N);
}
__device_inline void sample_cos_hemisphere(const float3 N,
float randu, float randv, float3 *omega_in, float *pdf)
{
// Default closure BSDF implementation: uniformly sample
// cosine-weighted hemisphere above the point.
to_unit_disk(&randu, &randv);
float costheta = sqrtf(max(1.0f - randu * randu - randv * randv, 0.0f));
float3 T, B;
make_orthonormals(N, &T, &B);
*omega_in = randu * T + randv * B + costheta * N;
*pdf = costheta *M_1_PI_F;
}
__device_inline void sample_uniform_hemisphere(const float3 N,
float randu, float randv,
float3 *omega_in, float *pdf)
{
float z = randu;
float r = sqrtf(max(0.f, 1.f - z*z));
float phi = 2.f * M_PI_F * randv;
float x = r * cosf(phi);
float y = r * sinf(phi);
float3 T, B;
make_orthonormals (N, &T, &B);
*omega_in = x * T + y * B + z * N;
*pdf = 0.5f * M_1_PI_F;
}
__device float3 sample_uniform_sphere(float u1, float u2)
{
float z = 1.0f - 2.0f*u1;
float r = sqrtf(fmaxf(0.0f, 1.0f - z*z));
float phi = 2.0f*M_PI_F*u2;
float x = r*cosf(phi);
float y = r*sinf(phi);
return make_float3(x, y, z);
}
__device float power_heuristic(float a, float b)
{
return (a*a)/(a*a + b*b);
}
__device float2 concentric_sample_disk(float u1, float u2)
{
float r, theta;
// Map uniform random numbers to $[-1,1]^2$
float sx = 2 * u1 - 1;
float sy = 2 * u2 - 1;
// Map square to $(r,\theta)$
// Handle degeneracy at the origin
if(sx == 0.0f && sy == 0.0f) {
return make_float2(0.0f, 0.0f);
}
if(sx >= -sy) {
if(sx > sy) {
// Handle first region of disk
r = sx;
if(sy > 0.0f) theta = sy/r;
else theta = 8.0f + sy/r;
}
else {
// Handle second region of disk
r = sy;
theta = 2.0f - sx/r;
}
}
else {
if(sx <= sy) {
// Handle third region of disk
r = -sx;
theta = 4.0f - sy/r;
}
else {
// Handle fourth region of disk
r = -sy;
theta = 6.0f + sx/r;
}
}
theta *= M_PI_4_F;
return make_float2(r * cosf(theta), r * sinf(theta));
}
/* Spherical coordinates <-> Cartesion direction */
__device float2 direction_to_spherical(float3 dir)
{
float theta = acosf(dir.z);
float phi = atan2f(dir.x, dir.y);
return make_float2(theta, phi);
}
__device float3 spherical_to_direction(float theta, float phi)
{
return make_float3(
sinf(theta)*cosf(phi),
sinf(theta)*sinf(phi),
cosf(theta));
}
CCL_NAMESPACE_END
#endif /* __KERNEL_MONTECARLO_CL__ */

@ -0,0 +1,68 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
enum ObjectTransform {
OBJECT_TRANSFORM = 0,
OBJECT_INVERSE_TRANSFORM = 4,
OBJECT_NORMAL_TRANSFORM = 8,
OBJECT_PROPERTIES = 12
};
__device_inline Transform object_fetch_transform(KernelGlobals *kg, int object, enum ObjectTransform type)
{
Transform tfm;
int offset = object*OBJECT_SIZE + (int)type;
tfm.x = kernel_tex_fetch(__objects, offset + 0);
tfm.y = kernel_tex_fetch(__objects, offset + 1);
tfm.z = kernel_tex_fetch(__objects, offset + 2);
tfm.w = kernel_tex_fetch(__objects, offset + 3);
return tfm;
}
__device_inline void object_position_transform(KernelGlobals *kg, int object, float3 *P)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
*P = transform(&tfm, *P);
}
__device_inline void object_normal_transform(KernelGlobals *kg, int object, float3 *N)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_NORMAL_TRANSFORM);
*N = normalize(transform_direction(&tfm, *N));
}
__device_inline void object_dir_transform(KernelGlobals *kg, int object, float3 *D)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
*D = transform_direction(&tfm, *D);
}
__device_inline float object_surface_area(KernelGlobals *kg, int object)
{
int offset = object*OBJECT_SIZE + OBJECT_PROPERTIES;
float4 f = kernel_tex_fetch(__objects, offset);
return f.x;
}
CCL_NAMESPACE_END

@ -0,0 +1,263 @@
/*
* 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 "kernel_differential.h"
#include "kernel_montecarlo.h"
#include "kernel_triangle.h"
#include "kernel_object.h"
#ifdef __QBVH__
#include "kernel_qbvh.h"
#else
#include "kernel_bvh.h"
#endif
#include "kernel_camera.h"
#include "kernel_shader.h"
#include "kernel_light.h"
#include "kernel_emission.h"
#include "kernel_random.h"
CCL_NAMESPACE_BEGIN
#ifdef __MODIFY_TP__
__device float3 path_terminate_modified_throughput(KernelGlobals *kg, __global float3 *buffer, int x, int y, int pass)
{
/* modify throughput to influence path termination probability, to avoid
darker regions receiving fewer samples than lighter regions. also RGB
are weighted differently. proper validation still remains to be done. */
const float3 weights = make_float3(1.0f, 1.33f, 0.66f);
const float3 one = make_float3(1.0f, 1.0f, 1.0f);
const int minpass = 5;
const float minL = 0.1f;
if(pass >= minpass) {
float3 L = buffer[x + y*kernel_data.cam.width];
float3 Lmin = make_float3(minL, minL, minL);
float correct = (float)(pass+1)/(float)pass;
L = film_map(L*correct, pass);
return weights/clamp(L, Lmin, one);
}
return weights;
}
#endif
__device float path_terminate_probability(KernelGlobals *kg, int bounce, const float3 throughput)
{
if(bounce >= kernel_data.integrator.maxbounce)
return 0.0f;
else if(bounce <= kernel_data.integrator.minbounce)
return 1.0f;
return average(throughput);
}
__device int path_flag_from_label(int path_flag, int label)
{
/* reflect/transmit */
if(label & LABEL_REFLECT) {
path_flag |= PATH_RAY_REFLECT;
path_flag &= ~PATH_RAY_TRANSMIT;
}
else {
kernel_assert(label & LABEL_TRANSMIT);
path_flag |= PATH_RAY_TRANSMIT;
path_flag &= ~PATH_RAY_REFLECT;
}
/* diffuse/glossy/singular */
if(label & LABEL_DIFFUSE) {
path_flag |= PATH_RAY_DIFFUSE;
path_flag &= ~(PATH_RAY_GLOSSY|PATH_RAY_SINGULAR);
}
else if(label & LABEL_GLOSSY) {
path_flag |= PATH_RAY_GLOSSY;
path_flag &= ~(PATH_RAY_DIFFUSE|PATH_RAY_SINGULAR);
}
else {
kernel_assert(label & (LABEL_SINGULAR|LABEL_STRAIGHT));
path_flag |= PATH_RAY_SINGULAR;
path_flag &= ~(PATH_RAY_DIFFUSE|PATH_RAY_GLOSSY);
}
/* ray through transparent is still camera ray */
if(!(label & LABEL_STRAIGHT))
path_flag &= ~PATH_RAY_CAMERA;
return path_flag;
}
__device float3 kernel_path_integrate(KernelGlobals *kg, RNG *rng, int pass, Ray ray, float3 throughput)
{
/* initialize */
float3 L = make_float3(0.0f, 0.0f, 0.0f);
#ifdef __EMISSION__
float ray_pdf = 0.0f;
#endif
int path_flag = PATH_RAY_CAMERA|PATH_RAY_SINGULAR;
int rng_offset = PRNG_BASE_NUM;
/* path iteration */
for(int bounce = 0; ; bounce++, rng_offset += PRNG_BOUNCE_NUM) {
/* intersect scene */
Intersection isect;
if(!scene_intersect(kg, &ray, false, &isect)) {
/* eval background shader if nothing hit */
#ifdef __BACKGROUND__
ShaderData sd;
shader_setup_from_background(kg, &sd, &ray);
L += throughput*shader_eval_background(kg, &sd, path_flag);
shader_release(kg, &sd);
#else
L += throughputmake_float3(0.8f, 0.8f, 0.8f);
#endif
break;
}
/* setup shading */
ShaderData sd;
shader_setup_from_ray(kg, &sd, &isect, &ray);
float rbsdf = path_rng(kg, rng, pass, rng_offset + PRNG_BSDF);
shader_eval_surface(kg, &sd, rbsdf, path_flag);
#ifdef __EMISSION__
/* emission */
if(kernel_data.integrator.use_emission) {
if(sd.flag & SD_EMISSION)
L += throughput*indirect_emission(kg, &sd, isect.t, path_flag, ray_pdf);
/* sample illumination from lights to find path contribution */
if((sd.flag & SD_BSDF_HAS_EVAL) &&
bounce != kernel_data.integrator.maxbounce) {
float light_t = path_rng(kg, rng, pass, rng_offset + PRNG_LIGHT);
float light_o = path_rng(kg, rng, pass, rng_offset + PRNG_LIGHT_F);
float light_u = path_rng(kg, rng, pass, rng_offset + PRNG_LIGHT_U);
float light_v = path_rng(kg, rng, pass, rng_offset + PRNG_LIGHT_V);
Ray light_ray;
float3 light_L;
if(direct_emission(kg, &sd, light_t, light_o, light_u, light_v, &light_ray, &light_L)) {
/* trace shadow ray */
if(!scene_intersect(kg, &light_ray, true, &isect))
L += throughput*light_L;
}
}
}
#endif
/* sample BSDF */
float bsdf_pdf;
float3 bsdf_eval;
float3 bsdf_omega_in;
differential3 bsdf_domega_in;
float bsdf_u = path_rng(kg, rng, pass, rng_offset + PRNG_BSDF_U);
float bsdf_v = path_rng(kg, rng, pass, rng_offset + PRNG_BSDF_V);
int label;
label = shader_bsdf_sample(kg, &sd, bsdf_u, bsdf_v, &bsdf_eval,
&bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf);
shader_release(kg, &sd);
if(bsdf_pdf == 0.0f || is_zero(bsdf_eval))
break;
/* modify throughput */
throughput *= bsdf_eval/bsdf_pdf;
/* set labels */
#ifdef __EMISSION__
ray_pdf = bsdf_pdf;
#endif
path_flag = path_flag_from_label(path_flag, label);
/* path termination */
float probability = path_terminate_probability(kg, bounce, throughput);
float terminate = path_rng(kg, rng, pass, rng_offset + PRNG_TERMINATE);
if(terminate >= probability)
break;
throughput /= probability;
/* setup ray */
ray.P = ray_offset(sd.P, (label & LABEL_TRANSMIT)? -sd.Ng: sd.Ng);
ray.D = bsdf_omega_in;
ray.t = FLT_MAX;
#ifdef __RAY_DIFFERENTIALS__
ray.dP = sd.dP;
ray.dD = bsdf_domega_in;
#endif
}
return L;
}
__device void kernel_path_trace(KernelGlobals *kg, __global float4 *buffer, __global uint *rng_state, int pass, int x, int y)
{
/* initialize random numbers */
RNG rng;
float filter_u;
float filter_v;
path_rng_init(kg, rng_state, pass, &rng, x, y, &filter_u, &filter_v);
/* sample camera ray */
Ray ray;
float lens_u = path_rng(kg, &rng, pass, PRNG_LENS_U);
float lens_v = path_rng(kg, &rng, pass, PRNG_LENS_V);
camera_sample(kg, x, y, filter_u, filter_v, lens_u, lens_v, &ray);
/* integrate */
#ifdef __MODIFY_TP__
float3 throughput = path_terminate_modified_throughput(kg, buffer, x, y, pass);
float3 L = kernel_path_integrate(kg, &rng, pass, ray, throughput)/throughput;
#else
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
float3 L = kernel_path_integrate(kg, &rng, pass, ray, throughput);
#endif
/* accumulate result in output buffer */
int index = x + y*kernel_data.cam.width;
float4 result;
result.x = L.x;
result.y = L.y;
result.z = L.z;
result.w = 1.0f;
if(pass == 0)
buffer[index] = result;
else
buffer[index] += result;
path_rng_end(kg, rng_state, rng, x, y);
}
CCL_NAMESPACE_END

@ -0,0 +1,413 @@
/*
* Adapted from code Copyright 2009-2010 NVIDIA Corporation
* Modifications Copyright 2011, 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.
*/
CCL_NAMESPACE_BEGIN
/*
* "Persistent while-while kernel" used in:
*
* "Understanding the Efficiency of Ray Traversal on GPUs",
* Timo Aila and Samuli Laine,
* Proc. High-Performance Graphics 2009
*/
/* bottom-most stack entry, indicating the end of traversal */
#define ENTRYPOINT_SENTINEL 0x76543210
/* 64 object BVH + 64 mesh BVH + 64 object node splitting */
#define QBVH_STACK_SIZE 192
#define QBVH_NODE_SIZE 8
#define TRI_NODE_SIZE 3
__device_inline float3 qbvh_inverse_direction(float3 dir)
{
// Avoid divide by zero (ooeps = exp2f(-80.0f))
float ooeps = 0.00000000000000000000000082718061255302767487140869206996285356581211090087890625f;
float3 idir;
idir.x = 1.0f/((fabsf(dir.x) > ooeps)? dir.x: copysignf(ooeps, dir.x));
idir.y = 1.0f/((fabsf(dir.y) > ooeps)? dir.y: copysignf(ooeps, dir.y));
idir.z = 1.0f/((fabsf(dir.z) > ooeps)? dir.z: copysignf(ooeps, dir.z));
return idir;
}
__device_inline void qbvh_instance_push(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *idir, float *t, const float tmax)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
*P = transform(&tfm, ray->P);
float3 dir = transform_direction(&tfm, ray->D);
float len;
dir = normalize_len(dir, &len);
*idir = qbvh_inverse_direction(dir);
if(*t != FLT_MAX)
*t *= len;
}
__device_inline void qbvh_instance_pop(KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *idir, float *t, const float tmax)
{
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
if(*t != FLT_MAX)
*t *= len(transform_direction(&tfm, 1.0f/(*idir)));
*P = ray->P;
*idir = qbvh_inverse_direction(ray->D);
}
#ifdef __KERNEL_CPU__
__device_inline void qbvh_node_intersect(KernelGlobals *kg, int *traverseChild,
int nodeAddrChild[4], float3 P, float3 idir, float t, int nodeAddr)
{
/* X axis */
const __m128 bminx = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+0);
const __m128 t0x = _mm_mul_ps(_mm_sub_ps(bminx, _mm_set_ps1(P.x)), _mm_set_ps1(idir.x));
const __m128 bmaxx = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+1);
const __m128 t1x = _mm_mul_ps(_mm_sub_ps(bmaxx, _mm_set_ps1(P.x)), _mm_set_ps1(idir.x));
__m128 tmin = _mm_max_ps(_mm_min_ps(t0x, t1x), _mm_setzero_ps());
__m128 tmax = _mm_min_ps(_mm_max_ps(t0x, t1x), _mm_set_ps1(t));
/* Y axis */
const __m128 bminy = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+2);
const __m128 t0y = _mm_mul_ps(_mm_sub_ps(bminy, _mm_set_ps1(P.y)), _mm_set_ps1(idir.y));
const __m128 bmaxy = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+3);
const __m128 t1y = _mm_mul_ps(_mm_sub_ps(bmaxy, _mm_set_ps1(P.y)), _mm_set_ps1(idir.y));
tmin = _mm_max_ps(_mm_min_ps(t0y, t1y), tmin);
tmax = _mm_min_ps(_mm_max_ps(t0y, t1y), tmax);
/* Z axis */
const __m128 bminz = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+4);
const __m128 t0z = _mm_mul_ps(_mm_sub_ps(bminz, _mm_set_ps1(P.z)), _mm_set_ps1(idir.z));
const __m128 bmaxz = kernel_tex_fetch_m128(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+5);
const __m128 t1z = _mm_mul_ps(_mm_sub_ps(bmaxz, _mm_set_ps1(P.z)), _mm_set_ps1(idir.z));
tmin = _mm_max_ps(_mm_min_ps(t0z, t1z), tmin);
tmax = _mm_min_ps(_mm_max_ps(t0z, t1z), tmax);
/* compare and get mask */
*traverseChild = _mm_movemask_ps(_mm_cmple_ps(tmin, tmax));
/* get node addresses */
float4 cnodes = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+6);
nodeAddrChild[0] = __float_as_int(cnodes.x);
nodeAddrChild[1] = __float_as_int(cnodes.y);
nodeAddrChild[2] = __float_as_int(cnodes.z);
nodeAddrChild[3] = __float_as_int(cnodes.w);
}
#else
__device_inline bool qbvh_bb_intersect(float3 bmin, float3 bmax, float3 P, float3 idir, float t)
{
float t0x = (bmin.x - P.x)*idir.x;
float t1x = (bmax.x - P.x)*idir.x;
float t0y = (bmin.y - P.y)*idir.y;
float t1y = (bmax.y - P.y)*idir.y;
float t0z = (bmin.z - P.z)*idir.z;
float t1z = (bmax.z - P.z)*idir.z;
float minx = min(t0x, t1x);
float maxx = max(t0x, t1x);
float miny = min(t0y, t1y);
float maxy = max(t0y, t1y);
float minz = min(t0z, t1z);
float maxz = max(t0z, t1z);
float tmin = max4(0.0f, minx, miny, minz);
float tmax = min4(t, maxx, maxy, maxz);
return (tmin <= tmax);
}
/* intersect four bounding boxes */
__device_inline void qbvh_node_intersect(KernelGlobals *kg, int *traverseChild,
int nodeAddrChild[4], float3 P, float3 idir, float t, int nodeAddr)
{
/* fetch node data */
float4 minx = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+0);
float4 miny = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+2);
float4 minz = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+4);
float4 maxx = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+1);
float4 maxy = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+3);
float4 maxz = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+5);
/* intersect bounding boxes */
bool traverseChild0 = qbvh_bb_intersect(make_float3(minx.x, miny.x, minz.x), make_float3(maxx.x, maxy.x, maxz.x), P, idir, t);
bool traverseChild1 = qbvh_bb_intersect(make_float3(minx.y, miny.y, minz.y), make_float3(maxx.y, maxy.y, maxz.y), P, idir, t);
bool traverseChild2 = qbvh_bb_intersect(make_float3(minx.z, miny.z, minz.z), make_float3(maxx.z, maxy.z, maxz.z), P, idir, t);
bool traverseChild3 = qbvh_bb_intersect(make_float3(minx.w, miny.w, minz.w), make_float3(maxx.w, maxy.w, maxz.w), P, idir, t);
*traverseChild = 0;
if(traverseChild0) *traverseChild |= 1;
if(traverseChild1) *traverseChild |= 2;
if(traverseChild2) *traverseChild |= 4;
if(traverseChild3) *traverseChild |= 8;
/* get node addresses */
float4 cnodes = kernel_tex_fetch(__bvh_nodes, nodeAddr*QBVH_NODE_SIZE+6);
nodeAddrChild[0] = __float_as_int(cnodes.x);
nodeAddrChild[1] = __float_as_int(cnodes.y);
nodeAddrChild[2] = __float_as_int(cnodes.z);
nodeAddrChild[3] = __float_as_int(cnodes.w);
}
#endif
/* Sven Woop's algorithm */
__device_inline void qbvh_triangle_intersect(KernelGlobals *kg, Intersection *isect, float3 P, float3 idir, int object, int triAddr)
{
/* compute and check intersection t-value */
float4 v00 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0);
float4 v11 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1);
float3 dir = 1.0f/idir;
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(dir.x*v00.x + dir.y*v00.y + dir.z*v00.z);
float t = Oz * invDz;
if(t > 0.0f && t < isect->t) {
/* compute and check barycentric u */
float Ox = v11.w + P.x*v11.x + P.y*v11.y + P.z*v11.z;
float Dx = dir.x*v11.x + dir.y*v11.y + dir.z*v11.z;
float u = Ox + t*Dx;
if(u >= 0.0f) {
/* compute and check barycentric v */
float4 v22 = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2);
float Oy = v22.w + P.x*v22.x + P.y*v22.y + P.z*v22.z;
float Dy = dir.x*v22.x + dir.y*v22.y + dir.z*v22.z;
float v = Oy + t*Dy;
if(v >= 0.0f && u + v <= 1.0f) {
/* record intersection */
isect->prim = triAddr;
isect->object = object;
isect->u = u;
isect->v = v;
isect->t = t;
}
}
}
}
__device_inline bool scene_intersect(KernelGlobals *kg, const Ray *ray, const bool isshadowray, Intersection *isect)
{
/* traversal stack in CUDA thread-local memory */
int traversalStack[QBVH_STACK_SIZE];
traversalStack[0] = ENTRYPOINT_SENTINEL;
/* traversal variables in registers */
int stackPtr = 0;
int nodeAddr = kernel_data.bvh.root;
/* ray parameters in registers */
const float tmax = ray->t;
float3 P = ray->P;
float3 idir = qbvh_inverse_direction(ray->D);
int object = ~0;
isect->t = tmax;
isect->object = ~0;
isect->prim = ~0;
isect->u = 0.0f;
isect->v = 0.0f;
/* traversal loop */
do {
do
{
/* traverse internal nodes */
while(nodeAddr >= 0 && nodeAddr != ENTRYPOINT_SENTINEL)
{
int traverseChild, nodeAddrChild[4];
qbvh_node_intersect(kg, &traverseChild, nodeAddrChild,
P, idir, isect->t, nodeAddr);
if(traverseChild & 1) {
++stackPtr;
traversalStack[stackPtr] = nodeAddrChild[0];
}
if(traverseChild & 2) {
++stackPtr;
traversalStack[stackPtr] = nodeAddrChild[1];
}
if(traverseChild & 4) {
++stackPtr;
traversalStack[stackPtr] = nodeAddrChild[2];
}
if(traverseChild & 8) {
++stackPtr;
traversalStack[stackPtr] = nodeAddrChild[3];
}
nodeAddr = traversalStack[stackPtr];
--stackPtr;
}
/* if node is leaf, fetch triangle list */
if(nodeAddr < 0) {
float4 leaf = kernel_tex_fetch(__bvh_nodes, (-nodeAddr-1)*QBVH_NODE_SIZE+(QBVH_NODE_SIZE-2));
int primAddr = __float_as_int(leaf.x);
#ifdef __INSTANCING__
if(primAddr >= 0) {
#endif
int primAddr2 = __float_as_int(leaf.y);
/* pop */
nodeAddr = traversalStack[stackPtr];
--stackPtr;
/* triangle intersection */
while(primAddr < primAddr2) {
/* intersect ray against triangle */
qbvh_triangle_intersect(kg, isect, P, idir, object, primAddr);
/* shadow ray early termination */
if(isshadowray && isect->prim != ~0)
return true;
primAddr++;
}
#ifdef __INSTANCING__
}
else {
/* instance push */
object = kernel_tex_fetch(__prim_object, -primAddr-1);
qbvh_instance_push(kg, object, ray, &P, &idir, &isect->t, tmax);
++stackPtr;
traversalStack[stackPtr] = ENTRYPOINT_SENTINEL;
nodeAddr = kernel_tex_fetch(__object_node, object);
}
#endif
}
} while(nodeAddr != ENTRYPOINT_SENTINEL);
#ifdef __INSTANCING__
if(stackPtr >= 0) {
kernel_assert(object != ~0);
/* instance pop */
qbvh_instance_pop(kg, object, ray, &P, &idir, &isect->t, tmax);
object = ~0;
nodeAddr = traversalStack[stackPtr];
--stackPtr;
}
#endif
} while(nodeAddr != ENTRYPOINT_SENTINEL);
return (isect->prim != ~0);
}
__device_inline float3 ray_offset(float3 P, float3 Ng)
{
#ifdef __INTERSECTION_REFINE__
const float epsilon_f = 1e-5f;
const int epsilon_i = 32;
float3 res;
/* x component */
if(fabsf(P.x) < epsilon_f) {
res.x = P.x + Ng.x*epsilon_f;
}
else {
uint ix = __float_as_uint(P.x);
ix += ((ix ^ __float_as_uint(Ng.x)) >> 31)? -epsilon_i: epsilon_i;
res.x = __uint_as_float(ix);
}
/* y component */
if(fabsf(P.y) < epsilon_f) {
res.y = P.y + Ng.y*epsilon_f;
}
else {
uint iy = __float_as_uint(P.y);
iy += ((iy ^ __float_as_uint(Ng.y)) >> 31)? -epsilon_i: epsilon_i;
res.y = __uint_as_float(iy);
}
/* z component */
if(fabsf(P.z) < epsilon_f) {
res.z = P.z + Ng.z*epsilon_f;
}
else {
uint iz = __float_as_uint(P.z);
iz += ((iz ^ __float_as_uint(Ng.z)) >> 31)? -epsilon_i: epsilon_i;
res.z = __uint_as_float(iz);
}
return res;
#else
const float epsilon_f = 1e-4f;
return P + epsilon_f*Ng;
#endif
}
__device_inline float3 bvh_triangle_refine(KernelGlobals *kg, const Intersection *isect, const Ray *ray)
{
float3 P = ray->P;
float3 D = ray->D;
float t = isect->t;
#ifdef __INTERSECTION_REFINE__
if(isect->object != ~0) {
Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM);
P = transform(&tfm, P);
D = transform_direction(&tfm, D*t);
D = normalize_len(D, &t);
}
P = P + D*t;
float4 v00 = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0);
float Oz = v00.w - P.x*v00.x - P.y*v00.y - P.z*v00.z;
float invDz = 1.0f/(D.x*v00.x + D.y*v00.y + D.z*v00.z);
float rt = Oz * invDz;
P = P + D*rt;
if(isect->object != ~0) {
Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM);
P = transform(&tfm, P);
}
return P;
#else
return P + D*t;
#endif
}
CCL_NAMESPACE_END

@ -0,0 +1,175 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
typedef uint RNG;
#ifdef __SOBOL__
/* High Dimensional Sobol */
/* van der corput radical inverse */
__device uint van_der_corput(uint bits)
{
bits = (bits << 16) | (bits >> 16);
bits = ((bits & 0x00ff00ff) << 8) | ((bits & 0xff00ff00) >> 8);
bits = ((bits & 0x0f0f0f0f) << 4) | ((bits & 0xf0f0f0f0) >> 4);
bits = ((bits & 0x33333333) << 2) | ((bits & 0xcccccccc) >> 2);
bits = ((bits & 0x55555555) << 1) | ((bits & 0xaaaaaaaa) >> 1);
return bits;
}
/* sobol radical inverse */
__device uint sobol(uint i)
{
uint r = 0;
for(uint v = 1U << 31; i; i >>= 1, v ^= v >> 1)
if(i & 1)
r ^= v;
return r;
}
/* inverse of sobol radical inverse */
__device uint sobol_inverse(uint i)
{
const uint msb = 1U << 31;
uint r = 0;
for(uint v = 1; i; i <<= 1, v ^= v << 1)
if(i & msb)
r ^= v;
return r;
}
/* multidimensional sobol with generator matrices
dimension 0 and 1 are equal to van_der_corput() and sobol() respectively */
__device uint sobol_dimension(KernelGlobals *kg, int index, int dimension)
{
uint result = 0;
uint i = index;
for(uint j = 0; i; i >>= 1, j++)
if(i & 1)
result ^= kernel_tex_fetch(__sobol_directions, 32*dimension + j);
return result;
}
/* lookup index and x/y coordinate, assumes m is a power of two */
__device uint sobol_lookup(const uint m, const uint frame, const uint ex, const uint ey, uint *x, uint *y)
{
/* shift is constant per frame */
const uint shift = frame << (m << 1);
const uint sobol_shift = sobol(shift);
/* van der Corput is its own inverse */
const uint lower = van_der_corput(ex << (32 - m));
/* need to compensate for ey difference and shift */
const uint sobol_lower = sobol(lower);
const uint mask = ~-(1 << m) << (32 - m); /* only m upper bits */
const uint delta = ((ey << (32 - m)) ^ sobol_lower ^ sobol_shift) & mask;
/* only use m upper bits for the index (m is a power of two) */
const uint sobol_result = delta | (delta >> m);
const uint upper = sobol_inverse(sobol_result);
const uint index = shift | upper | lower;
*x = van_der_corput(index);
*y = sobol_shift ^ sobol_result ^ sobol_lower;
return index;
}
__device_inline float path_rng(KernelGlobals *kg, RNG *rng, int pass, int dimension)
{
#ifdef __SOBOL_FULL_SCREEN__
uint result = sobol_dimension(kg, *rng, dimension);
float r = (float)result * (1.0f/(float)0xFFFFFFFF);
return r;
#else
/* compute sobol sequence value using direction vectors */
uint result = sobol_dimension(kg, pass, dimension);
float r = (float)result * (1.0f/(float)0xFFFFFFFF);
/* Cranly-Patterson rotation using rng seed */
float shift;
if(dimension & 1)
shift = (*rng >> 16)/((float)0xFFFF);
else
shift = (*rng & 0xFFFF)/((float)0xFFFF);
return r + shift - floor(r + shift);
#endif
}
__device_inline void path_rng_init(KernelGlobals *kg, __global uint *rng_state, int pass, RNG *rng, int x, int y, float *fx, float *fy)
{
#ifdef __SOBOL_FULL_SCREEN__
uint px, py;
uint bits = 16; /* limits us to 65536x65536 and 65536 samples */
uint size = 1 << bits;
uint frame = pass;
*rng = sobol_lookup(bits, frame, x, y, &px, &py);
*fx = size * (float)px * (1.0f/(float)0xFFFFFFFF) - x;
*fy = size * (float)py * (1.0f/(float)0xFFFFFFFF) - y;
#else
*rng = rng_state[x + y*kernel_data.cam.width];
*fx = path_rng(kg, rng, pass, PRNG_FILTER_U);
*fy = path_rng(kg, rng, pass, PRNG_FILTER_V);
#endif
}
__device void path_rng_end(KernelGlobals *kg, __global uint *rng_state, RNG rng, int x, int y)
{
/* nothing to do */
}
#else
/* Linear Congruential Generator */
__device float path_rng(KernelGlobals *kg, RNG *rng, int pass, int dimension)
{
/* implicit mod 2^32 */
*rng = (1103515245*(*rng) + 12345);
return (float)*rng * (1.0f/(float)0xFFFFFFFF);
}
__device void path_rng_init(KernelGlobals *kg, __global uint *rng_state, int pass, RNG *rng, int x, int y, float *fx, float *fy)
{
/* load state */
*rng = rng_state[x + y*kernel_data.cam.width];
*fx = path_rng(kg, rng, pass, PRNG_FILTER_U);
*fy = path_rng(kg, rng, pass, PRNG_FILTER_V);
}
__device void path_rng_end(KernelGlobals *kg, __global uint *rng_state, RNG rng, int x, int y)
{
/* store state for next pass */
rng_state[x + y*kernel_data.cam.width] = rng;
}
#endif
CCL_NAMESPACE_END

@ -0,0 +1,460 @@
/*
* 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.
*/
/*
* ShaderData, used in four steps:
*
* Setup from incoming ray, sampled position and background.
* Execute for surface, volume or displacement.
* Evaluate one or more closures.
* Release.
*
*/
#include "svm/bsdf.h"
#include "svm/emissive.h"
#include "svm/volume.h"
#include "svm/svm_bsdf.h"
#include "svm/svm.h"
#ifdef WITH_OSL
#include "osl_shader.h"
#endif
CCL_NAMESPACE_BEGIN
/* ShaderData setup from incoming ray */
__device_inline void shader_setup_from_ray(KernelGlobals *kg, ShaderData *sd,
const Intersection *isect, const Ray *ray)
{
/* fetch triangle data */
int prim = kernel_tex_fetch(__prim_index, isect->prim);
float4 Ns = kernel_tex_fetch(__tri_normal, prim);
float3 Ng = make_float3(Ns.x, Ns.y, Ns.z);
int shader = __float_as_int(Ns.w);
/* vectors */
sd->P = bvh_triangle_refine(kg, isect, ray);
sd->Ng = Ng;
sd->N = Ng;
sd->I = -ray->D;
sd->shader = shader;
sd->flag = 0;
/* triangle */
#ifdef __INSTANCING__
sd->object = isect->object;
#endif
sd->prim = prim;
#ifdef __UV__
sd->u = isect->u;
sd->v = isect->v;
#endif
/* smooth normal */
if(sd->shader < 0) {
sd->N = triangle_smooth_normal(kg, sd->prim, sd->u, sd->v);
sd->shader = -sd->shader;
}
#ifdef __DPDU__
/* dPdu/dPdv */
triangle_dPdudv(kg, &sd->dPdu, &sd->dPdv, sd->prim);
#endif
#ifdef __INSTANCING__
if(sd->object != ~0) {
/* instance transform */
object_normal_transform(kg, sd->object, &sd->N);
object_normal_transform(kg, sd->object, &sd->Ng);
#ifdef __DPDU__
object_dir_transform(kg, sd->object, &sd->dPdu);
object_dir_transform(kg, sd->object, &sd->dPdv);
#endif
}
else {
/* non-instanced object index */
sd->object = kernel_tex_fetch(__prim_object, isect->prim);
}
#endif
/* backfacing test */
bool backfacing = (dot(sd->Ng, sd->I) < 0.0f);
if(backfacing) {
sd->flag = SD_BACKFACING;
sd->Ng = -sd->Ng;
sd->N = -sd->N;
#ifdef __DPDU__
sd->dPdu = -sd->dPdu;
sd->dPdv = -sd->dPdv;
#endif
}
#ifdef __RAY_DIFFERENTIALS__
/* differentials */
differential_transfer(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, isect->t);
differential_incoming(&sd->dI, ray->dD);
differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng);
#endif
}
/* ShaderData setup from position sampled on mesh */
__device void shader_setup_from_sample(KernelGlobals *kg, ShaderData *sd,
const float3 P, const float3 Ng, const float3 I,
int shader, int object, int prim, float u, float v)
{
/* vectors */
sd->P = P;
sd->N = Ng;
sd->Ng = Ng;
sd->I = I;
sd->shader = shader;
sd->flag = 0;
/* primitive */
#ifdef __INSTANCING__
sd->object = object;
#endif
sd->prim = prim;
#ifdef __UV__
sd->u = u;
sd->v = v;
#endif
/* detect instancing, for non-instanced the object index is -object-1 */
bool instanced = false;
if(sd->prim != ~0) {
if(sd->object >= 0)
instanced = true;
else
sd->object = -sd->object-1;
}
/* smooth normal */
if(sd->shader < 0) {
sd->N = triangle_smooth_normal(kg, sd->prim, sd->u, sd->v);
sd->shader = -sd->shader;
#ifdef __INSTANCING__
if(instanced)
object_normal_transform(kg, sd->object, &sd->N);
#endif
}
#ifdef __DPDU__
/* dPdu/dPdv */
if(sd->prim == ~0) {
sd->dPdu = make_float3(0.0f, 0.0f, 0.0f);
sd->dPdv = make_float3(0.0f, 0.0f, 0.0f);
}
else {
triangle_dPdudv(kg, &sd->dPdu, &sd->dPdv, sd->prim);
#ifdef __INSTANCING__
if(instanced) {
object_dir_transform(kg, sd->object, &sd->dPdu);
object_dir_transform(kg, sd->object, &sd->dPdv);
}
#endif
}
#endif
/* backfacing test */
if(sd->prim != ~0) {
bool backfacing = (dot(sd->Ng, sd->I) < 0.0f);
if(backfacing) {
sd->flag = SD_BACKFACING;
sd->Ng = -sd->Ng;
sd->N = -sd->N;
#ifdef __DPDU__
sd->dPdu = -sd->dPdu;
sd->dPdv = -sd->dPdv;
#endif
}
}
#ifdef __RAY_DIFFERENTIALS__
/* no ray differentials here yet */
sd->dP.dx = make_float3(0.0f, 0.0f, 0.0f);
sd->dP.dy = make_float3(0.0f, 0.0f, 0.0f);
sd->dI.dx = make_float3(0.0f, 0.0f, 0.0f);
sd->dI.dy = make_float3(0.0f, 0.0f, 0.0f);
sd->du.dx = 0.0f;
sd->du.dy = 0.0f;
sd->dv.dx = 0.0f;
sd->dv.dy = 0.0f;
#endif
}
/* ShaderData setup for displacement */
__device void shader_setup_from_displace(KernelGlobals *kg, ShaderData *sd,
int object, int prim, float u, float v)
{
float3 P, Ng, I = make_float3(0.0f, 0.0f, 0.0f);
int shader;
P = triangle_point_MT(kg, prim, u, v);
Ng = triangle_normal_MT(kg, prim, &shader);
/* force smooth shading for displacement */
if(shader >= 0)
shader = -shader;
/* watch out: no instance transform currently */
shader_setup_from_sample(kg, sd, P, Ng, I, shader, object, prim, u, v);
}
/* ShaderData setup from ray into background */
__device_inline void shader_setup_from_background(KernelGlobals *kg, ShaderData *sd, const Ray *ray)
{
/* vectors */
sd->P = ray->D;
sd->N = -sd->P;
sd->Ng = -sd->P;
sd->I = -sd->P;
sd->shader = kernel_data.background.shader;
sd->flag = 0;
#ifdef __INSTANCING__
sd->object = ~0;
#endif
sd->prim = ~0;
#ifdef __UV__
sd->u = 0.0f;
sd->v = 0.0f;
#endif
#ifdef __DPDU__
/* dPdu/dPdv */
sd->dPdu = make_float3(0.0f, 0.0f, 0.0f);
sd->dPdv = make_float3(0.0f, 0.0f, 0.0f);
#endif
#ifdef __RAY_DIFFERENTIALS__
/* differentials */
sd->dP = ray->dD;
differential_incoming(&sd->dI, sd->dP);
sd->du.dx = 0.0f;
sd->du.dy = 0.0f;
sd->dv.dx = 0.0f;
sd->dv.dy = 0.0f;
#endif
}
/* BSDF */
__device int shader_bsdf_sample(KernelGlobals *kg, const ShaderData *sd,
float randu, float randv, float3 *eval,
float3 *omega_in, differential3 *domega_in, float *pdf)
{
int label;
*pdf = 0.0f;
#ifdef WITH_OSL
if(kg->osl.use)
label = OSLShader::bsdf_sample(sd, randu, randv, *eval, *omega_in, *domega_in, *pdf);
else
#endif
label = svm_bsdf_sample(sd, randu, randv, eval, omega_in, domega_in, pdf);
return label;
}
__device float3 shader_bsdf_eval(KernelGlobals *kg, const ShaderData *sd,
const float3 omega_in, float *pdf)
{
float3 eval;
*pdf = 0.0f;
#ifdef WITH_OSL
if(kg->osl.use)
eval = OSLShader::bsdf_eval(sd, omega_in, *pdf);
else
#endif
eval = svm_bsdf_eval(sd, omega_in, pdf);
return eval;
}
__device void shader_bsdf_blur(KernelGlobals *kg, ShaderData *sd, float roughness)
{
#ifdef WITH_OSL
if(!kg->osl.use)
#endif
svm_bsdf_blur(sd, roughness);
}
/* Emission */
__device float3 shader_emissive_eval(KernelGlobals *kg, ShaderData *sd)
{
#ifdef WITH_OSL
if(kg->osl.use) {
return OSLShader::emissive_eval(sd);
}
else
#endif
{
return svm_emissive_eval(sd);
}
}
__device void shader_emissive_sample(KernelGlobals *kg, ShaderData *sd,
float randu, float randv, float3 *eval, float3 *I, float *pdf)
{
#ifdef WITH_OSL
if(kg->osl.use) {
OSLShader::emissive_sample(sd, randu, randv, eval, I, pdf);
}
else
#endif
{
svm_emissive_sample(sd, randu, randv, eval, I, pdf);
}
}
/* Surface Evaluation */
__device void shader_eval_surface(KernelGlobals *kg, ShaderData *sd,
float randb, int path_flag)
{
#ifdef WITH_OSL
if(kg->osl.use) {
OSLShader::eval_surface(kg, sd, randb, path_flag);
}
else
#endif
{
#ifdef __SVM__
svm_eval_nodes(kg, sd, SHADER_TYPE_SURFACE, randb, path_flag);
#else
sd->svm_closure = CLOSURE_BSDF_DIFFUSE_ID;
sd->svm_closure_weight = make_float3(0.8f, 0.8f, 0.8f);
#endif
#ifdef __CAUSTICS_TRICKS__
/* caustic tricks */
if((path_flag & PATH_RAY_DIFFUSE) && (sd->flag & SD_BSDF_GLOSSY)) {
if(kernel_data.integrator.no_caustics) {
sd->flag &= ~(SD_BSDF_GLOSSY|SD_BSDF_HAS_EVAL|SD_EMISSION);
sd->svm_closure = NBUILTIN_CLOSURES;
sd->svm_closure_weight = make_float3(0.0f, 0.0f, 0.0f);
}
else if(kernel_data.integrator.blur_caustics > 0.0f)
shader_bsdf_blur(kg, sd, kernel_data.integrator.blur_caustics);
}
#endif
}
}
/* Background Evaluation */
__device float3 shader_eval_background(KernelGlobals *kg, ShaderData *sd, int path_flag)
{
#ifdef WITH_OSL
if(kg->osl.use) {
return OSLShader::eval_background(kg, sd, path_flag);
}
else
#endif
{
#ifdef __SVM__
svm_eval_nodes(kg, sd, SHADER_TYPE_SURFACE, 0.0f, path_flag);
#else
sd->svm_closure_weight = make_float3(0.8f, 0.8f, 0.8f);
#endif
return sd->svm_closure_weight;
}
}
/* Volume */
__device float3 shader_volume_eval_phase(KernelGlobals *kg, ShaderData *sd,
float3 omega_in, float3 omega_out)
{
#ifdef WITH_OSL
if(kg->osl.use) {
OSLShader::volume_eval_phase(sd, omega_in, omega_out);
}
else
#endif
{
return volume_eval_phase(sd, omega_in, omega_out);
}
}
/* Volume Evaluation */
__device void shader_eval_volume(KernelGlobals *kg, ShaderData *sd,
float randb, int path_flag)
{
#ifdef WITH_OSL
if(kg->osl.use) {
OSLShader::eval_volume(kg, sd, randb, path_flag);
}
else
#endif
{
#ifdef __SVM__
svm_eval_nodes(kg, sd, SHADER_TYPE_VOLUME, randb, path_flag);
#endif
}
}
/* Displacement Evaluation */
__device void shader_eval_displacement(KernelGlobals *kg, ShaderData *sd)
{
/* this will modify sd->P */
#ifdef WITH_OSL
if(kg->osl.use) {
OSLShader::eval_displacement(kg, sd);
}
else
#endif
{
#ifdef __SVM__
svm_eval_nodes(kg, sd, SHADER_TYPE_DISPLACEMENT, 0.0f, 0);
#endif
}
}
/* Free ShaderData */
__device void shader_release(KernelGlobals *kg, ShaderData *sd)
{
#ifdef WITH_OSL
if(kg->osl.use)
OSLShader::release(kg, sd);
#endif
}
CCL_NAMESPACE_END

@ -0,0 +1,183 @@
/*
* 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.
*/
CCL_NAMESPACE_BEGIN
/* Point on triangle for Moller-Trumbore triangles */
__device_inline float3 triangle_point_MT(KernelGlobals *kg, int tri_index, float u, float v)
{
/* load triangle vertices */
float3 tri_vindex = as_float3(kernel_tex_fetch(__tri_vindex, tri_index));
float3 v0 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)));
float3 v1 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)));
float3 v2 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)));
/* compute point */
float t = 1.0f - u - v;
return (u*v0 + v*v1 + t*v2);
}
/* Sample point on triangle */
__device_inline float3 triangle_sample_MT(KernelGlobals *kg, int tri_index, float randu, float randv)
{
/* compute point */
randu = sqrtf(randu);
float u = 1.0f - randu;
float v = randv*randu;
return triangle_point_MT(kg, tri_index, u, v);
}
/* Normal for Moller-Trumbore triangles */
__device_inline float3 triangle_normal_MT(KernelGlobals *kg, int tri_index, int *shader)
{
#if 0
/* load triangle vertices */
float3 tri_vindex = as_float3(kernel_tex_fetch(__tri_vindex, tri_index));
float3 v0 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)));
float3 v1 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)));
float3 v2 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)));
/* compute normal */
return normalize(cross(v2 - v0, v1 - v0));
#else
float4 Nm = kernel_tex_fetch(__tri_normal, tri_index);
*shader = __float_as_int(Nm.w);
return make_float3(Nm.x, Nm.y, Nm.z);
#endif
}
__device_inline float3 triangle_smooth_normal(KernelGlobals *kg, int tri_index, float u, float v)
{
/* load triangle vertices */
float3 tri_vindex = as_float3(kernel_tex_fetch(__tri_vindex, tri_index));
float3 n0 = as_float3(kernel_tex_fetch(__tri_vnormal, __float_as_int(tri_vindex.x)));
float3 n1 = as_float3(kernel_tex_fetch(__tri_vnormal, __float_as_int(tri_vindex.y)));
float3 n2 = as_float3(kernel_tex_fetch(__tri_vnormal, __float_as_int(tri_vindex.z)));
return normalize((1.0f - u - v)*n2 + u*n0 + v*n1);
}
__device_inline void triangle_dPdudv(KernelGlobals *kg, float3 *dPdu, float3 *dPdv, int tri)
{
/* fetch triangle vertex coordinates */
float3 tri_vindex = as_float3(kernel_tex_fetch(__tri_vindex, tri));
float3 p0 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)));
float3 p1 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)));
float3 p2 = as_float3(kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)));
/* compute derivatives of P w.r.t. uv */
*dPdu = (p0 - p2);
*dPdv = (p1 - p2);
}
/* attributes */
__device float triangle_attribute_float(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float *dx, float *dy)
{
if(elem == ATTR_ELEMENT_FACE) {
if(dx) *dx = 0.0f;
if(dy) *dy = 0.0f;
return kernel_tex_fetch(__attributes_float, offset + sd->prim);
}
else if(elem == ATTR_ELEMENT_VERTEX) {
float3 tri_vindex = as_float3(kernel_tex_fetch(__tri_vindex, sd->prim));
float f0 = kernel_tex_fetch(__attributes_float, offset + __float_as_int(tri_vindex.x));
float f1 = kernel_tex_fetch(__attributes_float, offset + __float_as_int(tri_vindex.y));
float f2 = kernel_tex_fetch(__attributes_float, offset + __float_as_int(tri_vindex.z));
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = sd->du.dx*f0 + sd->dv.dx*f1 - (sd->du.dx + sd->dv.dx)*f2;
if(dy) *dy = sd->du.dy*f0 + sd->dv.dy*f1 - (sd->du.dy + sd->dv.dy)*f2;
#endif
return sd->u*f0 + sd->v*f1 + (1.0f - sd->u - sd->v)*f2;
}
else if(elem == ATTR_ELEMENT_CORNER) {
int tri = offset + sd->prim;
float f0 = kernel_tex_fetch(__attributes_float, tri*3 + 0);
float f1 = kernel_tex_fetch(__attributes_float, tri*3 + 1);
float f2 = kernel_tex_fetch(__attributes_float, tri*3 + 2);
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = sd->du.dx*f0 + sd->dv.dx*f1 - (sd->du.dx + sd->dv.dx)*f2;
if(dy) *dy = sd->du.dy*f0 + sd->dv.dy*f1 - (sd->du.dy + sd->dv.dy)*f2;
#endif
return sd->u*f0 + sd->v*f1 + (1.0f - sd->u - sd->v)*f2;
}
else {
if(dx) *dx = 0.0f;
if(dy) *dy = 0.0f;
return 0.0f;
}
}
__device float3 triangle_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int offset, float3 *dx, float3 *dy)
{
if(elem == ATTR_ELEMENT_FACE) {
if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
return as_float3(kernel_tex_fetch(__attributes_float3, offset + sd->prim));
}
else if(elem == ATTR_ELEMENT_VERTEX) {
float3 tri_vindex = as_float3(kernel_tex_fetch(__tri_vindex, sd->prim));
float3 f0 = as_float3(kernel_tex_fetch(__attributes_float3, offset + __float_as_int(tri_vindex.x)));
float3 f1 = as_float3(kernel_tex_fetch(__attributes_float3, offset + __float_as_int(tri_vindex.y)));
float3 f2 = as_float3(kernel_tex_fetch(__attributes_float3, offset + __float_as_int(tri_vindex.z)));
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = sd->du.dx*f0 + sd->dv.dx*f1 - (sd->du.dx + sd->dv.dx)*f2;
if(dy) *dy = sd->du.dy*f0 + sd->dv.dy*f1 - (sd->du.dy + sd->dv.dy)*f2;
#endif
return sd->u*f0 + sd->v*f1 + (1.0f - sd->u - sd->v)*f2;
}
else if(elem == ATTR_ELEMENT_CORNER) {
int tri = offset + sd->prim;
float3 f0 = as_float3(kernel_tex_fetch(__attributes_float3, tri*3 + 0));
float3 f1 = as_float3(kernel_tex_fetch(__attributes_float3, tri*3 + 1));
float3 f2 = as_float3(kernel_tex_fetch(__attributes_float3, tri*3 + 2));
#ifdef __RAY_DIFFERENTIALS__
if(dx) *dx = sd->du.dx*f0 + sd->dv.dx*f1 - (sd->du.dx + sd->dv.dx)*f2;
if(dy) *dy = sd->du.dy*f0 + sd->dv.dy*f1 - (sd->du.dy + sd->dv.dy)*f2;
#endif
return sd->u*f0 + sd->v*f1 + (1.0f - sd->u - sd->v)*f2;
}
else {
if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f);
if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f);
return make_float3(0.0f, 0.0f, 0.0f);
}
}
CCL_NAMESPACE_END

@ -0,0 +1,374 @@
/*
* 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 __KERNEL_TYPES_H__
#define __KERNEL_TYPES_H__
#include "kernel_math.h"
#ifndef __KERNEL_OPENCL__
#include "svm_types.h"
#endif
CCL_NAMESPACE_BEGIN
#define OBJECT_SIZE 16
#define __INSTANCING__
#define __DPDU__
#define __UV__
#define __BACKGROUND__
#define __EMISSION__
#define __CAUSTICS_TRICKS__
#define __SVM__
#define __SOBOL__
#define __TEXTURES__
#define __RAY_DIFFERENTIALS__
#define __CAMERA_CLIPPING__
#define __INTERSECTION_REFINE__
//#define __SOBOL_FULL_SCREEN__
//#define __MODIFY_TP__
//#define __QBVH__
/* Path Tracing */
enum PathTraceDimension {
PRNG_FILTER_U = 0,
PRNG_FILTER_V = 1,
PRNG_LENS_U = 2,
PRNG_LENS_V = 3,
PRNG_BASE_NUM = 4,
PRNG_BSDF_U = 0,
PRNG_BSDF_V = 1,
PRNG_BSDF = 2,
PRNG_LIGHT = 3,
PRNG_LIGHT_U = 4,
PRNG_LIGHT_V = 5,
PRNG_LIGHT_F = 6,
PRNG_TERMINATE = 7,
PRNG_BOUNCE_NUM = 8
};
/* these flag values correspond exactly to OSL defaults, so be careful not to
* change this, or if you do, set the "raytypes" shading system attribute with
* your own new ray types and bitflag values */
enum PathRayFlag {
PATH_RAY_CAMERA = 1,
PATH_RAY_SHADOW = 2,
PATH_RAY_REFLECT = 4,
PATH_RAY_TRANSMIT = 8,
PATH_RAY_DIFFUSE = 16,
PATH_RAY_GLOSSY = 32,
PATH_RAY_SINGULAR = 64
};
/* Bidirectional Path Tracing */
enum BidirTraceDimension {
BRNG_FILTER_U = 0,
BRNG_FILTER_V = 1,
BRNG_LENS_U = 2,
BRNG_LENS_V = 3,
BRNG_LIGHT_U = 4,
BRNG_LIGHT_V = 5,
BRNG_LIGHT = 6,
BRNG_LIGHT_F = 7,
BRNG_EMISSIVE_U = 8,
BRNG_EMISSIVE_V = 9,
BRNG_BASE_NUM = 10,
BRNG_BSDF_U = 0,
BRNG_BSDF_V = 1,
BRNG_BSDF = 2,
BRNG_TERMINATE = 3,
BRNG_BOUNCE_NUM = 4
};
/* Closure Label */
typedef enum ClosureLabel {
LABEL_NONE = 0,
LABEL_CAMERA = 1,
LABEL_LIGHT = 2,
LABEL_BACKGROUND = 4,
LABEL_TRANSMIT = 8,
LABEL_REFLECT = 16,
LABEL_VOLUME = 32,
LABEL_OBJECT = 64,
LABEL_DIFFUSE = 128,
LABEL_GLOSSY = 256,
LABEL_SINGULAR = 512,
LABEL_STRAIGHT = 1024,
LABEL_STOP = 2048
} ClosureLabel;
/* Ray Type */
typedef enum RayType {
RayTypeCamera = 1,
RayTypeShadow = 2,
RayTypeReflection = 4,
RayTypeRefraction = 8,
RayTypeDiffuse = 16,
RayTypeGlossy = 32
} RayType;
/* Differential */
typedef struct differential3 {
float3 dx;
float3 dy;
} differential3;
typedef struct differential {
float dx;
float dy;
} differential;
/* Ray */
typedef struct Ray {
float3 P;
float3 D;
float t;
#ifdef __RAY_DIFFERENTIALS__
differential3 dP;
differential3 dD;
#endif
} Ray;
/* Intersection */
typedef struct Intersection {
float t, u, v;
int prim;
int object;
} Intersection;
/* Attributes */
typedef enum AttributeElement {
ATTR_ELEMENT_FACE,
ATTR_ELEMENT_VERTEX,
ATTR_ELEMENT_CORNER,
ATTR_ELEMENT_VALUE,
ATTR_ELEMENT_NONE
} AttributeElement;
/* OSL data */
#if !defined(__KERNEL_GPU__) && defined(WITH_OSL)
#define MAX_OSL_CLOSURE 8
struct FlatClosure {
void *prim;
float3 weight;
float sample_weight;
};
#endif
/* Shader Data
*
* Main shader state at a point on the surface or in a volume. All coordinates
* are in world space. */
enum ShaderDataFlag {
SD_BACKFACING = 1, /* backside of surface? */
SD_EMISSION = 2, /* have emissive closure? */
SD_BSDF_HAS_EVAL = 4, /* have non-singular bsdf closure? */
SD_BSDF_GLOSSY = 8 /* have glossy bsdf */
};
typedef struct ShaderData {
/* position */
float3 P;
/* smooth normal for shading */
float3 N;
/* true geometric normal */
float3 Ng;
/* view/incoming direction */
float3 I;
/* shader id */
int shader;
/* booleans describing shader, see ShaderDataFlag */
int flag;
/* primitive id if there is one, ~0 otherwise */
int prim;
/* parametric coordinates
* - barycentric weights for triangles
* - latlong coordinates for background */
float u, v;
/* object id if there is one, ~0 otherwise */
int object;
#ifdef __RAY_DIFFERENTIALS__
/* differential of P. these are orthogonal to Ng, not N */
differential3 dP;
/* differential of I */
differential3 dI;
/* differential of u, v */
differential du;
differential dv;
#endif
#ifdef __DPDU__
/* differential of P w.r.t. parametric coordinates. note that dPdu is
* not readily suitable as a tangent for shading on triangles. */
float3 dPdu, dPdv;
#endif
/* SVM closure data. we always sample a single closure, to get fixed
* memory usage, svm_closure_data contains closure parameters. */
#ifndef __KERNEL_OPENCL__
ClosureType svm_closure;
#endif
float3 svm_closure_weight;
float svm_closure_data[3]; /* CUDA gives compile error if out of bounds */
#if !defined(__KERNEL_GPU__) && defined(WITH_OSL)
/* OSL closure data and context. we store all closures flattened into
* lists per type, different from SVM. */
struct {
FlatClosure bsdf[MAX_OSL_CLOSURE];
FlatClosure emissive[MAX_OSL_CLOSURE];
FlatClosure volume[MAX_OSL_CLOSURE];
int num_bsdf;
int num_emissive;
int num_volume;
float bsdf_sample_sum;
float emissive_sample_sum;
float volume_sample_sum;
float randb;
} osl_closure;
void *osl_ctx;
#endif
} ShaderData;
/* Constrant Kernel Data */
typedef struct KernelCamera {
/* type */
int ortho;
int pad;
/* size */
int width, height;
/* matrices */
Transform cameratoworld;
Transform rastertocamera;
/* depth of field */
float lensradius;
float focaldistance;
/* motion blur */
float shutteropen;
float shutterclose;
/* differentials */
float3 dx, dy;
/* clipping */
float nearclip;
float cliplength;
/* more matrices */
Transform screentoworld;
Transform rastertoworld;
Transform ndctoworld;
Transform worldtoscreen;
Transform worldtoraster;
Transform worldtondc;
Transform worldtocamera;
} KernelCamera;
typedef struct KernelFilm {
float exposure;
int use_response_curve;
int pad1, pad2;
} KernelFilm;
typedef struct KernelBackground {
/* only shader index */
int shader;
int pad1, pad2, pad3;
} KernelBackground;
typedef struct KernelSunSky {
/* sun direction in spherical and cartesian */
float theta, phi;
float3 dir;
float pad;
/* perez function parameters */
float zenith_Y, zenith_x, zenith_y;
float perez_Y[5], perez_x[5], perez_y[5];
} KernelSunSky;
typedef struct KernelIntegrator {
/* emission */
int use_emission;
int num_triangles;
int num_distribution;
int num_lights;
float pdf_triangles;
float pdf_lights;
/* path tracing */
int minbounce;
int maxbounce;
/* caustics */
int no_caustics;
float blur_caustics;
/* padding */
int pad;
} KernelIntegrator;
typedef struct KernelBVH {
/* root node */
int root;
int attributes_map_stride;
int pad1, pad2;
} KernelBVH;
typedef struct KernelData {
KernelCamera cam;
KernelFilm film;
KernelBackground background;
KernelSunSky sunsky;
KernelIntegrator integrator;
KernelBVH bvh;
} KernelData;
CCL_NAMESPACE_END
#endif /* __KERNEL_TYPES_H__ */

@ -0,0 +1,33 @@
INCLUDE_DIRECTORIES(. ../ ../svm ../../render ../../util ../../device)
SET(sources
background.cpp
bsdf_ashikhmin_velvet.cpp
bsdf_diffuse.cpp
bsdf_microfacet.cpp
bsdf_reflection.cpp
bsdf_refraction.cpp
bsdf_transparent.cpp
bsdf_ward.cpp
bsdf_westin.cpp
bssrdf.cpp
debug.cpp
emissive.cpp
osl_closures.cpp
osl_services.cpp
osl_shader.cpp
vol_subsurface.cpp)
SET(headers
osl_closures.h
osl_globals.h
osl_services.h
osl_shader.h)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RTTI_DISABLE_FLAGS}")
ADD_LIBRARY(kernel_osl ${sources} ${headers})
ADD_SUBDIRECTORY(nodes)

@ -0,0 +1,100 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
/// Generic background closure
///
/// We only have a background closure for the shaders
/// to return a color in background shaders. No methods,
/// only the weight is taking into account
///
class GenericBackgroundClosure : public BackgroundClosure {
public:
GenericBackgroundClosure() { }
void setup() {};
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "background"; }
void print_on (std::ostream &out) const {
out << name() << " ()";
}
};
/// Holdout closure
///
/// This will be used by the shader to mark the
/// amount of holdout for the current shading
/// point. No parameters, only the weight will be
/// used
///
class HoldoutClosure : ClosurePrimitive {
public:
HoldoutClosure () : ClosurePrimitive (Holdout) { }
void setup() {};
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "holdout"; }
void print_on (std::ostream &out) const {
out << name() << " ()";
}
};
ClosureParam closure_background_params[] = {
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(GenericBackgroundClosure) };
CLOSURE_PREPARE(closure_background_prepare, GenericBackgroundClosure)
ClosureParam closure_holdout_params[] = {
CLOSURE_FINISH_PARAM(HoldoutClosure) };
CLOSURE_PREPARE(closure_holdout_prepare, HoldoutClosure)
CCL_NAMESPACE_END

@ -0,0 +1,175 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
class AshikhminVelvetClosure : public BSDFClosure {
public:
Vec3 m_N;
float m_sigma;
float m_invsigma2;
AshikhminVelvetClosure() : BSDFClosure(Labels::DIFFUSE) { }
void setup()
{
m_sigma = std::max(m_sigma, 0.01f);
m_invsigma2 = 1.0f/(m_sigma * m_sigma);
}
bool mergeable (const ClosurePrimitive *other) const {
const AshikhminVelvetClosure *comp = (const AshikhminVelvetClosure *)other;
return m_N == comp->m_N && m_sigma == comp->m_sigma &&
BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "ashikhmin_velvet"; }
void print_on (std::ostream &out) const
{
out << name() << " (";
out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
out << m_sigma;
out << ")";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNO > 0 && cosNI > 0) {
Vec3 H = omega_in + omega_out;
H.normalize();
float cosNH = m_N.dot(H);
float cosHO = fabsf(omega_out.dot(H));
float cosNHdivHO = cosNH / cosHO;
cosNHdivHO = std::max(cosNHdivHO, 0.00001f);
float fac1 = 2 * fabsf(cosNHdivHO * cosNO);
float fac2 = 2 * fabsf(cosNHdivHO * cosNI);
float sinNH2 = 1 - cosNH * cosNH;
float sinNH4 = sinNH2 * sinNH2;
float cotangent2 = (cosNH * cosNH) / sinNH2;
float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * float(M_1_PI) / sinNH4;
float G = std::min(1.0f, std::min(fac1, fac2)); // TODO: derive G from D analytically
float out = 0.25f * (D * G) / cosNO;
pdf = 0.5f * (float) M_1_PI;
return Color3 (out, out, out);
}
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
// we are viewing the surface from above - send a ray out with uniform
// distribution over the hemisphere
sample_uniform_hemisphere (m_N, omega_out, randu, randv, omega_in, pdf);
if (Ng.dot(omega_in) > 0) {
Vec3 H = omega_in + omega_out;
H.normalize();
float cosNI = m_N.dot(omega_in);
float cosNO = m_N.dot(omega_out);
float cosNH = m_N.dot(H);
float cosHO = fabsf(omega_out.dot(H));
float cosNHdivHO = cosNH / cosHO;
cosNHdivHO = std::max(cosNHdivHO, 0.00001f);
float fac1 = 2 * fabsf(cosNHdivHO * cosNO);
float fac2 = 2 * fabsf(cosNHdivHO * cosNI);
float sinNH2 = 1 - cosNH * cosNH;
float sinNH4 = sinNH2 * sinNH2;
float cotangent2 = (cosNH * cosNH) / sinNH2;
float D = expf(-cotangent2 * m_invsigma2) * m_invsigma2 * float(M_1_PI) / sinNH4;
float G = std::min(1.0f, std::min(fac1, fac2)); // TODO: derive G from D analytically
float power = 0.25f * (D * G) / cosNO;
eval.setValue(power, power, power);
// TODO: find a better approximation for the retroreflective bounce
domega_in_dx = (2 * m_N.dot(domega_out_dx)) * m_N - domega_out_dx;
domega_in_dy = (2 * m_N.dot(domega_out_dy)) * m_N - domega_out_dy;
domega_in_dx *= 125;
domega_in_dy *= 125;
} else
pdf = 0;
return Labels::REFLECT;
}
};
ClosureParam bsdf_ashikhmin_velvet_params[] = {
CLOSURE_VECTOR_PARAM(AshikhminVelvetClosure, m_N),
CLOSURE_FLOAT_PARAM (AshikhminVelvetClosure, m_sigma),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(AshikhminVelvetClosure) };
CLOSURE_PREPARE(bsdf_ashikhmin_velvet_prepare, AshikhminVelvetClosure)
CCL_NAMESPACE_END

@ -0,0 +1,181 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
class DiffuseClosure : public BSDFClosure {
public:
Vec3 m_N;
DiffuseClosure() : BSDFClosure(Labels::DIFFUSE) { }
void setup() {};
bool mergeable (const ClosurePrimitive *other) const {
const DiffuseClosure *comp = (const DiffuseClosure *)other;
return m_N == comp->m_N && BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "diffuse"; }
void print_on (std::ostream &out) const
{
out << name() << " ((" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "))";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
float cos_pi = std::max(m_N.dot(omega_in),0.0f) * (float) M_1_PI;
pdf = cos_pi;
return Color3 (cos_pi, cos_pi, cos_pi);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
// we are viewing the surface from the right side - send a ray out with cosine
// distribution over the hemisphere
sample_cos_hemisphere (m_N, omega_out, randu, randv, omega_in, pdf);
if (Ng.dot(omega_in) > 0) {
eval.setValue(pdf, pdf, pdf);
// TODO: find a better approximation for the diffuse bounce
domega_in_dx = (2 * m_N.dot(domega_out_dx)) * m_N - domega_out_dx;
domega_in_dy = (2 * m_N.dot(domega_out_dy)) * m_N - domega_out_dy;
domega_in_dx *= 125;
domega_in_dy *= 125;
} else
pdf = 0;
return Labels::REFLECT;
}
};
class TranslucentClosure : public BSDFClosure {
public:
Vec3 m_N;
TranslucentClosure() : BSDFClosure(Labels::DIFFUSE, Back) { }
void setup() {};
bool mergeable (const ClosurePrimitive *other) const {
const TranslucentClosure *comp = (const TranslucentClosure *)other;
return m_N == comp->m_N && BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "translucent"; }
void print_on (std::ostream &out) const
{
out << name() << " ((" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "))";
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
float cos_pi = std::max(-m_N.dot(omega_in), 0.0f) * (float) M_1_PI;
pdf = cos_pi;
return Color3 (cos_pi, cos_pi, cos_pi);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
// we are viewing the surface from the right side - send a ray out with cosine
// distribution over the hemisphere
sample_cos_hemisphere (-m_N, omega_out, randu, randv, omega_in, pdf);
if (Ng.dot(omega_in) < 0) {
eval.setValue(pdf, pdf, pdf);
// TODO: find a better approximation for the diffuse bounce
domega_in_dx = (2 * m_N.dot(domega_out_dx)) * m_N - domega_out_dx;
domega_in_dy = (2 * m_N.dot(domega_out_dy)) * m_N - domega_out_dy;
domega_in_dx *= -125;
domega_in_dy *= -125;
} else
pdf = 0;
return Labels::TRANSMIT;
}
};
ClosureParam bsdf_diffuse_params[] = {
CLOSURE_VECTOR_PARAM (DiffuseClosure, m_N),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM (DiffuseClosure) };
ClosureParam bsdf_translucent_params[] = {
CLOSURE_VECTOR_PARAM (TranslucentClosure, m_N),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM (TranslucentClosure) };
CLOSURE_PREPARE(bsdf_diffuse_prepare, DiffuseClosure)
CLOSURE_PREPARE(bsdf_translucent_prepare, TranslucentClosure)
CCL_NAMESPACE_END

@ -0,0 +1,533 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
#include "util_math.h"
using namespace OSL;
CCL_NAMESPACE_BEGIN
// TODO: fresnel_dielectric is only used for derivatives, could be optimized
// TODO: refactor these two classes so they share everything by the microfacet
// distribution terms
// microfacet model with GGX facet distribution
// see http://www.graphics.cornell.edu/~bjw/microfacetbsdf.pdf
template <int Refractive = 0>
class MicrofacetGGXClosure : public BSDFClosure {
public:
Vec3 m_N;
float m_ag; // width parameter (roughness)
float m_eta; // index of refraction (for fresnel term)
MicrofacetGGXClosure() : BSDFClosure(Labels::GLOSSY, Refractive ? Back : Front) { m_eta = 1.0f; }
void setup()
{
m_ag = clamp(m_ag, 1e-5f, 1.0f);
}
bool mergeable (const ClosurePrimitive *other) const {
const MicrofacetGGXClosure *comp = (const MicrofacetGGXClosure *)other;
return m_N == comp->m_N && m_ag == comp->m_ag &&
m_eta == comp->m_eta && BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const {
return Refractive ? "microfacet_ggx_refraction" : "microfacet_ggx";
}
void print_on (std::ostream &out) const {
out << name() << " (";
out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
out << m_ag << ", ";
out << m_eta;
out << ")";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
if (Refractive == 1) return Color3 (0, 0, 0);
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNI > 0 && cosNO > 0) {
// get half vector
Vec3 Hr = omega_in + omega_out;
Hr.normalize();
// eq. 20: (F*G*D)/(4*in*on)
// eq. 33: first we calculate D(m) with m=Hr:
float alpha2 = m_ag * m_ag;
float cosThetaM = m_N.dot(Hr);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = alpha2 / ((float) M_PI * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
// eq. 34: now calculate G1(i,m) and G1(o,m)
float G1o = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
float G1i = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
float G = G1o * G1i;
float out = (G * D) * 0.25f / cosNO;
// eq. 24
float pm = D * cosThetaM;
// convert into pdf of the sampled direction
// eq. 38 - but see also:
// eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
pdf = pm * 0.25f / Hr.dot(omega_out);
return Color3 (out, out, out);
}
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
if (Refractive == 0) return Color3 (0, 0, 0);
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNO <= 0 || cosNI >= 0)
return Color3 (0, 0, 0); // vectors on same side -- not possible
// compute half-vector of the refraction (eq. 16)
Vec3 ht = -(m_eta * omega_in + omega_out);
Vec3 Ht = ht; Ht.normalize();
float cosHO = Ht.dot(omega_out);
float cosHI = Ht.dot(omega_in);
// eq. 33: first we calculate D(m) with m=Ht:
float alpha2 = m_ag * m_ag;
float cosThetaM = m_N.dot(Ht);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = alpha2 / ((float) M_PI * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
// eq. 34: now calculate G1(i,m) and G1(o,m)
float G1o = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
float G1i = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
float G = G1o * G1i;
// probability
float invHt2 = 1 / ht.dot(ht);
pdf = D * fabsf(cosThetaM) * (fabsf(cosHI) * (m_eta * m_eta)) * invHt2;
float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D) * invHt2) / cosNO;
return Color3 (out, out, out);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
float cosNO = m_N.dot(omega_out);
if (cosNO > 0) {
Vec3 X, Y, Z = m_N;
make_orthonormals(Z, X, Y);
// generate a random microfacet normal m
// eq. 35,36:
// we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
// and sin(atan(x)) == x/sqrt(1+x^2)
float alpha2 = m_ag * m_ag;
float tanThetaM2 = alpha2 * randu / (1 - randu);
float cosThetaM = 1 / sqrtf(1 + tanThetaM2);
float sinThetaM = cosThetaM * sqrtf(tanThetaM2);
float phiM = 2 * float(M_PI) * randv;
Vec3 m = (cosf(phiM) * sinThetaM) * X +
(sinf(phiM) * sinThetaM) * Y +
cosThetaM * Z;
if (Refractive == 0) {
float cosMO = m.dot(omega_out);
if (cosMO > 0) {
// eq. 39 - compute actual reflected direction
omega_in = 2 * cosMO * m - omega_out;
if (Ng.dot(omega_in) > 0) {
// microfacet normal is visible to this ray
// eq. 33
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = alpha2 / (float(M_PI) * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
// eq. 24
float pm = D * cosThetaM;
// convert into pdf of the sampled direction
// eq. 38 - but see also:
// eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
pdf = pm * 0.25f / cosMO;
// eval BRDF*cosNI
float cosNI = m_N.dot(omega_in);
// eq. 34: now calculate G1(i,m) and G1(o,m)
float G1o = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
float G1i = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
float G = G1o * G1i;
// eq. 20: (F*G*D)/(4*in*on)
float out = (G * D) * 0.25f / cosNO;
eval.setValue(out, out, out);
domega_in_dx = (2 * m.dot(domega_out_dx)) * m - domega_out_dx;
domega_in_dy = (2 * m.dot(domega_out_dy)) * m - domega_out_dy;
/* disabled for now - gives texture filtering problems */
#if 0
// Since there is some blur to this reflection, make the
// derivatives a bit bigger. In theory this varies with the
// roughness but the exact relationship is complex and
// requires more ops than are practical.
domega_in_dx *= 10;
domega_in_dy *= 10;
#endif
}
}
} else {
// CAUTION: the i and o variables are inverted relative to the paper
// eq. 39 - compute actual refractive direction
Vec3 R, dRdx, dRdy;
Vec3 T, dTdx, dTdy;
bool inside;
fresnel_dielectric(m_eta, m, omega_out, domega_out_dx, domega_out_dy,
R, dRdx, dRdy,
T, dTdx, dTdy,
inside);
if (!inside) {
omega_in = T;
domega_in_dx = dTdx;
domega_in_dy = dTdy;
// eq. 33
float cosThetaM2 = cosThetaM * cosThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = alpha2 / (float(M_PI) * cosThetaM4 * (alpha2 + tanThetaM2) * (alpha2 + tanThetaM2));
// eq. 24
float pm = D * cosThetaM;
// eval BRDF*cosNI
float cosNI = m_N.dot(omega_in);
// eq. 34: now calculate G1(i,m) and G1(o,m)
float G1o = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNO * cosNO) / (cosNO * cosNO)));
float G1i = 2 / (1 + sqrtf(1 + alpha2 * (1 - cosNI * cosNI) / (cosNI * cosNI)));
float G = G1o * G1i;
// eq. 21
float cosHI = m.dot(omega_in);
float cosHO = m.dot(omega_out);
float Ht2 = m_eta * cosHI + cosHO;
Ht2 *= Ht2;
float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D)) / (cosNO * Ht2);
// eq. 38 and eq. 17
pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2;
eval.setValue(out, out, out);
/* disabled for now - gives texture filtering problems */
#if 0
// Since there is some blur to this refraction, make the
// derivatives a bit bigger. In theory this varies with the
// roughness but the exact relationship is complex and
// requires more ops than are practical.
domega_in_dx *= 10;
domega_in_dy *= 10;
#endif
}
}
}
return Refractive ? Labels::TRANSMIT : Labels::REFLECT;
}
};
// microfacet model with Beckmann facet distribution
// see http://www.graphics.cornell.edu/~bjw/microfacetbsdf.pdf
template <int Refractive = 0>
class MicrofacetBeckmannClosure : public BSDFClosure {
public:
Vec3 m_N;
float m_ab; // width parameter (roughness)
float m_eta; // index of refraction (for fresnel term)
MicrofacetBeckmannClosure() : BSDFClosure(Labels::GLOSSY, Refractive ? Back : Front) { }
void setup()
{
m_ab = clamp(m_ab, 1e-5f, 1.0f);
}
bool mergeable (const ClosurePrimitive *other) const {
const MicrofacetBeckmannClosure *comp = (const MicrofacetBeckmannClosure *)other;
return m_N == comp->m_N && m_ab == comp->m_ab &&
m_eta == comp->m_eta && BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char * name () const {
return Refractive ? "microfacet_beckmann_refraction"
: "microfacet_beckmann";
}
void print_on (std::ostream &out) const
{
out << name() << " (";
out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
out << m_ab << ", ";
out << m_eta;
out << ")";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
if (Refractive == 1) return Color3 (0, 0, 0);
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNO > 0 && cosNI > 0) {
// get half vector
Vec3 Hr = omega_in + omega_out;
Hr.normalize();
// eq. 20: (F*G*D)/(4*in*on)
// eq. 25: first we calculate D(m) with m=Hr:
float alpha2 = m_ab * m_ab;
float cosThetaM = m_N.dot(Hr);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = expf(-tanThetaM2 / alpha2) / (float(M_PI) * alpha2 * cosThetaM4);
// eq. 26, 27: now calculate G1(i,m) and G1(o,m)
float ao = 1 / (m_ab * sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
float ai = 1 / (m_ab * sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
float G = G1o * G1i;
float out = (G * D) * 0.25f / cosNO;
// eq. 24
float pm = D * cosThetaM;
// convert into pdf of the sampled direction
// eq. 38 - but see also:
// eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
pdf = pm * 0.25f / Hr.dot(omega_out);
return Color3 (out, out, out);
}
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
if (Refractive == 0) return Color3 (0, 0, 0);
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNO <= 0 || cosNI >= 0)
return Color3 (0, 0, 0);
// compute half-vector of the refraction (eq. 16)
Vec3 ht = -(m_eta * omega_in + omega_out);
Vec3 Ht = ht; Ht.normalize();
float cosHO = Ht.dot(omega_out);
float cosHI = Ht.dot(omega_in);
// eq. 33: first we calculate D(m) with m=Ht:
float alpha2 = m_ab * m_ab;
float cosThetaM = m_N.dot(Ht);
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = (1 - cosThetaM2) / cosThetaM2;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = expf(-tanThetaM2 / alpha2) / (float(M_PI) * alpha2 * cosThetaM4);
// eq. 26, 27: now calculate G1(i,m) and G1(o,m)
float ao = 1 / (m_ab * sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
float ai = 1 / (m_ab * sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
float G = G1o * G1i;
// probability
float invHt2 = 1 / ht.dot(ht);
pdf = D * fabsf(cosThetaM) * (fabsf(cosHI) * (m_eta * m_eta)) * invHt2;
float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D) * invHt2) / cosNO;
return Color3 (out, out, out);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
float cosNO = m_N.dot(omega_out);
if (cosNO > 0) {
Vec3 X, Y, Z = m_N;
make_orthonormals(Z, X, Y);
// generate a random microfacet normal m
// eq. 35,36:
// we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
// and sin(atan(x)) == x/sqrt(1+x^2)
float alpha2 = m_ab * m_ab;
float tanThetaM = sqrtf(-alpha2 * logf(1 - randu));
float cosThetaM = 1 / sqrtf(1 + tanThetaM * tanThetaM);
float sinThetaM = cosThetaM * tanThetaM;
float phiM = 2 * float(M_PI) * randv;
Vec3 m = (cosf(phiM) * sinThetaM) * X +
(sinf(phiM) * sinThetaM) * Y +
cosThetaM * Z;
if (Refractive == 0) {
float cosMO = m.dot(omega_out);
if (cosMO > 0) {
// eq. 39 - compute actual reflected direction
omega_in = 2 * cosMO * m - omega_out;
if (Ng.dot(omega_in) > 0) {
// microfacet normal is visible to this ray
// eq. 25
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = tanThetaM * tanThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = expf(-tanThetaM2 / alpha2) / (float(M_PI) * alpha2 * cosThetaM4);
// eq. 24
float pm = D * cosThetaM;
// convert into pdf of the sampled direction
// eq. 38 - but see also:
// eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
pdf = pm * 0.25f / cosMO;
// Eval BRDF*cosNI
float cosNI = m_N.dot(omega_in);
// eq. 26, 27: now calculate G1(i,m) and G1(o,m)
float ao = 1 / (m_ab * sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
float ai = 1 / (m_ab * sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
float G = G1o * G1i;
// eq. 20: (F*G*D)/(4*in*on)
float out = (G * D) * 0.25f / cosNO;
eval.setValue(out, out, out);
domega_in_dx = (2 * m.dot(domega_out_dx)) * m - domega_out_dx;
domega_in_dy = (2 * m.dot(domega_out_dy)) * m - domega_out_dy;
/* disabled for now - gives texture filtering problems */
#if 0
// Since there is some blur to this reflection, make the
// derivatives a bit bigger. In theory this varies with the
// roughness but the exact relationship is complex and
// requires more ops than are practical.
domega_in_dx *= 10;
domega_in_dy *= 10;
#endif
}
}
} else {
// CAUTION: the i and o variables are inverted relative to the paper
// eq. 39 - compute actual refractive direction
Vec3 R, dRdx, dRdy;
Vec3 T, dTdx, dTdy;
bool inside;
fresnel_dielectric(m_eta, m, omega_out, domega_out_dx, domega_out_dy,
R, dRdx, dRdy,
T, dTdx, dTdy,
inside);
if (!inside) {
omega_in = T;
domega_in_dx = dTdx;
domega_in_dy = dTdy;
// eq. 33
float cosThetaM2 = cosThetaM * cosThetaM;
float tanThetaM2 = tanThetaM * tanThetaM;
float cosThetaM4 = cosThetaM2 * cosThetaM2;
float D = expf(-tanThetaM2 / alpha2) / (float(M_PI) * alpha2 * cosThetaM4);
// eq. 24
float pm = D * cosThetaM;
// eval BRDF*cosNI
float cosNI = m_N.dot(omega_in);
// eq. 26, 27: now calculate G1(i,m) and G1(o,m)
float ao = 1 / (m_ab * sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
float ai = 1 / (m_ab * sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
float G = G1o * G1i;
// eq. 21
float cosHI = m.dot(omega_in);
float cosHO = m.dot(omega_out);
float Ht2 = m_eta * cosHI + cosHO;
Ht2 *= Ht2;
float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D)) / (cosNO * Ht2);
// eq. 38 and eq. 17
pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2;
eval.setValue(out, out, out);
/* disabled for now - gives texture filtering problems */
#if 0
// Since there is some blur to this refraction, make the
// derivatives a bit bigger. In theory this varies with the
// roughness but the exact relationship is complex and
// requires more ops than are practical.
domega_in_dx *= 10;
domega_in_dy *= 10;
#endif
}
}
}
return Refractive ? Labels::TRANSMIT : Labels::REFLECT;
}
};
ClosureParam bsdf_microfacet_ggx_params[] = {
CLOSURE_VECTOR_PARAM(MicrofacetGGXClosure<0>, m_N),
CLOSURE_FLOAT_PARAM (MicrofacetGGXClosure<0>, m_ag),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(MicrofacetGGXClosure<0>) };
ClosureParam bsdf_microfacet_ggx_refraction_params[] = {
CLOSURE_VECTOR_PARAM(MicrofacetGGXClosure<1>, m_N),
CLOSURE_FLOAT_PARAM (MicrofacetGGXClosure<1>, m_ag),
CLOSURE_FLOAT_PARAM (MicrofacetGGXClosure<1>, m_eta),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(MicrofacetGGXClosure<1>) };
ClosureParam bsdf_microfacet_beckmann_params[] = {
CLOSURE_VECTOR_PARAM(MicrofacetBeckmannClosure<0>, m_N),
CLOSURE_FLOAT_PARAM (MicrofacetBeckmannClosure<0>, m_ab),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(MicrofacetBeckmannClosure<0>) };
ClosureParam bsdf_microfacet_beckmann_refraction_params[] = {
CLOSURE_VECTOR_PARAM(MicrofacetBeckmannClosure<1>, m_N),
CLOSURE_FLOAT_PARAM (MicrofacetBeckmannClosure<1>, m_ab),
CLOSURE_FLOAT_PARAM (MicrofacetBeckmannClosure<1>, m_eta),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(MicrofacetBeckmannClosure<1>) };
CLOSURE_PREPARE(bsdf_microfacet_ggx_prepare, MicrofacetGGXClosure<0>)
CLOSURE_PREPARE(bsdf_microfacet_ggx_refraction_prepare, MicrofacetGGXClosure<1>)
CLOSURE_PREPARE(bsdf_microfacet_beckmann_prepare, MicrofacetBeckmannClosure<0>)
CLOSURE_PREPARE(bsdf_microfacet_beckmann_refraction_prepare, MicrofacetBeckmannClosure<1>)
CCL_NAMESPACE_END

@ -0,0 +1,108 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
class ReflectionClosure : public BSDFClosure {
public:
Vec3 m_N; // shading normal
ReflectionClosure() : BSDFClosure(Labels::SINGULAR) { }
void setup() {};
bool mergeable (const ClosurePrimitive *other) const {
const ReflectionClosure *comp = (const ReflectionClosure *)other;
return m_N == comp->m_N && BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "reflection"; }
void print_on (std::ostream &out) const {
out << name() << " (";
out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "))";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
// only one direction is possible
float cosNO = m_N.dot(omega_out);
if (cosNO > 0) {
omega_in = (2 * cosNO) * m_N - omega_out;
if (Ng.dot(omega_in) > 0) {
domega_in_dx = 2 * m_N.dot(domega_out_dx) * m_N - domega_out_dx;
domega_in_dy = 2 * m_N.dot(domega_out_dy) * m_N - domega_out_dy;
pdf = 1;
eval.setValue(1, 1, 1);
}
}
return Labels::REFLECT;
}
};
ClosureParam bsdf_reflection_params[] = {
CLOSURE_VECTOR_PARAM(ReflectionClosure, m_N),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(ReflectionClosure) };
CLOSURE_PREPARE(bsdf_reflection_prepare, ReflectionClosure)
CCL_NAMESPACE_END

@ -0,0 +1,120 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
class RefractionClosure : public BSDFClosure {
public:
Vec3 m_N; // shading normal
float m_eta; // ratio of indices of refraction (inside / outside)
RefractionClosure() : BSDFClosure(Labels::SINGULAR, Back) { }
void setup() {}
bool mergeable (const ClosurePrimitive *other) const {
const RefractionClosure *comp = (const RefractionClosure *)other;
return m_N == comp->m_N && m_eta == comp->m_eta &&
BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "refraction"; }
void print_on (std::ostream &out) const {
out << name() << " (";
out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
out << m_eta;
out << ")";
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
Vec3 R, dRdx, dRdy;
Vec3 T, dTdx, dTdy;
bool inside;
fresnel_dielectric(m_eta, m_N,
omega_out, domega_out_dx, domega_out_dy,
R, dRdx, dRdy,
T, dTdx, dTdy,
inside);
if (!inside) {
pdf = 1;
eval.setValue(1.0f, 1.0f, 1.0f);
omega_in = T;
domega_in_dx = dTdx;
domega_in_dy = dTdy;
}
return Labels::TRANSMIT;
}
};
ClosureParam bsdf_refraction_params[] = {
CLOSURE_VECTOR_PARAM(RefractionClosure, m_N),
CLOSURE_FLOAT_PARAM (RefractionClosure, m_eta),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(RefractionClosure) };
CLOSURE_PREPARE(bsdf_refraction_prepare, RefractionClosure)
CCL_NAMESPACE_END

@ -0,0 +1,97 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
class TransparentClosure : public BSDFClosure {
public:
TransparentClosure() : BSDFClosure(Labels::STRAIGHT, Back) { }
void setup() {}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "transparent"; }
void print_on (std::ostream &out) const {
out << name() << " ()";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
// only one direction is possible
omega_in = -omega_out;
domega_in_dx = -domega_out_dx;
domega_in_dy = -domega_out_dy;
pdf = 1;
eval.setValue(1, 1, 1);
return Labels::TRANSMIT;
}
};
ClosureParam bsdf_transparent_params[] = {
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(TransparentClosure) };
CLOSURE_PREPARE(bsdf_transparent_prepare, TransparentClosure)
CCL_NAMESPACE_END

@ -0,0 +1,222 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
#include "util_math.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
// anisotropic ward - leaks energy at grazing angles
// see http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
class WardClosure : public BSDFClosure {
public:
Vec3 m_N;
Vec3 m_T;
float m_ax, m_ay;
WardClosure() : BSDFClosure(Labels::GLOSSY) { }
void setup()
{
m_ax = clamp(m_ax, 1e-5f, 1.0f);
m_ay = clamp(m_ay, 1e-5f, 1.0f);
}
bool mergeable (const ClosurePrimitive *other) const {
const WardClosure *comp = (const WardClosure *)other;
return m_N == comp->m_N && m_T == comp->m_T &&
m_ax == comp->m_ax && m_ay == comp->m_ay &&
BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "ward"; }
void print_on (std::ostream &out) const {
out << name() << " ((";
out << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), (";
out << m_T[0] << ", " << m_T[1] << ", " << m_T[2] << "), ";
out << m_ax << ", " << m_ay << ")";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNI > 0 && cosNO > 0) {
// get half vector and get x,y basis on the surface for anisotropy
Vec3 H = omega_in + omega_out;
H.normalize(); // normalize needed for pdf
Vec3 X, Y;
make_orthonormals(m_N, m_T, X, Y);
// eq. 4
float dotx = H.dot(X) / m_ax;
float doty = H.dot(Y) / m_ay;
float dotn = H.dot(m_N);
float exp_arg = (dotx * dotx + doty * doty) / (dotn * dotn);
float denom = (4 * (float) M_PI * m_ax * m_ay * sqrtf(cosNO * cosNI));
float exp_val = expf(-exp_arg);
float out = cosNI * exp_val / denom;
float oh = H.dot(omega_out);
denom = 4 * (float) M_PI * m_ax * m_ay * oh * dotn * dotn * dotn;
pdf = exp_val / denom;
return Color3 (out, out, out);
}
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float& pdf) const
{
return Color3 (0, 0, 0);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
float cosNO = m_N.dot(omega_out);
if (cosNO > 0) {
// get x,y basis on the surface for anisotropy
Vec3 X, Y;
make_orthonormals(m_N, m_T, X, Y);
// generate random angles for the half vector
// eq. 7 (taking care around discontinuities to keep
// output angle in the right quadrant)
// we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
// and sin(atan(x)) == x/sqrt(1+x^2)
float alphaRatio = m_ay / m_ax;
float cosPhi, sinPhi;
if (randu < 0.25f) {
float val = 4 * randu;
float tanPhi = alphaRatio * tanf((float) M_PI_2 * val);
cosPhi = 1 / sqrtf(1 + tanPhi * tanPhi);
sinPhi = tanPhi * cosPhi;
} else if (randu < 0.5) {
float val = 1 - 4 * (0.5f - randu);
float tanPhi = alphaRatio * tanf((float) M_PI_2 * val);
// phi = (float) M_PI - phi;
cosPhi = -1 / sqrtf(1 + tanPhi * tanPhi);
sinPhi = -tanPhi * cosPhi;
} else if (randu < 0.75f) {
float val = 4 * (randu - 0.5f);
float tanPhi = alphaRatio * tanf((float) M_PI_2 * val);
//phi = (float) M_PI + phi;
cosPhi = -1 / sqrtf(1 + tanPhi * tanPhi);
sinPhi = tanPhi * cosPhi;
} else {
float val = 1 - 4 * (1 - randu);
float tanPhi = alphaRatio * tanf((float) M_PI_2 * val);
// phi = 2 * (float) M_PI - phi;
cosPhi = 1 / sqrtf(1 + tanPhi * tanPhi);
sinPhi = -tanPhi * cosPhi;
}
// eq. 6
// we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
// and sin(atan(x)) == x/sqrt(1+x^2)
float thetaDenom = (cosPhi * cosPhi) / (m_ax * m_ax) + (sinPhi * sinPhi) / (m_ay * m_ay);
float tanTheta2 = -logf(1 - randv) / thetaDenom;
float cosTheta = 1 / sqrtf(1 + tanTheta2);
float sinTheta = cosTheta * sqrtf(tanTheta2);
Vec3 h; // already normalized becaused expressed from spherical coordinates
h.x = sinTheta * cosPhi;
h.y = sinTheta * sinPhi;
h.z = cosTheta;
// compute terms that are easier in local space
float dotx = h.x / m_ax;
float doty = h.y / m_ay;
float dotn = h.z;
// transform to world space
h = h.x * X + h.y * Y + h.z * m_N;
// generate the final sample
float oh = h.dot(omega_out);
omega_in.x = 2 * oh * h.x - omega_out.x;
omega_in.y = 2 * oh * h.y - omega_out.y;
omega_in.z = 2 * oh * h.z - omega_out.z;
if (Ng.dot(omega_in) > 0) {
float cosNI = m_N.dot(omega_in);
if (cosNI > 0) {
// eq. 9
float exp_arg = (dotx * dotx + doty * doty) / (dotn * dotn);
float denom = 4 * (float) M_PI * m_ax * m_ay * oh * dotn * dotn * dotn;
pdf = expf(-exp_arg) / denom;
// compiler will reuse expressions already computed
denom = (4 * (float) M_PI * m_ax * m_ay * sqrtf(cosNO * cosNI));
float power = cosNI * expf(-exp_arg) / denom;
eval.setValue(power, power, power);
domega_in_dx = (2 * m_N.dot(domega_out_dx)) * m_N - domega_out_dx;
domega_in_dy = (2 * m_N.dot(domega_out_dy)) * m_N - domega_out_dy;
/* disabled for now - gives texture filtering problems */
#if 0
// Since there is some blur to this reflection, make the
// derivatives a bit bigger. In theory this varies with the
// roughness but the exact relationship is complex and
// requires more ops than are practical.
domega_in_dx *= 10;
domega_in_dy *= 10;
#endif
}
}
}
return Labels::REFLECT;
}
};
ClosureParam bsdf_ward_params[] = {
CLOSURE_VECTOR_PARAM(WardClosure, m_N),
CLOSURE_VECTOR_PARAM(WardClosure, m_T),
CLOSURE_FLOAT_PARAM (WardClosure, m_ax),
CLOSURE_FLOAT_PARAM (WardClosure, m_ay),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(WardClosure) };
CLOSURE_PREPARE(bsdf_ward_prepare, WardClosure)
CCL_NAMESPACE_END

@ -0,0 +1,239 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
#include "util_math.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
class WestinBackscatterClosure : public BSDFClosure {
public:
Vec3 m_N;
float m_roughness;
float m_invroughness;
WestinBackscatterClosure() : BSDFClosure(Labels::GLOSSY) { }
void setup()
{
m_roughness = clamp(m_roughness, 1e-5f, 1.0f);
m_invroughness = m_roughness > 0 ? 1 / m_roughness : 0;
}
bool mergeable (const ClosurePrimitive *other) const {
const WestinBackscatterClosure *comp = (const WestinBackscatterClosure *)other;
return m_N == comp->m_N && m_roughness == comp->m_roughness &&
BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "westin_backscatter"; }
void print_on (std::ostream &out) const
{
out << name() << " (";
out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
out << m_roughness;
out << ")";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float &pdf) const
{
// pdf is implicitly 0 (no indirect sampling)
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNO > 0 && cosNI > 0) {
float cosine = omega_out.dot(omega_in);
pdf = cosine > 0 ? (m_invroughness + 1) * powf(cosine, m_invroughness) : 0;
pdf *= 0.5f * float(M_1_PI);
return Color3 (pdf, pdf, pdf);
}
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float &pdf) const
{
return Color3 (0, 0, 0);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
float cosNO = m_N.dot(omega_out);
if (cosNO > 0) {
domega_in_dx = domega_out_dx;
domega_in_dy = domega_out_dy;
Vec3 T, B;
make_orthonormals (omega_out, T, B);
float phi = 2 * (float) M_PI * randu;
float cosTheta = powf(randv, 1 / (m_invroughness + 1));
float sinTheta2 = 1 - cosTheta * cosTheta;
float sinTheta = sinTheta2 > 0 ? sqrtf(sinTheta2) : 0;
omega_in = (cosf(phi) * sinTheta) * T +
(sinf(phi) * sinTheta) * B +
( cosTheta) * omega_out;
if (Ng.dot(omega_in) > 0)
{
// common terms for pdf and eval
float cosNI = m_N.dot(omega_in);
// make sure the direction we chose is still in the right hemisphere
if (cosNI > 0)
{
pdf = 0.5f * (float) M_1_PI * powf(cosTheta, m_invroughness);
pdf = (m_invroughness + 1) * pdf;
eval.setValue(pdf, pdf, pdf);
// Since there is some blur to this reflection, make the
// derivatives a bit bigger. In theory this varies with the
// exponent but the exact relationship is complex and
// requires more ops than are practical.
domega_in_dx *= 10;
domega_in_dy *= 10;
}
}
}
return Labels::REFLECT;
}
};
class WestinSheenClosure : public BSDFClosure {
public:
Vec3 m_N;
float m_edginess;
// float m_normalization;
WestinSheenClosure() : BSDFClosure(Labels::DIFFUSE) { }
void setup() {};
bool mergeable (const ClosurePrimitive *other) const {
const WestinSheenClosure *comp = (const WestinSheenClosure *)other;
return m_N == comp->m_N && m_edginess == comp->m_edginess &&
BSDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "westin_sheen"; }
void print_on (std::ostream &out) const
{
out << name() << " (";
out << "(" << m_N[0] << ", " << m_N[1] << ", " << m_N[2] << "), ";
out << m_edginess;
out << ")";
}
float albedo (const Vec3 &omega_out) const
{
return 1.0f;
}
Color3 eval_reflect (const Vec3 &omega_out, const Vec3 &omega_in, float &pdf) const
{
// pdf is implicitly 0 (no indirect sampling)
float cosNO = m_N.dot(omega_out);
float cosNI = m_N.dot(omega_in);
if (cosNO > 0 && cosNI > 0) {
float sinNO2 = 1 - cosNO * cosNO;
pdf = cosNI * float(M_1_PI);
float westin = sinNO2 > 0 ? powf(sinNO2, 0.5f * m_edginess) * pdf : 0;
return Color3 (westin, westin, westin);
}
return Color3 (0, 0, 0);
}
Color3 eval_transmit (const Vec3 &omega_out, const Vec3 &omega_in, float &pdf) const
{
return Color3 (0, 0, 0);
}
ustring sample (const Vec3 &Ng,
const Vec3 &omega_out, const Vec3 &domega_out_dx, const Vec3 &domega_out_dy,
float randu, float randv,
Vec3 &omega_in, Vec3 &domega_in_dx, Vec3 &domega_in_dy,
float &pdf, Color3 &eval) const
{
// we are viewing the surface from the right side - send a ray out with cosine
// distribution over the hemisphere
sample_cos_hemisphere (m_N, omega_out, randu, randv, omega_in, pdf);
if (Ng.dot(omega_in) > 0) {
// TODO: account for sheen when sampling
float cosNO = m_N.dot(omega_out);
float sinNO2 = 1 - cosNO * cosNO;
float westin = sinNO2 > 0 ? powf(sinNO2, 0.5f * m_edginess) * pdf : 0;
eval.setValue(westin, westin, westin);
// TODO: find a better approximation for the diffuse bounce
domega_in_dx = (2 * m_N.dot(domega_out_dx)) * m_N - domega_out_dx;
domega_in_dy = (2 * m_N.dot(domega_out_dy)) * m_N - domega_out_dy;
domega_in_dx *= 125;
domega_in_dy *= 125;
} else
pdf = 0;
return Labels::REFLECT;
}
};
ClosureParam bsdf_westin_backscatter_params[] = {
CLOSURE_VECTOR_PARAM(WestinBackscatterClosure, m_N),
CLOSURE_FLOAT_PARAM (WestinBackscatterClosure, m_roughness),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(WestinBackscatterClosure) };
ClosureParam bsdf_westin_sheen_params[] = {
CLOSURE_VECTOR_PARAM(WestinSheenClosure, m_N),
CLOSURE_FLOAT_PARAM (WestinSheenClosure, m_edginess),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(WestinSheenClosure) };
CLOSURE_PREPARE(bsdf_westin_backscatter_prepare, WestinBackscatterClosure)
CLOSURE_PREPARE(bsdf_westin_sheen_prepare, WestinSheenClosure)
CCL_NAMESPACE_END

@ -0,0 +1,105 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
class BSSRDFCubicClosure : public BSSRDFClosure {
public:
Color3 m_radius;
Color3 m_scale;
float m_max_radius;
template <typename T>
static inline T pow3 (const T &x) { return x * x * x; }
template <typename T>
static inline T pow5 (const T &x) { T x2 = x * x; return x2 * x2 * x; }
BSSRDFCubicClosure() { }
void setup()
{
// pre-compute some terms
m_max_radius = 0;
for (int i = 0; i < 3; i++) {
m_scale[i] = m_radius[i] > 0 ? 4 / pow5 (m_radius[i]) : 0;
m_max_radius = std::max (m_max_radius, m_radius[i]);
}
}
bool mergeable (const ClosurePrimitive *other) const {
const BSSRDFCubicClosure *comp = (const BSSRDFCubicClosure *)other;
return m_radius == comp->m_radius && BSSRDFClosure::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "bssrdf_cubic"; }
void print_on (std::ostream &out) const
{
out << name() << " ((" << m_radius[0] << ", " << m_radius[1] << ", " << m_radius[2] << "), ("
<< m_scale[0] << ", " << m_scale[1] << ", " << m_scale[2] << "))";
}
Color3 eval (float r) const
{
return Color3 ((r < m_radius.x) ? pow3 (m_radius.x - r) * m_scale.x : 0,
(r < m_radius.y) ? pow3 (m_radius.y - r) * m_scale.y : 0,
(r < m_radius.z) ? pow3 (m_radius.z - r) * m_scale.z : 0);
}
float max_radius() const
{
return m_max_radius;
}
};
ClosureParam closure_bssrdf_cubic_params[] = {
CLOSURE_COLOR_PARAM (BSSRDFCubicClosure, m_radius),
CLOSURE_STRING_KEYPARAM ("label"),
CLOSURE_FINISH_PARAM(BSSRDFCubicClosure) };
CLOSURE_PREPARE(closure_bssrdf_cubic_prepare, BSSRDFCubicClosure)
CCL_NAMESPACE_END

@ -0,0 +1,80 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
/// Debug closure
///
/// This is going to be used for mask AOV's and similar
/// purposes. A tag (string) is always associated with
/// this closure, that "selects: the channel where the
/// weight should be sent.
class DebugClosure : public ClosurePrimitive {
public:
ustring m_tag;
DebugClosure () : ClosurePrimitive (Debug) { }
bool mergeable (const ClosurePrimitive *other) const {
const DebugClosure *comp = (const DebugClosure *)other;
return m_tag == comp->m_tag &&
ClosurePrimitive::mergeable(other);
}
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "debug"; }
void print_on (std::ostream &out) const {
out << name() << " (\"" << m_tag.c_str() << "\")";
}
};
ClosureParam closure_debug_params[] = {
CLOSURE_STRING_PARAM(DebugClosure, m_tag),
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(DebugClosure) };
CLOSURE_PREPARE(closure_debug_prepare, DebugClosure)
CCL_NAMESPACE_END

@ -0,0 +1,107 @@
/*
* Adapted from Open Shading Language with this license:
*
* Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
* All Rights Reserved.
*
* Modifications Copyright 2011, Blender Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Sony Pictures Imageworks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <OpenImageIO/fmath.h>
#include <OSL/genclosure.h>
#include "osl_closures.h"
CCL_NAMESPACE_BEGIN
using namespace OSL;
/// Variable cone emissive closure
///
/// This primitive emits in a cone having a configurable
/// penumbra area where the light decays to 0 reaching the
/// outer_angle limit. It can also behave as a lambertian emitter
/// if the provided angles are PI/2, which is the default
///
class GenericEmissiveClosure : public EmissiveClosure {
public:
GenericEmissiveClosure() { }
void setup() { }
size_t memsize () const { return sizeof(*this); }
const char *name () const { return "emission"; }
void print_on (std::ostream &out) const {
out << name() << "()";
}
Color3 eval (const Vec3 &Ng, const Vec3 &omega_out) const
{
float cosNO = fabsf(Ng.dot(omega_out));
float res = cosNO > 0 ? 1.0f / float(M_PI) : 0.0f;
return Color3(res, res, res);
}
void sample (const Vec3 &Ng, float randu, float randv,
Vec3 &omega_out, float &pdf) const
{
// We don't do anything sophisticated here for the step
// We just sample the whole cone uniformly to the cosine
Vec3 T, B;
make_orthonormals(Ng, T, B);
float phi = 2 * (float) M_PI * randu;
float cosTheta = sqrtf(1.0f - 1.0f * randv);
float sinTheta = sqrtf(1.0f - cosTheta * cosTheta);
omega_out = (cosf(phi) * sinTheta) * T +
(sinf(phi) * sinTheta) * B +
cosTheta * Ng;
pdf = 1.0f / float(M_PI);
}
/// Return the probability distribution function in the direction omega_out,
/// given the parameters and the light's surface normal. This MUST match
/// the PDF computed by sample().
float pdf (const Vec3 &Ng,
const Vec3 &omega_out) const
{
float cosNO = Ng.dot(omega_out);
return cosNO > 0 ? 1.0f / float(M_PI) : 0.0f;
}
};
ClosureParam closure_emission_params[] = {
CLOSURE_STRING_KEYPARAM("label"),
CLOSURE_FINISH_PARAM(GenericEmissiveClosure) };
CLOSURE_PREPARE(closure_emission_prepare, GenericEmissiveClosure)
CCL_NAMESPACE_END

@ -0,0 +1,69 @@
# OSL node shaders
SET(osl_sources
node_add_closure.osl
node_attribute.osl
node_background.osl
node_blend_texture.osl
node_bump.osl
node_clouds_texture.osl
node_convert_from_color.osl
node_convert_from_float.osl
node_convert_from_normal.osl
node_convert_from_point.osl
node_convert_from_vector.osl
node_diffuse_bsdf.osl
node_distorted_noise_texture.osl
node_emission.osl
node_environment_texture.osl
node_fresnel.osl
node_geometry.osl
node_glass_bsdf.osl
node_glossy_bsdf.osl
node_image_texture.osl
node_light_path.osl
node_magic_texture.osl
node_mapping.osl
node_marble_texture.osl
node_math.osl
node_mix.osl
node_mix_closure.osl
node_musgrave_texture.osl
node_noise_texture.osl
node_output_displacement.osl
node_output_surface.osl
node_output_volume.osl
node_sky_texture.osl
node_stucci_texture.osl
node_texture_coordinate.osl
node_translucent_bsdf.osl
node_transparent_bsdf.osl
node_value.osl
node_vector_math.osl
node_velvet_bsdf.osl
node_voronoi_texture.osl
node_ward_bsdf.osl
node_wood_texture.osl)
SET(osl_headers
node_texture.h
stdosl.h)
SET(oso_sources)
FOREACH(_file ${osl_sources})
SET(osl_file ${CMAKE_CURRENT_SOURCE_DIR}/${_file})
STRING(REPLACE ".osl" ".oso" oso_file ${osl_file})
STRING(REPLACE ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} oso_file ${oso_file})
ADD_CUSTOM_COMMAND(
OUTPUT ${oso_file}
COMMAND ${OSL_COMPILER} -O2 ${osl_file}
DEPENDS ${osl_file} ${osl_headers})
LIST(APPEND oso_sources ${oso_file})
ENDFOREACH()
ADD_CUSTOM_TARGET(shader ALL DEPENDS ${oso_sources} ${osl_headers})
INSTALL(FILES ${oso_sources} DESTINATION ${INSTALL_PATH}/cycles/shader)

@ -0,0 +1,28 @@
/*
* 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 "stdosl.h"
shader node_add_closure(
closure color Closure1 = background(),
closure color Closure2 = background(),
output closure color Closure = background())
{
Closure = Closure1 + Closure2;
}

@ -0,0 +1,43 @@
/*
* 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 "stdosl.h"
shader node_attribute(
string bump_offset = "center",
string name = "",
output point Vector = point(0.0, 0.0, 0.0),
output color Color = color(0.0, 0.0, 0.0),
output float Fac = 0.0)
{
getattribute(name, Color);
Vector = point(Color);
getattribute(name, Fac);
if(bump_offset == "dx") {
Color += Dx(Color);
Vector += Dx(Vector);
Fac += Dx(Fac);
}
else if(bump_offset == "dy") {
Color += Dy(Color);
Vector += Dy(Vector);
Fac += Dy(Fac);
}
}

@ -0,0 +1,28 @@
/*
* 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 "stdosl.h"
shader node_background(
color Color = color(0.8, 0.8, 0.8),
float Strength = 1.0,
output closure color Background = background())
{
Background = Color*Strength*background();
}

@ -0,0 +1,78 @@
/*
* 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 "stdosl.h"
#include "node_texture.h"
/* Blend */
float blend(point p, string progression, string axis)
{
float x, y;
if(axis == "Vertical") {
x= p[1];
y= p[0];
}
else {
x= p[0];
y= p[1];
}
float result = 0.0;
if(progression == "Linear") {
result = (1.0 + x)/2.0;
}
else if(progression == "Quadratic") {
float r = max((1.0 + x)/2.0, 0.0);
result = r*r;
}
else if(progression == "Easing") {
float r = min(max((1.0 + x)/2.0, 0.0), 1.0);
float t = r*r;
result = (3.0*t - 2.0*t*r);
}
else if(progression == "Diagonal") {
result = (2.0 + x + y)/4.0;
}
else if(progression == "Radial") {
result = atan2(y, x)/(2*M_PI) + 0.5;
}
else {
float r = max(1.0 - sqrt(x*x + y*y + p[2]*p[2]), 0.0);
if(progression == "Quadratic Sphere")
result = r*r;
else if(progression == "Spherical")
result = r;
}
return result;
}
shader node_blend_texture(
string Progression = "Linear",
string Axis = "Horizontal",
point Vector = P,
output float Fac = 0.0)
{
Fac = blend(Vector, Progression, Axis);
}

Some files were not shown because too many files have changed in this diff Show More