Cycles: constant folding for RGB/Vector Curves and Color Ramp.
[blender.git] / intern / cycles / render / nodes.cpp
index 41f405c781b071b07a1e488f0707649ec85eb9dc..b3b010841e99a388378f2f0feb26b37c86f8c2e1 100644 (file)
 #include "nodes.h"
 #include "scene.h"
 #include "svm.h"
+#include "svm_color_util.h"
+#include "svm_ramp_util.h"
 #include "svm_math_util.h"
 #include "osl.h"
+#include "constant_fold.h"
 
 #include "util_sky_model.h"
 #include "util_foreach.h"
@@ -41,12 +44,12 @@ CCL_NAMESPACE_BEGIN
        \
        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); \
+       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);     \
@@ -207,8 +210,8 @@ NODE_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);
+       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);
@@ -227,10 +230,10 @@ NODE_DEFINE(ImageTextureNode)
        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);
+       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);
@@ -257,7 +260,7 @@ ImageTextureNode::ImageTextureNode()
 ImageTextureNode::~ImageTextureNode()
 {
        if(image_manager) {
-               image_manager->remove_image(filename,
+               image_manager->remove_image(filename.string(),
                                            builtin_data,
                                            interpolation,
                                            extension);
@@ -298,7 +301,7 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
        image_manager = compiler.image_manager;
        if(is_float == -1) {
                bool is_float_bool;
-               slot = image_manager->add_image(filename,
+               slot = image_manager->add_image(filename.string(),
                                                builtin_data,
                                                animated,
                                                0,
@@ -360,13 +363,13 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
        if(is_float == -1) {
                if(builtin_data == NULL) {
                        ImageManager::ImageDataType type;
-                       type = image_manager->get_image_metadata(filename, NULL, is_linear);
+                       type = image_manager->get_image_metadata(filename.string(), NULL, is_linear);
                        if(type == ImageManager::IMAGE_DATA_TYPE_FLOAT || type == ImageManager::IMAGE_DATA_TYPE_FLOAT4)
                                is_float = 1;
                }
                else {
                        bool is_float_bool;
-                       slot = image_manager->add_image(filename,
+                       slot = image_manager->add_image(filename.string(),
                                                        builtin_data,
                                                        animated,
                                                        0,
@@ -392,12 +395,12 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
                compiler.parameter("filename", string_printf("@%d", slot).c_str());
        }
        if(is_linear || color_space != NODE_COLOR_SPACE_COLOR)
-               compiler.parameter("color_space", "Linear");
+               compiler.parameter("color_space", "linear");
        else
                compiler.parameter("color_space", "sRGB");
        compiler.parameter(this, "projection");
        compiler.parameter(this, "projection_blend");
-       compiler.parameter(this, "is_float");
+       compiler.parameter("is_float", is_float);
        compiler.parameter("use_alpha", !alpha_out->links.empty());
        compiler.parameter(this, "interpolation");
        compiler.parameter(this, "extension");
@@ -416,8 +419,8 @@ NODE_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);
+       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);
@@ -430,8 +433,8 @@ NODE_DEFINE(EnvironmentTextureNode)
        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);
+       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);
@@ -456,7 +459,7 @@ EnvironmentTextureNode::EnvironmentTextureNode()
 EnvironmentTextureNode::~EnvironmentTextureNode()
 {
        if(image_manager) {
-               image_manager->remove_image(filename,
+               image_manager->remove_image(filename.string(),
                                            builtin_data,
                                            interpolation,
                                            EXTENSION_REPEAT);
@@ -495,7 +498,7 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler)
        image_manager = compiler.image_manager;
        if(slot == -1) {
                bool is_float_bool;
-               slot = image_manager->add_image(filename,
+               slot = image_manager->add_image(filename.string(),
                                                builtin_data,
                                                animated,
                                                0,
@@ -548,13 +551,13 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler)
        if(is_float == -1) {
                if(builtin_data == NULL) {
                        ImageManager::ImageDataType type;
-                       type = image_manager->get_image_metadata(filename, NULL, is_linear);
+                       type = image_manager->get_image_metadata(filename.string(), NULL, is_linear);
                        if(type == ImageManager::IMAGE_DATA_TYPE_FLOAT || type == ImageManager::IMAGE_DATA_TYPE_FLOAT4)
                                is_float = 1;
                }
                else {
                        bool is_float_bool;
-                       slot = image_manager->add_image(filename,
+                       slot = image_manager->add_image(filename.string(),
                                                        builtin_data,
                                                        animated,
                                                        0,
@@ -575,12 +578,12 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler)
        }
        compiler.parameter(this, "projection");
        if(is_linear || color_space != NODE_COLOR_SPACE_COLOR)
-               compiler.parameter("color_space", "Linear");
+               compiler.parameter("color_space", "linear");
        else
                compiler.parameter("color_space", "sRGB");
 
        compiler.parameter(this, "interpolation");
-       compiler.parameter(this, "is_float");
+       compiler.parameter("is_float", is_float);
        compiler.parameter("use_alpha", !alpha_out->links.empty());
        compiler.add(this, "node_environment_texture");
 }
@@ -610,10 +613,10 @@ static float sky_perez_function(float lam[6], float theta, float 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
-       */
+        * 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;
@@ -715,8 +718,8 @@ NODE_DEFINE(SkyTextureNode)
        TEXTURE_MAPPING_DEFINE(SkyTextureNode);
 
        static NodeEnum type_enum;
-       type_enum.insert("Preetham", NODE_SKY_OLD);
-       type_enum.insert("Hosek / Wilkie", NODE_SKY_NEW);
+       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));
@@ -749,10 +752,9 @@ void SkyTextureNode::compile(SVMCompiler& compiler)
                assert(false);
 
        int vector_offset = tex_mapping.compile_begin(compiler, vector_in);
-       int sky_model = type;
 
        compiler.stack_assign(color_out);
-       compiler.add_node(NODE_TEX_SKY, vector_offset, compiler.stack_assign(color_out), sky_model);
+       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]));
@@ -770,7 +772,6 @@ 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)
@@ -778,7 +779,7 @@ void SkyTextureNode::compile(OSLCompiler& compiler)
        else
                assert(false);
                
-       compiler.parameter("sky_model", type);
+       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));
@@ -797,13 +798,13 @@ NODE_DEFINE(GradientTextureNode)
        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);
+       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);
@@ -913,8 +914,8 @@ NODE_DEFINE(VoronoiTextureNode)
        TEXTURE_MAPPING_DEFINE(VoronoiTextureNode);
 
        static NodeEnum coloring_enum;
-       coloring_enum.insert("Intensity", NODE_VORONOI_INTENSITY);
-       coloring_enum.insert("Cells", NODE_VORONOI_CELLS);
+       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);
@@ -969,11 +970,11 @@ NODE_DEFINE(MusgraveTextureNode)
        TEXTURE_MAPPING_DEFINE(MusgraveTextureNode);
 
        static NodeEnum type_enum;
