Fix T43755: Wireframe attribute doesn't work with displace
[blender.git] / intern / cycles / render / nodes.cpp
index 3fbcadee42390504af40b450803faf00aa5dcdba..7a39811cacd24b900ae62030d798ea243e508122 100644 (file)
@@ -1,26 +1,27 @@
 /*
- * Copyright 2011, Blender Foundation.
+ * Copyright 2011-2013 Blender Foundation
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
+ * 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
  *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * http://www.apache.org/licenses/LICENSE-2.0
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * 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 "image.h"
 #include "nodes.h"
 #include "svm.h"
+#include "svm_math_util.h"
 #include "osl.h"
+#include "sky_model.h"
 
+#include "util_foreach.h"
 #include "util_transform.h"
 
 CCL_NAMESPACE_BEGIN
@@ -42,6 +43,8 @@ TextureMapping::TextureMapping()
        y_mapping = Y;
        z_mapping = Z;
 
+       type = TEXTURE;
+
        projection = FLAT;
 }
 
@@ -55,12 +58,52 @@ Transform TextureMapping::compute_transform()
                mmat[1][y_mapping-1] = 1.0f;
        if(z_mapping != NONE)
                mmat[2][z_mapping-1] = 1.0f;
-
-       Transform smat = transform_scale(scale);
+       
+       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);
 
-       return tmat*rmat*smat*mmat;
+       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()
@@ -98,6 +141,11 @@ void TextureMapping::compile(SVMCompiler& compiler, int offset_in, int offset_ou
                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);
+       }
 }
 
 void TextureMapping::compile(OSLCompiler &compiler)
@@ -126,8 +174,10 @@ static ShaderEnum image_projection_init()
 {
        ShaderEnum enm;
 
-       enm.insert("Flat", 0);
-       enm.insert("Box", 1);
+       enm.insert("Flat", NODE_IMAGE_PROJ_FLAT);
+       enm.insert("Box", NODE_IMAGE_PROJ_BOX);
+       enm.insert("Sphere", NODE_IMAGE_PROJ_SPHERE);
+       enm.insert("Tube", NODE_IMAGE_PROJ_TUBE);
 
        return enm;
 }
@@ -142,10 +192,12 @@ ImageTextureNode::ImageTextureNode()
        slot = -1;
        is_float = -1;
        is_linear = false;
+       use_alpha = true;
        filename = "";
        builtin_data = NULL;
        color_space = ustring("Color");
        projection = ustring("Flat");
+       interpolation = INTERPOLATION_LINEAR;
        projection_blend = 0.0f;
        animated = false;
 
@@ -157,7 +209,7 @@ ImageTextureNode::ImageTextureNode()
 ImageTextureNode::~ImageTextureNode()
 {
        if(image_manager)
-               image_manager->remove_image(filename, builtin_data);
+               image_manager->remove_image(filename, builtin_data, interpolation);
 }
 
 ShaderNode *ImageTextureNode::clone() const
@@ -170,6 +222,21 @@ ShaderNode *ImageTextureNode::clone() const
        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");
@@ -179,7 +246,9 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
        image_manager = compiler.image_manager;
        if(is_float == -1) {
                bool is_float_bool;
-               slot = image_manager->add_image(filename, builtin_data, animated, is_float_bool, is_linear);
+               slot = image_manager->add_image(filename, builtin_data,
+                                               animated, 0, is_float_bool, is_linear,
+                                               interpolation, use_alpha);
                is_float = (int)is_float_bool;
        }
 
@@ -199,14 +268,15 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
                        tex_mapping.compile(compiler, vector_in->stack_offset, vector_offset);
                }
 
-               if(projection == "Flat") {
+               if(projection != "Box") {
                        compiler.add_node(NODE_TEX_IMAGE,
                                slot,
                                compiler.encode_uchar4(
                                        vector_offset,
                                        color_out->stack_offset,
                                        alpha_out->stack_offset,
-                                       srgb));
+                                       srgb),
+                               projection_enum[projection]);
                }
                else {
                        compiler.add_node(NODE_TEX_IMAGE_BOX,
@@ -218,7 +288,7 @@ void ImageTextureNode::compile(SVMCompiler& compiler)
                                        srgb),
                                __float_as_int(projection_blend));
                }
-       
+
                if(vector_offset != vector_in->stack_offset)
                        compiler.stack_clear_offset(vector_in->type, vector_offset);
        }
@@ -241,10 +311,32 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
 
        tex_mapping.compile(compiler);
 
-       if(is_float == -1)
-               is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+       image_manager = compiler.image_manager;
+       if(is_float == -1) {
+               if(builtin_data == NULL) {
+                       is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+               }
+               else {
+                       bool is_float_bool;
+                       slot = image_manager->add_image(filename, builtin_data,
+                                                       animated, 0, is_float_bool, is_linear,
+                                                       interpolation, use_alpha);
+                       is_float = (int)is_float_bool;
+               }
+       }
 
-       compiler.parameter("filename", filename.c_str());
+       if(slot == -1) {
+               compiler.parameter("filename", filename.c_str());
+       }
+       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
+                * "@<slot_number>" 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 != "Color")
                compiler.parameter("color_space", "Linear");
        else
@@ -253,6 +345,22 @@ void ImageTextureNode::compile(OSLCompiler& compiler)
        compiler.parameter("projection_blend", projection_blend);
        compiler.parameter("is_float", is_float);
        compiler.parameter("use_alpha", !alpha_out->links.empty());
+
+       switch (interpolation) {
+               case INTERPOLATION_CLOSEST:
+                       compiler.parameter("interpolation", "closest");
+                       break;
+               case INTERPOLATION_CUBIC:
+                       compiler.parameter("interpolation", "cubic");
+                       break;
+               case INTERPOLATION_SMART:
+                       compiler.parameter("interpolation", "smart");
+                       break;
+               case INTERPOLATION_LINEAR:
+               default:
+                       compiler.parameter("interpolation", "linear");
+                       break;
+       }
        compiler.add(this, "node_image_texture");
 }
 
@@ -278,6 +386,7 @@ EnvironmentTextureNode::EnvironmentTextureNode()
        slot = -1;
        is_float = -1;
        is_linear = false;
+       use_alpha = true;
        filename = "";
        builtin_data = NULL;
        color_space = ustring("Color");
@@ -292,7 +401,7 @@ EnvironmentTextureNode::EnvironmentTextureNode()
 EnvironmentTextureNode::~EnvironmentTextureNode()
 {
        if(image_manager)
-               image_manager->remove_image(filename, builtin_data);
+               image_manager->remove_image(filename, builtin_data, INTERPOLATION_LINEAR);
 }
 
 ShaderNode *EnvironmentTextureNode::clone() const
@@ -305,6 +414,19 @@ ShaderNode *EnvironmentTextureNode::clone() const
        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");
@@ -314,7 +436,9 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler)
        image_manager = compiler.image_manager;
        if(slot == -1) {
                bool is_float_bool;
-               slot = image_manager->add_image(filename, builtin_data, animated, is_float_bool, is_linear);
+               slot = image_manager->add_image(filename, builtin_data,
+                                               animated, 0, is_float_bool, is_linear,
+                                               INTERPOLATION_LINEAR, use_alpha);
                is_float = (int)is_float_bool;
        }
 
@@ -365,10 +489,29 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler)
 
        tex_mapping.compile(compiler);
 
-       if(is_float == -1)
-               is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+       /* See comments in ImageTextureNode::compile about support
+        * of builtin images.
+        */
+       image_manager = compiler.image_manager;
+       if(is_float == -1) {
+               if(builtin_data == NULL) {
+                       is_float = (int)image_manager->is_float_image(filename, NULL, is_linear);
+               }
+               else {
+                       bool is_float_bool;
+                       slot = image_manager->add_image(filename, builtin_data,
+                                                       animated, 0, is_float_bool, is_linear,
+                                                       INTERPOLATION_LINEAR, use_alpha);
+                       is_float = (int)is_float_bool;
+               }
+       }
 
