/* * Copyright 2011-2013 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "render/image.h" #include "render/integrator.h" #include "render/nodes.h" #include "render/scene.h" #include "render/svm.h" #include "kernel/svm/svm_color_util.h" #include "kernel/svm/svm_ramp_util.h" #include "kernel/svm/svm_math_util.h" #include "render/osl.h" #include "render/constant_fold.h" #include "util/util_sky_model.h" #include "util/util_foreach.h" #include "util/util_logging.h" #include "util/util_transform.h" CCL_NAMESPACE_BEGIN /* Texture Mapping */ #define TEXTURE_MAPPING_DEFINE(TextureNode) \ SOCKET_POINT(tex_mapping.translation, "Translation", make_float3(0.0f, 0.0f, 0.0f)); \ SOCKET_VECTOR(tex_mapping.rotation, "Rotation", make_float3(0.0f, 0.0f, 0.0f)); \ SOCKET_VECTOR(tex_mapping.scale, "Scale", make_float3(1.0f, 1.0f, 1.0f)); \ \ SOCKET_VECTOR(tex_mapping.min, "Min", make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX)); \ SOCKET_VECTOR(tex_mapping.max, "Max", make_float3(FLT_MAX, FLT_MAX, FLT_MAX)); \ SOCKET_BOOLEAN(tex_mapping.use_minmax, "Use Min Max", false); \ \ static NodeEnum mapping_axis_enum; \ mapping_axis_enum.insert("none", TextureMapping::NONE); \ mapping_axis_enum.insert("x", TextureMapping::X); \ mapping_axis_enum.insert("y", TextureMapping::Y); \ mapping_axis_enum.insert("z", TextureMapping::Z); \ SOCKET_ENUM(tex_mapping.x_mapping, "x_mapping", mapping_axis_enum, TextureMapping::X); \ SOCKET_ENUM(tex_mapping.y_mapping, "y_mapping", mapping_axis_enum, TextureMapping::Y); \ SOCKET_ENUM(tex_mapping.z_mapping, "z_mapping", mapping_axis_enum, TextureMapping::Z); \ \ static NodeEnum mapping_type_enum; \ mapping_type_enum.insert("point", TextureMapping::POINT); \ mapping_type_enum.insert("texture", TextureMapping::TEXTURE); \ mapping_type_enum.insert("vector", TextureMapping::VECTOR); \ mapping_type_enum.insert("normal", TextureMapping::NORMAL); \ SOCKET_ENUM(tex_mapping.type, "Type", mapping_type_enum, TextureMapping::TEXTURE); \ \ static NodeEnum mapping_projection_enum; \ mapping_projection_enum.insert("flat", TextureMapping::FLAT); \ mapping_projection_enum.insert("cube", TextureMapping::CUBE); \ mapping_projection_enum.insert("tube", TextureMapping::TUBE); \ mapping_projection_enum.insert("sphere", TextureMapping::SPHERE); \ SOCKET_ENUM(tex_mapping.projection, "Projection", mapping_projection_enum, TextureMapping::FLAT); TextureMapping::TextureMapping() { } Transform TextureMapping::compute_transform() { Transform mmat = transform_scale(make_float3(0.0f, 0.0f, 0.0f)); if(x_mapping != NONE) mmat[0][x_mapping-1] = 1.0f; if(y_mapping != NONE) mmat[1][y_mapping-1] = 1.0f; if(z_mapping != NONE) mmat[2][z_mapping-1] = 1.0f; float3 scale_clamped = scale; if(type == TEXTURE || type == NORMAL) { /* keep matrix invertible */ if(fabsf(scale.x) < 1e-5f) scale_clamped.x = signf(scale.x)*1e-5f; if(fabsf(scale.y) < 1e-5f) scale_clamped.y = signf(scale.y)*1e-5f; if(fabsf(scale.z) < 1e-5f) scale_clamped.z = signf(scale.z)*1e-5f; } Transform smat = transform_scale(scale_clamped); Transform rmat = transform_euler(rotation); Transform tmat = transform_translate(translation); Transform mat; switch(type) { case TEXTURE: /* inverse transform on texture coordinate gives * forward transform on texture */ mat = tmat*rmat*smat; mat = transform_inverse(mat); break; case POINT: /* full transform */ mat = tmat*rmat*smat; break; case VECTOR: /* no translation for vectors */ mat = rmat*smat; break; case NORMAL: /* no translation for normals, and inverse transpose */ mat = rmat*smat; mat = transform_inverse(mat); mat = transform_transpose(mat); break; } /* projection last */ mat = mat*mmat; return mat; } bool TextureMapping::skip() { if(translation != make_float3(0.0f, 0.0f, 0.0f)) return false; if(rotation != make_float3(0.0f, 0.0f, 0.0f)) return false; if(scale != make_float3(1.0f, 1.0f, 1.0f)) return false; if(x_mapping != X || y_mapping != Y || z_mapping != Z) return false; if(use_minmax) return false; return true; } void TextureMapping::compile(SVMCompiler& compiler, int offset_in, int offset_out) { compiler.add_node(NODE_MAPPING, offset_in, offset_out); Transform tfm = compute_transform(); compiler.add_node(tfm.x); compiler.add_node(tfm.y); compiler.add_node(tfm.z); compiler.add_node(tfm.w); if(use_minmax) { compiler.add_node(NODE_MIN_MAX, offset_out, offset_out); compiler.add_node(float3_to_float4(min)); compiler.add_node(float3_to_float4(max)); } if(type == NORMAL) { compiler.add_node(NODE_VECTOR_MATH, NODE_VECTOR_MATH_NORMALIZE, offset_out, offset_out); compiler.add_node(NODE_VECTOR_MATH, SVM_STACK_INVALID, offset_out); } } /* Convenience function for texture nodes, allocating stack space to output * a modified vector and returning its offset */ int TextureMapping::compile_begin(SVMCompiler& compiler, ShaderInput *vector_in) { if(!skip()) { int offset_in = compiler.stack_assign(vector_in); int offset_out = compiler.stack_find_offset(SocketType::VECTOR); compile(compiler, offset_in, offset_out); return offset_out; } return compiler.stack_assign(vector_in); } void TextureMapping::compile_end(SVMCompiler& compiler, ShaderInput *vector_in, int vector_offset) { if(!skip()) { compiler.stack_clear_offset(vector_in->type(), vector_offset); } } void TextureMapping::compile(OSLCompiler &compiler) { if(!skip()) { Transform tfm = transform_transpose(compute_transform()); compiler.parameter("mapping", tfm); compiler.parameter("use_mapping", 1); } } /* Image Texture */ NODE_DEFINE(ImageTextureNode) { NodeType* type = NodeType::add("image_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(ImageTextureNode); SOCKET_STRING(filename, "Filename", ustring("")); static NodeEnum color_space_enum; color_space_enum.insert("none", NODE_COLOR_SPACE_NONE); color_space_enum.insert("color", NODE_COLOR_SPACE_COLOR); SOCKET_ENUM(color_space, "Color Space", color_space_enum, NODE_COLOR_SPACE_COLOR); SOCKET_BOOLEAN(use_alpha, "Use Alpha", true); static NodeEnum interpolation_enum; interpolation_enum.insert("closest", INTERPOLATION_CLOSEST); interpolation_enum.insert("linear", INTERPOLATION_LINEAR); interpolation_enum.insert("cubic", INTERPOLATION_CUBIC); interpolation_enum.insert("smart", INTERPOLATION_SMART); SOCKET_ENUM(interpolation, "Interpolation", interpolation_enum, INTERPOLATION_LINEAR); static NodeEnum extension_enum; extension_enum.insert("periodic", EXTENSION_REPEAT); extension_enum.insert("clamp", EXTENSION_EXTEND); extension_enum.insert("black", EXTENSION_CLIP); SOCKET_ENUM(extension, "Extension", extension_enum, EXTENSION_REPEAT); static NodeEnum projection_enum; projection_enum.insert("flat", NODE_IMAGE_PROJ_FLAT); projection_enum.insert("box", NODE_IMAGE_PROJ_BOX); projection_enum.insert("sphere", NODE_IMAGE_PROJ_SPHERE); projection_enum.insert("tube", NODE_IMAGE_PROJ_TUBE); SOCKET_ENUM(projection, "Projection", projection_enum, NODE_IMAGE_PROJ_FLAT); SOCKET_FLOAT(projection_blend, "Projection Blend", 0.0f); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_UV); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(node_type) { image_manager = NULL; slot = -1; is_float = -1; is_linear = false; builtin_data = NULL; animated = false; } ImageTextureNode::~ImageTextureNode() { if(image_manager) { image_manager->remove_image(filename.string(), builtin_data, interpolation, extension, use_alpha); } } ShaderNode *ImageTextureNode::clone() const { ImageTextureNode *node = new ImageTextureNode(*this); node->image_manager = NULL; node->slot = -1; node->is_float = -1; node->is_linear = false; return node; } void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) { #ifdef WITH_PTEX /* todo: avoid loading other texture coordinates when using ptex, * and hide texture coordinate socket in the UI */ if(shader->has_surface && string_endswith(filename, ".ptx")) { /* ptex */ attributes->add(ATTR_STD_PTEX_FACE_ID); attributes->add(ATTR_STD_PTEX_UV); } #endif ShaderNode::attributes(shader, attributes); } void ImageTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); image_manager = compiler.image_manager; if(is_float == -1) { bool is_float_bool; slot = image_manager->add_image(filename.string(), builtin_data, animated, 0, is_float_bool, is_linear, interpolation, extension, use_alpha); is_float = (int)is_float_bool; } if(slot != -1) { int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); if(projection != NODE_IMAGE_PROJ_BOX) { compiler.add_node(NODE_TEX_IMAGE, slot, compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), srgb), projection); } else { compiler.add_node(NODE_TEX_IMAGE_BOX, slot, compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), srgb), __float_as_int(projection_blend)); } tex_mapping.compile_end(compiler, vector_in, vector_offset); } else { /* image not found */ if(!color_out->links.empty()) { compiler.add_node(NODE_VALUE_V, compiler.stack_assign(color_out)); compiler.add_node(NODE_VALUE_V, make_float3(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B)); } if(!alpha_out->links.empty()) compiler.add_node(NODE_VALUE_F, __float_as_int(TEX_IMAGE_MISSING_A), compiler.stack_assign(alpha_out)); } } void ImageTextureNode::compile(OSLCompiler& compiler) { ShaderOutput *alpha_out = output("Alpha"); tex_mapping.compile(compiler); image_manager = compiler.image_manager; if(is_float == -1) { if(builtin_data == NULL) { ImageDataType type; bool builtin_free_cache; type = image_manager->get_image_metadata(filename.string(), NULL, is_linear, builtin_free_cache); if(type == IMAGE_DATA_TYPE_FLOAT || type == IMAGE_DATA_TYPE_FLOAT4) is_float = 1; } else { bool is_float_bool; slot = image_manager->add_image(filename.string(), builtin_data, animated, 0, is_float_bool, is_linear, interpolation, extension, use_alpha); is_float = (int)is_float_bool; } } if(slot == -1) { compiler.parameter(this, "filename"); } else { /* TODO(sergey): It's not so simple to pass custom attribute * to the texture() function in order to make builtin images * support more clear. So we use special file name which is * "@" and check whether file name matches this * mask in the OSLRenderServices::texture(). */ compiler.parameter("filename", string_printf("@%d", slot).c_str()); } if(is_linear || color_space != NODE_COLOR_SPACE_COLOR) compiler.parameter("color_space", "linear"); else compiler.parameter("color_space", "sRGB"); compiler.parameter(this, "projection"); compiler.parameter(this, "projection_blend"); compiler.parameter("is_float", is_float); compiler.parameter("use_alpha", !alpha_out->links.empty()); compiler.parameter(this, "interpolation"); compiler.parameter(this, "extension"); compiler.add(this, "node_image_texture"); } /* Environment Texture */ NODE_DEFINE(EnvironmentTextureNode) { NodeType* type = NodeType::add("environment_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(EnvironmentTextureNode); SOCKET_STRING(filename, "Filename", ustring("")); static NodeEnum color_space_enum; color_space_enum.insert("none", NODE_COLOR_SPACE_NONE); color_space_enum.insert("color", NODE_COLOR_SPACE_COLOR); SOCKET_ENUM(color_space, "Color Space", color_space_enum, NODE_COLOR_SPACE_COLOR); SOCKET_BOOLEAN(use_alpha, "Use Alpha", true); static NodeEnum interpolation_enum; interpolation_enum.insert("closest", INTERPOLATION_CLOSEST); interpolation_enum.insert("linear", INTERPOLATION_LINEAR); interpolation_enum.insert("cubic", INTERPOLATION_CUBIC); interpolation_enum.insert("smart", INTERPOLATION_SMART); SOCKET_ENUM(interpolation, "Interpolation", interpolation_enum, INTERPOLATION_LINEAR); static NodeEnum projection_enum; projection_enum.insert("equirectangular", NODE_ENVIRONMENT_EQUIRECTANGULAR); projection_enum.insert("mirror_ball", NODE_ENVIRONMENT_MIRROR_BALL); SOCKET_ENUM(projection, "Projection", projection_enum, NODE_ENVIRONMENT_EQUIRECTANGULAR); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_POSITION); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } EnvironmentTextureNode::EnvironmentTextureNode() : ImageSlotTextureNode(node_type) { image_manager = NULL; slot = -1; is_float = -1; is_linear = false; builtin_data = NULL; animated = false; } EnvironmentTextureNode::~EnvironmentTextureNode() { if(image_manager) { image_manager->remove_image(filename.string(), builtin_data, interpolation, EXTENSION_REPEAT, use_alpha); } } ShaderNode *EnvironmentTextureNode::clone() const { EnvironmentTextureNode *node = new EnvironmentTextureNode(*this); node->image_manager = NULL; node->slot = -1; node->is_float = -1; node->is_linear = false; return node; } void EnvironmentTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) { #ifdef WITH_PTEX if(shader->has_surface && string_endswith(filename, ".ptx")) { /* ptex */ attributes->add(ATTR_STD_PTEX_FACE_ID); attributes->add(ATTR_STD_PTEX_UV); } #endif ShaderNode::attributes(shader, attributes); } void EnvironmentTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); image_manager = compiler.image_manager; if(slot == -1) { bool is_float_bool; slot = image_manager->add_image(filename.string(), builtin_data, animated, 0, is_float_bool, is_linear, interpolation, EXTENSION_REPEAT, use_alpha); is_float = (int)is_float_bool; } if(slot != -1) { int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_ENVIRONMENT, slot, compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out), srgb), projection); tex_mapping.compile_end(compiler, vector_in, vector_offset); } else { /* image not found */ if(!color_out->links.empty()) { compiler.add_node(NODE_VALUE_V, compiler.stack_assign(color_out)); compiler.add_node(NODE_VALUE_V, make_float3(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B)); } if(!alpha_out->links.empty()) compiler.add_node(NODE_VALUE_F, __float_as_int(TEX_IMAGE_MISSING_A), compiler.stack_assign(alpha_out)); } } void EnvironmentTextureNode::compile(OSLCompiler& compiler) { ShaderOutput *alpha_out = output("Alpha"); tex_mapping.compile(compiler); /* See comments in ImageTextureNode::compile about support * of builtin images. */ image_manager = compiler.image_manager; if(is_float == -1) { if(builtin_data == NULL) { ImageDataType type; bool builtin_free_cache; type = image_manager->get_image_metadata(filename.string(), NULL, is_linear, builtin_free_cache); if(type == IMAGE_DATA_TYPE_FLOAT || type == IMAGE_DATA_TYPE_FLOAT4) is_float = 1; } else { bool is_float_bool; slot = image_manager->add_image(filename.string(), builtin_data, animated, 0, is_float_bool, is_linear, interpolation, EXTENSION_REPEAT, use_alpha); is_float = (int)is_float_bool; } } if(slot == -1) { compiler.parameter(this, "filename"); } else { compiler.parameter("filename", string_printf("@%d", slot).c_str()); } compiler.parameter(this, "projection"); if(is_linear || color_space != NODE_COLOR_SPACE_COLOR) compiler.parameter("color_space", "linear"); else compiler.parameter("color_space", "sRGB"); compiler.parameter(this, "interpolation"); compiler.parameter("is_float", is_float); compiler.parameter("use_alpha", !alpha_out->links.empty()); compiler.add(this, "node_environment_texture"); } /* Sky Texture */ static float2 sky_spherical_coordinates(float3 dir) { return make_float2(acosf(dir.z), atan2f(dir.x, dir.y)); } typedef struct SunSky { /* sun direction in spherical and cartesian */ float theta, phi; /* Parameter */ float radiance_x, radiance_y, radiance_z; float config_x[9], config_y[9], config_z[9]; } SunSky; /* Preetham model */ static float sky_perez_function(float lam[6], float theta, float gamma) { return (1.0f + lam[0]*expf(lam[1]/cosf(theta))) * (1.0f + lam[2]*expf(lam[3]*gamma) + lam[4]*cosf(gamma)*cosf(gamma)); } static void sky_texture_precompute_old(SunSky *sunsky, float3 dir, float turbidity) { /* * We re-use the SunSky struct of the new model, to avoid extra variables * zenith_Y/x/y is now radiance_x/y/z * perez_Y/x/y is now config_x/y/z */ float2 spherical = sky_spherical_coordinates(dir); float theta = spherical.x; float phi = spherical.y; sunsky->theta = theta; sunsky->phi = phi; float theta2 = theta*theta; float theta3 = theta2*theta; float T = turbidity; float T2 = T * T; float chi = (4.0f / 9.0f - T / 120.0f) * (M_PI_F - 2.0f * theta); sunsky->radiance_x = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f; sunsky->radiance_x *= 0.06f; sunsky->radiance_y = (0.00166f * theta3 - 0.00375f * theta2 + 0.00209f * theta) * T2 + (-0.02903f * theta3 + 0.06377f * theta2 - 0.03202f * theta + 0.00394f) * T + (0.11693f * theta3 - 0.21196f * theta2 + 0.06052f * theta + 0.25886f); sunsky->radiance_z = (0.00275f * theta3 - 0.00610f * theta2 + 0.00317f * theta) * T2 + (-0.04214f * theta3 + 0.08970f * theta2 - 0.04153f * theta + 0.00516f) * T + (0.15346f * theta3 - 0.26756f * theta2 + 0.06670f * theta + 0.26688f); sunsky->config_x[0] = (0.1787f * T - 1.4630f); sunsky->config_x[1] = (-0.3554f * T + 0.4275f); sunsky->config_x[2] = (-0.0227f * T + 5.3251f); sunsky->config_x[3] = (0.1206f * T - 2.5771f); sunsky->config_x[4] = (-0.0670f * T + 0.3703f); sunsky->config_y[0] = (-0.0193f * T - 0.2592f); sunsky->config_y[1] = (-0.0665f * T + 0.0008f); sunsky->config_y[2] = (-0.0004f * T + 0.2125f); sunsky->config_y[3] = (-0.0641f * T - 0.8989f); sunsky->config_y[4] = (-0.0033f * T + 0.0452f); sunsky->config_z[0] = (-0.0167f * T - 0.2608f); sunsky->config_z[1] = (-0.0950f * T + 0.0092f); sunsky->config_z[2] = (-0.0079f * T + 0.2102f); sunsky->config_z[3] = (-0.0441f * T - 1.6537f); sunsky->config_z[4] = (-0.0109f * T + 0.0529f); /* unused for old sky model */ for(int i = 5; i < 9; i++) { sunsky->config_x[i] = 0.0f; sunsky->config_y[i] = 0.0f; sunsky->config_z[i] = 0.0f; } sunsky->radiance_x /= sky_perez_function(sunsky->config_x, 0, theta); sunsky->radiance_y /= sky_perez_function(sunsky->config_y, 0, theta); sunsky->radiance_z /= sky_perez_function(sunsky->config_z, 0, theta); } /* Hosek / Wilkie */ static void sky_texture_precompute_new(SunSky *sunsky, float3 dir, float turbidity, float ground_albedo) { /* Calculate Sun Direction and save coordinates */ float2 spherical = sky_spherical_coordinates(dir); float theta = spherical.x; float phi = spherical.y; /* Clamp Turbidity */ turbidity = clamp(turbidity, 0.0f, 10.0f); /* Clamp to Horizon */ theta = clamp(theta, 0.0f, M_PI_2_F); sunsky->theta = theta; sunsky->phi = phi; float solarElevation = M_PI_2_F - theta; /* Initialize Sky Model */ ArHosekSkyModelState *sky_state; sky_state = arhosek_xyz_skymodelstate_alloc_init((double)turbidity, (double)ground_albedo, (double)solarElevation); /* Copy values from sky_state to SunSky */ for(int i = 0; i < 9; ++i) { sunsky->config_x[i] = (float)sky_state->configs[0][i]; sunsky->config_y[i] = (float)sky_state->configs[1][i]; sunsky->config_z[i] = (float)sky_state->configs[2][i]; } sunsky->radiance_x = (float)sky_state->radiances[0]; sunsky->radiance_y = (float)sky_state->radiances[1]; sunsky->radiance_z = (float)sky_state->radiances[2]; /* Free sky_state */ arhosekskymodelstate_free(sky_state); } NODE_DEFINE(SkyTextureNode) { NodeType* type = NodeType::add("sky_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(SkyTextureNode); static NodeEnum type_enum; type_enum.insert("preetham", NODE_SKY_OLD); type_enum.insert("hosek_wilkie", NODE_SKY_NEW); SOCKET_ENUM(type, "Type", type_enum, NODE_SKY_NEW); SOCKET_VECTOR(sun_direction, "Sun Direction", make_float3(0.0f, 0.0f, 1.0f)); SOCKET_FLOAT(turbidity, "Turbidity", 2.2f); SOCKET_FLOAT(ground_albedo, "Ground Albedo", 0.3f); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); return type; } SkyTextureNode::SkyTextureNode() : TextureNode(node_type) { } void SkyTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); SunSky sunsky; if(type == NODE_SKY_OLD) sky_texture_precompute_old(&sunsky, sun_direction, turbidity); else if(type == NODE_SKY_NEW) sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo); else assert(false); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.stack_assign(color_out); compiler.add_node(NODE_TEX_SKY, vector_offset, compiler.stack_assign(color_out), type); compiler.add_node(__float_as_uint(sunsky.phi), __float_as_uint(sunsky.theta), __float_as_uint(sunsky.radiance_x), __float_as_uint(sunsky.radiance_y)); compiler.add_node(__float_as_uint(sunsky.radiance_z), __float_as_uint(sunsky.config_x[0]), __float_as_uint(sunsky.config_x[1]), __float_as_uint(sunsky.config_x[2])); compiler.add_node(__float_as_uint(sunsky.config_x[3]), __float_as_uint(sunsky.config_x[4]), __float_as_uint(sunsky.config_x[5]), __float_as_uint(sunsky.config_x[6])); compiler.add_node(__float_as_uint(sunsky.config_x[7]), __float_as_uint(sunsky.config_x[8]), __float_as_uint(sunsky.config_y[0]), __float_as_uint(sunsky.config_y[1])); compiler.add_node(__float_as_uint(sunsky.config_y[2]), __float_as_uint(sunsky.config_y[3]), __float_as_uint(sunsky.config_y[4]), __float_as_uint(sunsky.config_y[5])); compiler.add_node(__float_as_uint(sunsky.config_y[6]), __float_as_uint(sunsky.config_y[7]), __float_as_uint(sunsky.config_y[8]), __float_as_uint(sunsky.config_z[0])); compiler.add_node(__float_as_uint(sunsky.config_z[1]), __float_as_uint(sunsky.config_z[2]), __float_as_uint(sunsky.config_z[3]), __float_as_uint(sunsky.config_z[4])); compiler.add_node(__float_as_uint(sunsky.config_z[5]), __float_as_uint(sunsky.config_z[6]), __float_as_uint(sunsky.config_z[7]), __float_as_uint(sunsky.config_z[8])); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void SkyTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); SunSky sunsky; if(type == NODE_SKY_OLD) sky_texture_precompute_old(&sunsky, sun_direction, turbidity); else if(type == NODE_SKY_NEW) sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo); else assert(false); compiler.parameter(this, "type"); compiler.parameter("theta", sunsky.theta); compiler.parameter("phi", sunsky.phi); compiler.parameter_color("radiance", make_float3(sunsky.radiance_x, sunsky.radiance_y, sunsky.radiance_z)); compiler.parameter_array("config_x", sunsky.config_x, 9); compiler.parameter_array("config_y", sunsky.config_y, 9); compiler.parameter_array("config_z", sunsky.config_z, 9); compiler.add(this, "node_sky_texture"); } /* Gradient Texture */ NODE_DEFINE(GradientTextureNode) { NodeType* type = NodeType::add("gradient_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(GradientTextureNode); static NodeEnum type_enum; type_enum.insert("linear", NODE_BLEND_LINEAR); type_enum.insert("quadratic", NODE_BLEND_QUADRATIC); type_enum.insert("easing", NODE_BLEND_EASING); type_enum.insert("diagonal", NODE_BLEND_DIAGONAL); type_enum.insert("radial", NODE_BLEND_RADIAL); type_enum.insert("quadratic_sphere", NODE_BLEND_QUADRATIC_SPHERE); type_enum.insert("spherical", NODE_BLEND_SPHERICAL); SOCKET_ENUM(type, "Type", type_enum, NODE_BLEND_LINEAR); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } GradientTextureNode::GradientTextureNode() : TextureNode(node_type) { } void GradientTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_GRADIENT, compiler.encode_uchar4( type, vector_offset, compiler.stack_assign_if_linked(fac_out), compiler.stack_assign_if_linked(color_out))); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void GradientTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "type"); compiler.add(this, "node_gradient_texture"); } /* Noise Texture */ NODE_DEFINE(NoiseTextureNode) { NodeType* type = NodeType::add("noise_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(NoiseTextureNode); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_FLOAT(detail, "Detail", 2.0f); SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } NoiseTextureNode::NoiseTextureNode() : TextureNode(node_type) { } void NoiseTextureNode::compile(SVMCompiler& compiler) { ShaderInput *distortion_in = input("Distortion"); ShaderInput *detail_in = input("Detail"); ShaderInput *scale_in = input("Scale"); ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_NOISE, compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(scale_in), compiler.stack_assign_if_linked(detail_in), compiler.stack_assign_if_linked(distortion_in)), compiler.encode_uchar4( compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out))); compiler.add_node( __float_as_int(scale), __float_as_int(detail), __float_as_int(distortion)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void NoiseTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.add(this, "node_noise_texture"); } /* Voronoi Texture */ NODE_DEFINE(VoronoiTextureNode) { NodeType* type = NodeType::add("voronoi_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(VoronoiTextureNode); static NodeEnum coloring_enum; coloring_enum.insert("intensity", NODE_VORONOI_INTENSITY); coloring_enum.insert("cells", NODE_VORONOI_CELLS); SOCKET_ENUM(coloring, "Coloring", coloring_enum, NODE_VORONOI_INTENSITY); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } VoronoiTextureNode::VoronoiTextureNode() : TextureNode(node_type) { } void VoronoiTextureNode::compile(SVMCompiler& compiler) { ShaderInput *scale_in = input("Scale"); ShaderInput *vector_in = input("Vector"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_VORONOI, coloring, compiler.encode_uchar4( compiler.stack_assign_if_linked(scale_in), vector_offset, compiler.stack_assign(fac_out), compiler.stack_assign(color_out)), __float_as_int(scale)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void VoronoiTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "coloring"); compiler.add(this, "node_voronoi_texture"); } /* Musgrave Texture */ NODE_DEFINE(MusgraveTextureNode) { NodeType* type = NodeType::add("musgrave_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(MusgraveTextureNode); static NodeEnum type_enum; type_enum.insert("multifractal", NODE_MUSGRAVE_MULTIFRACTAL); type_enum.insert("fBM", NODE_MUSGRAVE_FBM); type_enum.insert("hybrid_multifractal", NODE_MUSGRAVE_HYBRID_MULTIFRACTAL); type_enum.insert("ridged_multifractal", NODE_MUSGRAVE_RIDGED_MULTIFRACTAL); type_enum.insert("hetero_terrain", NODE_MUSGRAVE_HETERO_TERRAIN); SOCKET_ENUM(type, "Type", type_enum, NODE_MUSGRAVE_FBM); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_FLOAT(detail, "Detail", 2.0f); SOCKET_IN_FLOAT(dimension, "Dimension", 2.0f); SOCKET_IN_FLOAT(lacunarity, "Lacunarity", 1.0f); SOCKET_IN_FLOAT(offset, "Offset", 0.0f); SOCKET_IN_FLOAT(gain, "Gain", 1.0f); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } MusgraveTextureNode::MusgraveTextureNode() : TextureNode(node_type) { } void MusgraveTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *scale_in = input("Scale"); ShaderInput *dimension_in = input("Dimension"); ShaderInput *lacunarity_in = input("Lacunarity"); ShaderInput *detail_in = input("Detail"); ShaderInput *offset_in = input("Offset"); ShaderInput *gain_in = input("Gain"); ShaderOutput *fac_out = output("Fac"); ShaderOutput *color_out = output("Color"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_MUSGRAVE, compiler.encode_uchar4( type, vector_offset, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out)), compiler.encode_uchar4( compiler.stack_assign_if_linked(dimension_in), compiler.stack_assign_if_linked(lacunarity_in), compiler.stack_assign_if_linked(detail_in), compiler.stack_assign_if_linked(offset_in)), compiler.encode_uchar4( compiler.stack_assign_if_linked(gain_in), compiler.stack_assign_if_linked(scale_in))); compiler.add_node(__float_as_int(dimension), __float_as_int(lacunarity), __float_as_int(detail), __float_as_int(offset)); compiler.add_node(__float_as_int(gain), __float_as_int(scale)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void MusgraveTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "type"); compiler.add(this, "node_musgrave_texture"); } /* Wave Texture */ NODE_DEFINE(WaveTextureNode) { NodeType* type = NodeType::add("wave_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(WaveTextureNode); static NodeEnum type_enum; type_enum.insert("bands", NODE_WAVE_BANDS); type_enum.insert("rings", NODE_WAVE_RINGS); SOCKET_ENUM(type, "Type", type_enum, NODE_WAVE_BANDS); static NodeEnum profile_enum; profile_enum.insert("sine", NODE_WAVE_PROFILE_SIN); profile_enum.insert("saw", NODE_WAVE_PROFILE_SAW); SOCKET_ENUM(profile, "Profile", profile_enum, NODE_WAVE_PROFILE_SIN); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_IN_FLOAT(distortion, "Distortion", 0.0f); SOCKET_IN_FLOAT(detail, "Detail", 2.0f); SOCKET_IN_FLOAT(detail_scale, "Detail Scale", 0.0f); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } WaveTextureNode::WaveTextureNode() : TextureNode(node_type) { } void WaveTextureNode::compile(SVMCompiler& compiler) { ShaderInput *scale_in = input("Scale"); ShaderInput *distortion_in = input("Distortion"); ShaderInput *dscale_in = input("Detail Scale"); ShaderInput *detail_in = input("Detail"); ShaderInput *vector_in = input("Vector"); ShaderOutput *fac_out = output("Fac"); ShaderOutput *color_out = output("Color"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_WAVE, compiler.encode_uchar4( type, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out), compiler.stack_assign_if_linked(dscale_in)), compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(scale_in), compiler.stack_assign_if_linked(detail_in), compiler.stack_assign_if_linked(distortion_in)), profile); compiler.add_node( __float_as_int(scale), __float_as_int(detail), __float_as_int(distortion), __float_as_int(detail_scale)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void WaveTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "type"); compiler.parameter(this, "profile"); compiler.add(this, "node_wave_texture"); } /* Magic Texture */ NODE_DEFINE(MagicTextureNode) { NodeType* type = NodeType::add("magic_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(MagicTextureNode); SOCKET_INT(depth, "Depth", 2); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_FLOAT(scale, "Scale", 5.0f); SOCKET_IN_FLOAT(distortion, "Distortion", 1.0f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } MagicTextureNode::MagicTextureNode() : TextureNode(node_type) { } void MagicTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *scale_in = input("Scale"); ShaderInput *distortion_in = input("Distortion"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_MAGIC, compiler.encode_uchar4( depth, compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out)), compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(scale_in), compiler.stack_assign_if_linked(distortion_in))); compiler.add_node( __float_as_int(scale), __float_as_int(distortion)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void MagicTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "depth"); compiler.add(this, "node_magic_texture"); } /* Checker Texture */ NODE_DEFINE(CheckerTextureNode) { NodeType* type = NodeType::add("checker_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(CheckerTextureNode); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_COLOR(color1, "Color1", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_COLOR(color2, "Color2", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_FLOAT(scale, "Scale", 1.0f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } CheckerTextureNode::CheckerTextureNode() : TextureNode(node_type) { } void CheckerTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *color1_in = input("Color1"); ShaderInput *color2_in = input("Color2"); ShaderInput *scale_in = input("Scale"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_CHECKER, compiler.encode_uchar4( vector_offset, compiler.stack_assign(color1_in), compiler.stack_assign(color2_in), compiler.stack_assign_if_linked(scale_in)), compiler.encode_uchar4( compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out)), __float_as_int(scale)); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void CheckerTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.add(this, "node_checker_texture"); } /* Brick Texture */ NODE_DEFINE(BrickTextureNode) { NodeType* type = NodeType::add("brick_texture", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(BrickTextureNode); SOCKET_FLOAT(offset, "Offset", 0.5f); SOCKET_INT(offset_frequency, "Offset Frequency", 2); SOCKET_FLOAT(squash, "Squash", 1.0f); SOCKET_INT(squash_frequency, "Squash Frequency", 2); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TEXTURE_GENERATED); SOCKET_IN_COLOR(color1, "Color1", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_COLOR(color2, "Color2", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_COLOR(mortar, "Mortar", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_FLOAT(scale, "Scale", 5.0f); SOCKET_IN_FLOAT(mortar_size, "Mortar Size", 0.02f); SOCKET_IN_FLOAT(mortar_smooth, "Mortar Smooth", 0.0f); SOCKET_IN_FLOAT(bias, "Bias", 0.0f); SOCKET_IN_FLOAT(brick_width, "Brick Width", 0.5f); SOCKET_IN_FLOAT(row_height, "Row Height", 0.25f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } BrickTextureNode::BrickTextureNode() : TextureNode(node_type) { } void BrickTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderInput *color1_in = input("Color1"); ShaderInput *color2_in = input("Color2"); ShaderInput *mortar_in = input("Mortar"); ShaderInput *scale_in = input("Scale"); ShaderInput *mortar_size_in = input("Mortar Size"); ShaderInput *mortar_smooth_in = input("Mortar Smooth"); ShaderInput *bias_in = input("Bias"); ShaderInput *brick_width_in = input("Brick Width"); ShaderInput *row_height_in = input("Row Height"); ShaderOutput *color_out = output("Color"); ShaderOutput *fac_out = output("Fac"); int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_BRICK, compiler.encode_uchar4( vector_offset, compiler.stack_assign(color1_in), compiler.stack_assign(color2_in), compiler.stack_assign(mortar_in)), compiler.encode_uchar4( compiler.stack_assign_if_linked(scale_in), compiler.stack_assign_if_linked(mortar_size_in), compiler.stack_assign_if_linked(bias_in), compiler.stack_assign_if_linked(brick_width_in)), compiler.encode_uchar4( compiler.stack_assign_if_linked(row_height_in), compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(fac_out), compiler.stack_assign_if_linked(mortar_smooth_in))); compiler.add_node(compiler.encode_uchar4(offset_frequency, squash_frequency), __float_as_int(scale), __float_as_int(mortar_size), __float_as_int(bias)); compiler.add_node(__float_as_int(brick_width), __float_as_int(row_height), __float_as_int(offset), __float_as_int(squash)); compiler.add_node(__float_as_int(mortar_smooth), SVM_STACK_INVALID, SVM_STACK_INVALID, SVM_STACK_INVALID); tex_mapping.compile_end(compiler, vector_in, vector_offset); } void BrickTextureNode::compile(OSLCompiler& compiler) { tex_mapping.compile(compiler); compiler.parameter(this, "offset"); compiler.parameter(this, "offset_frequency"); compiler.parameter(this, "squash"); compiler.parameter(this, "squash_frequency"); compiler.add(this, "node_brick_texture"); } /* Point Density Texture */ NODE_DEFINE(PointDensityTextureNode) { NodeType* type = NodeType::add("point_density_texture", create, NodeType::SHADER); SOCKET_STRING(filename, "Filename", ustring("")); static NodeEnum space_enum; space_enum.insert("object", NODE_TEX_VOXEL_SPACE_OBJECT); space_enum.insert("world", NODE_TEX_VOXEL_SPACE_WORLD); SOCKET_ENUM(space, "Space", space_enum, NODE_TEX_VOXEL_SPACE_OBJECT); static NodeEnum interpolation_enum; interpolation_enum.insert("closest", INTERPOLATION_CLOSEST); interpolation_enum.insert("linear", INTERPOLATION_LINEAR); interpolation_enum.insert("cubic", INTERPOLATION_CUBIC); interpolation_enum.insert("smart", INTERPOLATION_SMART); SOCKET_ENUM(interpolation, "Interpolation", interpolation_enum, INTERPOLATION_LINEAR); SOCKET_TRANSFORM(tfm, "Transform", transform_identity()); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_POSITION); SOCKET_OUT_FLOAT(density, "Density"); SOCKET_OUT_COLOR(color, "Color"); return type; } PointDensityTextureNode::PointDensityTextureNode() : ShaderNode(node_type) { image_manager = NULL; slot = -1; builtin_data = NULL; } PointDensityTextureNode::~PointDensityTextureNode() { if(image_manager) { image_manager->remove_image(filename.string(), builtin_data, interpolation, EXTENSION_CLIP, true); } } ShaderNode *PointDensityTextureNode::clone() const { PointDensityTextureNode *node = new PointDensityTextureNode(*this); node->image_manager = NULL; node->slot = -1; return node; } void PointDensityTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_volume) attributes->add(ATTR_STD_GENERATED_TRANSFORM); ShaderNode::attributes(shader, attributes); } void PointDensityTextureNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *density_out = output("Density"); ShaderOutput *color_out = output("Color"); const bool use_density = !density_out->links.empty(); const bool use_color = !color_out->links.empty(); image_manager = compiler.image_manager; if(use_density || use_color) { if(slot == -1) { bool is_float, is_linear; slot = image_manager->add_image(filename.string(), builtin_data, false, 0, is_float, is_linear, interpolation, EXTENSION_CLIP, true); } if(slot != -1) { compiler.stack_assign(vector_in); compiler.add_node(NODE_TEX_VOXEL, slot, compiler.encode_uchar4(compiler.stack_assign(vector_in), compiler.stack_assign_if_linked(density_out), compiler.stack_assign_if_linked(color_out), space)); if(space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.add_node(tfm.x); compiler.add_node(tfm.y); compiler.add_node(tfm.z); compiler.add_node(tfm.w); } } else { if(use_density) { compiler.add_node(NODE_VALUE_F, __float_as_int(0.0f), compiler.stack_assign(density_out)); } if(use_color) { compiler.add_node(NODE_VALUE_V, compiler.stack_assign(color_out)); compiler.add_node(NODE_VALUE_V, make_float3(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B)); } } } } void PointDensityTextureNode::compile(OSLCompiler& compiler) { ShaderOutput *density_out = output("Density"); ShaderOutput *color_out = output("Color"); const bool use_density = !density_out->links.empty(); const bool use_color = !color_out->links.empty(); image_manager = compiler.image_manager; if(use_density || use_color) { if(slot == -1) { bool is_float, is_linear; slot = image_manager->add_image(filename.string(), builtin_data, false, 0, is_float, is_linear, interpolation, EXTENSION_CLIP, true); } if(slot != -1) { compiler.parameter("filename", string_printf("@%d", slot).c_str()); } if(space == NODE_TEX_VOXEL_SPACE_WORLD) { compiler.parameter("mapping", transform_transpose(tfm)); compiler.parameter("use_mapping", 1); } compiler.parameter(this, "interpolation"); compiler.add(this, "node_voxel_texture"); } } /* Normal */ NODE_DEFINE(NormalNode) { NodeType* type = NodeType::add("normal", create, NodeType::SHADER); SOCKET_VECTOR(direction, "direction", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_NORMAL(normal, "Normal"); SOCKET_OUT_FLOAT(dot, "Dot"); return type; } NormalNode::NormalNode() : ShaderNode(node_type) { } void NormalNode::compile(SVMCompiler& compiler) { ShaderInput *normal_in = input("Normal"); ShaderOutput *normal_out = output("Normal"); ShaderOutput *dot_out = output("Dot"); compiler.add_node(NODE_NORMAL, compiler.stack_assign(normal_in), compiler.stack_assign(normal_out), compiler.stack_assign(dot_out)); compiler.add_node( __float_as_int(direction.x), __float_as_int(direction.y), __float_as_int(direction.z)); } void NormalNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "direction"); compiler.add(this, "node_normal"); } /* Mapping */ NODE_DEFINE(MappingNode) { NodeType* type = NodeType::add("mapping", create, NodeType::SHADER); TEXTURE_MAPPING_DEFINE(MappingNode); SOCKET_IN_POINT(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_POINT(vector, "Vector"); return type; } MappingNode::MappingNode() : ShaderNode(node_type) { } void MappingNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *vector_out = output("Vector"); tex_mapping.compile(compiler, compiler.stack_assign(vector_in), compiler.stack_assign(vector_out)); } void MappingNode::compile(OSLCompiler& compiler) { Transform tfm = transform_transpose(tex_mapping.compute_transform()); compiler.parameter("Matrix", tfm); compiler.parameter_point("mapping_min", tex_mapping.min); compiler.parameter_point("mapping_max", tex_mapping.max); compiler.parameter("use_minmax", tex_mapping.use_minmax); compiler.add(this, "node_mapping"); } /* RGBToBW */ NODE_DEFINE(RGBToBWNode) { NodeType* type = NodeType::add("rgb_to_bw", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_FLOAT(val, "Val"); return type; } RGBToBWNode::RGBToBWNode() : ShaderNode(node_type) { } void RGBToBWNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant(linear_rgb_to_gray(color)); } } void RGBToBWNode::compile(SVMCompiler& compiler) { compiler.add_node(NODE_CONVERT, NODE_CONVERT_CF, compiler.stack_assign(inputs[0]), compiler.stack_assign(outputs[0])); } void RGBToBWNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_rgb_to_bw"); } /* Convert */ const NodeType* ConvertNode::node_types[ConvertNode::MAX_TYPE][ConvertNode::MAX_TYPE]; bool ConvertNode::initialized = ConvertNode::register_types(); Node* ConvertNode::create(const NodeType *type) { return new ConvertNode(type->inputs[0].type, type->outputs[0].type); } bool ConvertNode::register_types() { const int num_types = 8; SocketType::Type types[num_types] = {SocketType::FLOAT, SocketType::INT, SocketType::COLOR, SocketType::VECTOR, SocketType::POINT, SocketType::NORMAL, SocketType::STRING, SocketType::CLOSURE}; for(size_t i = 0; i < num_types; i++) { SocketType::Type from = types[i]; ustring from_name(SocketType::type_name(from)); ustring from_value_name("value_" + from_name.string()); for(size_t j = 0; j < num_types; j++) { SocketType::Type to = types[j]; ustring to_name(SocketType::type_name(to)); ustring to_value_name("value_" + to_name.string()); string node_name = "convert_" + from_name.string() + "_to_" + to_name.string(); NodeType* type = NodeType::add(node_name.c_str(), create, NodeType::SHADER); type->register_input(from_value_name, from_value_name, from, SOCKET_OFFSETOF(ConvertNode, value_float), SocketType::zero_default_value(), NULL, NULL, SocketType::LINKABLE); type->register_output(to_value_name, to_value_name, to); assert(from < MAX_TYPE); assert(to < MAX_TYPE); node_types[from][to] = type; } } return true; } ConvertNode::ConvertNode(SocketType::Type from_, SocketType::Type to_, bool autoconvert) : ShaderNode(node_types[from_][to_]) { from = from_; to = to_; if(from == to) special_type = SHADER_SPECIAL_TYPE_PROXY; else if(autoconvert) special_type = SHADER_SPECIAL_TYPE_AUTOCONVERT; } void ConvertNode::constant_fold(const ConstantFolder& folder) { /* proxy nodes should have been removed at this point */ assert(special_type != SHADER_SPECIAL_TYPE_PROXY); /* TODO(DingTo): conversion from/to int is not supported yet, don't fold in that case */ if(folder.all_inputs_constant()) { if(from == SocketType::FLOAT) { if(SocketType::is_float3(to)) { folder.make_constant(make_float3(value_float, value_float, value_float)); } } else if(SocketType::is_float3(from)) { if(to == SocketType::FLOAT) { if(from == SocketType::COLOR) { /* color to float */ folder.make_constant(linear_rgb_to_gray(value_color)); } else { /* vector/point/normal to float */ folder.make_constant(average(value_vector)); } } else if(SocketType::is_float3(to)) { folder.make_constant(value_color); } } } else { ShaderInput *in = inputs[0]; ShaderNode *prev = in->link->parent; /* no-op conversion of A to B to A */ if(prev->type == node_types[to][from]) { ShaderInput *prev_in = prev->inputs[0]; if(SocketType::is_float3(from) && (to == SocketType::FLOAT || SocketType::is_float3(to)) && prev_in->link) { folder.bypass(prev_in->link); } } } } void ConvertNode::compile(SVMCompiler& compiler) { /* proxy nodes should have been removed at this point */ assert(special_type != SHADER_SPECIAL_TYPE_PROXY); ShaderInput *in = inputs[0]; ShaderOutput *out = outputs[0]; if(from == SocketType::FLOAT) { if(to == SocketType::INT) /* float to int */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_FI, compiler.stack_assign(in), compiler.stack_assign(out)); else /* float to float3 */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_FV, compiler.stack_assign(in), compiler.stack_assign(out)); } else if(from == SocketType::INT) { if(to == SocketType::FLOAT) /* int to float */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_IF, compiler.stack_assign(in), compiler.stack_assign(out)); else /* int to vector/point/normal */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_IV, compiler.stack_assign(in), compiler.stack_assign(out)); } else if(to == SocketType::FLOAT) { if(from == SocketType::COLOR) /* color to float */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_CF, compiler.stack_assign(in), compiler.stack_assign(out)); else /* vector/point/normal to float */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_VF, compiler.stack_assign(in), compiler.stack_assign(out)); } else if(to == SocketType::INT) { if(from == SocketType::COLOR) /* color to int */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_CI, compiler.stack_assign(in), compiler.stack_assign(out)); else /* vector/point/normal to int */ compiler.add_node(NODE_CONVERT, NODE_CONVERT_VI, compiler.stack_assign(in), compiler.stack_assign(out)); } else { /* float3 to float3 */ if(in->link) { /* no op in SVM */ compiler.stack_link(in, out); } else { /* set 0,0,0 value */ compiler.add_node(NODE_VALUE_V, compiler.stack_assign(out)); compiler.add_node(NODE_VALUE_V, value_color); } } } void ConvertNode::compile(OSLCompiler& compiler) { /* proxy nodes should have been removed at this point */ assert(special_type != SHADER_SPECIAL_TYPE_PROXY); if(from == SocketType::FLOAT) compiler.add(this, "node_convert_from_float"); else if(from == SocketType::INT) compiler.add(this, "node_convert_from_int"); else if(from == SocketType::COLOR) compiler.add(this, "node_convert_from_color"); else if(from == SocketType::VECTOR) compiler.add(this, "node_convert_from_vector"); else if(from == SocketType::POINT) compiler.add(this, "node_convert_from_point"); else if(from == SocketType::NORMAL) compiler.add(this, "node_convert_from_normal"); else assert(0); } /* Base type for all closure-type nodes */ BsdfBaseNode::BsdfBaseNode(const NodeType *node_type) : ShaderNode(node_type) { special_type = SHADER_SPECIAL_TYPE_CLOSURE; } bool BsdfBaseNode::has_bump() { /* detect if anything is plugged into the normal input besides the default */ ShaderInput *normal_in = input("Normal"); return (normal_in && normal_in->link && normal_in->link->parent->special_type != SHADER_SPECIAL_TYPE_GEOMETRY); } /* BSDF Closure */ BsdfNode::BsdfNode(const NodeType *node_type) : BsdfBaseNode(node_type) { } void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3, ShaderInput *param4) { ShaderInput *color_in = input("Color"); ShaderInput *normal_in = input("Normal"); ShaderInput *tangent_in = input("Tangent"); if(color_in->link) compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(color_in)); else compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color); int normal_offset = (normal_in) ? compiler.stack_assign_if_linked(normal_in) : SVM_STACK_INVALID; int tangent_offset = (tangent_in) ? compiler.stack_assign_if_linked(tangent_in) : SVM_STACK_INVALID; int param3_offset = (param3) ? compiler.stack_assign(param3) : SVM_STACK_INVALID; int param4_offset = (param4) ? compiler.stack_assign(param4) : SVM_STACK_INVALID; compiler.add_node(NODE_CLOSURE_BSDF, compiler.encode_uchar4(closure, (param1)? compiler.stack_assign(param1): SVM_STACK_INVALID, (param2)? compiler.stack_assign(param2): SVM_STACK_INVALID, compiler.closure_mix_weight_offset()), __float_as_int((param1)? get_float(param1->socket_type): 0.0f), __float_as_int((param2)? get_float(param2->socket_type): 0.0f)); compiler.add_node(normal_offset, tangent_offset, param3_offset, param4_offset); } void BsdfNode::compile(SVMCompiler& compiler) { compile(compiler, NULL, NULL); } void BsdfNode::compile(OSLCompiler& /*compiler*/) { assert(0); } /* Anisotropic BSDF Closure */ NODE_DEFINE(AnisotropicBsdfNode) { NodeType* type = NodeType::add("anisotropic_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID); distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID); distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID); distribution_enum.insert("ashikhmin_shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID); SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID); SOCKET_IN_VECTOR(tangent, "Tangent", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TANGENT); SOCKET_IN_FLOAT(roughness, "Roughness", 0.2f); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.5f); SOCKET_IN_FLOAT(rotation, "Rotation", 0.0f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } AnisotropicBsdfNode::AnisotropicBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID; } void AnisotropicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { ShaderInput *tangent_in = input("Tangent"); if(!tangent_in->link) attributes->add(ATTR_STD_GENERATED); } ShaderNode::attributes(shader, attributes); } void AnisotropicBsdfNode::compile(SVMCompiler& compiler) { closure = distribution; if(closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ANISO_ID) BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"), input("Color")); else BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation")); } void AnisotropicBsdfNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_anisotropic_bsdf"); } /* Glossy BSDF Closure */ NODE_DEFINE(GlossyBsdfNode) { NodeType* type = NodeType::add("glossy_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("sharp", CLOSURE_BSDF_REFLECTION_ID); distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID); distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ID); distribution_enum.insert("ashikhmin_shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID); distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID); SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_ID); SOCKET_IN_FLOAT(roughness, "Roughness", 0.2f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } GlossyBsdfNode::GlossyBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_MICROFACET_GGX_ID; distribution_orig = NBUILTIN_CLOSURES; } void GlossyBsdfNode::simplify_settings(Scene *scene) { if(distribution_orig == NBUILTIN_CLOSURES) { roughness_orig = roughness; distribution_orig = distribution; } else { /* By default we use original values, so we don't worry about restoring * defaults later one and can only do override when needed. */ roughness = roughness_orig; distribution = distribution_orig; } Integrator *integrator = scene->integrator; ShaderInput *roughness_input = input("Roughness"); if(integrator->filter_glossy == 0.0f) { /* Fallback to Sharp closure for Roughness close to 0. * Note: Keep the epsilon in sync with kernel! */ if(!roughness_input->link && roughness <= 1e-4f) { VLOG(1) << "Using sharp glossy BSDF."; distribution = CLOSURE_BSDF_REFLECTION_ID; } } else { /* If filter glossy is used we replace Sharp glossy with GGX so we can * benefit from closure blur to remove unwanted noise. */ if(roughness_input->link == NULL && distribution == CLOSURE_BSDF_REFLECTION_ID) { VLOG(1) << "Using GGX glossy with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_ID; roughness = 0.0f; } } closure = distribution; } bool GlossyBsdfNode::has_integrator_dependency() { ShaderInput *roughness_input = input("Roughness"); return !roughness_input->link && (distribution == CLOSURE_BSDF_REFLECTION_ID || roughness <= 1e-4f); } void GlossyBsdfNode::compile(SVMCompiler& compiler) { closure = distribution; if(closure == CLOSURE_BSDF_REFLECTION_ID) BsdfNode::compile(compiler, NULL, NULL); else if(closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) BsdfNode::compile(compiler, input("Roughness"), NULL, input("Color")); else BsdfNode::compile(compiler, input("Roughness"), NULL); } void GlossyBsdfNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_glossy_bsdf"); } /* Glass BSDF Closure */ NODE_DEFINE(GlassBsdfNode) { NodeType* type = NodeType::add("glass_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("sharp", CLOSURE_BSDF_SHARP_GLASS_ID); distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID); distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID); distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID); SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); SOCKET_IN_FLOAT(IOR, "IOR", 0.3f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } GlassBsdfNode::GlassBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_SHARP_GLASS_ID; distribution_orig = NBUILTIN_CLOSURES; } void GlassBsdfNode::simplify_settings(Scene *scene) { if(distribution_orig == NBUILTIN_CLOSURES) { roughness_orig = roughness; distribution_orig = distribution; } else { /* By default we use original values, so we don't worry about restoring * defaults later one and can only do override when needed. */ roughness = roughness_orig; distribution = distribution_orig; } Integrator *integrator = scene->integrator; ShaderInput *roughness_input = input("Roughness"); if(integrator->filter_glossy == 0.0f) { /* Fallback to Sharp closure for Roughness close to 0. * Note: Keep the epsilon in sync with kernel! */ if(!roughness_input->link && roughness <= 1e-4f) { VLOG(1) << "Using sharp glass BSDF."; distribution = CLOSURE_BSDF_SHARP_GLASS_ID; } } else { /* If filter glossy is used we replace Sharp glossy with GGX so we can * benefit from closure blur to remove unwanted noise. */ if(roughness_input->link == NULL && distribution == CLOSURE_BSDF_SHARP_GLASS_ID) { VLOG(1) << "Using GGX glass with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID; roughness = 0.0f; } } closure = distribution; } bool GlassBsdfNode::has_integrator_dependency() { ShaderInput *roughness_input = input("Roughness"); return !roughness_input->link && (distribution == CLOSURE_BSDF_SHARP_GLASS_ID || roughness <= 1e-4f); } void GlassBsdfNode::compile(SVMCompiler& compiler) { closure = distribution; if(closure == CLOSURE_BSDF_SHARP_GLASS_ID) BsdfNode::compile(compiler, NULL, input("IOR")); else if(closure == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID) BsdfNode::compile(compiler, input("Roughness"), input("IOR"), input("Color")); else BsdfNode::compile(compiler, input("Roughness"), input("IOR")); } void GlassBsdfNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_glass_bsdf"); } /* Refraction BSDF Closure */ NODE_DEFINE(RefractionBsdfNode) { NodeType* type = NodeType::add("refraction_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum distribution_enum; distribution_enum.insert("sharp", CLOSURE_BSDF_REFRACTION_ID); distribution_enum.insert("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID); distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID); SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); SOCKET_IN_FLOAT(IOR, "IOR", 0.3f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } RefractionBsdfNode::RefractionBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_REFRACTION_ID; distribution_orig = NBUILTIN_CLOSURES; } void RefractionBsdfNode::simplify_settings(Scene *scene) { if(distribution_orig == NBUILTIN_CLOSURES) { roughness_orig = roughness; distribution_orig = distribution; } else { /* By default we use original values, so we don't worry about restoring * defaults later one and can only do override when needed. */ roughness = roughness_orig; distribution = distribution_orig; } Integrator *integrator = scene->integrator; ShaderInput *roughness_input = input("Roughness"); if(integrator->filter_glossy == 0.0f) { /* Fallback to Sharp closure for Roughness close to 0. * Note: Keep the epsilon in sync with kernel! */ if(!roughness_input->link && roughness <= 1e-4f) { VLOG(1) << "Using sharp refraction BSDF."; distribution = CLOSURE_BSDF_REFRACTION_ID; } } else { /* If filter glossy is used we replace Sharp glossy with GGX so we can * benefit from closure blur to remove unwanted noise. */ if(roughness_input->link == NULL && distribution == CLOSURE_BSDF_REFRACTION_ID) { VLOG(1) << "Using GGX refraction with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; roughness = 0.0f; } } closure = distribution; } bool RefractionBsdfNode::has_integrator_dependency() { ShaderInput *roughness_input = input("Roughness"); return !roughness_input->link && (distribution == CLOSURE_BSDF_REFRACTION_ID || roughness <= 1e-4f); } void RefractionBsdfNode::compile(SVMCompiler& compiler) { closure = distribution; if(closure == CLOSURE_BSDF_REFRACTION_ID) BsdfNode::compile(compiler, NULL, input("IOR")); else BsdfNode::compile(compiler, input("Roughness"), input("IOR")); } void RefractionBsdfNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_refraction_bsdf"); } /* Toon BSDF Closure */ NODE_DEFINE(ToonBsdfNode) { NodeType* type = NodeType::add("toon_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum component_enum; component_enum.insert("diffuse", CLOSURE_BSDF_DIFFUSE_TOON_ID); component_enum.insert("glossy", CLOSURE_BSDF_GLOSSY_TOON_ID); SOCKET_ENUM(component, "Component", component_enum, CLOSURE_BSDF_DIFFUSE_TOON_ID); SOCKET_IN_FLOAT(size, "Size", 0.5f); SOCKET_IN_FLOAT(smooth, "Smooth", 0.0f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } ToonBsdfNode::ToonBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_DIFFUSE_TOON_ID; } void ToonBsdfNode::compile(SVMCompiler& compiler) { closure = component; BsdfNode::compile(compiler, input("Size"), input("Smooth")); } void ToonBsdfNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "component"); compiler.add(this, "node_toon_bsdf"); } /* Velvet BSDF Closure */ NODE_DEFINE(VelvetBsdfNode) { NodeType* type = NodeType::add("velvet_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_FLOAT(sigma, "Sigma", 1.0f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } VelvetBsdfNode::VelvetBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_ASHIKHMIN_VELVET_ID; } void VelvetBsdfNode::compile(SVMCompiler& compiler) { BsdfNode::compile(compiler, input("Sigma"), NULL); } void VelvetBsdfNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_velvet_bsdf"); } /* Diffuse BSDF Closure */ NODE_DEFINE(DiffuseBsdfNode) { NodeType* type = NodeType::add("diffuse_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_FLOAT(roughness, "Roughness", 0.0f); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } DiffuseBsdfNode::DiffuseBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_DIFFUSE_ID; } void DiffuseBsdfNode::compile(SVMCompiler& compiler) { BsdfNode::compile(compiler, input("Roughness"), NULL); } void DiffuseBsdfNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_diffuse_bsdf"); } /* Disney principled BSDF Closure */ NODE_DEFINE(PrincipledBsdfNode) { NodeType* type = NodeType::add("principled_bsdf", create, NodeType::SHADER); static NodeEnum distribution_enum; distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID); distribution_enum.insert("Multiscatter GGX", CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); SOCKET_ENUM(distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); SOCKET_IN_COLOR(base_color, "Base Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_COLOR(subsurface_color, "Subsurface Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(metallic, "Metallic", 0.0f); SOCKET_IN_FLOAT(subsurface, "Subsurface", 0.0f); SOCKET_IN_VECTOR(subsurface_radius, "Subsurface Radius", make_float3(0.1f, 0.1f, 0.1f)); SOCKET_IN_FLOAT(specular, "Specular", 0.0f); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(specular_tint, "Specular Tint", 0.0f); SOCKET_IN_FLOAT(anisotropic, "Anisotropic", 0.0f); SOCKET_IN_FLOAT(sheen, "Sheen", 0.0f); SOCKET_IN_FLOAT(sheen_tint, "Sheen Tint", 0.0f); SOCKET_IN_FLOAT(clearcoat, "Clearcoat", 0.0f); SOCKET_IN_FLOAT(clearcoat_roughness, "Clearcoat Roughness", 0.03f); SOCKET_IN_FLOAT(ior, "IOR", 0.0f); SOCKET_IN_FLOAT(transmission, "Transmission", 0.0f); SOCKET_IN_FLOAT(transmission_roughness, "Transmission Roughness", 0.0f); SOCKET_IN_FLOAT(anisotropic_rotation, "Anisotropic Rotation", 0.0f); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_NORMAL(clearcoat_normal, "Clearcoat Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_NORMAL(tangent, "Tangent", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_TANGENT); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } PrincipledBsdfNode::PrincipledBsdfNode() : BsdfBaseNode(node_type) { closure = CLOSURE_BSDF_PRINCIPLED_ID; distribution = CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID; distribution_orig = NBUILTIN_CLOSURES; } bool PrincipledBsdfNode::has_surface_bssrdf() { ShaderInput *subsurface_in = input("Subsurface"); return (subsurface_in->link != NULL || subsurface > CLOSURE_WEIGHT_CUTOFF); } void PrincipledBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { ShaderInput *tangent_in = input("Tangent"); if(!tangent_in->link) attributes->add(ATTR_STD_GENERATED); } ShaderNode::attributes(shader, attributes); } void PrincipledBsdfNode::compile(SVMCompiler& compiler, ShaderInput *p_metallic, ShaderInput *p_subsurface, ShaderInput *p_subsurface_radius, ShaderInput *p_specular, ShaderInput *p_roughness, ShaderInput *p_specular_tint, ShaderInput *p_anisotropic, ShaderInput *p_sheen, ShaderInput *p_sheen_tint, ShaderInput *p_clearcoat, ShaderInput *p_clearcoat_roughness, ShaderInput *p_ior, ShaderInput *p_transmission, ShaderInput *p_anisotropic_rotation, ShaderInput *p_transmission_roughness) { ShaderInput *base_color_in = input("Base Color"); ShaderInput *subsurface_color_in = input("Subsurface Color"); ShaderInput *normal_in = input("Normal"); ShaderInput *clearcoat_normal_in = input("Clearcoat Normal"); ShaderInput *tangent_in = input("Tangent"); float3 weight = make_float3(1.0f, 1.0f, 1.0f); compiler.add_node(NODE_CLOSURE_SET_WEIGHT, weight); int normal_offset = compiler.stack_assign_if_linked(normal_in); int clearcoat_normal_offset = compiler.stack_assign_if_linked(clearcoat_normal_in); int tangent_offset = compiler.stack_assign_if_linked(tangent_in); int specular_offset = compiler.stack_assign(p_specular); int roughness_offset = compiler.stack_assign(p_roughness); int specular_tint_offset = compiler.stack_assign(p_specular_tint); int anisotropic_offset = compiler.stack_assign(p_anisotropic); int sheen_offset = compiler.stack_assign(p_sheen); int sheen_tint_offset = compiler.stack_assign(p_sheen_tint); int clearcoat_offset = compiler.stack_assign(p_clearcoat); int clearcoat_roughness_offset = compiler.stack_assign(p_clearcoat_roughness); int ior_offset = compiler.stack_assign(p_ior); int transmission_offset = compiler.stack_assign(p_transmission); int transmission_roughness_offset = compiler.stack_assign(p_transmission_roughness); int anisotropic_rotation_offset = compiler.stack_assign(p_anisotropic_rotation); int subsurface_radius_offset = compiler.stack_assign(p_subsurface_radius); compiler.add_node(NODE_CLOSURE_BSDF, compiler.encode_uchar4(closure, compiler.stack_assign(p_metallic), compiler.stack_assign(p_subsurface), compiler.closure_mix_weight_offset()), __float_as_int((p_metallic) ? get_float(p_metallic->socket_type) : 0.0f), __float_as_int((p_subsurface) ? get_float(p_subsurface->socket_type) : 0.0f)); compiler.add_node(normal_offset, tangent_offset, compiler.encode_uchar4(specular_offset, roughness_offset, specular_tint_offset, anisotropic_offset), compiler.encode_uchar4(sheen_offset, sheen_tint_offset, clearcoat_offset, clearcoat_roughness_offset)); compiler.add_node(compiler.encode_uchar4(ior_offset, transmission_offset, anisotropic_rotation_offset, transmission_roughness_offset), distribution, SVM_STACK_INVALID, SVM_STACK_INVALID); float3 bc_default = get_float3(base_color_in->socket_type); compiler.add_node(((base_color_in->link) ? compiler.stack_assign(base_color_in) : SVM_STACK_INVALID), __float_as_int(bc_default.x), __float_as_int(bc_default.y), __float_as_int(bc_default.z)); compiler.add_node(clearcoat_normal_offset, subsurface_radius_offset, SVM_STACK_INVALID, SVM_STACK_INVALID); float3 ss_default = get_float3(subsurface_color_in->socket_type); compiler.add_node(((subsurface_color_in->link) ? compiler.stack_assign(subsurface_color_in) : SVM_STACK_INVALID), __float_as_int(ss_default.x), __float_as_int(ss_default.y), __float_as_int(ss_default.z)); } bool PrincipledBsdfNode::has_integrator_dependency() { ShaderInput *roughness_input = input("Roughness"); return !roughness_input->link && roughness <= 1e-4f; } void PrincipledBsdfNode::compile(SVMCompiler& compiler) { compile(compiler, input("Metallic"), input("Subsurface"), input("Subsurface Radius"), input("Specular"), input("Roughness"), input("Specular Tint"), input("Anisotropic"), input("Sheen"), input("Sheen Tint"), input("Clearcoat"), input("Clearcoat Roughness"), input("IOR"), input("Transmission"), input("Anisotropic Rotation"), input("Transmission Roughness")); } void PrincipledBsdfNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "distribution"); compiler.add(this, "node_principled_bsdf"); } bool PrincipledBsdfNode::has_bssrdf_bump() { return has_surface_bssrdf() && has_bump(); } /* Translucent BSDF Closure */ NODE_DEFINE(TranslucentBsdfNode) { NodeType* type = NodeType::add("translucent_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } TranslucentBsdfNode::TranslucentBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_TRANSLUCENT_ID; } void TranslucentBsdfNode::compile(SVMCompiler& compiler) { BsdfNode::compile(compiler, NULL, NULL); } void TranslucentBsdfNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_translucent_bsdf"); } /* Transparent BSDF Closure */ NODE_DEFINE(TransparentBsdfNode) { NodeType* type = NodeType::add("transparent_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } TransparentBsdfNode::TransparentBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_TRANSPARENT_ID; } void TransparentBsdfNode::compile(SVMCompiler& compiler) { BsdfNode::compile(compiler, NULL, NULL); } void TransparentBsdfNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_transparent_bsdf"); } /* Subsurface Scattering Closure */ NODE_DEFINE(SubsurfaceScatteringNode) { NodeType* type = NodeType::add("subsurface_scattering", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum falloff_enum; falloff_enum.insert("cubic", CLOSURE_BSSRDF_CUBIC_ID); falloff_enum.insert("gaussian", CLOSURE_BSSRDF_GAUSSIAN_ID); falloff_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); SOCKET_ENUM(falloff, "Falloff", falloff_enum, CLOSURE_BSSRDF_BURLEY_ID); SOCKET_IN_FLOAT(scale, "Scale", 0.01f); SOCKET_IN_VECTOR(radius, "Radius", make_float3(0.1f, 0.1f, 0.1f)); SOCKET_IN_FLOAT(sharpness, "Sharpness", 0.0f); SOCKET_IN_FLOAT(texture_blur, "Texture Blur", 1.0f); SOCKET_OUT_CLOSURE(BSSRDF, "BSSRDF"); return type; } SubsurfaceScatteringNode::SubsurfaceScatteringNode() : BsdfNode(node_type) { closure = falloff; } void SubsurfaceScatteringNode::compile(SVMCompiler& compiler) { closure = falloff; BsdfNode::compile(compiler, input("Scale"), input("Texture Blur"), input("Radius"), input("Sharpness")); } void SubsurfaceScatteringNode::compile(OSLCompiler& compiler) { closure = falloff; compiler.parameter(this, "falloff"); compiler.add(this, "node_subsurface_scattering"); } bool SubsurfaceScatteringNode::has_bssrdf_bump() { /* detect if anything is plugged into the normal input besides the default */ ShaderInput *normal_in = input("Normal"); return (normal_in->link && normal_in->link->parent->special_type != SHADER_SPECIAL_TYPE_GEOMETRY); } /* Emissive Closure */ NODE_DEFINE(EmissionNode) { NodeType* type = NodeType::add("emission", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(strength, "Strength", 10.0f); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(emission, "Emission"); return type; } EmissionNode::EmissionNode() : ShaderNode(node_type) { } void EmissionNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); if(color_in->link || strength_in->link) { compiler.add_node(NODE_EMISSION_WEIGHT, compiler.stack_assign(color_in), compiler.stack_assign(strength_in)); } else compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color * strength); compiler.add_node(NODE_CLOSURE_EMISSION, compiler.closure_mix_weight_offset()); } void EmissionNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_emission"); } void EmissionNode::constant_fold(const ConstantFolder& folder) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); if((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) || (!strength_in->link && strength == 0.0f)) { folder.discard(); } } /* Background Closure */ NODE_DEFINE(BackgroundNode) { NodeType* type = NodeType::add("background_shader", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(strength, "Strength", 1.0f); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(background, "Background"); return type; } BackgroundNode::BackgroundNode() : ShaderNode(node_type) { } void BackgroundNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); if(color_in->link || strength_in->link) { compiler.add_node(NODE_EMISSION_WEIGHT, compiler.stack_assign(color_in), compiler.stack_assign(strength_in)); } else compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color*strength); compiler.add_node(NODE_CLOSURE_BACKGROUND, compiler.closure_mix_weight_offset()); } void BackgroundNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_background"); } void BackgroundNode::constant_fold(const ConstantFolder& folder) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); if((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) || (!strength_in->link && strength == 0.0f)) { folder.discard(); } } /* Holdout Closure */ NODE_DEFINE(HoldoutNode) { NodeType* type = NodeType::add("holdout", create, NodeType::SHADER); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(holdout, "Holdout"); return type; } HoldoutNode::HoldoutNode() : ShaderNode(node_type) { } void HoldoutNode::compile(SVMCompiler& compiler) { float3 value = make_float3(1.0f, 1.0f, 1.0f); compiler.add_node(NODE_CLOSURE_SET_WEIGHT, value); compiler.add_node(NODE_CLOSURE_HOLDOUT, compiler.closure_mix_weight_offset()); } void HoldoutNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_holdout"); } /* Ambient Occlusion */ NODE_DEFINE(AmbientOcclusionNode) { NodeType* type = NodeType::add("ambient_occlusion", create, NodeType::SHADER); SOCKET_IN_NORMAL(normal_osl, "NormalIn", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(AO, "AO"); return type; } AmbientOcclusionNode::AmbientOcclusionNode() : ShaderNode(node_type) { } void AmbientOcclusionNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Color"); if(color_in->link) compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(color_in)); else compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color); compiler.add_node(NODE_CLOSURE_AMBIENT_OCCLUSION, compiler.closure_mix_weight_offset()); } void AmbientOcclusionNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_ambient_occlusion"); } /* Volume Closure */ VolumeNode::VolumeNode(const NodeType *node_type) : ShaderNode(node_type) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; } void VolumeNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2) { ShaderInput *color_in = input("Color"); if(color_in->link) compiler.add_node(NODE_CLOSURE_WEIGHT, compiler.stack_assign(color_in)); else compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color); compiler.add_node(NODE_CLOSURE_VOLUME, compiler.encode_uchar4(closure, (param1)? compiler.stack_assign(param1): SVM_STACK_INVALID, (param2)? compiler.stack_assign(param2): SVM_STACK_INVALID, compiler.closure_mix_weight_offset()), __float_as_int((param1)? get_float(param1->socket_type): 0.0f), __float_as_int((param2)? get_float(param2->socket_type): 0.0f)); } void VolumeNode::compile(SVMCompiler& compiler) { compile(compiler, NULL, NULL); } void VolumeNode::compile(OSLCompiler& /*compiler*/) { assert(0); } /* Absorption Volume Closure */ NODE_DEFINE(AbsorptionVolumeNode) { NodeType* type = NodeType::add("absorption_volume", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(density, "Density", 1.0f); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(volume, "Volume"); return type; } AbsorptionVolumeNode::AbsorptionVolumeNode() : VolumeNode(node_type) { closure = CLOSURE_VOLUME_ABSORPTION_ID; } void AbsorptionVolumeNode::compile(SVMCompiler& compiler) { VolumeNode::compile(compiler, input("Density"), NULL); } void AbsorptionVolumeNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_absorption_volume"); } /* Scatter Volume Closure */ NODE_DEFINE(ScatterVolumeNode) { NodeType* type = NodeType::add("scatter_volume", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(density, "Density", 1.0f); SOCKET_IN_FLOAT(anisotropy, "Anisotropy", 0.0f); SOCKET_IN_FLOAT(volume_mix_weight, "VolumeMixWeight", 0.0f, SocketType::SVM_INTERNAL); SOCKET_OUT_CLOSURE(volume, "Volume"); return type; } ScatterVolumeNode::ScatterVolumeNode() : VolumeNode(node_type) { closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; } void ScatterVolumeNode::compile(SVMCompiler& compiler) { VolumeNode::compile(compiler, input("Density"), input("Anisotropy")); } void ScatterVolumeNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_scatter_volume"); } /* Hair BSDF Closure */ NODE_DEFINE(HairBsdfNode) { NodeType* type = NodeType::add("hair_bsdf", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); static NodeEnum component_enum; component_enum.insert("reflection", CLOSURE_BSDF_HAIR_REFLECTION_ID); component_enum.insert("transmission", CLOSURE_BSDF_HAIR_TRANSMISSION_ID); SOCKET_ENUM(component, "Component", component_enum, CLOSURE_BSDF_HAIR_REFLECTION_ID); SOCKET_IN_FLOAT(offset, "Offset", 0.0f); SOCKET_IN_FLOAT(roughness_u, "RoughnessU", 0.2f); SOCKET_IN_FLOAT(roughness_v, "RoughnessV", 0.2f); SOCKET_IN_VECTOR(tangent, "Tangent", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_CLOSURE(BSDF, "BSDF"); return type; } HairBsdfNode::HairBsdfNode() : BsdfNode(node_type) { closure = CLOSURE_BSDF_HAIR_REFLECTION_ID; } void HairBsdfNode::compile(SVMCompiler& compiler) { closure = component; BsdfNode::compile(compiler, input("RoughnessU"), input("RoughnessV"), input("Offset")); } void HairBsdfNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "component"); compiler.add(this, "node_hair_bsdf"); } /* Geometry */ NODE_DEFINE(GeometryNode) { NodeType* type = NodeType::add("geometry", create, NodeType::SHADER); SOCKET_IN_NORMAL(normal_osl, "NormalIn", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_OUT_POINT(position, "Position"); SOCKET_OUT_NORMAL(normal, "Normal"); SOCKET_OUT_NORMAL(tangent, "Tangent"); SOCKET_OUT_NORMAL(true_normal, "True Normal"); SOCKET_OUT_VECTOR(incoming, "Incoming"); SOCKET_OUT_POINT(parametric, "Parametric"); SOCKET_OUT_FLOAT(backfacing, "Backfacing"); SOCKET_OUT_FLOAT(pointiness, "Pointiness"); return type; } GeometryNode::GeometryNode() : ShaderNode(node_type) { special_type = SHADER_SPECIAL_TYPE_GEOMETRY; } void GeometryNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { if(!output("Tangent")->links.empty()) { attributes->add(ATTR_STD_GENERATED); } if(!output("Pointiness")->links.empty()) { attributes->add(ATTR_STD_POINTINESS); } } ShaderNode::attributes(shader, attributes); } void GeometryNode::compile(SVMCompiler& compiler) { ShaderOutput *out; ShaderNodeType geom_node = NODE_GEOMETRY; ShaderNodeType attr_node = NODE_ATTR; if(bump == SHADER_BUMP_DX) { geom_node = NODE_GEOMETRY_BUMP_DX; attr_node = NODE_ATTR_BUMP_DX; } else if(bump == SHADER_BUMP_DY) { geom_node = NODE_GEOMETRY_BUMP_DY; attr_node = NODE_ATTR_BUMP_DY; } out = output("Position"); if(!out->links.empty()) { compiler.add_node(geom_node, NODE_GEOM_P, compiler.stack_assign(out)); } out = output("Normal"); if(!out->links.empty()) { compiler.add_node(geom_node, NODE_GEOM_N, compiler.stack_assign(out)); } out = output("Tangent"); if(!out->links.empty()) { compiler.add_node(geom_node, NODE_GEOM_T, compiler.stack_assign(out)); } out = output("True Normal"); if(!out->links.empty()) { compiler.add_node(geom_node, NODE_GEOM_Ng, compiler.stack_assign(out)); } out = output("Incoming"); if(!out->links.empty()) { compiler.add_node(geom_node, NODE_GEOM_I, compiler.stack_assign(out)); } out = output("Parametric"); if(!out->links.empty()) { compiler.add_node(geom_node, NODE_GEOM_uv, compiler.stack_assign(out)); } out = output("Backfacing"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_backfacing, compiler.stack_assign(out)); } out = output("Pointiness"); if(!out->links.empty()) { if(compiler.output_type() != SHADER_TYPE_VOLUME) { compiler.add_node(attr_node, ATTR_STD_POINTINESS, compiler.stack_assign(out), NODE_ATTR_FLOAT); } else { compiler.add_node(NODE_VALUE_F, __float_as_int(0.0f), compiler.stack_assign(out)); } } } void GeometryNode::compile(OSLCompiler& compiler) { if(bump == SHADER_BUMP_DX) compiler.parameter("bump_offset", "dx"); else if(bump == SHADER_BUMP_DY) compiler.parameter("bump_offset", "dy"); else compiler.parameter("bump_offset", "center"); compiler.add(this, "node_geometry"); } /* TextureCoordinate */ NODE_DEFINE(TextureCoordinateNode) { NodeType* type = NodeType::add("texture_coordinate", create, NodeType::SHADER); SOCKET_BOOLEAN(from_dupli, "From Dupli", false); SOCKET_BOOLEAN(use_transform, "Use Transform", false); SOCKET_TRANSFORM(ob_tfm, "Object Transform", transform_identity()); SOCKET_IN_NORMAL(normal_osl, "NormalIn", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_OUT_POINT(generated, "Generated"); SOCKET_OUT_NORMAL(normal, "Normal"); SOCKET_OUT_POINT(UV, "UV"); SOCKET_OUT_POINT(object, "Object"); SOCKET_OUT_POINT(camera, "Camera"); SOCKET_OUT_POINT(window, "Window"); SOCKET_OUT_NORMAL(reflection, "Reflection"); return type; } TextureCoordinateNode::TextureCoordinateNode() : ShaderNode(node_type) { } void TextureCoordinateNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { if(!from_dupli) { if(!output("Generated")->links.empty()) attributes->add(ATTR_STD_GENERATED); if(!output("UV")->links.empty()) attributes->add(ATTR_STD_UV); } } if(shader->has_volume) { if(!from_dupli) { if(!output("Generated")->links.empty()) { attributes->add(ATTR_STD_GENERATED_TRANSFORM); } } } ShaderNode::attributes(shader, attributes); } void TextureCoordinateNode::compile(SVMCompiler& compiler) { ShaderOutput *out; ShaderNodeType texco_node = NODE_TEX_COORD; ShaderNodeType attr_node = NODE_ATTR; ShaderNodeType geom_node = NODE_GEOMETRY; if(bump == SHADER_BUMP_DX) { texco_node = NODE_TEX_COORD_BUMP_DX; attr_node = NODE_ATTR_BUMP_DX; geom_node = NODE_GEOMETRY_BUMP_DX; } else if(bump == SHADER_BUMP_DY) { texco_node = NODE_TEX_COORD_BUMP_DY; attr_node = NODE_ATTR_BUMP_DY; geom_node = NODE_GEOMETRY_BUMP_DY; } out = output("Generated"); if(!out->links.empty()) { if(compiler.background) { compiler.add_node(geom_node, NODE_GEOM_P, compiler.stack_assign(out)); } else { if(from_dupli) { compiler.add_node(texco_node, NODE_TEXCO_DUPLI_GENERATED, compiler.stack_assign(out)); } else if(compiler.output_type() == SHADER_TYPE_VOLUME) { compiler.add_node(texco_node, NODE_TEXCO_VOLUME_GENERATED, compiler.stack_assign(out)); } else { int attr = compiler.attribute(ATTR_STD_GENERATED); compiler.add_node(attr_node, attr, compiler.stack_assign(out), NODE_ATTR_FLOAT3); } } } out = output("Normal"); if(!out->links.empty()) { compiler.add_node(texco_node, NODE_TEXCO_NORMAL, compiler.stack_assign(out)); } out = output("UV"); if(!out->links.empty()) { if(from_dupli) { compiler.add_node(texco_node, NODE_TEXCO_DUPLI_UV, compiler.stack_assign(out)); } else { int attr = compiler.attribute(ATTR_STD_UV); compiler.add_node(attr_node, attr, compiler.stack_assign(out), NODE_ATTR_FLOAT3); } } out = output("Object"); if(!out->links.empty()) { compiler.add_node(texco_node, NODE_TEXCO_OBJECT, compiler.stack_assign(out), use_transform); if(use_transform) { Transform ob_itfm = transform_inverse(ob_tfm); compiler.add_node(ob_itfm.x); compiler.add_node(ob_itfm.y); compiler.add_node(ob_itfm.z); compiler.add_node(ob_itfm.w); } } out = output("Camera"); if(!out->links.empty()) { compiler.add_node(texco_node, NODE_TEXCO_CAMERA, compiler.stack_assign(out)); } out = output("Window"); if(!out->links.empty()) { compiler.add_node(texco_node, NODE_TEXCO_WINDOW, compiler.stack_assign(out)); } out = output("Reflection"); if(!out->links.empty()) { if(compiler.background) { compiler.add_node(geom_node, NODE_GEOM_I, compiler.stack_assign(out)); } else { compiler.add_node(texco_node, NODE_TEXCO_REFLECTION, compiler.stack_assign(out)); } } } void TextureCoordinateNode::compile(OSLCompiler& compiler) { if(bump == SHADER_BUMP_DX) compiler.parameter("bump_offset", "dx"); else if(bump == SHADER_BUMP_DY) compiler.parameter("bump_offset", "dy"); else compiler.parameter("bump_offset", "center"); if(compiler.background) compiler.parameter("is_background", true); if(compiler.output_type() == SHADER_TYPE_VOLUME) compiler.parameter("is_volume", true); compiler.parameter(this, "use_transform"); Transform ob_itfm = transform_transpose(transform_inverse(ob_tfm)); compiler.parameter("object_itfm", ob_itfm); compiler.parameter(this, "from_dupli"); compiler.add(this, "node_texture_coordinate"); } /* UV Map */ NODE_DEFINE(UVMapNode) { NodeType* type = NodeType::add("uvmap", create, NodeType::SHADER); SOCKET_IN_STRING(attribute, "attribute", ustring("")); SOCKET_IN_BOOLEAN(from_dupli, "from dupli", false); SOCKET_OUT_POINT(UV, "UV"); return type; } UVMapNode::UVMapNode() : ShaderNode(node_type) { } void UVMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { if(!from_dupli) { if(!output("UV")->links.empty()) { if(attribute != "") attributes->add(attribute); else attributes->add(ATTR_STD_UV); } } } ShaderNode::attributes(shader, attributes); } void UVMapNode::compile(SVMCompiler& compiler) { ShaderOutput *out = output("UV"); ShaderNodeType texco_node = NODE_TEX_COORD; ShaderNodeType attr_node = NODE_ATTR; int attr; if(bump == SHADER_BUMP_DX) { texco_node = NODE_TEX_COORD_BUMP_DX; attr_node = NODE_ATTR_BUMP_DX; } else if(bump == SHADER_BUMP_DY) { texco_node = NODE_TEX_COORD_BUMP_DY; attr_node = NODE_ATTR_BUMP_DY; } if(!out->links.empty()) { if(from_dupli) { compiler.add_node(texco_node, NODE_TEXCO_DUPLI_UV, compiler.stack_assign(out)); } else { if(attribute != "") attr = compiler.attribute(attribute); else attr = compiler.attribute(ATTR_STD_UV); compiler.add_node(attr_node, attr, compiler.stack_assign(out), NODE_ATTR_FLOAT3); } } } void UVMapNode::compile(OSLCompiler& compiler) { if(bump == SHADER_BUMP_DX) compiler.parameter("bump_offset", "dx"); else if(bump == SHADER_BUMP_DY) compiler.parameter("bump_offset", "dy"); else compiler.parameter("bump_offset", "center"); compiler.parameter(this, "from_dupli"); compiler.parameter(this, "attribute"); compiler.add(this, "node_uv_map"); } /* Light Path */ NODE_DEFINE(LightPathNode) { NodeType* type = NodeType::add("light_path", create, NodeType::SHADER); SOCKET_OUT_FLOAT(is_camera_ray, "Is Camera Ray"); SOCKET_OUT_FLOAT(is_shadow_ray, "Is Shadow Ray"); SOCKET_OUT_FLOAT(is_diffuse_ray, "Is Diffuse Ray"); SOCKET_OUT_FLOAT(is_glossy_ray, "Is Glossy Ray"); SOCKET_OUT_FLOAT(is_singular_ray, "Is Singular Ray"); SOCKET_OUT_FLOAT(is_reflection_ray, "Is Reflection Ray"); SOCKET_OUT_FLOAT(is_transmission_ray, "Is Transmission Ray"); SOCKET_OUT_FLOAT(is_volume_scatter_ray, "Is Volume Scatter Ray"); SOCKET_OUT_FLOAT(ray_length, "Ray Length"); SOCKET_OUT_FLOAT(ray_depth, "Ray Depth"); SOCKET_OUT_FLOAT(diffuse_depth, "Diffuse Depth"); SOCKET_OUT_FLOAT(glossy_depth, "Glossy Depth"); SOCKET_OUT_FLOAT(transparent_depth, "Transparent Depth"); SOCKET_OUT_FLOAT(transmission_depth, "Transmission Depth"); return type; } LightPathNode::LightPathNode() : ShaderNode(node_type) { } void LightPathNode::compile(SVMCompiler& compiler) { ShaderOutput *out; out = output("Is Camera Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_camera, compiler.stack_assign(out)); } out = output("Is Shadow Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_shadow, compiler.stack_assign(out)); } out = output("Is Diffuse Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_diffuse, compiler.stack_assign(out)); } out = output("Is Glossy Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_glossy, compiler.stack_assign(out)); } out = output("Is Singular Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_singular, compiler.stack_assign(out)); } out = output("Is Reflection Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_reflection, compiler.stack_assign(out)); } out = output("Is Transmission Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_transmission, compiler.stack_assign(out)); } out = output("Is Volume Scatter Ray"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_volume_scatter, compiler.stack_assign(out)); } out = output("Ray Length"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_length, compiler.stack_assign(out)); } out = output("Ray Depth"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_depth, compiler.stack_assign(out)); } out = output("Diffuse Depth"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_diffuse, compiler.stack_assign(out)); } out = output("Glossy Depth"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_glossy, compiler.stack_assign(out)); } out = output("Transparent Depth"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_transparent, compiler.stack_assign(out)); } out = output("Transmission Depth"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_transmission, compiler.stack_assign(out)); } } void LightPathNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_light_path"); } /* Light Falloff */ NODE_DEFINE(LightFalloffNode) { NodeType* type = NodeType::add("light_fallof", create, NodeType::SHADER); SOCKET_IN_FLOAT(strength, "Strength", 100.0f); SOCKET_IN_FLOAT(smooth, "Smooth", 0.0f); SOCKET_OUT_FLOAT(quadratic, "Quadratic"); SOCKET_OUT_FLOAT(linear, "Linear"); SOCKET_OUT_FLOAT(constant, "Constant"); return type; } LightFalloffNode::LightFalloffNode() : ShaderNode(node_type) { } void LightFalloffNode::compile(SVMCompiler& compiler) { ShaderInput *strength_in = input("Strength"); ShaderInput *smooth_in = input("Smooth"); ShaderOutput *out = output("Quadratic"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_FALLOFF, NODE_LIGHT_FALLOFF_QUADRATIC, compiler.encode_uchar4( compiler.stack_assign(strength_in), compiler.stack_assign(smooth_in), compiler.stack_assign(out))); } out = output("Linear"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_FALLOFF, NODE_LIGHT_FALLOFF_LINEAR, compiler.encode_uchar4( compiler.stack_assign(strength_in), compiler.stack_assign(smooth_in), compiler.stack_assign(out))); } out = output("Constant"); if(!out->links.empty()) { compiler.add_node(NODE_LIGHT_FALLOFF, NODE_LIGHT_FALLOFF_CONSTANT, compiler.encode_uchar4( compiler.stack_assign(strength_in), compiler.stack_assign(smooth_in), compiler.stack_assign(out))); } } void LightFalloffNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_light_falloff"); } /* Object Info */ NODE_DEFINE(ObjectInfoNode) { NodeType* type = NodeType::add("object_info", create, NodeType::SHADER); SOCKET_OUT_VECTOR(location, "Location"); SOCKET_OUT_FLOAT(object_index, "Object Index"); SOCKET_OUT_FLOAT(material_index, "Material Index"); SOCKET_OUT_FLOAT(random, "Random"); return type; } ObjectInfoNode::ObjectInfoNode() : ShaderNode(node_type) { } void ObjectInfoNode::compile(SVMCompiler& compiler) { ShaderOutput *out = output("Location"); if(!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_LOCATION, compiler.stack_assign(out)); } out = output("Object Index"); if(!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_INDEX, compiler.stack_assign(out)); } out = output("Material Index"); if(!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_MAT_INDEX, compiler.stack_assign(out)); } out = output("Random"); if(!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_RANDOM, compiler.stack_assign(out)); } } void ObjectInfoNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_object_info"); } /* Particle Info */ NODE_DEFINE(ParticleInfoNode) { NodeType* type = NodeType::add("particle_info", create, NodeType::SHADER); SOCKET_OUT_FLOAT(index, "Index"); SOCKET_OUT_FLOAT(age, "Age"); SOCKET_OUT_FLOAT(lifetime, "Lifetime"); SOCKET_OUT_POINT(location, "Location"); #if 0 /* not yet supported */ SOCKET_OUT_QUATERNION(rotation, "Rotation"); #endif SOCKET_OUT_FLOAT(size, "Size"); SOCKET_OUT_VECTOR(velocity, "Velocity"); SOCKET_OUT_VECTOR(angular_velocity, "Angular Velocity"); return type; } ParticleInfoNode::ParticleInfoNode() : ShaderNode(node_type) { } void ParticleInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(!output("Index")->links.empty()) attributes->add(ATTR_STD_PARTICLE); if(!output("Age")->links.empty()) attributes->add(ATTR_STD_PARTICLE); if(!output("Lifetime")->links.empty()) attributes->add(ATTR_STD_PARTICLE); if(!output("Location")->links.empty()) attributes->add(ATTR_STD_PARTICLE); #if 0 /* not yet supported */ if(!output("Rotation")->links.empty()) attributes->add(ATTR_STD_PARTICLE); #endif if(!output("Size")->links.empty()) attributes->add(ATTR_STD_PARTICLE); if(!output("Velocity")->links.empty()) attributes->add(ATTR_STD_PARTICLE); if(!output("Angular Velocity")->links.empty()) attributes->add(ATTR_STD_PARTICLE); ShaderNode::attributes(shader, attributes); } void ParticleInfoNode::compile(SVMCompiler& compiler) { ShaderOutput *out; out = output("Index"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_INDEX, compiler.stack_assign(out)); } out = output("Age"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_AGE, compiler.stack_assign(out)); } out = output("Lifetime"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_LIFETIME, compiler.stack_assign(out)); } out = output("Location"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_LOCATION, compiler.stack_assign(out)); } /* quaternion data is not yet supported by Cycles */ #if 0 out = output("Rotation"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_ROTATION, compiler.stack_assign(out)); } #endif out = output("Size"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_SIZE, compiler.stack_assign(out)); } out = output("Velocity"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_VELOCITY, compiler.stack_assign(out)); } out = output("Angular Velocity"); if(!out->links.empty()) { compiler.add_node(NODE_PARTICLE_INFO, NODE_INFO_PAR_ANGULAR_VELOCITY, compiler.stack_assign(out)); } } void ParticleInfoNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_particle_info"); } /* Hair Info */ NODE_DEFINE(HairInfoNode) { NodeType* type = NodeType::add("hair_info", create, NodeType::SHADER); SOCKET_OUT_FLOAT(is_strand, "Is Strand"); SOCKET_OUT_FLOAT(intercept, "Intercept"); SOCKET_OUT_FLOAT(thickness, "Thickness"); SOCKET_OUT_NORMAL(tangent Normal, "Tangent Normal"); #if 0 /*output for minimum hair width transparency - deactivated */ SOCKET_OUT_FLOAT(fade, "Fade"); #endif return type; } HairInfoNode::HairInfoNode() : ShaderNode(node_type) { } void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { ShaderOutput *intercept_out = output("Intercept"); if(!intercept_out->links.empty()) attributes->add(ATTR_STD_CURVE_INTERCEPT); } ShaderNode::attributes(shader, attributes); } void HairInfoNode::compile(SVMCompiler& compiler) { ShaderOutput *out; out = output("Is Strand"); if(!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_IS_STRAND, compiler.stack_assign(out)); } out = output("Intercept"); if(!out->links.empty()) { int attr = compiler.attribute(ATTR_STD_CURVE_INTERCEPT); compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_FLOAT); } out = output("Thickness"); if(!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_THICKNESS, compiler.stack_assign(out)); } out = output("Tangent Normal"); if(!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_TANGENT_NORMAL, compiler.stack_assign(out)); } /*out = output("Fade"); if(!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_FADE, compiler.stack_assign(out)); }*/ } void HairInfoNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_hair_info"); } /* Value */ NODE_DEFINE(ValueNode) { NodeType* type = NodeType::add("value", create, NodeType::SHADER); SOCKET_FLOAT(value, "Value", 0.0f); SOCKET_OUT_FLOAT(value, "Value"); return type; } ValueNode::ValueNode() : ShaderNode(node_type) { } void ValueNode::constant_fold(const ConstantFolder& folder) { folder.make_constant(value); } void ValueNode::compile(SVMCompiler& compiler) { ShaderOutput *val_out = output("Value"); compiler.add_node(NODE_VALUE_F, __float_as_int(value), compiler.stack_assign(val_out)); } void ValueNode::compile(OSLCompiler& compiler) { compiler.parameter("value_value", value); compiler.add(this, "node_value"); } /* Color */ NODE_DEFINE(ColorNode) { NodeType* type = NodeType::add("color", create, NodeType::SHADER); SOCKET_COLOR(value, "Value", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_COLOR(color, "Color"); return type; } ColorNode::ColorNode() : ShaderNode(node_type) { } void ColorNode::constant_fold(const ConstantFolder& folder) { folder.make_constant(value); } void ColorNode::compile(SVMCompiler& compiler) { ShaderOutput *color_out = output("Color"); if(!color_out->links.empty()) { compiler.add_node(NODE_VALUE_V, compiler.stack_assign(color_out)); compiler.add_node(NODE_VALUE_V, value); } } void ColorNode::compile(OSLCompiler& compiler) { compiler.parameter_color("color_value", value); compiler.add(this, "node_value"); } /* Add Closure */ NODE_DEFINE(AddClosureNode) { NodeType* type = NodeType::add("add_closure", create, NodeType::SHADER); SOCKET_IN_CLOSURE(closure1, "Closure1"); SOCKET_IN_CLOSURE(closure2, "Closure2"); SOCKET_OUT_CLOSURE(closure, "Closure"); return type; } AddClosureNode::AddClosureNode() : ShaderNode(node_type) { special_type = SHADER_SPECIAL_TYPE_COMBINE_CLOSURE; } void AddClosureNode::compile(SVMCompiler& /*compiler*/) { /* handled in the SVM compiler */ } void AddClosureNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_add_closure"); } void AddClosureNode::constant_fold(const ConstantFolder& folder) { ShaderInput *closure1_in = input("Closure1"); ShaderInput *closure2_in = input("Closure2"); /* remove useless add closures nodes */ if(!closure1_in->link) { folder.bypass_or_discard(closure2_in); } else if(!closure2_in->link) { folder.bypass_or_discard(closure1_in); } } /* Mix Closure */ NODE_DEFINE(MixClosureNode) { NodeType* type = NodeType::add("mix_closure", create, NodeType::SHADER); SOCKET_IN_FLOAT(fac, "Fac", 0.5f); SOCKET_IN_CLOSURE(closure1, "Closure1"); SOCKET_IN_CLOSURE(closure2, "Closure2"); SOCKET_OUT_CLOSURE(closure, "Closure"); return type; } MixClosureNode::MixClosureNode() : ShaderNode(node_type) { special_type = SHADER_SPECIAL_TYPE_COMBINE_CLOSURE; } void MixClosureNode::compile(SVMCompiler& /*compiler*/) { /* handled in the SVM compiler */ } void MixClosureNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_mix_closure"); } void MixClosureNode::constant_fold(const ConstantFolder& folder) { ShaderInput *fac_in = input("Fac"); ShaderInput *closure1_in = input("Closure1"); ShaderInput *closure2_in = input("Closure2"); /* remove useless mix closures nodes */ if(closure1_in->link == closure2_in->link) { folder.bypass_or_discard(closure1_in); } /* remove unused mix closure input when factor is 0.0 or 1.0 * check for closure links and make sure factor link is disconnected */ else if(!fac_in->link) { /* factor 0.0 */ if(fac <= 0.0f) { folder.bypass_or_discard(closure1_in); } /* factor 1.0 */ else if(fac >= 1.0f) { folder.bypass_or_discard(closure2_in); } } } /* Mix Closure */ NODE_DEFINE(MixClosureWeightNode) { NodeType* type = NodeType::add("mix_closure_weight", create, NodeType::SHADER); SOCKET_IN_FLOAT(weight, "Weight", 1.0f); SOCKET_IN_FLOAT(fac, "Fac", 1.0f); SOCKET_OUT_FLOAT(weight1, "Weight1"); SOCKET_OUT_FLOAT(weight2, "Weight2"); return type; } MixClosureWeightNode::MixClosureWeightNode() : ShaderNode(node_type) { } void MixClosureWeightNode::compile(SVMCompiler& compiler) { ShaderInput *weight_in = input("Weight"); ShaderInput *fac_in = input("Fac"); ShaderOutput *weight1_out = output("Weight1"); ShaderOutput *weight2_out = output("Weight2"); compiler.add_node(NODE_MIX_CLOSURE, compiler.encode_uchar4( compiler.stack_assign(fac_in), compiler.stack_assign(weight_in), compiler.stack_assign(weight1_out), compiler.stack_assign(weight2_out))); } void MixClosureWeightNode::compile(OSLCompiler& /*compiler*/) { assert(0); } /* Invert */ NODE_DEFINE(InvertNode) { NodeType* type = NodeType::add("invert", create, NodeType::SHADER); SOCKET_IN_FLOAT(fac, "Fac", 1.0f); SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_COLOR(color, "Color"); return type; } InvertNode::InvertNode() : ShaderNode(node_type) { } void InvertNode::constant_fold(const ConstantFolder& folder) { ShaderInput *fac_in = input("Fac"); ShaderInput *color_in = input("Color"); if(!fac_in->link) { /* evaluate fully constant node */ if(!color_in->link) { folder.make_constant(interp(color, make_float3(1.0f, 1.0f, 1.0f) - color, fac)); } /* remove no-op node */ else if(fac == 0.0f) { folder.bypass(color_in->link); } } } void InvertNode::compile(SVMCompiler& compiler) { ShaderInput *fac_in = input("Fac"); ShaderInput *color_in = input("Color"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_INVERT, compiler.stack_assign(fac_in), compiler.stack_assign(color_in), compiler.stack_assign(color_out)); } void InvertNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_invert"); } /* Mix */ NODE_DEFINE(MixNode) { NodeType* type = NodeType::add("mix", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("mix", NODE_MIX_BLEND); type_enum.insert("add", NODE_MIX_ADD); type_enum.insert("multiply", NODE_MIX_MUL); type_enum.insert("screen", NODE_MIX_SCREEN); type_enum.insert("overlay", NODE_MIX_OVERLAY); type_enum.insert("subtract", NODE_MIX_SUB); type_enum.insert("divide", NODE_MIX_DIV); type_enum.insert("difference", NODE_MIX_DIFF); type_enum.insert("darken", NODE_MIX_DARK); type_enum.insert("lighten", NODE_MIX_LIGHT); type_enum.insert("dodge", NODE_MIX_DODGE); type_enum.insert("burn", NODE_MIX_BURN); type_enum.insert("hue", NODE_MIX_HUE); type_enum.insert("saturation", NODE_MIX_SAT); type_enum.insert("value", NODE_MIX_VAL); type_enum.insert("color", NODE_MIX_COLOR); type_enum.insert("soft_light", NODE_MIX_SOFT); type_enum.insert("linear_light", NODE_MIX_LINEAR); SOCKET_ENUM(type, "Type", type_enum, NODE_MIX_BLEND); SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); SOCKET_IN_FLOAT(fac, "Fac", 0.5f); SOCKET_IN_COLOR(color1, "Color1", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_COLOR(color2, "Color2", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_COLOR(color, "Color"); return type; } MixNode::MixNode() : ShaderNode(node_type) { } void MixNode::compile(SVMCompiler& compiler) { ShaderInput *fac_in = input("Fac"); ShaderInput *color1_in = input("Color1"); ShaderInput *color2_in = input("Color2"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_MIX, compiler.stack_assign(fac_in), compiler.stack_assign(color1_in), compiler.stack_assign(color2_in)); compiler.add_node(NODE_MIX, type, compiler.stack_assign(color_out)); if(use_clamp) { compiler.add_node(NODE_MIX, 0, compiler.stack_assign(color_out)); compiler.add_node(NODE_MIX, NODE_MIX_CLAMP, compiler.stack_assign(color_out)); } } void MixNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "type"); compiler.parameter(this, "use_clamp"); compiler.add(this, "node_mix"); } void MixNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant_clamp(svm_mix(type, fac, color1, color2), use_clamp); } else { folder.fold_mix(type, use_clamp); } } /* Combine RGB */ NODE_DEFINE(CombineRGBNode) { NodeType* type = NodeType::add("combine_rgb", create, NodeType::SHADER); SOCKET_IN_FLOAT(r, "R", 0.0f); SOCKET_IN_FLOAT(g, "G", 0.0f); SOCKET_IN_FLOAT(b, "B", 0.0f); SOCKET_OUT_COLOR(image, "Image"); return type; } CombineRGBNode::CombineRGBNode() : ShaderNode(node_type) { } void CombineRGBNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant(make_float3(r, g, b)); } } void CombineRGBNode::compile(SVMCompiler& compiler) { ShaderInput *red_in = input("R"); ShaderInput *green_in = input("G"); ShaderInput *blue_in = input("B"); ShaderOutput *color_out = output("Image"); compiler.add_node(NODE_COMBINE_VECTOR, compiler.stack_assign(red_in), 0, compiler.stack_assign(color_out)); compiler.add_node(NODE_COMBINE_VECTOR, compiler.stack_assign(green_in), 1, compiler.stack_assign(color_out)); compiler.add_node(NODE_COMBINE_VECTOR, compiler.stack_assign(blue_in), 2, compiler.stack_assign(color_out)); } void CombineRGBNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_combine_rgb"); } /* Combine XYZ */ NODE_DEFINE(CombineXYZNode) { NodeType* type = NodeType::add("combine_xyz", create, NodeType::SHADER); SOCKET_IN_FLOAT(x, "X", 0.0f); SOCKET_IN_FLOAT(y, "Y", 0.0f); SOCKET_IN_FLOAT(z, "Z", 0.0f); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } CombineXYZNode::CombineXYZNode() : ShaderNode(node_type) { } void CombineXYZNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant(make_float3(x, y, z)); } } void CombineXYZNode::compile(SVMCompiler& compiler) { ShaderInput *x_in = input("X"); ShaderInput *y_in = input("Y"); ShaderInput *z_in = input("Z"); ShaderOutput *vector_out = output("Vector"); compiler.add_node(NODE_COMBINE_VECTOR, compiler.stack_assign(x_in), 0, compiler.stack_assign(vector_out)); compiler.add_node(NODE_COMBINE_VECTOR, compiler.stack_assign(y_in), 1, compiler.stack_assign(vector_out)); compiler.add_node(NODE_COMBINE_VECTOR, compiler.stack_assign(z_in), 2, compiler.stack_assign(vector_out)); } void CombineXYZNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_combine_xyz"); } /* Combine HSV */ NODE_DEFINE(CombineHSVNode) { NodeType* type = NodeType::add("combine_hsv", create, NodeType::SHADER); SOCKET_IN_FLOAT(h, "H", 0.0f); SOCKET_IN_FLOAT(s, "S", 0.0f); SOCKET_IN_FLOAT(v, "V", 0.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } CombineHSVNode::CombineHSVNode() : ShaderNode(node_type) { } void CombineHSVNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant(hsv_to_rgb(make_float3(h, s, v))); } } void CombineHSVNode::compile(SVMCompiler& compiler) { ShaderInput *hue_in = input("H"); ShaderInput *saturation_in = input("S"); ShaderInput *value_in = input("V"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_COMBINE_HSV, compiler.stack_assign(hue_in), compiler.stack_assign(saturation_in), compiler.stack_assign(value_in)); compiler.add_node(NODE_COMBINE_HSV, compiler.stack_assign(color_out)); } void CombineHSVNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_combine_hsv"); } /* Gamma */ NODE_DEFINE(GammaNode) { NodeType* type = NodeType::add("gamma", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_FLOAT(gamma, "Gamma", 1.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } GammaNode::GammaNode() : ShaderNode(node_type) { } void GammaNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant(svm_math_gamma_color(color, gamma)); } else { ShaderInput *color_in = input("Color"); ShaderInput *gamma_in = input("Gamma"); /* 1 ^ X == X ^ 0 == 1 */ if(folder.is_one(color_in) || folder.is_zero(gamma_in)) { folder.make_one(); } /* X ^ 1 == X */ else if(folder.is_one(gamma_in)) { folder.try_bypass_or_make_constant(color_in, false); } } } void GammaNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Color"); ShaderInput *gamma_in = input("Gamma"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_GAMMA, compiler.stack_assign(gamma_in), compiler.stack_assign(color_in), compiler.stack_assign(color_out)); } void GammaNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_gamma"); } /* Bright Contrast */ NODE_DEFINE(BrightContrastNode) { NodeType* type = NodeType::add("brightness_contrast", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_FLOAT(bright, "Bright", 0.0f); SOCKET_IN_FLOAT(contrast, "Contrast", 0.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } BrightContrastNode::BrightContrastNode() : ShaderNode(node_type) { } void BrightContrastNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant(svm_brightness_contrast(color, bright, contrast)); } } void BrightContrastNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Color"); ShaderInput *bright_in = input("Bright"); ShaderInput *contrast_in = input("Contrast"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_BRIGHTCONTRAST, compiler.stack_assign(color_in), compiler.stack_assign(color_out), compiler.encode_uchar4( compiler.stack_assign(bright_in), compiler.stack_assign(contrast_in))); } void BrightContrastNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_brightness"); } /* Separate RGB */ NODE_DEFINE(SeparateRGBNode) { NodeType* type = NodeType::add("separate_rgb", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Image", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_FLOAT(r, "R"); SOCKET_OUT_FLOAT(g, "G"); SOCKET_OUT_FLOAT(b, "B"); return type; } SeparateRGBNode::SeparateRGBNode() : ShaderNode(node_type) { } void SeparateRGBNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { for(int channel = 0; channel < 3; channel++) { if(outputs[channel] == folder.output) { folder.make_constant(color[channel]); return; } } } } void SeparateRGBNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Image"); ShaderOutput *red_out = output("R"); ShaderOutput *green_out = output("G"); ShaderOutput *blue_out = output("B"); compiler.add_node(NODE_SEPARATE_VECTOR, compiler.stack_assign(color_in), 0, compiler.stack_assign(red_out)); compiler.add_node(NODE_SEPARATE_VECTOR, compiler.stack_assign(color_in), 1, compiler.stack_assign(green_out)); compiler.add_node(NODE_SEPARATE_VECTOR, compiler.stack_assign(color_in), 2, compiler.stack_assign(blue_out)); } void SeparateRGBNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_separate_rgb"); } /* Separate XYZ */ NODE_DEFINE(SeparateXYZNode) { NodeType* type = NodeType::add("separate_xyz", create, NodeType::SHADER); SOCKET_IN_COLOR(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_FLOAT(x, "X"); SOCKET_OUT_FLOAT(y, "Y"); SOCKET_OUT_FLOAT(z, "Z"); return type; } SeparateXYZNode::SeparateXYZNode() : ShaderNode(node_type) { } void SeparateXYZNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { for(int channel = 0; channel < 3; channel++) { if(outputs[channel] == folder.output) { folder.make_constant(vector[channel]); return; } } } } void SeparateXYZNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *x_out = output("X"); ShaderOutput *y_out = output("Y"); ShaderOutput *z_out = output("Z"); compiler.add_node(NODE_SEPARATE_VECTOR, compiler.stack_assign(vector_in), 0, compiler.stack_assign(x_out)); compiler.add_node(NODE_SEPARATE_VECTOR, compiler.stack_assign(vector_in), 1, compiler.stack_assign(y_out)); compiler.add_node(NODE_SEPARATE_VECTOR, compiler.stack_assign(vector_in), 2, compiler.stack_assign(z_out)); } void SeparateXYZNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_separate_xyz"); } /* Separate HSV */ NODE_DEFINE(SeparateHSVNode) { NodeType* type = NodeType::add("separate_hsv", create, NodeType::SHADER); SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_FLOAT(h, "H"); SOCKET_OUT_FLOAT(s, "S"); SOCKET_OUT_FLOAT(v, "V"); return type; } SeparateHSVNode::SeparateHSVNode() : ShaderNode(node_type) { } void SeparateHSVNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { float3 hsv = rgb_to_hsv(color); for(int channel = 0; channel < 3; channel++) { if(outputs[channel] == folder.output) { folder.make_constant(hsv[channel]); return; } } } } void SeparateHSVNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Color"); ShaderOutput *hue_out = output("H"); ShaderOutput *saturation_out = output("S"); ShaderOutput *value_out = output("V"); compiler.add_node(NODE_SEPARATE_HSV, compiler.stack_assign(color_in), compiler.stack_assign(hue_out), compiler.stack_assign(saturation_out)); compiler.add_node(NODE_SEPARATE_HSV, compiler.stack_assign(value_out)); } void SeparateHSVNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_separate_hsv"); } /* Hue Saturation Value */ NODE_DEFINE(HSVNode) { NodeType* type = NodeType::add("hsv", create, NodeType::SHADER); SOCKET_IN_FLOAT(hue, "Hue", 0.5f); SOCKET_IN_FLOAT(saturation, "Saturation", 1.0f); SOCKET_IN_FLOAT(value, "Value", 1.0f); SOCKET_IN_FLOAT(fac, "Fac", 1.0f); SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_COLOR(color, "Color"); return type; } HSVNode::HSVNode() : ShaderNode(node_type) { } void HSVNode::compile(SVMCompiler& compiler) { ShaderInput *hue_in = input("Hue"); ShaderInput *saturation_in = input("Saturation"); ShaderInput *value_in = input("Value"); ShaderInput *fac_in = input("Fac"); ShaderInput *color_in = input("Color"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_HSV, compiler.encode_uchar4( compiler.stack_assign(color_in), compiler.stack_assign(fac_in), compiler.stack_assign(color_out)), compiler.encode_uchar4( compiler.stack_assign(hue_in), compiler.stack_assign(saturation_in), compiler.stack_assign(value_in))); } void HSVNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_hsv"); } /* Attribute */ NODE_DEFINE(AttributeNode) { NodeType* type = NodeType::add("attribute", create, NodeType::SHADER); SOCKET_STRING(attribute, "Attribute", ustring("")); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_VECTOR(vector, "Vector"); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } AttributeNode::AttributeNode() : ShaderNode(node_type) { } void AttributeNode::attributes(Shader *shader, AttributeRequestSet *attributes) { ShaderOutput *color_out = output("Color"); ShaderOutput *vector_out = output("Vector"); ShaderOutput *fac_out = output("Fac"); if(!color_out->links.empty() || !vector_out->links.empty() || !fac_out->links.empty()) { AttributeStandard std = Attribute::name_standard(attribute.c_str()); if(std != ATTR_STD_NONE) attributes->add(std); else attributes->add(attribute); } if(shader->has_volume) attributes->add(ATTR_STD_GENERATED_TRANSFORM); ShaderNode::attributes(shader, attributes); } void AttributeNode::compile(SVMCompiler& compiler) { ShaderOutput *color_out = output("Color"); ShaderOutput *vector_out = output("Vector"); ShaderOutput *fac_out = output("Fac"); ShaderNodeType attr_node = NODE_ATTR; AttributeStandard std = Attribute::name_standard(attribute.c_str()); int attr; if(std != ATTR_STD_NONE) attr = compiler.attribute(std); else attr = compiler.attribute(attribute); if(bump == SHADER_BUMP_DX) attr_node = NODE_ATTR_BUMP_DX; else if(bump == SHADER_BUMP_DY) attr_node = NODE_ATTR_BUMP_DY; if(!color_out->links.empty() || !vector_out->links.empty()) { if(!color_out->links.empty()) { compiler.add_node(attr_node, attr, compiler.stack_assign(color_out), NODE_ATTR_FLOAT3); } if(!vector_out->links.empty()) { compiler.add_node(attr_node, attr, compiler.stack_assign(vector_out), NODE_ATTR_FLOAT3); } } if(!fac_out->links.empty()) { compiler.add_node(attr_node, attr, compiler.stack_assign(fac_out), NODE_ATTR_FLOAT); } } void AttributeNode::compile(OSLCompiler& compiler) { if(bump == SHADER_BUMP_DX) compiler.parameter("bump_offset", "dx"); else if(bump == SHADER_BUMP_DY) compiler.parameter("bump_offset", "dy"); else compiler.parameter("bump_offset", "center"); if(Attribute::name_standard(attribute.c_str()) != ATTR_STD_NONE) compiler.parameter("name", (string("geom:") + attribute.c_str()).c_str()); else compiler.parameter("name", attribute.c_str()); compiler.add(this, "node_attribute"); } /* Camera */ NODE_DEFINE(CameraNode) { NodeType* type = NodeType::add("camera_info", create, NodeType::SHADER); SOCKET_OUT_VECTOR(view_vector, "View Vector"); SOCKET_OUT_FLOAT(view_z_depth, "View Z Depth"); SOCKET_OUT_FLOAT(view_distance, "View Distance"); return type; } CameraNode::CameraNode() : ShaderNode(node_type) { } void CameraNode::compile(SVMCompiler& compiler) { ShaderOutput *vector_out = output("View Vector"); ShaderOutput *z_depth_out = output("View Z Depth"); ShaderOutput *distance_out = output("View Distance"); compiler.add_node(NODE_CAMERA, compiler.stack_assign(vector_out), compiler.stack_assign(z_depth_out), compiler.stack_assign(distance_out)); } void CameraNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_camera"); } /* Fresnel */ NODE_DEFINE(FresnelNode) { NodeType* type = NodeType::add("fresnel", create, NodeType::SHADER); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_IN_FLOAT(IOR, "IOR", 1.45f); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } FresnelNode::FresnelNode() : ShaderNode(node_type) { } void FresnelNode::compile(SVMCompiler& compiler) { ShaderInput *normal_in = input("Normal"); ShaderInput *IOR_in = input("IOR"); ShaderOutput *fac_out = output("Fac"); compiler.add_node(NODE_FRESNEL, compiler.stack_assign(IOR_in), __float_as_int(IOR), compiler.encode_uchar4( compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(fac_out))); } void FresnelNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_fresnel"); } /* Layer Weight */ NODE_DEFINE(LayerWeightNode) { NodeType* type = NodeType::add("layer_weight", create, NodeType::SHADER); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_IN_FLOAT(blend, "Blend", 0.5f); SOCKET_OUT_FLOAT(fresnel, "Fresnel"); SOCKET_OUT_FLOAT(facing, "Facing"); return type; } LayerWeightNode::LayerWeightNode() : ShaderNode(node_type) { } void LayerWeightNode::compile(SVMCompiler& compiler) { ShaderInput *normal_in = input("Normal"); ShaderInput *blend_in = input("Blend"); ShaderOutput *fresnel_out = output("Fresnel"); ShaderOutput *facing_out = output("Facing"); if(!fresnel_out->links.empty()) { compiler.add_node(NODE_LAYER_WEIGHT, compiler.stack_assign_if_linked(blend_in), __float_as_int(blend), compiler.encode_uchar4(NODE_LAYER_WEIGHT_FRESNEL, compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(fresnel_out))); } if(!facing_out->links.empty()) { compiler.add_node(NODE_LAYER_WEIGHT, compiler.stack_assign_if_linked(blend_in), __float_as_int(blend), compiler.encode_uchar4(NODE_LAYER_WEIGHT_FACING, compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(facing_out))); } } void LayerWeightNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_layer_weight"); } /* Wireframe */ NODE_DEFINE(WireframeNode) { NodeType* type = NodeType::add("wireframe", create, NodeType::SHADER); SOCKET_BOOLEAN(use_pixel_size, "Use Pixel Size", false); SOCKET_IN_FLOAT(size, "Size", 0.01f); SOCKET_OUT_FLOAT(fac, "Fac"); return type; } WireframeNode::WireframeNode() : ShaderNode(node_type) { } void WireframeNode::compile(SVMCompiler& compiler) { ShaderInput *size_in = input("Size"); ShaderOutput *fac_out = output("Fac"); NodeBumpOffset bump_offset = NODE_BUMP_OFFSET_CENTER; if(bump == SHADER_BUMP_DX) { bump_offset = NODE_BUMP_OFFSET_DX; } else if(bump == SHADER_BUMP_DY) { bump_offset = NODE_BUMP_OFFSET_DY; } compiler.add_node(NODE_WIREFRAME, compiler.stack_assign(size_in), compiler.stack_assign(fac_out), compiler.encode_uchar4(use_pixel_size, bump_offset, 0, 0)); } void WireframeNode::compile(OSLCompiler& compiler) { if(bump == SHADER_BUMP_DX) { compiler.parameter("bump_offset", "dx"); } else if(bump == SHADER_BUMP_DY) { compiler.parameter("bump_offset", "dy"); } else { compiler.parameter("bump_offset", "center"); } compiler.parameter(this, "use_pixel_size"); compiler.add(this, "node_wireframe"); } /* Wavelength */ NODE_DEFINE(WavelengthNode) { NodeType* type = NodeType::add("wavelength", create, NodeType::SHADER); SOCKET_IN_FLOAT(wavelength, "Wavelength", 500.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } WavelengthNode::WavelengthNode() : ShaderNode(node_type) { } void WavelengthNode::compile(SVMCompiler& compiler) { ShaderInput *wavelength_in = input("Wavelength"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_WAVELENGTH, compiler.stack_assign(wavelength_in), compiler.stack_assign(color_out)); } void WavelengthNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_wavelength"); } /* Blackbody */ NODE_DEFINE(BlackbodyNode) { NodeType* type = NodeType::add("blackbody", create, NodeType::SHADER); SOCKET_IN_FLOAT(temperature, "Temperature", 1200.0f); SOCKET_OUT_COLOR(color, "Color"); return type; } BlackbodyNode::BlackbodyNode() : ShaderNode(node_type) { } void BlackbodyNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant(svm_math_blackbody_color(temperature)); } } void BlackbodyNode::compile(SVMCompiler& compiler) { ShaderInput *temperature_in = input("Temperature"); ShaderOutput *color_out = output("Color"); compiler.add_node(NODE_BLACKBODY, compiler.stack_assign(temperature_in), compiler.stack_assign(color_out)); } void BlackbodyNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_blackbody"); } /* Output */ NODE_DEFINE(OutputNode) { NodeType* type = NodeType::add("output", create, NodeType::SHADER); SOCKET_IN_CLOSURE(surface, "Surface"); SOCKET_IN_CLOSURE(volume, "Volume"); SOCKET_IN_FLOAT(displacement, "Displacement", 0.0f); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f)); return type; } OutputNode::OutputNode() : ShaderNode(node_type) { special_type = SHADER_SPECIAL_TYPE_OUTPUT; } void OutputNode::compile(SVMCompiler& compiler) { if(compiler.output_type() == SHADER_TYPE_DISPLACEMENT) { ShaderInput *displacement_in = input("Displacement"); if(displacement_in->link) { compiler.add_node(NODE_SET_DISPLACEMENT, compiler.stack_assign(displacement_in)); } } } void OutputNode::compile(OSLCompiler& compiler) { if(compiler.output_type() == SHADER_TYPE_SURFACE) compiler.add(this, "node_output_surface"); else if(compiler.output_type() == SHADER_TYPE_VOLUME) compiler.add(this, "node_output_volume"); else if(compiler.output_type() == SHADER_TYPE_DISPLACEMENT) compiler.add(this, "node_output_displacement"); } /* Math */ NODE_DEFINE(MathNode) { NodeType* type = NodeType::add("math", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("add", NODE_MATH_ADD); type_enum.insert("subtract", NODE_MATH_SUBTRACT); type_enum.insert("multiply", NODE_MATH_MULTIPLY); type_enum.insert("divide", NODE_MATH_DIVIDE); type_enum.insert("sine", NODE_MATH_SINE); type_enum.insert("cosine", NODE_MATH_COSINE); type_enum.insert("tangent", NODE_MATH_TANGENT); type_enum.insert("arcsine", NODE_MATH_ARCSINE); type_enum.insert("arccosine", NODE_MATH_ARCCOSINE); type_enum.insert("arctangent", NODE_MATH_ARCTANGENT); type_enum.insert("power", NODE_MATH_POWER); type_enum.insert("logarithm", NODE_MATH_LOGARITHM); type_enum.insert("minimum", NODE_MATH_MINIMUM); type_enum.insert("maximum", NODE_MATH_MAXIMUM); type_enum.insert("round", NODE_MATH_ROUND); type_enum.insert("less_than", NODE_MATH_LESS_THAN); type_enum.insert("greater_than", NODE_MATH_GREATER_THAN); type_enum.insert("modulo", NODE_MATH_MODULO); type_enum.insert("absolute", NODE_MATH_ABSOLUTE); SOCKET_ENUM(type, "Type", type_enum, NODE_MATH_ADD); SOCKET_BOOLEAN(use_clamp, "Use Clamp", false); SOCKET_IN_FLOAT(value1, "Value1", 0.0f); SOCKET_IN_FLOAT(value2, "Value2", 0.0f); SOCKET_OUT_FLOAT(value, "Value"); return type; } MathNode::MathNode() : ShaderNode(node_type) { } void MathNode::constant_fold(const ConstantFolder& folder) { if(folder.all_inputs_constant()) { folder.make_constant_clamp(svm_math(type, value1, value2), use_clamp); } else { folder.fold_math(type, use_clamp); } } void MathNode::compile(SVMCompiler& compiler) { ShaderInput *value1_in = input("Value1"); ShaderInput *value2_in = input("Value2"); ShaderOutput *value_out = output("Value"); compiler.add_node(NODE_MATH, type, compiler.stack_assign(value1_in), compiler.stack_assign(value2_in)); compiler.add_node(NODE_MATH, compiler.stack_assign(value_out)); if(use_clamp) { compiler.add_node(NODE_MATH, NODE_MATH_CLAMP, compiler.stack_assign(value_out)); compiler.add_node(NODE_MATH, compiler.stack_assign(value_out)); } } void MathNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "type"); compiler.parameter(this, "use_clamp"); compiler.add(this, "node_math"); } /* VectorMath */ NODE_DEFINE(VectorMathNode) { NodeType* type = NodeType::add("vector_math", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("add", NODE_VECTOR_MATH_ADD); type_enum.insert("subtract", NODE_VECTOR_MATH_SUBTRACT); type_enum.insert("average", NODE_VECTOR_MATH_AVERAGE); type_enum.insert("dot_product", NODE_VECTOR_MATH_DOT_PRODUCT); type_enum.insert("cross_product", NODE_VECTOR_MATH_CROSS_PRODUCT); type_enum.insert("normalize", NODE_VECTOR_MATH_NORMALIZE); SOCKET_ENUM(type, "Type", type_enum, NODE_VECTOR_MATH_ADD); SOCKET_IN_VECTOR(vector1, "Vector1", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_IN_VECTOR(vector2, "Vector2", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_FLOAT(value, "Value"); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } VectorMathNode::VectorMathNode() : ShaderNode(node_type) { } void VectorMathNode::constant_fold(const ConstantFolder& folder) { float value; float3 vector; if(folder.all_inputs_constant()) { svm_vector_math(&value, &vector, type, vector1, vector2); if(folder.output == output("Value")) { folder.make_constant(value); } else if(folder.output == output("Vector")) { folder.make_constant(vector); } } else { folder.fold_vector_math(type); } } void VectorMathNode::compile(SVMCompiler& compiler) { ShaderInput *vector1_in = input("Vector1"); ShaderInput *vector2_in = input("Vector2"); ShaderOutput *value_out = output("Value"); ShaderOutput *vector_out = output("Vector"); compiler.add_node(NODE_VECTOR_MATH, type, compiler.stack_assign(vector1_in), compiler.stack_assign(vector2_in)); compiler.add_node(NODE_VECTOR_MATH, compiler.stack_assign(value_out), compiler.stack_assign(vector_out)); } void VectorMathNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "type"); compiler.add(this, "node_vector_math"); } /* VectorTransform */ NODE_DEFINE(VectorTransformNode) { NodeType* type = NodeType::add("vector_transform", create, NodeType::SHADER); static NodeEnum type_enum; type_enum.insert("vector", NODE_VECTOR_TRANSFORM_TYPE_VECTOR); type_enum.insert("point", NODE_VECTOR_TRANSFORM_TYPE_POINT); type_enum.insert("normal", NODE_VECTOR_TRANSFORM_TYPE_NORMAL); SOCKET_ENUM(type, "Type", type_enum, NODE_VECTOR_TRANSFORM_TYPE_VECTOR); static NodeEnum space_enum; space_enum.insert("world", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD); space_enum.insert("object", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT); space_enum.insert("camera", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA); SOCKET_ENUM(convert_from, "Convert From", space_enum, NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD); SOCKET_ENUM(convert_to, "Convert To", space_enum, NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT); SOCKET_IN_VECTOR(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_VECTOR(vector, "Vector"); return type; } VectorTransformNode::VectorTransformNode() : ShaderNode(node_type) { } void VectorTransformNode::compile(SVMCompiler& compiler) { ShaderInput *vector_in = input("Vector"); ShaderOutput *vector_out = output("Vector"); compiler.add_node(NODE_VECTOR_TRANSFORM, compiler.encode_uchar4(type, convert_from, convert_to), compiler.encode_uchar4(compiler.stack_assign(vector_in), compiler.stack_assign(vector_out))); } void VectorTransformNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "type"); compiler.parameter(this, "convert_from"); compiler.parameter(this, "convert_to"); compiler.add(this, "node_vector_transform"); } /* BumpNode */ NODE_DEFINE(BumpNode) { NodeType* type = NodeType::add("bump", create, NodeType::SHADER); SOCKET_BOOLEAN(invert, "Invert", false); SOCKET_BOOLEAN(use_object_space, "UseObjectSpace", false); /* this input is used by the user, but after graph transform it is no longer * used and moved to sampler center/x/y instead */ SOCKET_IN_FLOAT(height, "Height", 1.0f); SOCKET_IN_FLOAT(sample_center, "SampleCenter", 0.0f); SOCKET_IN_FLOAT(sample_x, "SampleX", 0.0f); SOCKET_IN_FLOAT(sample_y, "SampleY", 0.0f); SOCKET_IN_NORMAL(normal, "Normal", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(strength, "Strength", 1.0f); SOCKET_IN_FLOAT(distance, "Distance", 0.1f); SOCKET_OUT_NORMAL(normal, "Normal"); return type; } BumpNode::BumpNode() : ShaderNode(node_type) { special_type = SHADER_SPECIAL_TYPE_BUMP; } void BumpNode::compile(SVMCompiler& compiler) { ShaderInput *center_in = input("SampleCenter"); ShaderInput *dx_in = input("SampleX"); ShaderInput *dy_in = input("SampleY"); ShaderInput *normal_in = input("Normal"); ShaderInput *strength_in = input("Strength"); ShaderInput *distance_in = input("Distance"); ShaderOutput *normal_out = output("Normal"); /* pack all parameters in the node */ compiler.add_node(NODE_SET_BUMP, compiler.encode_uchar4( compiler.stack_assign_if_linked(normal_in), compiler.stack_assign(distance_in), invert, use_object_space), compiler.encode_uchar4( compiler.stack_assign(center_in), compiler.stack_assign(dx_in), compiler.stack_assign(dy_in), compiler.stack_assign(strength_in)), compiler.stack_assign(normal_out)); } void BumpNode::compile(OSLCompiler& compiler) { compiler.parameter(this, "invert"); compiler.parameter(this, "use_object_space"); compiler.add(this, "node_bump"); } void BumpNode::constant_fold(const ConstantFolder& folder) { ShaderInput *height_in = input("Height"); ShaderInput *normal_in = input("Normal"); if(height_in->link == NULL) { if(normal_in->link == NULL) { GeometryNode *geom = new GeometryNode(); folder.graph->add(geom); folder.bypass(geom->output("Normal")); } else { folder.bypass(normal_in->link); } } /* TODO(sergey): Ignore bump with zero strength. */ } /* Curve node */ CurvesNode::CurvesNode(const NodeType *node_type) : ShaderNode(node_type) { } void CurvesNode::constant_fold(const ConstantFolder& folder, ShaderInput *value_in) { ShaderInput *fac_in = input("Fac"); /* evaluate fully constant node */ if(folder.all_inputs_constant()) { if(curves.size() == 0) { return; } float3 pos = (value - make_float3(min_x, min_x, min_x)) / (max_x - min_x); float3 result; result[0] = rgb_ramp_lookup(curves.data(), pos[0], true, true, curves.size()).x; result[1] = rgb_ramp_lookup(curves.data(), pos[1], true, true, curves.size()).y; result[2] = rgb_ramp_lookup(curves.data(), pos[2], true, true, curves.size()).z; folder.make_constant(interp(value, result, fac)); } /* remove no-op node */ else if(!fac_in->link && fac == 0.0f) { /* link is not null because otherwise all inputs are constant */ folder.bypass(value_in->link); } } void CurvesNode::compile(SVMCompiler& compiler, int type, ShaderInput *value_in, ShaderOutput *value_out) { if(curves.size() == 0) return; ShaderInput *fac_in = input("Fac"); compiler.add_node(type, compiler.encode_uchar4(compiler.stack_assign(fac_in), compiler.stack_assign(value_in), compiler.stack_assign(value_out)), __float_as_int(min_x), __float_as_int(max_x)); compiler.add_node(curves.size()); for(int i = 0; i < curves.size(); i++) compiler.add_node(float3_to_float4(curves[i])); } void CurvesNode::compile(OSLCompiler& compiler, const char* name) { if(curves.size() == 0) return; compiler.parameter_color_array("ramp", curves); compiler.parameter(this, "min_x"); compiler.parameter(this, "max_x"); compiler.add(this, name); } void CurvesNode::compile(SVMCompiler& /*compiler*/) { assert(0); } void CurvesNode::compile(OSLCompiler& /*compiler*/) { assert(0); } /* RGBCurvesNode */ NODE_DEFINE(RGBCurvesNode) { NodeType* type = NodeType::add("rgb_curves", create, NodeType::SHADER); SOCKET_COLOR_ARRAY(curves, "Curves", array()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_IN_COLOR(value, "Color", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_COLOR(value, "Color"); return type; } RGBCurvesNode::RGBCurvesNode() : CurvesNode(node_type) { } void RGBCurvesNode::constant_fold(const ConstantFolder& folder) { CurvesNode::constant_fold(folder, input("Color")); } void RGBCurvesNode::compile(SVMCompiler& compiler) { CurvesNode::compile(compiler, NODE_RGB_CURVES, input("Color"), output("Color")); } void RGBCurvesNode::compile(OSLCompiler& compiler) { CurvesNode::compile(compiler, "node_rgb_curves"); } /* VectorCurvesNode */ NODE_DEFINE(VectorCurvesNode) { NodeType* type = NodeType::add("vector_curves", create, NodeType::SHADER); SOCKET_VECTOR_ARRAY(curves, "Curves", array()); SOCKET_FLOAT(min_x, "Min X", 0.0f); SOCKET_FLOAT(max_x, "Max X", 1.0f); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_IN_VECTOR(value, "Vector", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_VECTOR(value, "Vector"); return type; } VectorCurvesNode::VectorCurvesNode() : CurvesNode(node_type) { } void VectorCurvesNode::constant_fold(const ConstantFolder& folder) { CurvesNode::constant_fold(folder, input("Vector")); } void VectorCurvesNode::compile(SVMCompiler& compiler) { CurvesNode::compile(compiler, NODE_VECTOR_CURVES, input("Vector"), output("Vector")); } void VectorCurvesNode::compile(OSLCompiler& compiler) { CurvesNode::compile(compiler, "node_vector_curves"); } /* RGBRampNode */ NODE_DEFINE(RGBRampNode) { NodeType* type = NodeType::add("rgb_ramp", create, NodeType::SHADER); SOCKET_COLOR_ARRAY(ramp, "Ramp", array()); SOCKET_FLOAT_ARRAY(ramp_alpha, "Ramp Alpha", array()); SOCKET_BOOLEAN(interpolate, "Interpolate", true); SOCKET_IN_FLOAT(fac, "Fac", 0.0f); SOCKET_OUT_COLOR(color, "Color"); SOCKET_OUT_FLOAT(alpha, "Alpha"); return type; } RGBRampNode::RGBRampNode() : ShaderNode(node_type) { } void RGBRampNode::constant_fold(const ConstantFolder& folder) { if(ramp.size() == 0 || ramp.size() != ramp_alpha.size()) return; if(folder.all_inputs_constant()) { float f = clamp(fac, 0.0f, 1.0f) * (ramp.size() - 1); /* clamp int as well in case of NaN */ int i = clamp((int)f, 0, ramp.size()-1); float t = f - (float)i; bool use_lerp = interpolate && t > 0.0f; if(folder.output == output("Color")) { float3 color = rgb_ramp_lookup(ramp.data(), fac, use_lerp, false, ramp.size()); folder.make_constant(color); } else if(folder.output == output("Alpha")) { float alpha = float_ramp_lookup(ramp_alpha.data(), fac, use_lerp, false, ramp_alpha.size()); folder.make_constant(alpha); } } } void RGBRampNode::compile(SVMCompiler& compiler) { if(ramp.size() == 0 || ramp.size() != ramp_alpha.size()) return; ShaderInput *fac_in = input("Fac"); ShaderOutput *color_out = output("Color"); ShaderOutput *alpha_out = output("Alpha"); compiler.add_node(NODE_RGB_RAMP, compiler.encode_uchar4( compiler.stack_assign(fac_in), compiler.stack_assign_if_linked(color_out), compiler.stack_assign_if_linked(alpha_out)), interpolate); compiler.add_node(ramp.size()); for(int i = 0; i < ramp.size(); i++) compiler.add_node(make_float4(ramp[i].x, ramp[i].y, ramp[i].z, ramp_alpha[i])); } void RGBRampNode::compile(OSLCompiler& compiler) { if(ramp.size() == 0 || ramp.size() != ramp_alpha.size()) return; compiler.parameter_color_array("ramp_color", ramp); compiler.parameter_array("ramp_alpha", ramp_alpha.data(), ramp_alpha.size()); compiler.parameter(this, "interpolate"); compiler.add(this, "node_rgb_ramp"); } /* Set Normal Node */ NODE_DEFINE(SetNormalNode) { NodeType* type = NodeType::add("set_normal", create, NodeType::SHADER); SOCKET_IN_VECTOR(direction, "Direction", make_float3(0.0f, 0.0f, 0.0f)); SOCKET_OUT_NORMAL(normal, "Normal"); return type; } SetNormalNode::SetNormalNode() : ShaderNode(node_type) { } void SetNormalNode::compile(SVMCompiler& compiler) { ShaderInput *direction_in = input("Direction"); ShaderOutput *normal_out = output("Normal"); compiler.add_node(NODE_CLOSURE_SET_NORMAL, compiler.stack_assign(direction_in), compiler.stack_assign(normal_out)); } void SetNormalNode::compile(OSLCompiler& compiler) { compiler.add(this, "node_set_normal"); } /* OSLNode */ OSLNode::OSLNode() : ShaderNode(new NodeType(NodeType::SHADER)) { special_type = SHADER_SPECIAL_TYPE_SCRIPT; } OSLNode::~OSLNode() { delete type; } ShaderNode *OSLNode::clone() const { return OSLNode::create(this->inputs.size(), this); } OSLNode* OSLNode::create(size_t num_inputs, const OSLNode *from) { /* allocate space for the node itself and parameters, aligned to 16 bytes * assuming that's the most parameter types need */ size_t node_size = align_up(sizeof(OSLNode), 16); size_t inputs_size = align_up(SocketType::max_size(), 16) * num_inputs; char *node_memory = (char*) operator new(node_size + inputs_size); memset(node_memory, 0, node_size + inputs_size); if(!from) { return new(node_memory) OSLNode(); } else { /* copy input default values and node type for cloning */ memcpy(node_memory + node_size, (char*)from + node_size, inputs_size); OSLNode *node = new(node_memory) OSLNode(*from); node->type = new NodeType(*(from->type)); return node; } } char* OSLNode::input_default_value() { /* pointer to default value storage, which is the same as our actual value */ size_t num_inputs = type->inputs.size(); size_t inputs_size = align_up(SocketType::max_size(), 16) * num_inputs; return (char*)this + align_up(sizeof(OSLNode), 16) + inputs_size; } void OSLNode::add_input(ustring name, SocketType::Type socket_type) { char *memory = input_default_value(); size_t offset = memory - (char*)this; const_cast(type)->register_input(name, name, socket_type, offset, memory, NULL, NULL, SocketType::LINKABLE); } void OSLNode::add_output(ustring name, SocketType::Type socket_type) { const_cast(type)->register_output(name, name, socket_type); } void OSLNode::compile(SVMCompiler&) { /* doesn't work for SVM, obviously ... */ } void OSLNode::compile(OSLCompiler& compiler) { if(!filepath.empty()) compiler.add(this, filepath.c_str(), true); else compiler.add(this, bytecode_hash.c_str(), false); } /* Normal Map */ NODE_DEFINE(NormalMapNode) { NodeType* type = NodeType::add("normal_map", create, NodeType::SHADER); static NodeEnum space_enum; space_enum.insert("tangent", NODE_NORMAL_MAP_TANGENT); space_enum.insert("object", NODE_NORMAL_MAP_OBJECT); space_enum.insert("world", NODE_NORMAL_MAP_WORLD); space_enum.insert("blender_object", NODE_NORMAL_MAP_BLENDER_OBJECT); space_enum.insert("blender_world", NODE_NORMAL_MAP_BLENDER_WORLD); SOCKET_ENUM(space, "Space", space_enum, NODE_TANGENT_RADIAL); SOCKET_STRING(attribute, "Attribute", ustring("")); SOCKET_IN_NORMAL(normal_osl, "NormalIn", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_IN_FLOAT(strength, "Strength", 1.0f); SOCKET_IN_COLOR(color, "Color", make_float3(0.5f, 0.5f, 1.0f)); SOCKET_OUT_NORMAL(normal, "Normal"); return type; } NormalMapNode::NormalMapNode() : ShaderNode(node_type) { } void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface && space == NODE_NORMAL_MAP_TANGENT) { if(attribute == ustring("")) { attributes->add(ATTR_STD_UV_TANGENT); attributes->add(ATTR_STD_UV_TANGENT_SIGN); } else { attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } attributes->add(ATTR_STD_VERTEX_NORMAL); } ShaderNode::attributes(shader, attributes); } void NormalMapNode::compile(SVMCompiler& compiler) { ShaderInput *color_in = input("Color"); ShaderInput *strength_in = input("Strength"); ShaderOutput *normal_out = output("Normal"); int attr = 0, attr_sign = 0; if(space == NODE_NORMAL_MAP_TANGENT) { if(attribute == ustring("")) { attr = compiler.attribute(ATTR_STD_UV_TANGENT); attr_sign = compiler.attribute(ATTR_STD_UV_TANGENT_SIGN); } else { attr = compiler.attribute(ustring((string(attribute.c_str()) + ".tangent").c_str())); attr_sign = compiler.attribute(ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } } compiler.add_node(NODE_NORMAL_MAP, compiler.encode_uchar4( compiler.stack_assign(color_in), compiler.stack_assign(strength_in), compiler.stack_assign(normal_out), space), attr, attr_sign); } void NormalMapNode::compile(OSLCompiler& compiler) { if(space == NODE_NORMAL_MAP_TANGENT) { if(attribute == ustring("")) { compiler.parameter("attr_name", ustring("geom:tangent")); compiler.parameter("attr_sign_name", ustring("geom:tangent_sign")); } else { compiler.parameter("attr_name", ustring((string(attribute.c_str()) + ".tangent").c_str())); compiler.parameter("attr_sign_name", ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } } compiler.parameter(this, "space"); compiler.add(this, "node_normal_map"); } /* Tangent */ NODE_DEFINE(TangentNode) { NodeType* type = NodeType::add("tangent", create, NodeType::SHADER); static NodeEnum direction_type_enum; direction_type_enum.insert("radial", NODE_TANGENT_RADIAL); direction_type_enum.insert("uv_map", NODE_TANGENT_UVMAP); SOCKET_ENUM(direction_type, "Direction Type", direction_type_enum, NODE_TANGENT_RADIAL); static NodeEnum axis_enum; axis_enum.insert("x", NODE_TANGENT_AXIS_X); axis_enum.insert("y", NODE_TANGENT_AXIS_Y); axis_enum.insert("z", NODE_TANGENT_AXIS_Z); SOCKET_ENUM(axis, "Axis", axis_enum, NODE_TANGENT_AXIS_X); SOCKET_STRING(attribute, "Attribute", ustring("")); SOCKET_IN_NORMAL(normal_osl, "NormalIn", make_float3(0.0f, 0.0f, 0.0f), SocketType::LINK_NORMAL | SocketType::OSL_INTERNAL); SOCKET_OUT_NORMAL(tangent, "Tangent"); return type; } TangentNode::TangentNode() : ShaderNode(node_type) { } void TangentNode::attributes(Shader *shader, AttributeRequestSet *attributes) { if(shader->has_surface) { if(direction_type == NODE_TANGENT_UVMAP) { if(attribute == ustring("")) attributes->add(ATTR_STD_UV_TANGENT); else attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); } else attributes->add(ATTR_STD_GENERATED); } ShaderNode::attributes(shader, attributes); } void TangentNode::compile(SVMCompiler& compiler) { ShaderOutput *tangent_out = output("Tangent"); int attr; if(direction_type == NODE_TANGENT_UVMAP) { if(attribute == ustring("")) attr = compiler.attribute(ATTR_STD_UV_TANGENT); else attr = compiler.attribute(ustring((string(attribute.c_str()) + ".tangent").c_str())); } else attr = compiler.attribute(ATTR_STD_GENERATED); compiler.add_node(NODE_TANGENT, compiler.encode_uchar4( compiler.stack_assign(tangent_out), direction_type, axis), attr); } void TangentNode::compile(OSLCompiler& compiler) { if(direction_type == NODE_TANGENT_UVMAP) { if(attribute == ustring("")) compiler.parameter("attr_name", ustring("geom:tangent")); else compiler.parameter("attr_name", ustring((string(attribute.c_str()) + ".tangent").c_str())); } compiler.parameter(this, "direction_type"); compiler.parameter(this, "axis"); compiler.add(this, "node_tangent"); } CCL_NAMESPACE_END