-       type_enum.insert("Multifractal", NODE_MUSGRAVE_MULTIFRACTAL);
+       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);
+       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);
@@ -1050,13 +1051,13 @@ NODE_DEFINE(WaveTextureNode)
        TEXTURE_MAPPING_DEFINE(WaveTextureNode);
 
        static NodeEnum type_enum;
-       type_enum.insert("Bands", NODE_WAVE_BANDS);
-       type_enum.insert("Rings", NODE_WAVE_RINGS);
+       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);
+       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);
@@ -1337,8 +1338,8 @@ NODE_DEFINE(PointDensityTextureNode)
        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);
+       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;
@@ -1566,8 +1567,8 @@ NODE_DEFINE(RGBToBWNode)
 {
        NodeType* type = NodeType::add("rgb_to_bw", create, NodeType::SHADER);
 
-       SOCKET_IN_POINT(color, "Color", make_float3(0.0f, 0.0f, 0.0f));
-       SOCKET_OUT_POINT(val, "Val");
+       SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f));
+       SOCKET_OUT_FLOAT(val, "Val");
 
        return type;
 }
@@ -1577,14 +1578,11 @@ RGBToBWNode::RGBToBWNode()
 {
 }
 
-bool RGBToBWNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void RGBToBWNode::constant_fold(const ConstantFolder& folder)
 {
-       if(inputs[0]->link == NULL) {
-               optimized->set(linear_rgb_to_gray(color));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(linear_rgb_to_gray(color));
        }
-
-       return false;
 }
 
 void RGBToBWNode::compile(SVMCompiler& compiler)