-       compiler.parameter("filename", filename.c_str());
+       if(slot == -1) {
+               compiler.parameter("filename", filename.c_str());
+       }
+       else {
+               compiler.parameter("filename", string_printf("@%d", slot).c_str());
+       }
        compiler.parameter("projection", projection);
        if(is_linear || color_space != "Color")
                compiler.parameter("color_space", "Linear");
@@ -386,19 +529,35 @@ 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(KernelSunSky *ksunsky, float3 dir, float turbidity)
+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;
 
-       ksunsky->theta = theta;
-       ksunsky->phi = phi;
+       sunsky->theta = theta;
+       sunsky->phi = phi;
 
        float theta2 = theta*theta;
        float theta3 = theta2*theta;
@@ -406,47 +565,106 @@ static void sky_texture_precompute(KernelSunSky *ksunsky, float3 dir, float turb
        float T2 = T * T;
 
        float chi = (4.0f / 9.0f - T / 120.0f) * (M_PI_F - 2.0f * theta);
-       ksunsky->zenith_Y = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;
-       ksunsky->zenith_Y *= 0.06f;
+       sunsky->radiance_x = (4.0453f * T - 4.9710f) * tanf(chi) - 0.2155f * T + 2.4192f;
+       sunsky->radiance_x *= 0.06f;
 
-       ksunsky->zenith_x =
+       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);
 
-       ksunsky->zenith_y =
+       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);
 
-       ksunsky->perez_Y[0] = (0.1787f * T  - 1.4630f);
-       ksunsky->perez_Y[1] = (-0.3554f * T  + 0.4275f);
-       ksunsky->perez_Y[2] = (-0.0227f * T  + 5.3251f);
-       ksunsky->perez_Y[3] = (0.1206f * T  - 2.5771f);
-       ksunsky->perez_Y[4] = (-0.0670f * T  + 0.3703f);
+       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;
+
+       double solarElevation = M_PI_2_F - theta;
+
+       /* Initialize Sky Model */
+       ArHosekSkyModelState *sky_state;
+       sky_state = arhosek_xyz_skymodelstate_alloc_init(turbidity, ground_albedo, 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);
+}
 
-       ksunsky->perez_x[0] = (-0.0193f * T  - 0.2592f);
-       ksunsky->perez_x[1] = (-0.0665f * T  + 0.0008f);
-       ksunsky->perez_x[2] = (-0.0004f * T  + 0.2125f);
-       ksunsky->perez_x[3] = (-0.0641f * T  - 0.8989f);
-       ksunsky->perez_x[4] = (-0.0033f * T  + 0.0452f);
+static ShaderEnum sky_type_init()
+{
+       ShaderEnum enm;
 
-       ksunsky->perez_y[0] = (-0.0167f * T  - 0.2608f);
-       ksunsky->perez_y[1] = (-0.0950f * T  + 0.0092f);
-       ksunsky->perez_y[2] = (-0.0079f * T  + 0.2102f);
-       ksunsky->perez_y[3] = (-0.0441f * T  - 1.6537f);
-       ksunsky->perez_y[4] = (-0.0109f * T  + 0.0529f);
+       enm.insert("Preetham", NODE_SKY_OLD);
+       enm.insert("Hosek / Wilkie", NODE_SKY_NEW);
 
-       ksunsky->zenith_Y /= sky_perez_function(ksunsky->perez_Y, 0, theta);
-       ksunsky->zenith_x /= sky_perez_function(ksunsky->perez_x, 0, theta);
-       ksunsky->zenith_y /= sky_perez_function(ksunsky->perez_y, 0, theta);
+       return enm;
 }
 
+ShaderEnum SkyTextureNode::type_enum = sky_type_init();
+
 SkyTextureNode::SkyTextureNode()
 : TextureNode("sky_texture")
 {
+       type = ustring("Hosek / Wilkie");
+       
        sun_direction = make_float3(0.0f, 0.0f, 1.0f);
        turbidity = 2.2f;
+       ground_albedo = 0.3f;
 
        add_input("Vector", SHADER_SOCKET_VECTOR, ShaderInput::POSITION);
        add_output("Color", SHADER_SOCKET_COLOR);
@@ -457,15 +675,19 @@ void SkyTextureNode::compile(SVMCompiler& compiler)
        ShaderInput *vector_in = input("Vector");
        ShaderOutput *color_out = output("Color");
 
-       if(compiler.sunsky) {
-               sky_texture_precompute(compiler.sunsky, sun_direction, turbidity);
-               compiler.sunsky = NULL;
-       }
+       SunSky sunsky;
+       if(type_enum[type] == NODE_SKY_OLD)
+               sky_texture_precompute_old(&sunsky, sun_direction, turbidity);
+       else if(type_enum[type] == NODE_SKY_NEW)
+               sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo);
+       else
+               assert(false);
 
        if(vector_in->link)
                compiler.stack_assign(vector_in);
 
        int vector_offset = vector_in->stack_offset;
+       int sky_model = type_enum[type];
 
        if(!tex_mapping.skip()) {
                vector_offset = compiler.stack_find_offset(SHADER_SOCKET_VECTOR);
@@ -473,7 +695,15 @@ void SkyTextureNode::compile(SVMCompiler& compiler)
        }
 
        compiler.stack_assign(color_out);
-       compiler.add_node(NODE_TEX_SKY, vector_offset, color_out->stack_offset);
+       compiler.add_node(NODE_TEX_SKY, vector_offset, color_out->stack_offset, sky_model);
+       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]));
 
        if(vector_offset != vector_in->stack_offset)
                compiler.stack_clear_offset(vector_in->type, vector_offset);
@@ -483,8 +713,22 @@ void SkyTextureNode::compile(OSLCompiler& compiler)
 {
        tex_mapping.compile(compiler);
 
-       compiler.parameter_vector("sun_direction", sun_direction);
-       compiler.parameter("turbidity", turbidity);
+       SunSky sunsky;
+
+       if(type_enum[type] == NODE_SKY_OLD)
+               sky_texture_precompute_old(&sunsky, sun_direction, turbidity);
+       else if(type_enum[type] == NODE_SKY_NEW)
+               sky_texture_precompute_new(&sunsky, sun_direction, turbidity, ground_albedo);
+       else
+               assert(false);
+               
+       compiler.parameter("sky_model", 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");
 }
 
@@ -1276,19 +1520,21 @@ void ProxyNode::compile(OSLCompiler& compiler)
 BsdfNode::BsdfNode(bool scattering_)
 : ShaderNode("bsdf"), scattering(scattering_)
 {
-       closure = ccl::CLOSURE_BSSRDF_ID;
-
        add_input("Color", SHADER_SOCKET_COLOR, make_float3(0.8f, 0.8f, 0.8f));
        add_input("Normal", SHADER_SOCKET_NORMAL, ShaderInput::NORMAL);
        add_input("SurfaceMixWeight", SHADER_SOCKET_FLOAT, 0.0f, ShaderInput::USE_SVM);
 
-       if(scattering)
+       if(scattering) {
+               closure = CLOSURE_BSSRDF_CUBIC_ID;
                add_output("BSSRDF", SHADER_SOCKET_CLOSURE);
-       else
+       }
+       else {
+               closure = CLOSURE_BSDF_DIFFUSE_ID;
                add_output("BSDF", SHADER_SOCKET_CLOSURE);
+       }
 }
 
-void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3)
+void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *param2, ShaderInput *param3, ShaderInput *param4)
 {
        ShaderInput *color_in = input("Color");
        ShaderInput *normal_in = input("Normal");
@@ -1307,6 +1553,8 @@ void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *
                compiler.stack_assign(param2);
        if(param3)
                compiler.stack_assign(param3);
+       if(param4)
+               compiler.stack_assign(param4);
 
        if(normal_in->link)
                compiler.stack_assign(normal_in);
@@ -1323,12 +1571,14 @@ void BsdfNode::compile(SVMCompiler& compiler, ShaderInput *param1, ShaderInput *
                __float_as_int((param2)? param2->value.x: 0.0f));
 
        if(tangent_in) {
-               compiler.add_node(NODE_CLOSURE_BSDF, normal_in->stack_offset, tangent_in->stack_offset,
-                       (param3)? param3->stack_offset: SVM_STACK_INVALID);
+               compiler.add_node(normal_in->stack_offset, tangent_in->stack_offset,
+                       (param3)? param3->stack_offset: SVM_STACK_INVALID,
+                       (param4)? param4->stack_offset: SVM_STACK_INVALID);
        }
        else {
-               compiler.add_node(NODE_CLOSURE_BSDF, normal_in->stack_offset, SVM_STACK_INVALID,
-                       (param3)? param3->stack_offset: SVM_STACK_INVALID);
+               compiler.add_node(normal_in->stack_offset, SVM_STACK_INVALID,
+                       (param3)? param3->stack_offset: SVM_STACK_INVALID,
+                       (param4)? param4->stack_offset: SVM_STACK_INVALID);
        }
 }
 