@@ -1597,7 +1595,7 @@ void RGBToBWNode::compile(SVMCompiler& compiler)
 
 void RGBToBWNode::compile(OSLCompiler& compiler)
 {
-       compiler.add(this, "node_convert_from_color");
+       compiler.add(this, "node_rgb_to_bw");
 }
 
 /* Convert */
@@ -1662,40 +1660,35 @@ ConvertNode::ConvertNode(SocketType::Type from_, SocketType::Type to_, bool auto
                special_type = SHADER_SPECIAL_TYPE_AUTOCONVERT;
 }
 
-bool ConvertNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void ConvertNode::constant_fold(const ConstantFolder& folder)
 {
        /* proxy nodes should have been removed at this point */
        assert(special_type != SHADER_SPECIAL_TYPE_PROXY);
 
-       ShaderInput *in = inputs[0];
-
        /* TODO(DingTo): conversion from/to int is not supported yet, don't fold in that case */
 
-       if(in->link == NULL) {
+       if(folder.all_inputs_constant()) {
                if(from == SocketType::FLOAT) {
                        if(SocketType::is_float3(to)) {
-                               optimized->set(make_float3(value_float, value_float, value_float));
-                               return true;
+                               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)
+                               if(from == SocketType::COLOR) {
                                        /* color to float */
-                                       optimized->set(linear_rgb_to_gray(value_color));
-                               else
+                                       folder.make_constant(linear_rgb_to_gray(value_color));
+                               }
+                               else {
                                        /* vector/point/normal to float */
-                                       optimized->set(average(value_vector));
-                               return true;
+                                       folder.make_constant(average(value_vector));
+                               }
                        }
                        else if(SocketType::is_float3(to)) {
-                               optimized->set(value_color);
-                               return true;
+                               folder.make_constant(value_color);
                        }
                }
        }
-
-       return false;
 }
 
 void ConvertNode::compile(SVMCompiler& compiler)
@@ -1829,9 +1822,10 @@ NODE_DEFINE(AnisotropicBsdfNode)
        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("beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID);
        distribution_enum.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID);
-       distribution_enum.insert("Ashikhmin-Shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_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);
@@ -1867,7 +1861,10 @@ void AnisotropicBsdfNode::compile(SVMCompiler& compiler)
 {
        closure = distribution;
 
-       BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"));
+       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)
@@ -1887,10 +1884,11 @@ NODE_DEFINE(GlossyBsdfNode)
        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("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("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);
 
@@ -1940,6 +1938,8 @@ void GlossyBsdfNode::compile(SVMCompiler& compiler)
 
        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);
 }
@@ -1961,9 +1961,10 @@ NODE_DEFINE(GlassBsdfNode)
        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("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);
@@ -2014,6 +2015,8 @@ void GlassBsdfNode::compile(SVMCompiler& compiler)
 
        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"));
 }
@@ -2035,8 +2038,8 @@ NODE_DEFINE(RefractionBsdfNode)
        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("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);
 
@@ -2110,8 +2113,8 @@ NODE_DEFINE(ToonBsdfNode)
        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);
+       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);
@@ -2276,9 +2279,9 @@ NODE_DEFINE(SubsurfaceScatteringNode)
        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);
+       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));
@@ -2293,6 +2296,7 @@ NODE_DEFINE(SubsurfaceScatteringNode)
 SubsurfaceScatteringNode::SubsurfaceScatteringNode()
 : BsdfNode(node_type)
 {
+       closure = falloff;
 }
 
 void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
@@ -2303,6 +2307,7 @@ void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
 
 void SubsurfaceScatteringNode::compile(OSLCompiler& compiler)
 {
+       closure = falloff;
        compiler.parameter(this, "falloff");
        compiler.add(this, "node_subsurface_scattering");
 }
@@ -2321,7 +2326,7 @@ 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", 1.0f);
+       SOCKET_IN_FLOAT(strength, "Strength", 10.0f);
        SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL);
 
        SOCKET_OUT_CLOSURE(emission, "Emission");
@@ -2342,7 +2347,7 @@ void EmissionNode::compile(SVMCompiler& compiler)
        if(color_in->link || strength_in->link) {
                compiler.add_node(NODE_EMISSION_WEIGHT,
                                  compiler.stack_assign(color_in),
-                                                 compiler.stack_assign(strength_in));
+                                 compiler.stack_assign(strength_in));
        }
        else
                compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color * strength);
@@ -2355,13 +2360,15 @@ void EmissionNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_emission");
 }
 
-bool EmissionNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *)
+void EmissionNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *color_in = input("Color");
        ShaderInput *strength_in = input("Strength");
 
-       return ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
-               (!strength_in->link && strength == 0.0f));
+       if ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
+           (!strength_in->link && strength == 0.0f)) {
+               folder.discard();
+       }
 }
 
 /* Background Closure */
@@ -2405,13 +2412,15 @@ void BackgroundNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_background");
 }
 
-bool BackgroundNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *)
+void BackgroundNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *color_in = input("Color");
        ShaderInput *strength_in = input("Strength");
 
-       return ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
-               (!strength_in->link && strength == 0.0f));
+       if ((!color_in->link && color == make_float3(0.0f, 0.0f, 0.0f)) ||
+           (!strength_in->link && strength == 0.0f)) {
+               folder.discard();
+       }
 }
 
 /* Holdout Closure */
@@ -2593,8 +2602,8 @@ NODE_DEFINE(HairBsdfNode)
        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);
+       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);
@@ -2975,7 +2984,7 @@ void UVMapNode::compile(OSLCompiler& compiler)
                compiler.parameter("bump_offset", "center");
 
        compiler.parameter(this, "from_dupli");
-       compiler.parameter("name", attribute.c_str());
+       compiler.parameter(this, "attribute");
        compiler.add(this, "node_uv_map");
 }
 
@@ -2987,7 +2996,7 @@ NODE_DEFINE(LightPathNode)
 
        SOCKET_OUT_FLOAT(is_camera_ray, "Is Camera Ray");
        SOCKET_OUT_FLOAT(is_shadow_ray, "Is Shadow Ray");
-       SOCKET_OUT_FLOAT(is_diffus_ray, "Is Diffus 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");
@@ -3373,10 +3382,9 @@ ValueNode::ValueNode()
 {
 }
 
-bool ValueNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void ValueNode::constant_fold(const ConstantFolder& folder)
 {
-       optimized->set(value);
-       return true;
+       folder.make_constant(value);
 }
 
 void ValueNode::compile(SVMCompiler& compiler)
@@ -3409,10 +3417,9 @@ ColorNode::ColorNode()
 {
 }
 
-bool ColorNode::constant_fold(ShaderGraph *, ShaderOutput *, ShaderInput *optimized)
+void ColorNode::constant_fold(const ConstantFolder& folder)
 {
-       optimized->set(value);
-       return true;
+       folder.make_constant(value);
 }
 
 void ColorNode::compile(SVMCompiler& compiler)
@@ -3461,6 +3468,20 @@ 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)
@@ -3492,35 +3513,28 @@ void MixClosureNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_mix_closure");
 }
 