@@ -1342,11 +1592,24 @@ void BsdfNode::compile(OSLCompiler& compiler)
        assert(0);
 }
 
-/* Ward BSDF Closure */
+/* Anisotropic BSDF Closure */
 
-WardBsdfNode::WardBsdfNode()
+static ShaderEnum aniso_distribution_init()
 {
-       closure = CLOSURE_BSDF_WARD_ID;
+       ShaderEnum enm;
+
+       enm.insert("Beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ANISO_ID);
+       enm.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ANISO_ID);
+       enm.insert("Ashikhmin-Shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ANISO_ID);
+
+       return enm;
+}
+
+ShaderEnum AnisotropicBsdfNode::distribution_enum = aniso_distribution_init();
+
+AnisotropicBsdfNode::AnisotropicBsdfNode()
+{
+       distribution = ustring("GGX");
 
        add_input("Tangent", SHADER_SOCKET_VECTOR, ShaderInput::TANGENT);
 
@@ -1355,24 +1618,29 @@ WardBsdfNode::WardBsdfNode()
        add_input("Rotation", SHADER_SOCKET_FLOAT, 0.0f);
 }
 
-void WardBsdfNode::attributes(AttributeRequestSet *attributes)
+void AnisotropicBsdfNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
-       ShaderInput *tangent_in = input("Tangent");
+       if(shader->has_surface) {
+               ShaderInput *tangent_in = input("Tangent");
 
-       if(!tangent_in->link)
-               attributes->add(ATTR_STD_GENERATED);
+               if(!tangent_in->link)
+                       attributes->add(ATTR_STD_GENERATED);
+       }
 
-       ShaderNode::attributes(attributes);
+       ShaderNode::attributes(shader, attributes);
 }
 
-void WardBsdfNode::compile(SVMCompiler& compiler)
+void AnisotropicBsdfNode::compile(SVMCompiler& compiler)
 {
+       closure = (ClosureType)distribution_enum[distribution];
+
        BsdfNode::compile(compiler, input("Roughness"), input("Anisotropy"), input("Rotation"));
 }
 
-void WardBsdfNode::compile(OSLCompiler& compiler)
+void AnisotropicBsdfNode::compile(OSLCompiler& compiler)
 {
-       compiler.add(this, "node_ward_bsdf");
+       compiler.parameter("distribution", distribution);
+       compiler.add(this, "node_anisotropic_bsdf");
 }
 
 /* Glossy BSDF Closure */
@@ -1384,6 +1652,7 @@ static ShaderEnum glossy_distribution_init()
        enm.insert("Sharp", CLOSURE_BSDF_REFLECTION_ID);
        enm.insert("Beckmann", CLOSURE_BSDF_MICROFACET_BECKMANN_ID);
        enm.insert("GGX", CLOSURE_BSDF_MICROFACET_GGX_ID);
+       enm.insert("Ashikhmin-Shirley", CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID);
 
        return enm;
 }
@@ -1392,7 +1661,7 @@ ShaderEnum GlossyBsdfNode::distribution_enum = glossy_distribution_init();
 
 GlossyBsdfNode::GlossyBsdfNode()
 {
-       distribution = ustring("Beckmann");
+       distribution = ustring("GGX");
 
        add_input("Roughness", SHADER_SOCKET_FLOAT, 0.2f);
 }
@@ -1600,34 +1869,53 @@ void TransparentBsdfNode::compile(OSLCompiler& compiler)
 
 /* Subsurface Scattering Closure */
 