-bool MixClosureNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *)
+void MixClosureNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *fac_in = input("Fac");
        ShaderInput *closure1_in = input("Closure1");
        ShaderInput *closure2_in = input("Closure2");
-       ShaderOutput *closure_out = output("Closure");
 
        /* remove useless mix closures nodes */
        if(closure1_in->link == closure2_in->link) {
-               graph->relink(this, closure_out, closure1_in->link);
-               return true;
+               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 */
-       if(closure1_in->link && closure2_in->link && !fac_in->link) {
+       /* 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) {
-                       graph->relink(this, closure_out, closure1_in->link);
-                       return true;
+               if(fac <= 0.0f) {
+                       folder.bypass_or_discard(closure1_in);
                }
                /* factor 1.0 */
-               else if(fac == 1.0f) {
-                       graph->relink(this, closure_out, closure2_in->link);
-                       return true;
+               else if(fac >= 1.0f) {
+                       folder.bypass_or_discard(closure2_in);
                }
        }
-
-       return false;
 }
 
 /* Mix Closure */
@@ -3582,6 +3596,23 @@ InvertNode::InvertNode()
 {
 }
 
+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");
@@ -3606,24 +3637,24 @@ 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);
+       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);
@@ -3668,44 +3699,47 @@ void MixNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_mix");
 }
 
-bool MixNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *optimized)
+void MixNode::constant_fold(const ConstantFolder& folder)
 {
-       if(type != NODE_MIX_BLEND) {
-               return false;
-       }
-
        ShaderInput *fac_in = input("Fac");
        ShaderInput *color1_in = input("Color1");
        ShaderInput *color2_in = input("Color2");
-       ShaderOutput *color_out = output("Color");
 
-       /* remove useless mix colors nodes */
-       if(color1_in->link && color1_in->link == color2_in->link) {
-               graph->relink(this, color_out, color1_in->link);
-               return true;
+       /* evaluate fully constant node */
+       if(folder.all_inputs_constant()) {
+               folder.make_constant_clamp(svm_mix(type, fac, color1, color2), use_clamp);
+               return;
        }
 
-       /* remove unused mix color input when factor is 0.0 or 1.0 */
-       if(!fac_in->link) {
-               /* factor 0.0 */
-               if(fac == 0.0f) {
-                       if(color1_in->link)
-                               graph->relink(this, color_out, color1_in->link);
-                       else
-                               optimized->set(color1);
-                       return true;
+       /* remove no-op node when factor is 0.0 */
+       if(!fac_in->link && fac <= 0.0f) {
+               /* note that some of the modes will clamp out of bounds values even without use_clamp */
+               if(type == NODE_MIX_LIGHT || type == NODE_MIX_DODGE || type == NODE_MIX_BURN) {
+                       if(!color1_in->link) {
+                               folder.make_constant_clamp(svm_mix(type, 0.0f, color1, color1), use_clamp);
+                               return;
+                       }
                }
-               /* factor 1.0 */
-               else if(fac == 1.0f) {
-                       if(color2_in->link)
-                               graph->relink(this, color_out, color2_in->link);
-                       else
-                               optimized->set(color2);
-                       return true;
+               else if(folder.try_bypass_or_make_constant(color1_in, color1, use_clamp)) {
+                       return;
                }
        }
 
-       return false;
+       if(type == NODE_MIX_BLEND) {
+               /* remove useless mix colors nodes */
+               if(color1_in->link ? (color1_in->link == color2_in->link) : (!color2_in->link && color1 == color2)) {
+                       if(folder.try_bypass_or_make_constant(color1_in, color1, use_clamp)) {
+                               return;
+                       }
+               }
+
+               /* remove no-op mix color node when factor is 1.0 */
+               if(!fac_in->link && fac >= 1.0f) {
+                       if(folder.try_bypass_or_make_constant(color2_in, color2, use_clamp)) {
+                               return;
+                       }
+               }
+       }
 }
 
 /* Combine RGB */
@@ -3728,6 +3762,13 @@ CombineRGBNode::CombineRGBNode()
 {
 }
 
+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");
@@ -3763,7 +3804,7 @@ NODE_DEFINE(CombineXYZNode)
        SOCKET_IN_FLOAT(y, "Y", 0.0f);
        SOCKET_IN_FLOAT(z, "Z", 0.0f);
 
-       SOCKET_OUT_COLOR(color, "Image");
+       SOCKET_OUT_VECTOR(vector, "Vector");
 
        return type;
 }
@@ -3773,6 +3814,13 @@ CombineXYZNode::CombineXYZNode()
 {
 }
 
+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");
@@ -3818,6 +3866,13 @@ CombineHSVNode::CombineHSVNode()
 {
 }
 
+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");
@@ -3856,17 +3911,11 @@ GammaNode::GammaNode()
 {
 }
 
-bool GammaNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void GammaNode::constant_fold(const ConstantFolder& folder)
 {
-       ShaderInput *color_in = input("Color");
-       ShaderInput *gamma_in = input("Gamma");
-
-       if(color_in->link == NULL && gamma_in->link == NULL) {
-               optimized->set(svm_math_gamma_color(color, gamma));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(svm_math_gamma_color(color, gamma));
        }
-
-       return false;
 }
 
 void GammaNode::compile(SVMCompiler& compiler)
@@ -3906,6 +3955,13 @@ BrightContrastNode::BrightContrastNode()
 {
 }
 
+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");
@@ -3946,6 +4002,18 @@ SeparateRGBNode::SeparateRGBNode()
 {
 }
 
+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");
@@ -3991,6 +4059,18 @@ SeparateXYZNode::SeparateXYZNode()
 {
 }
 
+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");
@@ -4036,6 +4116,20 @@ SeparateHSVNode::SeparateHSVNode()
 {
 }
 
+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");
@@ -4419,16 +4513,11 @@ BlackbodyNode::BlackbodyNode()
 {
 }
 
-bool BlackbodyNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void BlackbodyNode::constant_fold(const ConstantFolder& folder)
 {
-       ShaderInput *temperature_in = input("Temperature");
-
-       if(temperature_in->link == NULL) {
-               optimized->set(svm_math_blackbody_color(temperature));
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant(svm_math_blackbody_color(temperature));
        }
-
-       return false;
 }
 
 void BlackbodyNode::compile(SVMCompiler& compiler)
@@ -4494,25 +4583,25 @@ 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);
+       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);
@@ -4530,24 +4619,11 @@ MathNode::MathNode()
 {
 }
 
-bool MathNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void MathNode::constant_fold(const ConstantFolder& folder)
 {
-       ShaderInput *value1_in = input("Value1");
-       ShaderInput *value2_in = input("Value2");
-
-       if(value1_in->link == NULL && value2_in->link == NULL) {
-               float value = svm_math(type, value1, value2);
-
-               if(use_clamp) {
-                       value = saturate(value);
-               }
-
-               optimized->set(value);
-
-               return true;
+       if(folder.all_inputs_constant()) {
+               folder.make_constant_clamp(svm_math(type, value1, value2), use_clamp);
        }
-
-       return false;
 }
 
 void MathNode::compile(SVMCompiler& compiler)
@@ -4579,12 +4655,12 @@ 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);
+       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));
@@ -4601,32 +4677,25 @@ VectorMathNode::VectorMathNode()
 {
 }
 