+static ShaderEnum subsurface_falloff_init()
+{
+       ShaderEnum enm;
+
+       enm.insert("Cubic", CLOSURE_BSSRDF_CUBIC_ID);
+       enm.insert("Gaussian", CLOSURE_BSSRDF_GAUSSIAN_ID);
+
+       return enm;
+}
+
+ShaderEnum SubsurfaceScatteringNode::falloff_enum = subsurface_falloff_init();
+
 SubsurfaceScatteringNode::SubsurfaceScatteringNode()
 : BsdfNode(true)
 {
        name = "subsurface_scattering";
-       closure = CLOSURE_BSSRDF_ID;
+       closure = CLOSURE_BSSRDF_CUBIC_ID;
 
        add_input("Scale", SHADER_SOCKET_FLOAT, 0.01f);
        add_input("Radius", SHADER_SOCKET_VECTOR, make_float3(0.1f, 0.1f, 0.1f));
-       add_input("IOR", SHADER_SOCKET_FLOAT, 1.3f);
+       add_input("Sharpness", SHADER_SOCKET_FLOAT, 0.0f);
+       add_input("Texture Blur", SHADER_SOCKET_FLOAT, 1.0f);
 }
 
 void SubsurfaceScatteringNode::compile(SVMCompiler& compiler)
 {
-       BsdfNode::compile(compiler, input("Scale"), input("IOR"), input("Radius"));
+       BsdfNode::compile(compiler, input("Scale"), input("Texture Blur"), input("Radius"), input("Sharpness"));
 }
 
 void SubsurfaceScatteringNode::compile(OSLCompiler& compiler)
 {
+       compiler.parameter("Falloff", falloff_enum[closure]);
        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 */
 
 EmissionNode::EmissionNode()
 : ShaderNode("emission")
 {
-       total_power = false;
-
        add_input("Color", SHADER_SOCKET_COLOR, make_float3(0.8f, 0.8f, 0.8f));
        add_input("Strength", SHADER_SOCKET_FLOAT, 10.0f);
        add_input("SurfaceMixWeight", SHADER_SOCKET_FLOAT, 0.0f, ShaderInput::USE_SVM);
@@ -1643,10 +1931,8 @@ void EmissionNode::compile(SVMCompiler& compiler)
        if(color_in->link || strength_in->link) {
                compiler.stack_assign(color_in);
                compiler.stack_assign(strength_in);
-               compiler.add_node(NODE_EMISSION_WEIGHT, color_in->stack_offset, strength_in->stack_offset, total_power? 1: 0);
+               compiler.add_node(NODE_EMISSION_WEIGHT, color_in->stack_offset, strength_in->stack_offset);
        }
-       else if(total_power)
-               compiler.add_node(NODE_EMISSION_SET_WEIGHT_TOTAL, color_in->value * strength_in->value.x);
        else
                compiler.add_node(NODE_CLOSURE_SET_WEIGHT, color_in->value * strength_in->value.x);
 
@@ -1655,7 +1941,6 @@ void EmissionNode::compile(SVMCompiler& compiler)
 
 void EmissionNode::compile(OSLCompiler& compiler)
 {
-       compiler.parameter("TotalPower", (total_power)? 1: 0);
        compiler.add(this, "node_emission");
 }
 
@@ -1664,6 +1949,8 @@ void EmissionNode::compile(OSLCompiler& compiler)
 BackgroundNode::BackgroundNode()
 : ShaderNode("background")
 {
+       special_type = SHADER_SPECIAL_TYPE_BACKGROUND;
+
        add_input("Color", SHADER_SOCKET_COLOR, make_float3(0.8f, 0.8f, 0.8f));
        add_input("Strength", SHADER_SOCKET_FLOAT, 1.0f);
        add_input("SurfaceMixWeight", SHADER_SOCKET_FLOAT, 0.0f, ShaderInput::USE_SVM);
@@ -1752,7 +2039,7 @@ void AmbientOcclusionNode::compile(OSLCompiler& compiler)
 VolumeNode::VolumeNode()
 : ShaderNode("volume")
 {
-       closure = ccl::CLOSURE_VOLUME_ISOTROPIC_ID;
+       closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
 
        add_input("Color", SHADER_SOCKET_COLOR, make_float3(0.8f, 0.8f, 0.8f));
        add_input("Density", SHADER_SOCKET_FLOAT, 1.0f);
@@ -1796,38 +2083,77 @@ void VolumeNode::compile(OSLCompiler& compiler)
        assert(0);
 }
 
-/* Transparent Volume Closure */
+/* Absorption Volume Closure */
 
-TransparentVolumeNode::TransparentVolumeNode()
+AbsorptionVolumeNode::AbsorptionVolumeNode()
 {
-       closure = CLOSURE_VOLUME_TRANSPARENT_ID;
+       closure = CLOSURE_VOLUME_ABSORPTION_ID;
 }
 
-void TransparentVolumeNode::compile(SVMCompiler& compiler)
+void AbsorptionVolumeNode::compile(SVMCompiler& compiler)
 {
        VolumeNode::compile(compiler, input("Density"), NULL);
 }
 
-void TransparentVolumeNode::compile(OSLCompiler& compiler)
+void AbsorptionVolumeNode::compile(OSLCompiler& compiler)
 {
-       compiler.add(this, "node_isotropic_volume");
+       compiler.add(this, "node_absorption_volume");
 }
 
-/* Isotropic Volume Closure */
+/* Scatter Volume Closure */
 
-IsotropicVolumeNode::IsotropicVolumeNode()
+ScatterVolumeNode::ScatterVolumeNode()
 {
-       closure = CLOSURE_VOLUME_ISOTROPIC_ID;
+       closure = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID;
+       
+       add_input("Anisotropy", SHADER_SOCKET_FLOAT, 0.0f);
 }
 
-void IsotropicVolumeNode::compile(SVMCompiler& compiler)
+void ScatterVolumeNode::compile(SVMCompiler& compiler)
 {
-       VolumeNode::compile(compiler, input("Density"), NULL);
+       VolumeNode::compile(compiler, input("Density"), input("Anisotropy"));
 }
 
-void IsotropicVolumeNode::compile(OSLCompiler& compiler)
+void ScatterVolumeNode::compile(OSLCompiler& compiler)
 {
-       compiler.add(this, "node_isotropic_volume");
+       compiler.add(this, "node_scatter_volume");
+}
+
+/* Hair BSDF Closure */
+
+static ShaderEnum hair_component_init()
+{
+       ShaderEnum enm;
+
+       enm.insert("Reflection", CLOSURE_BSDF_HAIR_REFLECTION_ID);
+       enm.insert("Transmission", CLOSURE_BSDF_HAIR_TRANSMISSION_ID);
+
+       return enm;
+}
+
+ShaderEnum HairBsdfNode::component_enum = hair_component_init();
+
+HairBsdfNode::HairBsdfNode()
+{
+       component = ustring("Reflection");
+
+       add_input("Offset", SHADER_SOCKET_FLOAT);
+       add_input("RoughnessU", SHADER_SOCKET_FLOAT);
+       add_input("RoughnessV", SHADER_SOCKET_FLOAT);
+}
+
+void HairBsdfNode::compile(SVMCompiler& compiler)
+{
+       closure = (ClosureType)component_enum[component];
+
+       BsdfNode::compile(compiler, input("RoughnessU"), input("RoughnessV"), input("Offset"));
+}
+
+void HairBsdfNode::compile(OSLCompiler& compiler)
+{
+       compiler.parameter("component", component);
+
+       compiler.add(this, "node_hair_bsdf");
 }
 
 /* Geometry */
@@ -1835,6 +2161,8 @@ void IsotropicVolumeNode::compile(OSLCompiler& compiler)
 GeometryNode::GeometryNode()
 : ShaderNode("geometry")
 {
+       special_type = SHADER_SPECIAL_TYPE_GEOMETRY;
+
        add_input("NormalIn", SHADER_SOCKET_NORMAL, ShaderInput::NORMAL, ShaderInput::USE_OSL);
        add_output("Position", SHADER_SOCKET_POINT);
        add_output("Normal", SHADER_SOCKET_NORMAL);
@@ -1843,25 +2171,37 @@ GeometryNode::GeometryNode()
        add_output("Incoming", SHADER_SOCKET_VECTOR);
        add_output("Parametric", SHADER_SOCKET_POINT);
        add_output("Backfacing", SHADER_SOCKET_FLOAT);
+       add_output("Pointiness", SHADER_SOCKET_FLOAT);
 }
 
-void GeometryNode::attributes(AttributeRequestSet *attributes)
+void GeometryNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
-       if(!output("Tangent")->links.empty())
-               attributes->add(ATTR_STD_GENERATED);
+       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(attributes);
+       ShaderNode::attributes(shader, attributes);
 }
 
 void GeometryNode::compile(SVMCompiler& compiler)
 {
        ShaderOutput *out;
        NodeType geom_node = NODE_GEOMETRY;
+       NodeType attr_node = NODE_ATTR;
 
-       if(bump == SHADER_BUMP_DX)
+       if(bump == SHADER_BUMP_DX) {
                geom_node = NODE_GEOMETRY_BUMP_DX;
-       else if(bump == SHADER_BUMP_DY)
+               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()) {
@@ -1904,6 +2244,15 @@ void GeometryNode::compile(SVMCompiler& compiler)
                compiler.stack_assign(out);
                compiler.add_node(NODE_LIGHT_PATH, NODE_LP_backfacing, out->stack_offset);
        }
+
+       out = output("Pointiness");
+       if(!out->links.empty()) {
+               compiler.stack_assign(out);
+               compiler.add_node(attr_node,
+                                 ATTR_STD_POINTINESS,
+                                 out->stack_offset,
+                                 NODE_ATTR_FLOAT);
+       }
 }
 
 void GeometryNode::compile(OSLCompiler& compiler)
@@ -1933,18 +2282,30 @@ TextureCoordinateNode::TextureCoordinateNode()
        add_output("Reflection", SHADER_SOCKET_NORMAL);
 
        from_dupli = false;
+       use_transform = false;
+       ob_tfm = transform_identity();
 }
 
-void TextureCoordinateNode::attributes(AttributeRequestSet *attributes)
+void TextureCoordinateNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
-       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_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(attributes);
+       ShaderNode::attributes(shader, attributes);
 }
 
 void TextureCoordinateNode::compile(SVMCompiler& compiler)
@@ -1976,6 +2337,10 @@ void TextureCoordinateNode::compile(SVMCompiler& compiler)
                                compiler.stack_assign(out);
                                compiler.add_node(texco_node, NODE_TEXCO_DUPLI_GENERATED, out->stack_offset);
                        }