-bool VectorMathNode::constant_fold(ShaderGraph *, ShaderOutput *socket, ShaderInput *optimized)
+void VectorMathNode::constant_fold(const ConstantFolder& folder)
 {
-       ShaderInput *vector1_in = input("Vector1");
-       ShaderInput *vector2_in = input("Vector2");
-
        float value;
        float3 vector;
 
-       if(vector1_in->link == NULL && vector2_in->link == NULL) {
+       if(folder.all_inputs_constant()) {
                svm_vector_math(&value,
                                &vector,
                                type,
                                vector1,
                                vector2);
 
-               if(socket == output("Value")) {
-                       optimized->set(value);
-                       return true;
+               if(folder.output == output("Value")) {
+                       folder.make_constant(value);
                }
-               else if(socket == output("Vector")) {
-                       optimized->set(vector);
-                       return true;
+               else if(folder.output == output("Vector")) {
+                       folder.make_constant(vector);
                }
        }
-
-       return false;
 }
 
 void VectorMathNode::compile(SVMCompiler& compiler)
@@ -4658,9 +4727,9 @@ 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);
+       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;
@@ -4760,7 +4829,7 @@ void BumpNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_bump");
 }
 
-bool BumpNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *)
+void BumpNode::constant_fold(const ConstantFolder& folder)
 {
        ShaderInput *height_in = input("Height");
        ShaderInput *normal_in = input("Normal");
@@ -4768,56 +4837,60 @@ bool BumpNode::constant_fold(ShaderGraph *graph, ShaderOutput *, ShaderInput *)
        if(height_in->link == NULL) {
                if(normal_in->link == NULL) {
                        GeometryNode *geom = new GeometryNode();
-                       graph->add(geom);
-                       graph->relink(this, outputs[0], geom->output("Normal"));
+                       folder.graph->add(geom);
+                       folder.bypass(geom->output("Normal"));
                }
                else {
-                       graph->relink(this, outputs[0], normal_in->link);
+                       folder.bypass(normal_in->link);
                }
-               return true;
        }
 
        /* TODO(sergey): Ignore bump with zero strength. */
-
-       return false;
 }
 
-/* RGBCurvesNode */
 
-NODE_DEFINE(RGBCurvesNode)
+/* Curve node */
+
+CurvesNode::CurvesNode(const NodeType *node_type)
+: ShaderNode(node_type)
 {
-       NodeType* type = NodeType::add("rgb_curves", create, NodeType::SHADER);
+}
 
-       SOCKET_COLOR_ARRAY(curves, "Curves", array<float3>());
-       SOCKET_FLOAT(min_x, "Min X", 0.0f);
-       SOCKET_FLOAT(max_x, "Max X", 1.0f);
+void CurvesNode::constant_fold(const ConstantFolder& folder, ShaderInput *value_in)
+{
+       ShaderInput *fac_in = input("Fac");
 
-       SOCKET_IN_FLOAT(fac, "Fac", 0.0f);
-       SOCKET_IN_COLOR(color, "Color", make_float3(0.0f, 0.0f, 0.0f));
+       /* remove no-op node */
+       if(!fac_in->link && fac == 0.0f) {
+               folder.bypass(value_in->link);
+       }
+       /* evaluate fully constant node */
+       else if(folder.all_inputs_constant()) {
+               if (curves.size() == 0)
+                       return;
 
-       SOCKET_OUT_COLOR(color, "Color");
+               float3 pos = (value - make_float3(min_x, min_x, min_x)) / (max_x - min_x);
+               float3 result;
 
-       return type;
-}
+               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;
 
-RGBCurvesNode::RGBCurvesNode()
-: ShaderNode(node_type)
-{
+               folder.make_constant(interp(value, result, fac));
+       }
 }
 
-void RGBCurvesNode::compile(SVMCompiler& compiler)
+void CurvesNode::compile(SVMCompiler& compiler, int type, ShaderInput *value_in, ShaderOutput *value_out)
 {
        if(curves.size() == 0)
                return;
 
        ShaderInput *fac_in = input("Fac");
-       ShaderInput *color_in = input("Color");
-       ShaderOutput *color_out = output("Color");
 
-       compiler.add_node(NODE_RGB_CURVES,
+       compiler.add_node(type,
                          compiler.encode_uchar4(compiler.stack_assign(fac_in),
-                                                compiler.stack_assign(color_in),
-                                                compiler.stack_assign(color_out)),
+                                                compiler.stack_assign(value_in),
+                                                compiler.stack_assign(value_out)),
                          __float_as_int(min_x),
                          __float_as_int(max_x));
 
@@ -4826,7 +4899,7 @@ void RGBCurvesNode::compile(SVMCompiler& compiler)
                compiler.add_node(float3_to_float4(curves[i]));
 }
 