+                       else if(compiler.output_type() == SHADER_TYPE_VOLUME) {
+                               compiler.stack_assign(out);
+                               compiler.add_node(texco_node, NODE_TEXCO_VOLUME_GENERATED, out->stack_offset);
+                       }
                        else {
                                int attr = compiler.attribute(ATTR_STD_GENERATED);
                                compiler.stack_assign(out);
@@ -2006,7 +2371,14 @@ void TextureCoordinateNode::compile(SVMCompiler& compiler)
        out = output("Object");
        if(!out->links.empty()) {
                compiler.stack_assign(out);
-               compiler.add_node(texco_node, NODE_TEXCO_OBJECT, out->stack_offset);
+               compiler.add_node(texco_node, NODE_TEXCO_OBJECT, out->stack_offset, 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");
@@ -2045,12 +2417,89 @@ void TextureCoordinateNode::compile(OSLCompiler& compiler)
        
        if(compiler.background)
                compiler.parameter("is_background", true);
-       
+       if(compiler.output_type() == SHADER_TYPE_VOLUME)
+               compiler.parameter("is_volume", true);
+       compiler.parameter("use_transform", use_transform);
+       Transform ob_itfm = transform_transpose(transform_inverse(ob_tfm));
+       compiler.parameter("object_itfm", ob_itfm);
+
        compiler.parameter("from_dupli", from_dupli);
 
        compiler.add(this, "node_texture_coordinate");
 }
 
+UVMapNode::UVMapNode()
+: ShaderNode("uvmap")
+{
+       attribute = "";
+       from_dupli = false;
+
+       add_output("UV", SHADER_SOCKET_POINT);
+}
+
+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");
+       NodeType texco_node = NODE_TEX_COORD;
+       NodeType 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.stack_assign(out);
+                       compiler.add_node(texco_node, NODE_TEXCO_DUPLI_UV, out->stack_offset);
+               }
+               else {
+                       if (attribute != "")
+                               attr = compiler.attribute(attribute);
+                       else
+                               attr = compiler.attribute(ATTR_STD_UV);
+
+                       compiler.stack_assign(out);
+                       compiler.add_node(attr_node, attr, out->stack_offset, 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("from_dupli", from_dupli);
+       compiler.parameter("name", attribute.c_str());
+       compiler.add(this, "node_uv_map");
+}
+
 /* Light Path */
 
 LightPathNode::LightPathNode()
@@ -2063,7 +2512,10 @@ LightPathNode::LightPathNode()
        add_output("Is Singular Ray", SHADER_SOCKET_FLOAT);
        add_output("Is Reflection Ray", SHADER_SOCKET_FLOAT);
        add_output("Is Transmission Ray", SHADER_SOCKET_FLOAT);
+       add_output("Is Volume Scatter Ray", SHADER_SOCKET_FLOAT);
        add_output("Ray Length", SHADER_SOCKET_FLOAT);
+       add_output("Ray Depth", SHADER_SOCKET_FLOAT);
+       add_output("Transparent Depth", SHADER_SOCKET_FLOAT);
 }
 
 void LightPathNode::compile(SVMCompiler& compiler)
@@ -2113,12 +2565,29 @@ void LightPathNode::compile(SVMCompiler& compiler)
                compiler.add_node(NODE_LIGHT_PATH, NODE_LP_transmission, out->stack_offset);
        }
        
+       out = output("Is Volume Scatter Ray");
+       if(!out->links.empty()) {
+               compiler.stack_assign(out);
+               compiler.add_node(NODE_LIGHT_PATH, NODE_LP_volume_scatter, out->stack_offset);
+       }
+
        out = output("Ray Length");
        if(!out->links.empty()) {
                compiler.stack_assign(out);
                compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_length, out->stack_offset);
        }
+       
+       out = output("Ray Depth");
+       if(!out->links.empty()) {
+               compiler.stack_assign(out);
+               compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_depth, out->stack_offset);
+       }
 
+       out = output("Transparent Depth");
+       if(!out->links.empty()) {
+               compiler.stack_assign(out);
+               compiler.add_node(NODE_LIGHT_PATH, NODE_LP_ray_transparent, out->stack_offset);
+       }
 }
 
 void LightPathNode::compile(OSLCompiler& compiler)
@@ -2129,7 +2598,7 @@ void LightPathNode::compile(OSLCompiler& compiler)
 /* Light Falloff */
 
 LightFalloffNode::LightFalloffNode()
-: ShaderNode("light_path")
+: ShaderNode("light_fallof")
 {
        add_input("Strength", SHADER_SOCKET_FLOAT, 100.0f);
        add_input("Smooth", SHADER_SOCKET_FLOAT, 0.0f);
@@ -2225,15 +2694,15 @@ ParticleInfoNode::ParticleInfoNode()
        add_output("Age", SHADER_SOCKET_FLOAT);
        add_output("Lifetime", SHADER_SOCKET_FLOAT);
        add_output("Location", SHADER_SOCKET_POINT);
-       #if 0   /* not yet supported */
+#if 0  /* not yet supported */
        add_output("Rotation", SHADER_SOCKET_QUATERNION);
-       #endif
+#endif
        add_output("Size", SHADER_SOCKET_FLOAT);
        add_output("Velocity", SHADER_SOCKET_VECTOR);
        add_output("Angular Velocity", SHADER_SOCKET_VECTOR);
 }
 
-void ParticleInfoNode::attributes(AttributeRequestSet *attributes)
+void ParticleInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
        if(!output("Index")->links.empty())
                attributes->add(ATTR_STD_PARTICLE);
@@ -2243,10 +2712,10 @@ void ParticleInfoNode::attributes(AttributeRequestSet *attributes)
                attributes->add(ATTR_STD_PARTICLE);
        if(!output("Location")->links.empty())
                attributes->add(ATTR_STD_PARTICLE);
-       #if 0   /* not yet supported */
+#if 0  /* not yet supported */
        if(!output("Rotation")->links.empty())
                attributes->add(ATTR_STD_PARTICLE);
-       #endif
+#endif
        if(!output("Size")->links.empty())
                attributes->add(ATTR_STD_PARTICLE);
        if(!output("Velocity")->links.empty())
@@ -2254,7 +2723,7 @@ void ParticleInfoNode::attributes(AttributeRequestSet *attributes)
        if(!output("Angular Velocity")->links.empty())
                attributes->add(ATTR_STD_PARTICLE);
 
-       ShaderNode::attributes(attributes);
+       ShaderNode::attributes(shader, attributes);
 }
 
 void ParticleInfoNode::compile(SVMCompiler& compiler)
@@ -2331,14 +2800,16 @@ HairInfoNode::HairInfoNode()
        /*add_output("Fade", SHADER_SOCKET_FLOAT);*/
 }
 
-void HairInfoNode::attributes(AttributeRequestSet *attributes)
+void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
-       ShaderOutput *intercept_out = output("Intercept");
+       if(shader->has_surface) {
+               ShaderOutput *intercept_out = output("Intercept");
 
-       if(!intercept_out->links.empty())
-               attributes->add(ATTR_STD_CURVE_INTERCEPT);
-       
-       ShaderNode::attributes(attributes);
+               if(!intercept_out->links.empty())
+                       attributes->add(ATTR_STD_CURVE_INTERCEPT);
+       }
+
+       ShaderNode::attributes(shader, attributes);
 }
 
 void HairInfoNode::compile(SVMCompiler& compiler)
@@ -2630,13 +3101,13 @@ void CombineRGBNode::compile(SVMCompiler& compiler)
        compiler.stack_assign(color_out);
 
        compiler.stack_assign(red_in);
-       compiler.add_node(NODE_COMBINE_RGB, red_in->stack_offset, 0, color_out->stack_offset);
+       compiler.add_node(NODE_COMBINE_VECTOR, red_in->stack_offset, 0, color_out->stack_offset);
 
        compiler.stack_assign(green_in);
-       compiler.add_node(NODE_COMBINE_RGB, green_in->stack_offset, 1, color_out->stack_offset);
+       compiler.add_node(NODE_COMBINE_VECTOR, green_in->stack_offset, 1, color_out->stack_offset);
 
        compiler.stack_assign(blue_in);
-       compiler.add_node(NODE_COMBINE_RGB, blue_in->stack_offset, 2, color_out->stack_offset);
+       compiler.add_node(NODE_COMBINE_VECTOR, blue_in->stack_offset, 2, color_out->stack_offset);
 }
 
 void CombineRGBNode::compile(OSLCompiler& compiler)
@@ -2644,6 +3115,71 @@ void CombineRGBNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_combine_rgb");
 }
 
+/* Combine XYZ */
+CombineXYZNode::CombineXYZNode()
+: ShaderNode("combine_xyz")
+{
+       add_input("X", SHADER_SOCKET_FLOAT);
+       add_input("Y", SHADER_SOCKET_FLOAT);
+       add_input("Z", SHADER_SOCKET_FLOAT);
+       add_output("Vector", SHADER_SOCKET_VECTOR);
+}
+
+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.stack_assign(vector_out);
+
+       compiler.stack_assign(x_in);
+       compiler.add_node(NODE_COMBINE_VECTOR, x_in->stack_offset, 0, vector_out->stack_offset);
+
+       compiler.stack_assign(y_in);
+       compiler.add_node(NODE_COMBINE_VECTOR, y_in->stack_offset, 1, vector_out->stack_offset);
+
+       compiler.stack_assign(z_in);
+       compiler.add_node(NODE_COMBINE_VECTOR, z_in->stack_offset, 2, vector_out->stack_offset);
+}
+
+void CombineXYZNode::compile(OSLCompiler& compiler)
+{
+       compiler.add(this, "node_combine_xyz");
+}
+
+/* Combine HSV */
+CombineHSVNode::CombineHSVNode()
+: ShaderNode("combine_hsv")
+{
+       add_input("H", SHADER_SOCKET_FLOAT);
+       add_input("S", SHADER_SOCKET_FLOAT);
+       add_input("V", SHADER_SOCKET_FLOAT);
+       add_output("Color", SHADER_SOCKET_COLOR);
+}
+
+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.stack_assign(color_out);
+       compiler.stack_assign(hue_in);
+       compiler.stack_assign(saturation_in);
+       compiler.stack_assign(value_in);
+       
+       compiler.add_node(NODE_COMBINE_HSV, hue_in->stack_offset, saturation_in->stack_offset, value_in->stack_offset);
+       compiler.add_node(NODE_COMBINE_HSV, color_out->stack_offset);
+}
+
+void CombineHSVNode::compile(OSLCompiler& compiler)
+{
+       compiler.add(this, "node_combine_hsv");
+}
+
 /* Gamma */
 GammaNode::GammaNode()
 : ShaderNode("gamma")
@@ -2723,13 +3259,13 @@ void SeparateRGBNode::compile(SVMCompiler& compiler)
        compiler.stack_assign(color_in);
 
        compiler.stack_assign(red_out);
-       compiler.add_node(NODE_SEPARATE_RGB, color_in->stack_offset, 0, red_out->stack_offset);
+       compiler.add_node(NODE_SEPARATE_VECTOR, color_in->stack_offset, 0, red_out->stack_offset);
 
        compiler.stack_assign(green_out);
-       compiler.add_node(NODE_SEPARATE_RGB, color_in->stack_offset, 1, green_out->stack_offset);
+       compiler.add_node(NODE_SEPARATE_VECTOR, color_in->stack_offset, 1, green_out->stack_offset);
 
        compiler.stack_assign(blue_out);
-       compiler.add_node(NODE_SEPARATE_RGB, color_in->stack_offset, 2, blue_out->stack_offset);
+       compiler.add_node(NODE_SEPARATE_VECTOR, color_in->stack_offset, 2, blue_out->stack_offset);
 }
 
 void SeparateRGBNode::compile(OSLCompiler& compiler)
@@ -2737,7 +3273,73 @@ void SeparateRGBNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_separate_rgb");
 }
 
-/* Separate RGB */
+/* Separate XYZ */
+SeparateXYZNode::SeparateXYZNode()
+: ShaderNode("separate_xyz")
+{
+       add_input("Vector", SHADER_SOCKET_VECTOR);
+       add_output("X", SHADER_SOCKET_FLOAT);
+       add_output("Y", SHADER_SOCKET_FLOAT);
+       add_output("Z", SHADER_SOCKET_FLOAT);
+}
+
+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.stack_assign(vector_in);
+
+       compiler.stack_assign(x_out);
+       compiler.add_node(NODE_SEPARATE_VECTOR, vector_in->stack_offset, 0, x_out->stack_offset);
+
+       compiler.stack_assign(y_out);
+       compiler.add_node(NODE_SEPARATE_VECTOR, vector_in->stack_offset, 1, y_out->stack_offset);
+
+       compiler.stack_assign(z_out);
+       compiler.add_node(NODE_SEPARATE_VECTOR, vector_in->stack_offset, 2, z_out->stack_offset);
+}
+
+void SeparateXYZNode::compile(OSLCompiler& compiler)
+{
+       compiler.add(this, "node_separate_xyz");
+}
+
+/* Separate HSV */
+SeparateHSVNode::SeparateHSVNode()
+: ShaderNode("separate_hsv")
+{
+       add_input("Color", SHADER_SOCKET_COLOR);
+       add_output("H", SHADER_SOCKET_FLOAT);
+       add_output("S", SHADER_SOCKET_FLOAT);
+       add_output("V", SHADER_SOCKET_FLOAT);
+}
+
+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.stack_assign(color_in);
+       compiler.stack_assign(hue_out);
+       compiler.stack_assign(saturation_out);
+       compiler.stack_assign(value_out);
+       
+       compiler.add_node(NODE_SEPARATE_HSV, color_in->stack_offset, hue_out->stack_offset, saturation_out->stack_offset);
+       compiler.add_node(NODE_SEPARATE_HSV, value_out->stack_offset);
+
+}
+
+void SeparateHSVNode::compile(OSLCompiler& compiler)
+{
+       compiler.add(this, "node_separate_hsv");
+}
+
+/* Hue Saturation Value */
 HSVNode::HSVNode()
 : ShaderNode("hsv")
 {
@@ -2786,16 +3388,25 @@ AttributeNode::AttributeNode()
        add_output("Fac",  SHADER_SOCKET_FLOAT);
 }
 
-void AttributeNode::attributes(AttributeRequestSet *attributes)
+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())
-               attributes->add(attribute);
-       
-       ShaderNode::attributes(attributes);
+       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)
@@ -2804,6 +3415,13 @@ void AttributeNode::compile(SVMCompiler& compiler)
        ShaderOutput *vector_out = output("Vector");
        ShaderOutput *fac_out = output("Fac");
        NodeType 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;
@@ -2811,8 +3429,6 @@ void AttributeNode::compile(SVMCompiler& compiler)
                attr_node = NODE_ATTR_BUMP_DY;
 
        if(!color_out->links.empty() || !vector_out->links.empty()) {
-               int attr = compiler.attribute(attribute);
-
                if(!color_out->links.empty()) {
                        compiler.stack_assign(color_out);
                        compiler.add_node(attr_node, attr, color_out->stack_offset, NODE_ATTR_FLOAT3);
@@ -2824,8 +3440,6 @@ void AttributeNode::compile(SVMCompiler& compiler)
        }
 
        if(!fac_out->links.empty()) {
-               int attr = compiler.attribute(attribute);
-
                compiler.stack_assign(fac_out);
                compiler.add_node(attr_node, attr, fac_out->stack_offset, NODE_ATTR_FLOAT);
        }
@@ -2839,8 +3453,12 @@ void AttributeNode::compile(OSLCompiler& compiler)
                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.parameter("name", attribute.c_str());
        compiler.add(this, "node_attribute");
 }
 