-void RGBCurvesNode::compile(OSLCompiler& compiler)
+void CurvesNode::compile(OSLCompiler& compiler, const char* name)
 {
        if(curves.size() == 0)
                return;
@@ -4834,7 +4907,55 @@ void RGBCurvesNode::compile(OSLCompiler& compiler)
        compiler.parameter_color_array("ramp", curves);
        compiler.parameter(this, "min_x");
        compiler.parameter(this, "max_x");
-       compiler.add(this, "node_rgb_curves");
+       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<float3>());
+       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 */
@@ -4848,48 +4969,31 @@ NODE_DEFINE(VectorCurvesNode)
        SOCKET_FLOAT(max_x, "Max X", 1.0f);
 
        SOCKET_IN_FLOAT(fac, "Fac", 0.0f);
-       SOCKET_IN_VECTOR(vector, "Vector", make_float3(0.0f, 0.0f, 0.0f));
+       SOCKET_IN_VECTOR(value, "Vector", make_float3(0.0f, 0.0f, 0.0f));
 
-       SOCKET_OUT_VECTOR(vector, "Vector");
+       SOCKET_OUT_VECTOR(value, "Vector");
 
        return type;
 }
 
 VectorCurvesNode::VectorCurvesNode()
-: ShaderNode(node_type)
+: CurvesNode(node_type)
 {
 }
 
-void VectorCurvesNode::compile(SVMCompiler& compiler)
+void VectorCurvesNode::constant_fold(const ConstantFolder& folder)
 {
-       if(curves.size() == 0)
-               return;
-
-       ShaderInput *fac_in = input("Fac");
-       ShaderInput *vector_in = input("Vector");
-       ShaderOutput *vector_out = output("Vector");
-
-       compiler.add_node(NODE_VECTOR_CURVES,
-                         compiler.encode_uchar4(compiler.stack_assign(fac_in),
-                                                compiler.stack_assign(vector_in),
-                                                compiler.stack_assign(vector_out)),
-                         __float_as_int(min_x),
-                         __float_as_int(max_x));
+       CurvesNode::constant_fold(folder, input("Vector"));
+}
 
-       compiler.add_node(curves.size());
-       for(int i = 0; i < curves.size(); i++)
-               compiler.add_node(float3_to_float4(curves[i]));
+void VectorCurvesNode::compile(SVMCompiler& compiler)
+{
+       CurvesNode::compile(compiler, NODE_VECTOR_CURVES, input("Vector"), output("Vector"));
 }
 
 void VectorCurvesNode::compile(OSLCompiler& compiler)
 {
-       if(curves.size() == 0)
-               return;
-
-       compiler.parameter_color_array("ramp", curves);
-       compiler.parameter(this, "min_x");
-       compiler.parameter(this, "max_x");
-       compiler.add(this, "node_vector_curves");
+       CurvesNode::compile(compiler, "node_vector_curves");
 }
 
 /* RGBRampNode */
@@ -4915,6 +5019,31 @@ RGBRampNode::RGBRampNode()
 {
 }
 
+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())
@@ -4993,7 +5122,12 @@ OSLNode::~OSLNode()
        delete type;
 }
 
-OSLNode* OSLNode::create(size_t num_inputs)
+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 */
@@ -5003,7 +5137,17 @@ OSLNode* OSLNode::create(size_t num_inputs)
        char *node_memory = (char*) operator new(node_size + inputs_size);
        memset(node_memory, 0, node_size + inputs_size);
 
-       return new(node_memory) OSLNode();
+       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()
@@ -5046,11 +5190,11 @@ 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);
+       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(""));
@@ -5138,14 +5282,14 @@ 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);
+       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);
+       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(""));