@@ -2874,7 +3492,7 @@ void CameraNode::compile(OSLCompiler& compiler)
 /* Fresnel */
 
 FresnelNode::FresnelNode()
-: ShaderNode("Fresnel")
+: ShaderNode("fresnel")
 {
        add_input("Normal", SHADER_SOCKET_NORMAL, ShaderInput::NORMAL, ShaderInput::USE_OSL);
        add_input("IOR", SHADER_SOCKET_FLOAT, 1.45f);
@@ -2883,12 +3501,17 @@ FresnelNode::FresnelNode()
 
 void FresnelNode::compile(SVMCompiler& compiler)
 {
+       ShaderInput *normal_in = input("Normal");
        ShaderInput *ior_in = input("IOR");
        ShaderOutput *fac_out = output("Fac");
 
        compiler.stack_assign(ior_in);
        compiler.stack_assign(fac_out);
-       compiler.add_node(NODE_FRESNEL, ior_in->stack_offset, __float_as_int(ior_in->value.x), fac_out->stack_offset);
+       
+       if(normal_in->link)
+               compiler.stack_assign(normal_in);
+       
+       compiler.add_node(NODE_FRESNEL, ior_in->stack_offset, __float_as_int(ior_in->value.x), compiler.encode_uchar4(normal_in->stack_offset, fac_out->stack_offset));
 }
 
 void FresnelNode::compile(OSLCompiler& compiler)
@@ -2896,10 +3519,10 @@ void FresnelNode::compile(OSLCompiler& compiler)
        compiler.add(this, "node_fresnel");
 }
 
-/* Blend Weight */
+/* Layer Weight */
 
 LayerWeightNode::LayerWeightNode()
-: ShaderNode("LayerWeight")
+: ShaderNode("layer_weight")
 {
        add_input("Normal", SHADER_SOCKET_NORMAL, ShaderInput::NORMAL, ShaderInput::USE_OSL);
        add_input("Blend", SHADER_SOCKET_FLOAT, 0.5f);
@@ -2910,8 +3533,12 @@ LayerWeightNode::LayerWeightNode()
 
 void LayerWeightNode::compile(SVMCompiler& compiler)
 {
+       ShaderInput *normal_in = input("Normal");
        ShaderInput *blend_in = input("Blend");
 
+       if(normal_in->link)
+               compiler.stack_assign(normal_in);
+
        if(blend_in->link)
                compiler.stack_assign(blend_in);
 
@@ -2919,14 +3546,14 @@ void LayerWeightNode::compile(SVMCompiler& compiler)
        if(!fresnel_out->links.empty()) {
                compiler.stack_assign(fresnel_out);
                compiler.add_node(NODE_LAYER_WEIGHT, blend_in->stack_offset, __float_as_int(blend_in->value.x),
-                       compiler.encode_uchar4(NODE_LAYER_WEIGHT_FRESNEL, fresnel_out->stack_offset));
+                       compiler.encode_uchar4(NODE_LAYER_WEIGHT_FRESNEL, normal_in->stack_offset, fresnel_out->stack_offset));
        }
 
        ShaderOutput *facing_out = output("Facing");
        if(!facing_out->links.empty()) {
                compiler.stack_assign(facing_out);
                compiler.add_node(NODE_LAYER_WEIGHT, blend_in->stack_offset, __float_as_int(blend_in->value.x),
-                       compiler.encode_uchar4(NODE_LAYER_WEIGHT_FACING, facing_out->stack_offset));
+                       compiler.encode_uchar4(NODE_LAYER_WEIGHT_FACING, normal_in->stack_offset, facing_out->stack_offset));
        }
 }
 
@@ -2938,7 +3565,7 @@ void LayerWeightNode::compile(OSLCompiler& compiler)
 /* Wireframe */
 
 WireframeNode::WireframeNode()
-: ShaderNode("Wireframe")
+: ShaderNode("wireframe")
 {
        add_input("Size", SHADER_SOCKET_FLOAT, 0.01f);
        add_output("Fac", SHADER_SOCKET_FLOAT);
@@ -2950,14 +3577,34 @@ 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.stack_assign(size_in);
        compiler.stack_assign(fac_out);
-       compiler.add_node(NODE_WIREFRAME, size_in->stack_offset, fac_out->stack_offset, use_pixel_size);
+       compiler.add_node(NODE_WIREFRAME,
+                         size_in->stack_offset,
+                         fac_out->stack_offset,
+                         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("use_pixel_size", use_pixel_size);
        compiler.add(this, "node_wireframe");
 }
@@ -2965,7 +3612,7 @@ void WireframeNode::compile(OSLCompiler& compiler)
 /* Wavelength */
 
 WavelengthNode::WavelengthNode()
-: ShaderNode("Wavelength")
+: ShaderNode("wavelength")
 {
        add_input("Wavelength", SHADER_SOCKET_FLOAT, 500.0f);
        add_output("Color", SHADER_SOCKET_COLOR);
@@ -2989,7 +3636,7 @@ void WavelengthNode::compile(OSLCompiler& compiler)
 /* Blackbody */
 
 BlackbodyNode::BlackbodyNode()
-: ShaderNode("Blackbody")
+: ShaderNode("blackbody")
 {
        add_input("Temperature", SHADER_SOCKET_FLOAT, 1200.0f);
        add_output("Color", SHADER_SOCKET_COLOR);
@@ -3079,6 +3726,7 @@ static ShaderEnum math_type_init()
        enm.insert("Less Than", NODE_MATH_LESS_THAN);
        enm.insert("Greater Than", NODE_MATH_GREATER_THAN);
        enm.insert("Modulo", NODE_MATH_MODULO);
+       enm.insert("Absolute", NODE_MATH_ABSOLUTE);
 
        return enm;
 }
@@ -3091,9 +3739,24 @@ void MathNode::compile(SVMCompiler& compiler)
        ShaderInput *value2_in = input("Value2");
        ShaderOutput *value_out = output("Value");
 
+       compiler.stack_assign(value_out);
+
+       /* Optimize math node without links to a single value node. */
+       if(value1_in->link == NULL && value2_in->link == NULL) {
+               float optimized_value = svm_math((NodeMath)type_enum[type],
+                                                value1_in->value.x,
+                                                value2_in->value.x);
+               if(use_clamp) {
+                       optimized_value = clamp(optimized_value, 0.0f, 1.0f);
+               }
+               compiler.add_node(NODE_VALUE_F,
+                                 __float_as_int(optimized_value),
+                                 value_out->stack_offset);
+               return;
+       }
+
        compiler.stack_assign(value1_in);
        compiler.stack_assign(value2_in);
-       compiler.stack_assign(value_out);
 
        compiler.add_node(NODE_MATH, type_enum[type], value1_in->stack_offset, value2_in->stack_offset);
        compiler.add_node(NODE_MATH, value_out->stack_offset);
@@ -3147,11 +3810,31 @@ void VectorMathNode::compile(SVMCompiler& compiler)
        ShaderOutput *value_out = output("Value");
        ShaderOutput *vector_out = output("Vector");
 
-       compiler.stack_assign(vector1_in);
-       compiler.stack_assign(vector2_in);
        compiler.stack_assign(value_out);
        compiler.stack_assign(vector_out);
 
+       /* Optimize vector math node without links to a single value node. */
+       if(vector1_in->link == NULL && vector2_in->link == NULL) {
+               float optimized_value;
+               float3 optimized_vector;
+               svm_vector_math(&optimized_value,
+                               &optimized_vector,
+                               (NodeVectorMath)type_enum[type],
+                               vector1_in->value,
+                               vector2_in->value);
+
+               compiler.add_node(NODE_VALUE_F,
+                                 __float_as_int(optimized_value),
+                                 value_out->stack_offset);
+
+               compiler.add_node(NODE_VALUE_V, vector_out->stack_offset);
+               compiler.add_node(NODE_VALUE_V, optimized_vector);
+               return;
+       }
+
+       compiler.stack_assign(vector1_in);
+       compiler.stack_assign(vector2_in);
+
        compiler.add_node(NODE_VECTOR_MATH, type_enum[type], vector1_in->stack_offset, vector2_in->stack_offset);
        compiler.add_node(NODE_VECTOR_MATH, value_out->stack_offset, vector_out->stack_offset);
 }
@@ -3168,8 +3851,8 @@ VectorTransformNode::VectorTransformNode()
 : ShaderNode("vector_transform")
 {
        type = ustring("Vector");
-       convert_from = ustring("World");
-       convert_to = ustring("Object");
+       convert_from = ustring("world");
+       convert_to = ustring("object");
 
        add_input("Vector", SHADER_SOCKET_VECTOR);
        add_output("Vector",  SHADER_SOCKET_VECTOR);
@@ -3181,35 +3864,24 @@ static ShaderEnum vector_transform_type_init()
 
        enm.insert("Vector", NODE_VECTOR_TRANSFORM_TYPE_VECTOR);
        enm.insert("Point", NODE_VECTOR_TRANSFORM_TYPE_POINT);
+       enm.insert("Normal", NODE_VECTOR_TRANSFORM_TYPE_NORMAL);
 
        return enm;
 }
 
-static ShaderEnum vector_transform_convert_from_init()
-{
-       ShaderEnum enm;
-
-       enm.insert("World", NODE_VECTOR_TRANSFORM_CONVERT_FROM_WORLD);
-       enm.insert("Object", NODE_VECTOR_TRANSFORM_CONVERT_FROM_OBJECT);
-       enm.insert("Camera", NODE_VECTOR_TRANSFORM_CONVERT_FROM_CAMERA);
-
-       return enm;
-}
-
-static ShaderEnum vector_transform_convert_to_init()
+static ShaderEnum vector_transform_convert_space_init()
 {
        ShaderEnum enm;
 
-       enm.insert("World", NODE_VECTOR_TRANSFORM_CONVERT_TO_WORLD);
-       enm.insert("Object", NODE_VECTOR_TRANSFORM_CONVERT_TO_OBJECT);
-       enm.insert("Camera", NODE_VECTOR_TRANSFORM_CONVERT_TO_CAMERA);
+       enm.insert("world", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD);
+       enm.insert("object", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT);
+       enm.insert("camera", NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA);
 
        return enm;
 }
 
 ShaderEnum VectorTransformNode::type_enum = vector_transform_type_init();
-ShaderEnum VectorTransformNode::convert_from_enum = vector_transform_convert_from_init();
-ShaderEnum VectorTransformNode::convert_to_enum = vector_transform_convert_to_init();
+ShaderEnum VectorTransformNode::convert_space_enum = vector_transform_convert_space_init();
 
 void VectorTransformNode::compile(SVMCompiler& compiler)
 {
@@ -3220,7 +3892,7 @@ void VectorTransformNode::compile(SVMCompiler& compiler)
        compiler.stack_assign(vector_out);
 
        compiler.add_node(NODE_VECTOR_TRANSFORM,
-               compiler.encode_uchar4(type_enum[type], convert_from_enum[convert_from], convert_to_enum[convert_to]),
+               compiler.encode_uchar4(type_enum[type], convert_space_enum[convert_from], convert_space_enum[convert_to]),
                compiler.encode_uchar4(vector_in->stack_offset, vector_out->stack_offset));
 }
 
@@ -3328,7 +4000,7 @@ void RGBCurvesNode::compile(OSLCompiler& compiler)
 /* VectorCurvesNode */
 
 VectorCurvesNode::VectorCurvesNode()
-: ShaderNode("rgb_curves")
+: ShaderNode("vector_curves")
 {
        add_input("Fac", SHADER_SOCKET_FLOAT);
        add_input("Vector", SHADER_SOCKET_VECTOR);
@@ -3447,6 +4119,7 @@ void SetNormalNode::compile(OSLCompiler& compiler)
 OSLScriptNode::OSLScriptNode()
 : ShaderNode("osl_script")
 {
+       special_type = SHADER_SPECIAL_TYPE_SCRIPT;
 }
 
 void OSLScriptNode::compile(SVMCompiler& compiler)
@@ -3456,6 +4129,26 @@ void OSLScriptNode::compile(SVMCompiler& compiler)
 
 void OSLScriptNode::compile(OSLCompiler& compiler)
 {
+       /* XXX fix for #36790:
+        * point and normal parameters are reflected as generic SOCK_VECTOR sockets
+        * on the node. Socket fixed input values need to be copied explicitly here for
+        * vector sockets, otherwise OSL will reject the value due to mismatching type.
+        */
+       foreach(ShaderInput *input, this->inputs) {
+               if(!input->link) {
+                       /* no need for compatible_name here, OSL parameter names are always unique */
+                       string param_name(input->name);
+                       switch(input->type) {
+                               case SHADER_SOCKET_VECTOR:
+                                       compiler.parameter_point(param_name.c_str(), input->value);
+                                       compiler.parameter_normal(param_name.c_str(), input->value);
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+       }
+
        if(!filepath.empty())
                compiler.add(this, filepath.c_str(), true);
        else
@@ -3492,9 +4185,9 @@ NormalMapNode::NormalMapNode()
        add_output("Normal", SHADER_SOCKET_NORMAL);
 }
 
-void NormalMapNode::attributes(AttributeRequestSet *attributes)
+void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
-       if(space == ustring("Tangent")) {
+       if(shader->has_surface && space == ustring("Tangent")) {
                if(attribute == ustring("")) {
                        attributes->add(ATTR_STD_UV_TANGENT);
                        attributes->add(ATTR_STD_UV_TANGENT_SIGN);
@@ -3507,7 +4200,7 @@ void NormalMapNode::attributes(AttributeRequestSet *attributes)
                attributes->add(ATTR_STD_VERTEX_NORMAL);
        }
        
-       ShaderNode::attributes(attributes);
+       ShaderNode::attributes(shader, attributes);
 }
 
 void NormalMapNode::compile(SVMCompiler& compiler)
@@ -3586,7 +4279,7 @@ ShaderEnum TangentNode::direction_type_enum = tangent_direction_type_init();
 ShaderEnum TangentNode::axis_enum = tangent_axis_init();
 
 TangentNode::TangentNode()
-: ShaderNode("normal_map")
+: ShaderNode("tangent")
 {
        direction_type = ustring("Radial");
        axis = ustring("X");
@@ -3596,18 +4289,20 @@ TangentNode::TangentNode()
        add_output("Tangent", SHADER_SOCKET_NORMAL);
 }
 
-void TangentNode::attributes(AttributeRequestSet *attributes)
+void TangentNode::attributes(Shader *shader, AttributeRequestSet *attributes)
 {
-       if(direction_type == ustring("UV Map")) {
-               if(attribute == ustring(""))
-                       attributes->add(ATTR_STD_UV_TANGENT);
+       if(shader->has_surface) {
+               if(direction_type == ustring("UV Map")) {
+                       if(attribute == ustring(""))
+                               attributes->add(ATTR_STD_UV_TANGENT);
+                       else
+                               attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str()));
+               }
                else
-                       attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str()));
+                       attributes->add(ATTR_STD_GENERATED);
        }
-       else
-               attributes->add(ATTR_STD_GENERATED);
        
-       ShaderNode::attributes(attributes);
+       ShaderNode::attributes(shader, attributes);
 }
 
 void TangentNode::compile(SVMCompiler& compiler)
@@ -3648,4 +4343,3 @@ void TangentNode::compile(OSLCompiler& compiler)
 }
 
 CCL_NAMESPACE_END
-