Merge branch 'blender2.7'
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Thu, 14 Feb 2019 19:00:37 +0000 (20:00 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Thu, 14 Feb 2019 19:00:37 +0000 (20:00 +0100)
1  2 
source/blender/gpu/shaders/gpu_shader_material.glsl
source/blender/nodes/shader/nodes/node_shader_hueSatVal.c

@@@ -1,18 -1,12 +1,18 @@@
 +
 +uniform mat4 ModelViewMatrix;
 +uniform mat4 ModelViewMatrixInverse;
 +uniform mat3 NormalMatrix;
 +
 +#ifndef USE_ATTR
 +uniform mat4 ModelMatrix;
 +uniform mat4 ModelMatrixInverse;
 +#endif
 +
  /* Converters */
  
  float convert_rgba_to_float(vec4 color)
  {
 -#ifdef USE_NEW_SHADING
 -      return color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
 -#else
 -      return (color.r + color.g + color.b) / 3.0;
 -#endif
 +      return dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
  }
  
  float exp_blender(float f)
@@@ -59,7 -53,7 +59,7 @@@ void rgb_to_hsv(vec4 rgb, out vec4 outc
                h = 0.0;
        }
        else {
 -              c = (vec3(cmax, cmax, cmax) - rgb.xyz) / cdelta;
 +              c = (vec3(cmax) - rgb.xyz) / cdelta;
  
                if (rgb.x == cmax) h = c[2] - c[1];
                else if (rgb.y == cmax) h = 2.0 + c[0] -  c[2];
@@@ -141,25 -135,59 +141,25 @@@ void linearrgb_to_srgb(vec4 col_from, o
        col_to.a = col_from.a;
  }
  
 -void color_to_normal(vec3 color, out vec3 normal)
 -{
 -      normal.x =  2.0 * ((color.r) - 0.5);
 -      normal.y = -2.0 * ((color.g) - 0.5);
 -      normal.z =  2.0 * ((color.b) - 0.5);
 -}
 -
  void color_to_normal_new_shading(vec3 color, out vec3 normal)
  {
 -      normal.x =  2.0 * ((color.r) - 0.5);
 -      normal.y =  2.0 * ((color.g) - 0.5);
 -      normal.z =  2.0 * ((color.b) - 0.5);
 +      normal = vec3(2.0) * color - vec3(1.0);
  }
  
  void color_to_blender_normal_new_shading(vec3 color, out vec3 normal)
  {
 -      normal.x =  2.0 * ((color.r) - 0.5);
 -      normal.y = -2.0 * ((color.g) - 0.5);
 -      normal.z = -2.0 * ((color.b) - 0.5);
 +      normal = vec3(2.0, -2.0, -2.0) * color - vec3(1.0);
  }
  
 +#ifndef M_PI
  #define M_PI 3.14159265358979323846
 -#define M_1_PI 0.31830988618379069
 +#endif
 +#ifndef M_1_PI
 +#define M_1_PI 0.318309886183790671538
 +#endif
  
  /*********** SHADER NODES ***************/
  
 -void vcol_attribute(vec4 attvcol, out vec4 vcol)
 -{
 -      vcol = vec4(attvcol.x, attvcol.y, attvcol.z, 1.0);
 -}
 -
 -void uv_attribute(vec2 attuv, out vec3 uv)
 -{
 -      uv = vec3(attuv * 2.0 - vec2(1.0, 1.0), 0.0);
 -}
 -
 -void geom(
 -        vec3 co, vec3 nor, mat4 viewinvmat, vec3 attorco, vec2 attuv, vec4 attvcol,
 -        out vec3 global, out vec3 local, out vec3 view, out vec3 orco, out vec3 uv,
 -        out vec3 normal, out vec4 vcol, out float vcol_alpha, out float frontback)
 -{
 -      local = co;
 -      view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(local) : vec3(0.0, 0.0, -1.0);
 -      global = (viewinvmat * vec4(local, 1.0)).xyz;
 -      orco = attorco;
 -      uv_attribute(attuv, uv);
 -      normal = -normalize(nor);   /* blender render normal is negated */
 -      vcol_attribute(attvcol, vcol);
 -      srgb_to_linearrgb(vcol, vcol);
 -      vcol_alpha = attvcol.a;
 -      frontback = (gl_FrontFacing) ? 1.0 : 0.0;
 -}
 -
  void particle_info(
          vec4 sprops, vec4 loc, vec3 vel, vec3 avel,
          out float index, out float random, out float age,
@@@ -194,13 -222,7 +194,13 @@@ void point_transform_m4v3(vec3 vin, mat
  
  void point_texco_remap_square(vec3 vin, out vec3 vout)
  {
 -      vout = vec3(vin - vec3(0.5, 0.5, 0.5)) * 2.0;
 +      vout = vin * 2.0 - 1.0;
 +}
 +
 +void point_texco_clamp(vec3 vin, sampler2D ima, out vec3 vout)
 +{
 +      vec2 half_texel_size = 0.5 / vec2(textureSize(ima, 0).xy);
 +      vout = clamp(vin, half_texel_size.xyy, 1.0 - half_texel_size.xyy);
  }
  
  void point_map_to_sphere(vec3 vin, out vec3 vout)
@@@ -234,11 -256,13 +234,11 @@@ void point_map_to_tube(vec3 vin, out ve
        vout = vec3(u, v, 0.0);
  }
  
 -void mapping(vec3 vec, mat4 mat, vec3 minvec, vec3 maxvec, float domin, float domax, out vec3 outvec)
 +void mapping(vec3 vec, vec4 m0, vec4 m1, vec4 m2, vec4 m3, vec3 minvec, vec3 maxvec, out vec3 outvec)
  {
 +      mat4 mat = mat4(m0, m1, m2, m3);
        outvec = (mat * vec4(vec, 1.0)).xyz;
 -      if (domin == 1.0)
 -              outvec = max(outvec, minvec);
 -      if (domax == 1.0)
 -              outvec = min(outvec, maxvec);
 +      outvec = clamp(outvec, minvec, maxvec);
  }
  
  void camera(vec3 co, out vec3 outview, out float outdepth, out float outdist)
        outview = normalize(co);
  }
  
 -void lamp(
 -        vec4 col, float energy, vec3 lv, float dist, vec3 shadow, float visifac,
 -        out vec4 outcol, out vec3 outlv, out float outdist, out vec4 outshadow, out float outvisifac)
 -{
 -      outcol = col * energy;
 -      outlv = lv;
 -      outdist = dist;
 -      outshadow = vec4(shadow, 1.0);
 -      outvisifac = visifac;
 -}
 -
  void math_add(float val1, float val2, out float outval)
  {
        outval = val1 + val2;
@@@ -414,13 -449,13 +414,13 @@@ void squeeze(float val, float width, fl
  void vec_math_add(vec3 v1, vec3 v2, out vec3 outvec, out float outval)
  {
        outvec = v1 + v2;
 -      outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) / 3.0;
 +      outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) * 0.333333;
  }
  
  void vec_math_sub(vec3 v1, vec3 v2, out vec3 outvec, out float outval)
  {
        outvec = v1 - v2;
 -      outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) / 3.0;
 +      outval = (abs(outvec[0]) + abs(outvec[1]) + abs(outvec[2])) * 0.333333;
  }
  
  void vec_math_average(vec3 v1, vec3 v2, out vec3 outvec, out float outval)
@@@ -436,7 -471,7 +436,7 @@@ void vec_math_mix(float strength, vec3 
  
  void vec_math_dot(vec3 v1, vec3 v2, out vec3 outvec, out float outval)
  {
 -      outvec = vec3(0, 0, 0);
 +      outvec = vec3(0);
        outval = dot(v1, v2);
  }
  
@@@ -464,32 -499,39 +464,32 @@@ void invert_z(vec3 v, out vec3 outv
        outv = v;
  }
  
 -void normal(vec3 dir, vec3 nor, out vec3 outnor, out float outdot)
 -{
 -      outnor = nor;
 -      outdot = -dot(dir, nor);
 -}
 -
 -void normal_new_shading(vec3 dir, vec3 nor, out vec3 outnor, out float outdot)
 +void normal_new_shading(vec3 nor, vec3 dir, out vec3 outnor, out float outdot)
  {
 -      outnor = normalize(nor);
 -      outdot = dot(normalize(dir), nor);
 +      outnor = dir;
 +      outdot = dot(normalize(nor), dir);
  }
  
 -void curves_vec(float fac, vec3 vec, sampler2D curvemap, out vec3 outvec)
 +void curves_vec(float fac, vec3 vec, sampler1DArray curvemap, float layer, out vec3 outvec)
  {
 -      outvec.x = texture2D(curvemap, vec2((vec.x + 1.0) * 0.5, 0.0)).x;
 -      outvec.y = texture2D(curvemap, vec2((vec.y + 1.0) * 0.5, 0.0)).y;
 -      outvec.z = texture2D(curvemap, vec2((vec.z + 1.0) * 0.5, 0.0)).z;
 -
 -      if (fac != 1.0)
 -              outvec = (outvec * fac) + (vec * (1.0 - fac));
 -
 +      vec4 co = vec4(vec * 0.5 + 0.5, layer);
 +      outvec.x = texture(curvemap, co.xw).x;
 +      outvec.y = texture(curvemap, co.yw).y;
 +      outvec.z = texture(curvemap, co.zw).z;
 +      outvec = mix(vec, outvec, fac);
  }
  
 -void curves_rgb(float fac, vec4 col, sampler2D curvemap, out vec4 outcol)
 +void curves_rgb(float fac, vec4 col, sampler1DArray curvemap, float layer, out vec4 outcol)
  {
 -      outcol.r = texture2D(curvemap, vec2(texture2D(curvemap, vec2(col.r, 0.0)).a, 0.0)).r;
 -      outcol.g = texture2D(curvemap, vec2(texture2D(curvemap, vec2(col.g, 0.0)).a, 0.0)).g;
 -      outcol.b = texture2D(curvemap, vec2(texture2D(curvemap, vec2(col.b, 0.0)).a, 0.0)).b;
 -
 -      if (fac != 1.0)
 -              outcol = (outcol * fac) + (col * (1.0 - fac));
 -
 +      vec4 co = vec4(col.rgb, layer);
 +      co.x = texture(curvemap, co.xw).a;
 +      co.y = texture(curvemap, co.yw).a;
 +      co.z = texture(curvemap, co.zw).a;
 +      outcol.r = texture(curvemap, co.xw).r;
 +      outcol.g = texture(curvemap, co.yw).g;
 +      outcol.b = texture(curvemap, co.zw).b;
        outcol.a = col.a;
 +      outcol = mix(col, outcol, fac);
  }
  
  void set_value(float val, out float outval)
@@@ -802,41 -844,24 +802,41 @@@ void mix_linear(float fac, vec4 col1, v
        outcol = col1 + fac * (2.0 * (col2 - vec4(0.5)));
  }
  
 -void valtorgb(float fac, sampler2D colormap, out vec4 outcol, out float outalpha)
 +void valtorgb_opti_constant(float fac, float edge, vec4 color1, vec4 color2, out vec4 outcol, out float outalpha)
 +{
 +      outcol = (fac > edge) ? color2 : color1;
 +      outalpha = outcol.a;
 +}
 +
 +void valtorgb_opti_linear(float fac, vec2 mulbias, vec4 color1, vec4 color2, out vec4 outcol, out float outalpha)
 +{
 +      fac = clamp(fac * mulbias.x + mulbias.y, 0.0, 1.0);
 +      outcol = mix(color1, color2, fac);
 +      outalpha = outcol.a;
 +}
 +
 +void valtorgb(float fac, sampler1DArray colormap, float layer, out vec4 outcol, out float outalpha)
 +{
 +      outcol = texture(colormap, vec2(fac, layer));
 +      outalpha = outcol.a;
 +}
 +
 +void valtorgb_nearest(float fac, sampler1DArray colormap, float layer, out vec4 outcol, out float outalpha)
  {
 -      outcol = texture2D(colormap, vec2(fac, 0.0));
 +      fac = clamp(fac, 0.0, 1.0);
 +      outcol = texelFetch(colormap, ivec2(fac * (textureSize(colormap, 0).x - 1), layer), 0);
        outalpha = outcol.a;
  }
  
  void rgbtobw(vec4 color, out float outval)
  {
 -#ifdef USE_NEW_SHADING
 -      outval = color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722;
 -#else
 -      outval = color.r * 0.35 + color.g * 0.45 + color.b * 0.2; /* keep these factors in sync with texture.h:RGBTOBW */
 -#endif
 +      vec3 factors = vec3(0.2126, 0.7152, 0.0722);
 +      outval = dot(color.rgb, factors);
  }
  
  void invert(float fac, vec4 col, out vec4 outcol)
  {
 -      outcol.xyz = mix(col.xyz, vec3(1.0, 1.0, 1.0) - col.xyz, fac);
 +      outcol.xyz = mix(col.xyz, vec3(1.0) - col.xyz, fac);
        outcol.w = col.w;
  }
  
@@@ -857,7 -882,7 +857,7 @@@ void hue_sat(float hue, float sat, floa
        rgb_to_hsv(col, hsv);
  
        hsv[0] = fract(hsv[0] + hue + 0.5);
-       hsv[1] = hsv[1] * clamp(sat, 0.0, 1.0);
+       hsv[1] = clamp(hsv[1] * sat, 0.0, 1.0);
        hsv[2] = hsv[2] * value;
  
        hsv_to_rgb(hsv, outcol);
@@@ -911,706 -936,1922 +911,706 @@@ void output_node(vec4 rgb, float alpha
  
  /*********** TEXTURES ***************/
  
 -void texture_flip_blend(vec3 vec, out vec3 outvec)
 +void texco_norm(vec3 normal, out vec3 outnormal)
  {
 -      outvec = vec.yxz;
 +      /* corresponds to shi->orn, which is negated so cancels
 +         out blender normal negation */
 +      outnormal = normalize(normal);
  }
  
 -void texture_blend_lin(vec3 vec, out float outval)
 +vec3 mtex_2d_mapping(vec3 vec)
  {
 -      outval = (1.0 + vec.x) / 2.0;
 +      return vec3(vec.xy * 0.5 + vec2(0.5), vec.z);
  }
  
 -void texture_blend_quad(vec3 vec, out float outval)
 +/** helper method to extract the upper left 3x3 matrix from a 4x4 matrix */
 +mat3 to_mat3(mat4 m4)
  {
 -      outval = max((1.0 + vec.x) / 2.0, 0.0);
 -      outval *= outval;
 +      mat3 m3;
 +      m3[0] = m4[0].xyz;
 +      m3[1] = m4[1].xyz;
 +      m3[2] = m4[2].xyz;
 +      return m3;
  }
  
 -void texture_wood_sin(vec3 vec, out float value, out vec4 color, out vec3 normal)
 +/*********** NEW SHADER UTILITIES **************/
 +
 +float fresnel_dielectric_0(float eta)
  {
 -      float a = sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) * 20.0;
 -      float wi = 0.5 + 0.5 * sin(a);
 +      /* compute fresnel reflactance at normal incidence => cosi = 1.0 */
 +      float A = (eta - 1.0) / (eta + 1.0);
  
 -      value = wi;
 -      color = vec4(wi, wi, wi, 1.0);
 -      normal = vec3(0.0, 0.0, 0.0);
 +      return A * A;
  }
  
 -void texture_image(vec3 vec, sampler2D ima, out float value, out vec4 color, out vec3 normal)
 +float fresnel_dielectric_cos(float cosi, float eta)
  {
 -      color = texture2D(ima, (vec.xy + vec2(1.0, 1.0)) * 0.5);
 -      value = color.a;
 -
 -      normal.x = 2.0 * (color.r - 0.5);
 -      normal.y = 2.0 * (0.5 - color.g);
 -      normal.z = 2.0 * (color.b - 0.5);
 -}
 +      /* compute fresnel reflectance without explicitly computing
 +       * the refracted direction */
 +      float c = abs(cosi);
 +      float g = eta * eta - 1.0 + c * c;
 +      float result;
  
 -/************* MTEX *****************/
 +      if (g > 0.0) {
 +              g = sqrt(g);
 +              float A = (g - c) / (g + c);
 +              float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
 +              result = 0.5 * A * A * (1.0 + B * B);
 +      }
 +      else {
 +              result = 1.0;  /* TIR (no refracted component) */
 +      }
  
 -void texco_orco(vec3 attorco, out vec3 orco)
 -{
 -      orco = attorco;
 +      return result;
  }
  
 -void texco_uv(vec2 attuv, out vec3 uv)
 +float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
  {
 -      /* disabled for now, works together with leaving out mtex_2d_mapping */
 -      // uv = vec3(attuv*2.0 - vec2(1.0, 1.0), 0.0); */
 -      uv = vec3(attuv, 0.0);
 +      /* compute fresnel reflectance without explicitly computing
 +       * the refracted direction */
 +      return fresnel_dielectric_cos(dot(Incoming, Normal), eta);
  }
  
 -void texco_norm(vec3 normal, out vec3 outnormal)
 +float hypot(float x, float y)
  {
 -      /* corresponds to shi->orn, which is negated so cancels
 -         out blender normal negation */
 -      outnormal = normalize(normal);
 +      return sqrt(x * x + y * y);
  }
  
 -void texco_tangent(vec4 tangent, out vec3 outtangent)
 +void generated_from_orco(vec3 orco, out vec3 generated)
  {
 -      outtangent = normalize(tangent.xyz);
 +#ifdef VOLUMETRICS
 +#ifdef MESH_SHADER
 +      generated = volumeObjectLocalCoord;
 +#else
 +      generated = worldPosition;
 +#endif
 +#else
 +      generated = orco;
 +#endif
  }
  
 -void texco_global(mat4 viewinvmat, vec3 co, out vec3 global)
 +int floor_to_int(float x)
  {
 -      global = (viewinvmat * vec4(co, 1.0)).xyz;
 +      return int(floor(x));
  }
  
 -void texco_object(mat4 viewinvmat, mat4 obinvmat, vec3 co, out vec3 object)
 +int quick_floor(float x)
  {
 -      object = (obinvmat * (viewinvmat * vec4(co, 1.0))).xyz;
 +      return int(x) - ((x < 0) ? 1 : 0);
  }
  
 -void texco_refl(vec3 vn, vec3 view, out vec3 ref)
 +float integer_noise(int n)
  {
 -      ref = view - 2.0 * dot(vn, view) * vn;
 +      int nn;
 +      n = (n + 1013) & 0x7fffffff;
 +      n = (n >> 13) ^ n;
 +      nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
 +      return 0.5 * (float(nn) / 1073741824.0);
  }
  
 -void shade_norm(vec3 normal, out vec3 outnormal)
 +uint hash(uint kx, uint ky, uint kz)
  {
 -      /* blender render normal is negated */
 -      outnormal = -normalize(normal);
 +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
 +#define final(a, b, c) \
 +{ \
 +      c ^= b; c -= rot(b, 14); \
 +      a ^= c; a -= rot(c, 11); \
 +      b ^= a; b -= rot(a, 25); \
 +      c ^= b; c -= rot(b, 16); \
 +      a ^= c; a -= rot(c, 4);  \
 +      b ^= a; b -= rot(a, 14); \
 +      c ^= b; c -= rot(b, 24); \
  }
 +      // now hash the data!
 +      uint a, b, c, len = 3u;
 +      a = b = c = 0xdeadbeefu + (len << 2u) + 13u;
  
 -void mtex_mirror(vec3 tcol, vec4 refcol, float tin, float colmirfac, out vec4 outrefcol)
 -{
 -      outrefcol = mix(refcol, vec4(1.0, tcol), tin * colmirfac);
 +      c += kz;
 +      b += ky;
 +      a += kx;
 +      final (a, b, c);
 +
 +      return c;
 +#undef rot
 +#undef final
  }
  
 -void mtex_rgb_blend(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +uint hash(int kx, int ky, int kz)
  {
 -      float facm;
 -
 -      fact *= facg;
 -      facm = 1.0 - fact;
 -
 -      incol = fact * texcol + facm * outcol;
 +      return hash(uint(kx), uint(ky), uint(kz));
  }
  
 -void mtex_rgb_mul(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +float bits_to_01(uint bits)
  {
 -      float facm;
 -
 -      fact *= facg;
 -      facm = 1.0 - fact;
 -
 -      incol = (facm + fact * texcol) * outcol;
 +      return (float(bits) / 4294967295.0);
  }
  
 -void mtex_rgb_screen(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +float cellnoise(vec3 p)
  {
 -      float facm;
 -
 -      fact *= facg;
 -      facm = 1.0 - fact;
 +      int ix = quick_floor(p.x);
 +      int iy = quick_floor(p.y);
 +      int iz = quick_floor(p.z);
  
 -      incol = vec3(1.0) - (vec3(facm) + fact * (vec3(1.0) - texcol)) * (vec3(1.0) - outcol);
 +      return bits_to_01(hash(uint(ix), uint(iy), uint(iz)));
  }
  
 -void mtex_rgb_overlay(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +vec3 cellnoise_color(vec3 p)
  {
 -      float facm;
 -
 -      fact *= facg;
 -      facm = 1.0 - fact;
 -
 -      if (outcol.r < 0.5)
 -              incol.r = outcol.r * (facm + 2.0 * fact * texcol.r);
 -      else
 -              incol.r = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.r)) * (1.0 - outcol.r);
 -
 -      if (outcol.g < 0.5)
 -              incol.g = outcol.g * (facm + 2.0 * fact * texcol.g);
 -      else
 -              incol.g = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.g)) * (1.0 - outcol.g);
 +      float r = cellnoise(p.xyz);
 +      float g = cellnoise(p.yxz);
 +      float b = cellnoise(p.yzx);
  
 -      if (outcol.b < 0.5)
 -              incol.b = outcol.b * (facm + 2.0 * fact * texcol.b);
 -      else
 -              incol.b = 1.0 - (facm + 2.0 * fact * (1.0 - texcol.b)) * (1.0 - outcol.b);
 +      return vec3(r, g, b);
  }
  
 -void mtex_rgb_sub(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +float floorfrac(float x, out int i)
  {
 -      incol = -fact * facg * texcol + outcol;
 +      i = floor_to_int(x);
 +      return x - i;
  }
  
 -void mtex_rgb_add(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 -{
 -      incol = fact * facg * texcol + outcol;
 -}
 +/* bsdfs */
  
 -void mtex_rgb_div(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +vec3 tint_from_color(vec3 color)
  {
 -      float facm;
 -
 -      fact *= facg;
 -      facm = 1.0 - fact;
 -
 -      if (texcol.r != 0.0) incol.r = facm * outcol.r + fact * outcol.r / texcol.r;
 -      if (texcol.g != 0.0) incol.g = facm * outcol.g + fact * outcol.g / texcol.g;
 -      if (texcol.b != 0.0) incol.b = facm * outcol.b + fact * outcol.b / texcol.b;
 +      float lum = dot(color, vec3(0.3, 0.6, 0.1)); /* luminance approx. */
 +      return (lum > 0) ? color / lum : vec3(1.0); /* normalize lum. to isolate hue+sat */
  }
  
 -void mtex_rgb_diff(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +void convert_metallic_to_specular_tinted(
 +        vec3 basecol, vec3 basecol_tint, float metallic, float specular_fac, float specular_tint,
 +        out vec3 diffuse, out vec3 f0)
  {
 -      float facm;
 -
 -      fact *= facg;
 -      facm = 1.0 - fact;
 -
 -      incol = facm * outcol + fact * abs(texcol - outcol);
 +      vec3 tmp_col = mix(vec3(1.0), basecol_tint, specular_tint);
 +      f0 = mix((0.08 * specular_fac) * tmp_col, basecol, metallic);
 +      diffuse = basecol * (1.0 - metallic);
  }
  
 -void mtex_rgb_dark(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +vec3 principled_sheen(float NV, vec3 basecol_tint, float sheen_tint)
  {
 -      float facm, col;
 -
 -      fact *= facg;
 -      facm = 1.0 - fact;
 -
 -      incol.r = min(outcol.r, texcol.r) * fact + outcol.r * facm;
 -      incol.g = min(outcol.g, texcol.g) * fact + outcol.g * facm;
 -      incol.b = min(outcol.b, texcol.b) * fact + outcol.b * facm;
 +      float f = 1.0 - NV;
 +      /* Empirical approximation (manual curve fitting). Can be refined. */
 +      float sheen = f*f*f*0.077 + f*0.01 + 0.00026;
 +      return sheen * mix(vec3(1.0), basecol_tint, sheen_tint);
  }
  
 -void mtex_rgb_light(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +#ifndef VOLUMETRICS
 +void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result)
  {
 -      float facm, col;
 -
 -      fact *= facg;
 -
 -      col = fact * texcol.r;
 -      if (col > outcol.r) incol.r = col; else incol.r = outcol.r;
 -      col = fact * texcol.g;
 -      if (col > outcol.g) incol.g = col; else incol.g = outcol.g;
 -      col = fact * texcol.b;
 -      if (col > outcol.b) incol.b = col; else incol.b = outcol.b;
 +      N = normalize(N);
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      eevee_closure_diffuse(N, color.rgb, 1.0, result.radiance);
 +      result.radiance *= color.rgb;
  }
  
 -void mtex_rgb_hue(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +void node_bsdf_glossy(vec4 color, float roughness, vec3 N, float ssr_id, out Closure result)
  {
 -      vec4 col;
 -
 -      mix_hue(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col);
 -      incol.rgb = col.rgb;
 +      N = normalize(N);
 +      vec3 out_spec, ssr_spec;
 +      eevee_closure_glossy(N, vec3(1.0), int(ssr_id), roughness, 1.0, out_spec, ssr_spec);
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = out_spec * color.rgb;
 +      result.ssr_data = vec4(ssr_spec * color.rgb, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
  }
  
 -void mtex_rgb_sat(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +void node_bsdf_anisotropic(
 +        vec4 color, float roughness, float anisotropy, float rotation, vec3 N, vec3 T,
 +        out Closure result)
  {
 -      vec4 col;
 -
 -      mix_sat(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col);
 -      incol.rgb = col.rgb;
 +      node_bsdf_glossy(color, roughness, N, -1, result);
  }
  
 -void mtex_rgb_val(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +void node_bsdf_glass(vec4 color, float roughness, float ior, vec3 N, float ssr_id, out Closure result)
  {
 -      vec4 col;
 -
 -      mix_val(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col);
 -      incol.rgb = col.rgb;
 +      N = normalize(N);
 +      vec3 out_spec, out_refr, ssr_spec;
 +      vec3 refr_color = (refractionDepth > 0.0) ? color.rgb * color.rgb : color.rgb; /* Simulate 2 transmission event */
 +      eevee_closure_glass(N, vec3(1.0), int(ssr_id), roughness, 1.0, ior, out_spec, out_refr, ssr_spec);
 +      out_refr *= refr_color;
 +      out_spec *= color.rgb;
 +      float fresnel = F_eta(ior, dot(N, cameraVec));
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = mix(out_refr, out_spec, fresnel);
 +      result.ssr_data = vec4(ssr_spec * color.rgb * fresnel, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
  }
  
 -void mtex_rgb_color(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out Closure result)
  {
 -      vec4 col;
 -
 -      mix_color(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col);
 -      incol.rgb = col.rgb;
 +      node_bsdf_diffuse(color, 0.0, N, result);
  }
  
 -void mtex_rgb_soft(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 -{
 -      vec4 col;
 +void node_bsdf_principled(
 +        vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular,
 +        float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat,
 +        float clearcoat_roughness, float ior, float transmission, float transmission_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, float ssr_id,
 +        float sss_id, vec3 sss_scale, out Closure result)
 +{
 +      N = normalize(N);
 +      ior = max(ior, 1e-5);
 +      metallic = saturate(metallic);
 +      transmission = saturate(transmission);
 +      float dielectric = 1.0 - metallic;
 +      transmission *= dielectric;
 +      sheen *= dielectric;
 +      subsurface_color *= dielectric;
 +
 +      vec3 diffuse, f0, out_diff, out_spec, out_trans, out_refr, ssr_spec;
 +      vec3 ctint = tint_from_color(base_color.rgb);
 +      convert_metallic_to_specular_tinted(base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
 +
 +      float NV = dot(N, cameraVec);
 +      vec3 out_sheen = sheen * principled_sheen(NV, ctint, sheen_tint);
 +
 +      /* Far from being accurate, but 2 glossy evaluation is too expensive.
 +       * Most noticeable difference is at grazing angles since the bsdf lut
 +       * f0 color interpolation is done on top of this interpolation. */
 +      vec3 f0_glass = mix(vec3(1.0), base_color.rgb, specular_tint);
 +      float fresnel = F_eta(ior, NV);
 +      vec3 spec_col = F_color_blend(ior, fresnel, f0_glass) * fresnel;
 +      f0 = mix(f0, spec_col, transmission);
 +
 +      vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface);
 +
 +      float sss_scalef = dot(sss_scale, vec3(1.0 / 3.0)) * subsurface;
 +      eevee_closure_principled(N, mixed_ss_base_color, f0, int(ssr_id), roughness,
 +                               CN, clearcoat * 0.25, clearcoat_roughness, 1.0, sss_scalef, ior,
 +                               out_diff, out_trans, out_spec, out_refr, ssr_spec);
 +
 +      vec3 refr_color = base_color.rgb;
 +      refr_color *= (refractionDepth > 0.0) ? refr_color : vec3(1.0); /* Simulate 2 transmission event */
 +      out_refr *= refr_color * (1.0 - fresnel) * transmission;
 +
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = out_spec + out_refr;
 +      result.radiance += out_diff * out_sheen; /* Coarse approx. */
 +#ifndef USE_SSS
 +      result.radiance += (out_diff + out_trans) * mixed_ss_base_color *  (1.0 - transmission);
 +#endif
 +      result.ssr_data = vec4(ssr_spec, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
 +#ifdef USE_SSS
 +      result.sss_data.a = sss_scalef;
 +      result.sss_data.rgb = out_diff + out_trans;
 +#  ifdef USE_SSS_ALBEDO
 +      result.sss_albedo.rgb = mixed_ss_base_color;
 +#  else
 +      result.sss_data.rgb *= mixed_ss_base_color;
 +#  endif
 +      result.sss_data.rgb *= (1.0 - transmission);
 +#endif
 +}
  
 -      mix_soft(fact * facg, vec4(outcol, 1.0), vec4(texcol, 1.0), col);
 -      incol.rgb = col.rgb;
 +void node_bsdf_principled_dielectric(
 +        vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular,
 +        float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat,
 +        float clearcoat_roughness, float ior, float transmission, float transmission_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, float ssr_id,
 +        float sss_id, vec3 sss_scale, out Closure result)
 +{
 +      N = normalize(N);
 +      metallic = saturate(metallic);
 +      float dielectric = 1.0 - metallic;
 +
 +      vec3 diffuse, f0, out_diff, out_spec, ssr_spec;
 +      vec3 ctint = tint_from_color(base_color.rgb);
 +      convert_metallic_to_specular_tinted(base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
 +
 +      float NV = dot(N, cameraVec);
 +      vec3 out_sheen = sheen * principled_sheen(NV, ctint, sheen_tint);
 +
 +      eevee_closure_default(N, diffuse, f0, int(ssr_id), roughness, 1.0, out_diff, out_spec, ssr_spec);
 +
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = out_spec + out_diff * (diffuse + out_sheen);
 +      result.ssr_data = vec4(ssr_spec, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
 +}
 +
 +void node_bsdf_principled_metallic(
 +        vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular,
 +        float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat,
 +        float clearcoat_roughness, float ior, float transmission, float transmission_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, float ssr_id,
 +        float sss_id, vec3 sss_scale, out Closure result)
 +{
 +      N = normalize(N);
 +      vec3 out_spec, ssr_spec;
 +
 +      eevee_closure_glossy(N, base_color.rgb, int(ssr_id), roughness, 1.0, out_spec, ssr_spec);
 +
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = out_spec;
 +      result.ssr_data = vec4(ssr_spec, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
 +}
 +
 +void node_bsdf_principled_clearcoat(
 +        vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular,
 +        float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat,
 +        float clearcoat_roughness, float ior, float transmission, float transmission_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, float ssr_id,
 +        float sss_id, vec3 sss_scale, out Closure result)
 +{
 +      vec3 out_spec, ssr_spec;
 +      N = normalize(N);
 +
 +      eevee_closure_clearcoat(N, base_color.rgb, int(ssr_id), roughness, CN, clearcoat * 0.25, clearcoat_roughness,
 +                              1.0, out_spec, ssr_spec);
 +
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = out_spec;
 +      result.ssr_data = vec4(ssr_spec, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
 +}
 +
 +void node_bsdf_principled_subsurface(
 +        vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular,
 +        float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat,
 +        float clearcoat_roughness, float ior, float transmission, float transmission_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, float ssr_id,
 +        float sss_id, vec3 sss_scale, out Closure result)
 +{
 +      metallic = saturate(metallic);
 +      N = normalize(N);
 +
 +      vec3 diffuse, f0, out_diff, out_spec, out_trans, ssr_spec;
 +      vec3 ctint = tint_from_color(base_color.rgb);
 +      convert_metallic_to_specular_tinted(base_color.rgb, ctint, metallic, specular, specular_tint, diffuse, f0);
 +
 +      subsurface_color = subsurface_color * (1.0 - metallic);
 +      vec3 mixed_ss_base_color = mix(diffuse, subsurface_color.rgb, subsurface);
 +      float sss_scalef = dot(sss_scale, vec3(1.0 / 3.0)) * subsurface;
 +
 +      float NV = dot(N, cameraVec);
 +      vec3 out_sheen = sheen * principled_sheen(NV, ctint, sheen_tint);
 +
 +      eevee_closure_skin(N, mixed_ss_base_color, f0, int(ssr_id), roughness, 1.0, sss_scalef,
 +                         out_diff, out_trans, out_spec, ssr_spec);
 +
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = out_spec;
 +      result.ssr_data = vec4(ssr_spec, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
 +#ifdef USE_SSS
 +      result.sss_data.a = sss_scalef;
 +      result.sss_data.rgb = out_diff + out_trans;
 +#  ifdef USE_SSS_ALBEDO
 +      result.sss_albedo.rgb = mixed_ss_base_color;
 +#  else
 +      result.sss_data.rgb *= mixed_ss_base_color;
 +#  endif
 +#else
 +      result.radiance += (out_diff + out_trans) * mixed_ss_base_color;
 +#endif
 +      result.radiance += out_diff * out_sheen;
  }
  
 -void mtex_rgb_linear(vec3 outcol, vec3 texcol, float fact, float facg, out vec3 incol)
 +void node_bsdf_principled_glass(
 +        vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular,
 +        float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat,
 +        float clearcoat_roughness, float ior, float transmission, float transmission_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, float ssr_id,
 +        float sss_id, vec3 sss_scale, out Closure result)
  {
 -      fact *= facg;
 +      ior = max(ior, 1e-5);
 +      N = normalize(N);
  
 -      if (texcol.r > 0.5)
 -              incol.r = outcol.r + fact * (2.0 * (texcol.r - 0.5));
 -      else
 -              incol.r = outcol.r + fact * (2.0 * (texcol.r) - 1.0);
 +      vec3 f0, out_spec, out_refr, ssr_spec;
 +      f0 = mix(vec3(1.0), base_color.rgb, specular_tint);
  
 -      if (texcol.g > 0.5)
 -              incol.g = outcol.g + fact * (2.0 * (texcol.g - 0.5));
 -      else
 -              incol.g = outcol.g + fact * (2.0 * (texcol.g) - 1.0);
 +      eevee_closure_glass(N, vec3(1.0), int(ssr_id), roughness, 1.0, ior, out_spec, out_refr, ssr_spec);
  
 -      if (texcol.b > 0.5)
 -              incol.b = outcol.b + fact * (2.0 * (texcol.b - 0.5));
 -      else
 -              incol.b = outcol.b + fact * (2.0 * (texcol.b) - 1.0);
 -}
 +      vec3 refr_color = base_color.rgb;
 +      refr_color *= (refractionDepth > 0.0) ? refr_color : vec3(1.0); /* Simulate 2 transmission events */
 +      out_refr *= refr_color;
  
 -void mtex_value_vars(inout float fact, float facg, out float facm)
 -{
 -      fact *= abs(facg);
 -      facm = 1.0 - fact;
 +      float fresnel = F_eta(ior, dot(N, cameraVec));
 +      vec3 spec_col = F_color_blend(ior, fresnel, f0);
 +      out_spec *= spec_col;
 +      ssr_spec *= spec_col * fresnel;
  
 -      if (facg < 0.0) {
 -              float tmp = fact;
 -              fact = facm;
 -              facm = tmp;
 -      }
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = mix(out_refr, out_spec, fresnel);
 +      result.ssr_data = vec4(ssr_spec, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
  }
  
 -void mtex_value_blend(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_bsdf_translucent(vec4 color, vec3 N, out Closure result)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 -
 -      incol = fact * texcol + facm * outcol;
 +      node_bsdf_diffuse(color, 0.0, -N, result);
  }
  
 -void mtex_value_mul(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_bsdf_transparent(vec4 color, out Closure result)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 -
 -      facm = 1.0 - facg;
 -      incol = (facm + fact * texcol) * outcol;
 +      /* this isn't right */
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = vec3(0.0);
 +      result.opacity = clamp(1.0 - dot(color.rgb, vec3(0.3333334)), 0.0, 1.0);
 +      result.ssr_id = TRANSPARENT_CLOSURE_FLAG;
  }
  
 -void mtex_value_screen(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out Closure result)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 +      node_bsdf_diffuse(color, 0.0, N, result);
 +}
  
 -      facm = 1.0 - facg;
 -      incol = 1.0 - (facm + fact * (1.0 - texcol)) * (1.0 - outcol);
 +void node_subsurface_scattering(
 +        vec4 color, float scale, vec3 radius, float sharpen, float texture_blur, vec3 N, float sss_id,
 +        out Closure result)
 +{
 +#if defined(USE_SSS)
 +      N = normalize(N);
 +      vec3 out_diff, out_trans;
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.ssr_data = vec4(0.0);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = -1;
 +      result.sss_data.a = scale;
 +      eevee_closure_subsurface(N, color.rgb, 1.0, scale, out_diff, out_trans);
 +      result.sss_data.rgb = out_diff + out_trans;
 +#  ifdef USE_SSS_ALBEDO
 +      /* Not perfect for texture_blur not exactly equal to 0.0 or 1.0. */
 +      result.sss_albedo.rgb = mix(color.rgb, vec3(1.0), texture_blur);
 +      result.sss_data.rgb *= mix(vec3(1.0), color.rgb, texture_blur);
 +#  else
 +      result.sss_data.rgb *= color.rgb;
 +#  endif
 +#else
 +      node_bsdf_diffuse(color, 0.0, N, result);
 +#endif
  }
  
 -void mtex_value_sub(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Closure result)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 -
 -      fact = -fact;
 -      incol = fact * texcol + outcol;
 +      N = normalize(N);
 +      vec3 out_refr;
 +      color.rgb *= (refractionDepth > 0.0) ? color.rgb : vec3(1.0); /* Simulate 2 absorption event. */
 +      eevee_closure_refraction(N, roughness, ior, out_refr);
 +      vec3 vN = mat3(ViewMatrix) * N;
 +      result = CLOSURE_DEFAULT;
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.radiance = out_refr * color.rgb;
 +      result.ssr_id = REFRACT_CLOSURE_FLAG;
  }
  
 -void mtex_value_add(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_ambient_occlusion(vec4 color, float distance, vec3 normal, out vec4 result_color, out float result_ao)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 -
 -      fact = fact;
 -      incol = fact * texcol + outcol;
 +      vec3 bent_normal;
 +      vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0);
 +      result_ao = occlusion_compute(normalize(normal), viewPosition, 1.0, rand, bent_normal);
 +      result_color = result_ao * color;
  }
  
 -void mtex_value_div(float outcol, float texcol, float fact, float facg, out float incol)
 -{
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 +#endif /* VOLUMETRICS */
  
 -      if (texcol != 0.0)
 -              incol = facm * outcol + fact * outcol / texcol;
 -      else
 -              incol = 0.0;
 -}
 +/* emission */
  
 -void mtex_value_diff(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_emission(vec4 color, float strength, vec3 vN, out Closure result)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 -
 -      incol = facm * outcol + fact * abs(texcol - outcol);
 +#ifndef VOLUMETRICS
 +      color *= strength;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = color.rgb;
 +      result.opacity = color.a;
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +#else
 +      result = Closure(vec3(0.0), vec3(0.0), color.rgb * strength, 0.0);
 +#endif
  }
  
 -void mtex_value_dark(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_wireframe(float size, vec2 barycentric, vec3 barycentric_dist, out float fac)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 +      vec3 barys = barycentric.xyy;
 +      barys.z = 1.0 - barycentric.x - barycentric.y;
  
 -      incol = facm * outcol + fact * min(outcol, texcol);
 +      size *= 0.5;
 +      vec3 s = step(-size, -barys * barycentric_dist);
 +
 +      fac = max(s.x, max(s.y, s.z));
  }
  
 -void mtex_value_light(float outcol, float texcol, float fact, float facg, out float incol)
 +void node_wireframe_screenspace(float size, vec2 barycentric, out float fac)
  {
 -      float facm;
 -      mtex_value_vars(fact, facg, facm);
 +      vec3 barys = barycentric.xyy;
 +      barys.z = 1.0 - barycentric.x - barycentric.y;
  
 -      float col = fact * texcol;
 -      if (col > outcol) incol = col; else incol = outcol;
 -}
 +      size *= (1.0 / 3.0);
 +      vec3 dx = dFdx(barys);
 +      vec3 dy = dFdy(barys);
 +      vec3 deltas = sqrt(dx * dx + dy * dy);
  
 -void mtex_value_clamp_positive(float fac, out float outfac)
 -{
 -      outfac = max(fac, 0.0);
 -}
 +      vec3 s = step(-deltas * size, -barys);
  
 -void mtex_value_clamp(float fac, out float outfac)
 -{
 -      outfac = clamp(fac, 0.0, 1.0);
 +      fac = max(s.x, max(s.y, s.z));
  }
  
 -void mtex_har_divide(float har, out float outhar)
 -{
 -      outhar = har / 128.0;
 -}
 +/* background */
  
 -void mtex_har_multiply_clamp(float har, out float outhar)
 +void node_tex_environment_texco(vec3 viewvec, out vec3 worldvec)
  {
 -      har *= 128.0;
 +#ifdef MESH_SHADER
 +      worldvec = worldPosition;
 +#else
 +      vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
 +      vec4 co_homogenous = (ProjectionMatrixInverse * v);
  
 -      if (har < 1.0) outhar = 1.0;
 -      else if (har > 511.0) outhar = 511.0;
 -      else outhar = har;
 +      vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
 +#  if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
 +      worldvec = (ViewMatrixInverse * co).xyz;
 +#  else
 +      worldvec = (ModelViewMatrixInverse * co).xyz;
 +#  endif
 +#endif
  }
  
 -void mtex_alpha_from_col(vec4 col, out float alpha)
 +void node_background(vec4 color, float strength, out Closure result)
  {
 -      alpha = col.a;
 +#ifndef VOLUMETRICS
 +      color *= strength;
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = color.rgb;
 +      result.opacity = color.a;
 +#else
 +      result = CLOSURE_DEFAULT;
 +#endif
  }
  
 -void mtex_alpha_to_col(vec4 col, float alpha, out vec4 outcol)
 -{
 -      outcol = vec4(col.rgb, alpha);
 -}
 +/* volumes */
  
 -void mtex_alpha_multiply_value(vec4 col, float value, out vec4 outcol)
 +void node_volume_scatter(vec4 color, float density, float anisotropy, out Closure result)
  {
 -      outcol = vec4(col.rgb, col.a * value);
 +#ifdef VOLUMETRICS
 +      result = Closure(vec3(0.0), color.rgb * density, vec3(0.0), anisotropy);
 +#else
 +      result = CLOSURE_DEFAULT;
 +#endif
  }
  
 -void mtex_rgbtoint(vec4 rgb, out float intensity)
 +void node_volume_absorption(vec4 color, float density, out Closure result)
  {
 -      intensity = dot(vec3(0.35, 0.45, 0.2), rgb.rgb);
 +#ifdef VOLUMETRICS
 +      result = Closure((1.0 - color.rgb) * density, vec3(0.0), vec3(0.0), 0.0);
 +#else
 +      result = CLOSURE_DEFAULT;
 +#endif
  }
  
 -void mtex_value_invert(float invalue, out float outvalue)
 +void node_blackbody(float temperature, sampler1DArray spectrummap, float layer, out vec4 color)
  {
 -      outvalue = 1.0 - invalue;
 +    if (temperature >= 12000.0) {
 +        color = vec4(0.826270103, 0.994478524, 1.56626022, 1.0);
 +    }
 +    else if (temperature < 965.0) {
 +        color = vec4(4.70366907, 0.0, 0.0, 1.0);
 +    }
 +      else {
 +              float t = (temperature - 965.0) / (12000.0 - 965.0);
 +              color = vec4(texture(spectrummap, vec2(t, layer)).rgb, 1.0);
 +      }
  }
  
 -void mtex_rgb_invert(vec4 inrgb, out vec4 outrgb)
 +void node_volume_principled(
 +      vec4 color,
 +      float density,
 +      float anisotropy,
 +      vec4 absorption_color,
 +      float emission_strength,
 +      vec4 emission_color,
 +      float blackbody_intensity,
 +      vec4 blackbody_tint,
 +      float temperature,
 +      float density_attribute,
 +      vec4 color_attribute,
 +      float temperature_attribute,
 +      sampler1DArray spectrummap,
 +      float layer,
 +      out Closure result)
  {
 -      outrgb = vec4(vec3(1.0) - inrgb.rgb, inrgb.a);
 -}
 +#ifdef VOLUMETRICS
 +      vec3 absorption_coeff = vec3(0.0);
 +      vec3 scatter_coeff = vec3(0.0);
 +      vec3 emission_coeff = vec3(0.0);
  
 -void mtex_value_stencil(float stencil, float intensity, out float outstencil, out float outintensity)
 -{
 -      float fact = intensity;
 -      outintensity = intensity * stencil;
 -      outstencil = stencil * fact;
 -}
 +      /* Compute density. */
 +      density = max(density, 0.0);
  
 -void mtex_rgb_stencil(float stencil, vec4 rgb, out float outstencil, out vec4 outrgb)
 -{
 -      float fact = rgb.a;
 -      outrgb = vec4(rgb.rgb, rgb.a * stencil);
 -      outstencil = stencil * fact;
 -}
 +      if(density > 1e-5) {
 +              density = max(density * density_attribute, 0.0);
 +      }
  
 -void mtex_mapping_ofs(vec3 texco, vec3 ofs, out vec3 outtexco)
 -{
 -      outtexco = texco + ofs;
 +      if(density > 1e-5) {
 +              /* Compute scattering and absorption coefficients. */
 +              vec3 scatter_color = color.rgb * color_attribute.rgb;
 +
 +              scatter_coeff = scatter_color * density;
 +              absorption_color.rgb = sqrt(max(absorption_color.rgb, 0.0));
 +              absorption_coeff = max(1.0 - scatter_color, 0.0) * max(1.0 - absorption_color.rgb, 0.0) * density;
 +      }
 +
 +      /* Compute emission. */
 +      emission_strength = max(emission_strength, 0.0);
 +
 +      if(emission_strength > 1e-5) {
 +              emission_coeff += emission_strength * emission_color.rgb;
 +      }
 +
 +      if(blackbody_intensity > 1e-3) {
 +              /* Add temperature from attribute. */
 +              float T = max(temperature * max(temperature_attribute, 0.0), 0.0);
 +
 +              /* Stefan-Boltzman law. */
 +              float T4 = (T * T) * (T * T);
 +              float sigma = 5.670373e-8 * 1e-6 / M_PI;
 +              float intensity = sigma * mix(1.0, T4, blackbody_intensity);
 +
 +              if(intensity > 1e-5) {
 +                      vec4 bb;
 +                      node_blackbody(T, spectrummap, layer, bb);
 +                      emission_coeff += bb.rgb * blackbody_tint.rgb * intensity;
 +              }
 +      }
 +
 +      result = Closure(absorption_coeff, scatter_coeff, emission_coeff, anisotropy);
 +#else
 +      result = CLOSURE_DEFAULT;
 +#endif
  }
  
 -void mtex_mapping_size(vec3 texco, vec3 size, out vec3 outtexco)
 +/* closures */
 +
 +void node_mix_shader(float fac, Closure shader1, Closure shader2, out Closure shader)
  {
 -      outtexco = size * texco;
 +      shader = closure_mix(shader1, shader2, fac);
  }
  
 -void mtex_2d_mapping(vec3 vec, out vec3 outvec)
 +void node_add_shader(Closure shader1, Closure shader2, out Closure shader)
  {
 -      outvec = vec3(vec.xy * 0.5 + vec2(0.5), vec.z);
 -}
 -
 -vec3 mtex_2d_mapping(vec3 vec)
 -{
 -      return vec3(vec.xy * 0.5 + vec2(0.5), vec.z);
 -}
 -
 -void mtex_cube_map(vec3 co, samplerCube ima, out float value, out vec4 color)
 -{
 -      color = textureCube(ima, co);
 -      value = 1.0;
 -}
 -
 -void mtex_cube_map_refl_from_refldir(
 -        samplerCube ima, vec3 reflecteddirection, out float value, out vec4 color)
 -{
 -        color = textureCube(ima, reflecteddirection);
 -        value = color.a;
 -}
 -
 -void mtex_cube_map_refl(
 -        samplerCube ima, vec3 vp, vec3 vn, mat4 viewmatrixinverse, mat4 viewmatrix,
 -        out float value, out vec4 color)
 -{
 -      vec3 viewdirection = vec3(viewmatrixinverse * vec4(vp, 0.0));
 -      vec3 normaldirection = normalize(vec3(vec4(vn, 0.0) * viewmatrix));
 -      vec3 reflecteddirection = reflect(viewdirection, normaldirection);
 -      color = textureCube(ima, reflecteddirection);
 -      value = 1.0;
 -}
 -
 -void mtex_image(vec3 texco, sampler2D ima, out float value, out vec4 color)
 -{
 -      color = texture2D(ima, texco.xy);
 -      value = 1.0;
 -}
 -
 -void mtex_normal(vec3 texco, sampler2D ima, out vec3 normal)
 -{
 -      // The invert of the red channel is to make
 -      // the normal map compliant with the outside world.
 -      // It needs to be done because in Blender
 -      // the normal used points inward.
 -      // Should this ever change this negate must be removed.
 -      vec4 color = texture2D(ima, texco.xy);
 -      normal = 2.0 * (vec3(-color.r, color.g, color.b) - vec3(-0.5, 0.5, 0.5));
 -}
 -
 -void mtex_bump_normals_init(vec3 vN, out vec3 vNorg, out vec3 vNacc, out float fPrevMagnitude)
 -{
 -      vNorg = vN;
 -      vNacc = vN;
 -      fPrevMagnitude = 1.0;
 -}
 -
 -/** helper method to extract the upper left 3x3 matrix from a 4x4 matrix */
 -mat3 to_mat3(mat4 m4)
 -{
 -      mat3 m3;
 -      m3[0] = m4[0].xyz;
 -      m3[1] = m4[1].xyz;
 -      m3[2] = m4[2].xyz;
 -      return m3;
 -}
 -
 -void mtex_bump_init_objspace(
 -        vec3 surf_pos, vec3 surf_norm,
 -        mat4 mView, mat4 mViewInv, mat4 mObj, mat4 mObjInv,
 -        float fPrevMagnitude_in, vec3 vNacc_in,
 -        out float fPrevMagnitude_out, out vec3 vNacc_out,
 -        out vec3 vR1, out vec3 vR2, out float fDet)
 -{
 -      mat3 obj2view = to_mat3(gl_ModelViewMatrix);
 -      mat3 view2obj = to_mat3(gl_ModelViewMatrixInverse);
 -
 -      vec3 vSigmaS = view2obj * dFdx(surf_pos);
 -      vec3 vSigmaT = view2obj * dFdy(surf_pos);
 -      vec3 vN = normalize(surf_norm * obj2view);
 -
 -      vR1 = cross(vSigmaT, vN);
 -      vR2 = cross(vN, vSigmaS);
 -      fDet = dot(vSigmaS, vR1);
 -
 -      /* pretransform vNacc (in mtex_bump_apply) using the inverse transposed */
 -      vR1 = vR1 * view2obj;
 -      vR2 = vR2 * view2obj;
 -      vN = vN * view2obj;
 -
 -      float fMagnitude = abs(fDet) * length(vN);
 -      vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in);
 -      fPrevMagnitude_out = fMagnitude;
 -}
 -
 -void mtex_bump_init_texturespace(
 -        vec3 surf_pos, vec3 surf_norm,
 -        float fPrevMagnitude_in, vec3 vNacc_in,
 -        out float fPrevMagnitude_out, out vec3 vNacc_out,
 -        out vec3 vR1, out vec3 vR2, out float fDet)
 -{
 -      vec3 vSigmaS = dFdx(surf_pos);
 -      vec3 vSigmaT = dFdy(surf_pos);
 -      vec3 vN = surf_norm; /* normalized interpolated vertex normal */
 -
 -      vR1 = normalize(cross(vSigmaT, vN));
 -      vR2 = normalize(cross(vN, vSigmaS));
 -      fDet = sign(dot(vSigmaS, vR1));
 -
 -      float fMagnitude = abs(fDet);
 -      vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in);
 -      fPrevMagnitude_out = fMagnitude;
 -}
 -
 -void mtex_bump_init_viewspace(
 -        vec3 surf_pos, vec3 surf_norm,
 -        float fPrevMagnitude_in, vec3 vNacc_in,
 -        out float fPrevMagnitude_out, out vec3 vNacc_out,
 -        out vec3 vR1, out vec3 vR2, out float fDet)
 -{
 -      vec3 vSigmaS = dFdx(surf_pos);
 -      vec3 vSigmaT = dFdy(surf_pos);
 -      vec3 vN = surf_norm; /* normalized interpolated vertex normal */
 -
 -      vR1 = cross(vSigmaT, vN);
 -      vR2 = cross(vN, vSigmaS);
 -      fDet = dot(vSigmaS, vR1);
 -
 -      float fMagnitude = abs(fDet);
 -      vNacc_out = vNacc_in * (fMagnitude / fPrevMagnitude_in);
 -      fPrevMagnitude_out = fMagnitude;
 -}
 -
 -void mtex_bump_tap3(
 -        vec3 texco, sampler2D ima, float hScale,
 -        out float dBs, out float dBt)
 -{
 -      vec2 STll = texco.xy;
 -      vec2 STlr = texco.xy + dFdx(texco.xy);
 -      vec2 STul = texco.xy + dFdy(texco.xy);
 -
 -      float Hll, Hlr, Hul;
 -      rgbtobw(texture2D(ima, STll), Hll);
 -      rgbtobw(texture2D(ima, STlr), Hlr);
 -      rgbtobw(texture2D(ima, STul), Hul);
 -
 -      dBs = hScale * (Hlr - Hll);
 -      dBt = hScale * (Hul - Hll);
 -}
 -
 -#ifdef BUMP_BICUBIC
 -
 -void mtex_bump_bicubic(
 -        vec3 texco, sampler2D ima, float hScale,
 -        out float dBs, out float dBt )
 -{
 -      float Hl;
 -      float Hr;
 -      float Hd;
 -      float Hu;
 -
 -      vec2 TexDx = dFdx(texco.xy);
 -      vec2 TexDy = dFdy(texco.xy);
 -
 -      vec2 STl = texco.xy - 0.5 * TexDx;
 -      vec2 STr = texco.xy + 0.5 * TexDx;
 -      vec2 STd = texco.xy - 0.5 * TexDy;
 -      vec2 STu = texco.xy + 0.5 * TexDy;
 -
 -      rgbtobw(texture2D(ima, STl), Hl);
 -      rgbtobw(texture2D(ima, STr), Hr);
 -      rgbtobw(texture2D(ima, STd), Hd);
 -      rgbtobw(texture2D(ima, STu), Hu);
 -
 -      vec2 dHdxy = vec2(Hr - Hl, Hu - Hd);
 -      float fBlend = clamp(1.0 - textureQueryLOD(ima, texco.xy).x, 0.0, 1.0);
 -      if (fBlend != 0.0) {
 -              // the derivative of the bicubic sampling of level 0
 -              ivec2 vDim;
 -              vDim = textureSize(ima, 0);
 -
 -              // taking the fract part of the texture coordinate is a hardcoded wrap mode.
 -              // this is acceptable as textures use wrap mode exclusively in 3D view elsewhere in blender.
 -              // this is done so that we can still get a valid texel with uvs outside the 0,1 range
 -              // by texelFetch below, as coordinates are clamped when using this function.
 -              vec2 fTexLoc = vDim * fract(texco.xy) - vec2(0.5, 0.5);
 -              ivec2 iTexLoc = ivec2(floor(fTexLoc));
 -              vec2 t = clamp(fTexLoc - iTexLoc, 0.0, 1.0);        // sat just to be pedantic
 -
 -/*******************************************************************************************
 - * This block will replace the one below when one channel textures are properly supported. *
 - *******************************************************************************************
 -              vec4 vSamplesUL = textureGather(ima, (iTexLoc+ivec2(-1,-1) + vec2(0.5,0.5))/vDim);
 -              vec4 vSamplesUR = textureGather(ima, (iTexLoc+ivec2(1,-1) + vec2(0.5,0.5))/vDim);
 -              vec4 vSamplesLL = textureGather(ima, (iTexLoc+ivec2(-1,1) + vec2(0.5,0.5))/vDim);
 -              vec4 vSamplesLR = textureGather(ima, (iTexLoc+ivec2(1,1) + vec2(0.5,0.5))/vDim);
 -
 -              mat4 H = mat4(vSamplesUL.w, vSamplesUL.x, vSamplesLL.w, vSamplesLL.x,
 -                          vSamplesUL.z, vSamplesUL.y, vSamplesLL.z, vSamplesLL.y,
 -                          vSamplesUR.w, vSamplesUR.x, vSamplesLR.w, vSamplesLR.x,
 -                          vSamplesUR.z, vSamplesUR.y, vSamplesLR.z, vSamplesLR.y);
 - */
 -              ivec2 iTexLocMod = iTexLoc + ivec2(-1, -1);
 -
 -              mat4 H;
 -
 -              for (int i = 0; i < 4; i++) {
 -                      for (int j = 0; j < 4; j++) {
 -                              ivec2 iTexTmp = iTexLocMod + ivec2(i, j);
 -
 -                              // wrap texture coordinates manually for texelFetch to work on uvs oitside the 0,1 range.
 -                              // this is guaranteed to work since we take the fractional part of the uv above.
 -                              iTexTmp.x = (iTexTmp.x < 0) ? iTexTmp.x + vDim.x : ((iTexTmp.x >= vDim.x) ? iTexTmp.x - vDim.x : iTexTmp.x);
 -                              iTexTmp.y = (iTexTmp.y < 0) ? iTexTmp.y + vDim.y : ((iTexTmp.y >= vDim.y) ? iTexTmp.y - vDim.y : iTexTmp.y);
 -
 -                              rgbtobw(texelFetch(ima, iTexTmp, 0), H[i][j]);
 -                      }
 -              }
 -
 -              float x = t.x, y = t.y;
 -              float x2 = x * x, x3 = x2 * x, y2 = y * y, y3 = y2 * y;
 -
 -              vec4 X  = vec4(-0.5 * (x3 + x) + x2,    1.5 * x3 - 2.5 * x2 + 1, -1.5 * x3 + 2 * x2 + 0.5 * x, 0.5 * (x3 - x2));
 -              vec4 Y  = vec4(-0.5 * (y3 + y) + y2,    1.5 * y3 - 2.5 * y2 + 1, -1.5 * y3 + 2 * y2 + 0.5 * y, 0.5 * (y3 - y2));
 -              vec4 dX = vec4(-1.5 * x2 + 2 * x - 0.5, 4.5 * x2 - 5 * x,        -4.5 * x2 + 4 * x + 0.5,      1.5 * x2 - x);
 -              vec4 dY = vec4(-1.5 * y2 + 2 * y - 0.5, 4.5 * y2 - 5 * y,        -4.5 * y2 + 4 * y + 0.5,      1.5 * y2 - y);
 -
 -              // complete derivative in normalized coordinates (mul by vDim)
 -              vec2 dHdST = vDim * vec2(dot(Y, H * dX), dot(dY, H * X));
 -
 -              // transform derivative to screen-space
 -              vec2 dHdxy_bicubic = vec2(dHdST.x * TexDx.x + dHdST.y * TexDx.y,
 -                                        dHdST.x * TexDy.x + dHdST.y * TexDy.y);
 -
 -              // blend between the two
 -              dHdxy = dHdxy * (1 - fBlend) + dHdxy_bicubic * fBlend;
 -      }
 -
 -      dBs = hScale * dHdxy.x;
 -      dBt = hScale * dHdxy.y;
 -}
 -
 -#endif
 -
 -void mtex_bump_tap5(
 -        vec3 texco, sampler2D ima, float hScale,
 -        out float dBs, out float dBt)
 -{
 -      vec2 TexDx = dFdx(texco.xy);
 -      vec2 TexDy = dFdy(texco.xy);
 -
 -      vec2 STc = texco.xy;
 -      vec2 STl = texco.xy - 0.5 * TexDx;
 -      vec2 STr = texco.xy + 0.5 * TexDx;
 -      vec2 STd = texco.xy - 0.5 * TexDy;
 -      vec2 STu = texco.xy + 0.5 * TexDy;
 -
 -      float Hc, Hl, Hr, Hd, Hu;
 -      rgbtobw(texture2D(ima, STc), Hc);
 -      rgbtobw(texture2D(ima, STl), Hl);
 -      rgbtobw(texture2D(ima, STr), Hr);
 -      rgbtobw(texture2D(ima, STd), Hd);
 -      rgbtobw(texture2D(ima, STu), Hu);
 -
 -      dBs = hScale * (Hr - Hl);
 -      dBt = hScale * (Hu - Hd);
 -}
 -
 -void mtex_bump_deriv(
 -        vec3 texco, sampler2D ima, float ima_x, float ima_y, float hScale,
 -        out float dBs, out float dBt)
 -{
 -      float s = 1.0;      // negate this if flipped texture coordinate
 -      vec2 TexDx = dFdx(texco.xy);
 -      vec2 TexDy = dFdy(texco.xy);
 -
 -      // this variant using a derivative map is described here
 -      // http://mmikkelsen3d.blogspot.com/2011/07/derivative-maps.html
 -      vec2 dim = vec2(ima_x, ima_y);
 -      vec2 dBduv = hScale * dim * (2.0 * texture2D(ima, texco.xy).xy - 1.0);
 -
 -      dBs = dBduv.x * TexDx.x + s * dBduv.y * TexDx.y;
 -      dBt = dBduv.x * TexDy.x + s * dBduv.y * TexDy.y;
 -}
 -
 -void mtex_bump_apply(
 -        float fDet, float dBs, float dBt, vec3 vR1, vec3 vR2, vec3 vNacc_in,
 -        out vec3 vNacc_out, out vec3 perturbed_norm)
 -{
 -      vec3 vSurfGrad = sign(fDet) * (dBs * vR1 + dBt * vR2);
 -
 -      vNacc_out = vNacc_in - vSurfGrad;
 -      perturbed_norm = normalize(vNacc_out);
 -}
 -
 -void mtex_bump_apply_texspace(
 -        float fDet, float dBs, float dBt, vec3 vR1, vec3 vR2,
 -        sampler2D ima, vec3 texco, float ima_x, float ima_y, vec3 vNacc_in,
 -        out vec3 vNacc_out, out vec3 perturbed_norm)
 -{
 -      vec2 TexDx = dFdx(texco.xy);
 -      vec2 TexDy = dFdy(texco.xy);
 -
 -      vec3 vSurfGrad = sign(fDet) * (
 -              dBs / length(vec2(ima_x * TexDx.x, ima_y * TexDx.y)) * vR1 +
 -              dBt / length(vec2(ima_x * TexDy.x, ima_y * TexDy.y)) * vR2);
 -
 -      vNacc_out = vNacc_in - vSurfGrad;
 -      perturbed_norm = normalize(vNacc_out);
 -}
 -
 -void mtex_negate_texnormal(vec3 normal, out vec3 outnormal)
 -{
 -      outnormal = vec3(-normal.x, -normal.y, normal.z);
 -}
 -
 -void mtex_nspace_tangent(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnormal)
 -{
 -      vec3 B = tangent.w * cross(normal, tangent.xyz);
 -
 -      outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * normal;
 -      outnormal = normalize(outnormal);
 -}
 -
 -void mtex_nspace_world(mat4 viewmat, vec3 texnormal, out vec3 outnormal)
 -{
 -      outnormal = normalize((viewmat * vec4(texnormal, 0.0)).xyz);
 -}
 -
 -void mtex_nspace_object(vec3 texnormal, out vec3 outnormal)
 -{
 -      outnormal = normalize(gl_NormalMatrix * texnormal);
 -}
 -
 -void mtex_blend_normal(float norfac, vec3 normal, vec3 newnormal, out vec3 outnormal)
 -{
 -      outnormal = (1.0 - norfac) * normal + norfac * newnormal;
 -      outnormal = normalize(outnormal);
 -}
 -
 -/******* MATERIAL *********/
 -
 -void lamp_visibility_sun_hemi(vec3 lampvec, out vec3 lv, out float dist, out float visifac)
 -{
 -      lv = lampvec;
 -      dist = 1.0;
 -      visifac = 1.0;
 -}
 -
 -void lamp_visibility_other(vec3 co, vec3 lampco, out vec3 lv, out float dist, out float visifac)
 -{
 -      lv = co - lampco;
 -      dist = length(lv);
 -      lv = normalize(lv);
 -      visifac = 1.0;
 -}
 -
 -void lamp_falloff_invlinear(float lampdist, float dist, out float visifac)
 -{
 -      visifac = lampdist / (lampdist + dist);
 -}
 -
 -void lamp_falloff_invsquare(float lampdist, float dist, out float visifac)
 -{
 -      visifac = lampdist / (lampdist + dist * dist);
 -}
 -
 -void lamp_falloff_sliders(float lampdist, float ld1, float ld2, float dist, out float visifac)
 -{
 -      float lampdistkw = lampdist * lampdist;
 -
 -      visifac = lampdist / (lampdist + ld1 * dist);
 -      visifac *= lampdistkw / (lampdistkw + ld2 * dist * dist);
 -}
 -
 -void lamp_falloff_invcoefficients(float coeff_const, float coeff_lin, float coeff_quad, float dist, out float visifac)
 -{
 -      vec3 coeff = vec3(coeff_const, coeff_lin, coeff_quad);
 -      vec3 d_coeff = vec3(1.0, dist, dist * dist);
 -      float visifac_r = dot(coeff, d_coeff);
 -      if (visifac_r > 0.0)
 -              visifac = 1.0 / visifac_r;
 -      else
 -              visifac = 0.0;
 -}
 -
 -void lamp_falloff_curve(float lampdist, sampler2D curvemap, float dist, out float visifac)
 -{
 -      visifac = texture2D(curvemap, vec2(dist / lampdist, 0.0)).x;
 -}
 -
 -void lamp_visibility_sphere(float lampdist, float dist, float visifac, out float outvisifac)
 -{
 -      float t = lampdist - dist;
 -
 -      outvisifac = visifac * max(t, 0.0) / lampdist;
 -}
 -
 -void lamp_visibility_spot_square(vec3 lampvec, mat4 lampimat, vec2 scale, vec3 lv, out float inpr)
 -{
 -      if (dot(lv, lampvec) > 0.0) {
 -              vec3 lvrot = (lampimat * vec4(lv, 0.0)).xyz;
 -              /* without clever non-uniform scale, we could do: */
 -              // float x = max(abs(lvrot.x / lvrot.z), abs(lvrot.y / lvrot.z));
 -              float x = max(abs((lvrot.x / scale.x) / lvrot.z), abs((lvrot.y / scale.y) / lvrot.z));
 -
 -              inpr = 1.0 / sqrt(1.0 + x * x);
 -      }
 -      else
 -              inpr = 0.0;
 -}
 -
 -void lamp_visibility_spot_circle(vec3 lampvec, mat4 lampimat, vec2 scale, vec3 lv, out float inpr)
 -{
 -      /* without clever non-uniform scale, we could do: */
 -      // inpr = dot(lv, lampvec);
 -      if (dot(lv, lampvec) > 0.0) {
 -              vec3 lvrot = (lampimat * vec4(lv, 0.0)).xyz;
 -              float x = abs(lvrot.x / lvrot.z);
 -              float y = abs(lvrot.y / lvrot.z);
 -
 -              float ellipse = abs((x * x) / (scale.x * scale.x) + (y * y) / (scale.y * scale.y));
 -
 -              inpr = 1.0 / sqrt(1.0 + ellipse);
 -      }
 -      else
 -              inpr = 0.0;
 -}
 -
 -void lamp_visibility_spot(float spotsi, float spotbl, float inpr, float visifac, out float outvisifac)
 -{
 -      float t = spotsi;
 -
 -      if (inpr <= t) {
 -              outvisifac = 0.0;
 -      }
 -      else {
 -              t = inpr - t;
 -
 -              /* soft area */
 -              if (spotbl != 0.0)
 -                      inpr *= smoothstep(0.0, 1.0, t / spotbl);
 -
 -              outvisifac = visifac * inpr;
 -      }
 -}
 -
 -void lamp_visibility_clamp(float visifac, out float outvisifac)
 -{
 -      outvisifac = (visifac < 0.001) ? 0.0 : visifac;
 -}
 -
 -void world_paper_view(vec3 vec, out vec3 outvec)
 -{
 -      vec3 nvec = normalize(vec);
 -      outvec = (gl_ProjectionMatrix[3][3] == 0.0) ? vec3(nvec.x, 0.0, nvec.y) : vec3(0.0, 0.0, -1.0);
 -}
 -
 -void world_zen_mapping(vec3 view, float zenup, float zendown, out float zenfac)
 -{
 -      if (view.z >= 0.0)
 -              zenfac = zenup;
 -      else
 -              zenfac = zendown;
 -}
 -
 -void world_blend_paper_real(vec3 vec, out float blend)
 -{
 -      blend = abs(vec.y);
 -}
 -
 -void world_blend_paper(vec3 vec, out float blend)
 -{
 -      blend = (vec.y + 1.0) * 0.5;
 -}
 -
 -void world_blend_real(vec3 vec, out float blend)
 -{
 -      blend = abs(normalize(vec).z);
 -}
 -
 -void world_blend(vec3 vec, out float blend)
 -{
 -      blend = (normalize(vec).z + 1) * 0.5;
 -}
 -
 -void shade_view(vec3 co, out vec3 view)
 -{
 -      /* handle perspective/orthographic */
 -      view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(co) : vec3(0.0, 0.0, -1.0);
 -}
 -
 -void shade_tangent_v(vec3 lv, vec3 tang, out vec3 vn)
 -{
 -      vec3 c = cross(lv, tang);
 -      vec3 vnor = cross(c, tang);
 -
 -      vn = -normalize(vnor);
 -}
 -
 -void shade_inp(vec3 vn, vec3 lv, out float inp)
 -{
 -      inp = dot(vn, lv);
 -}
 -
 -void shade_is_no_diffuse(out float is)
 -{
 -      is = 0.0;
 -}
 -
 -void shade_is_hemi(float inp, out float is)
 -{
 -      is = 0.5 * inp + 0.5;
 -}
 -
 -float area_lamp_energy(mat4 area, vec3 co, vec3 vn)
 -{
 -      vec3 vec[4], c[4];
 -      float rad[4], fac;
 -
 -      vec[0] = normalize(co - area[0].xyz);
 -      vec[1] = normalize(co - area[1].xyz);
 -      vec[2] = normalize(co - area[2].xyz);
 -      vec[3] = normalize(co - area[3].xyz);
 -
 -      c[0] = normalize(cross(vec[0], vec[1]));
 -      c[1] = normalize(cross(vec[1], vec[2]));
 -      c[2] = normalize(cross(vec[2], vec[3]));
 -      c[3] = normalize(cross(vec[3], vec[0]));
 -
 -      rad[0] = acos(dot(vec[0], vec[1]));
 -      rad[1] = acos(dot(vec[1], vec[2]));
 -      rad[2] = acos(dot(vec[2], vec[3]));
 -      rad[3] = acos(dot(vec[3], vec[0]));
 -
 -      fac =  rad[0] * dot(vn, c[0]);
 -      fac += rad[1] * dot(vn, c[1]);
 -      fac += rad[2] * dot(vn, c[2]);
 -      fac += rad[3] * dot(vn, c[3]);
 -
 -      return max(fac, 0.0);
 -}
 -
 -void shade_inp_area(
 -        vec3 position, vec3 lampco, vec3 lampvec, vec3 vn, mat4 area, float areasize, float k,
 -        out float inp)
 -{
 -      vec3 co = position;
 -      vec3 vec = co - lampco;
 -
 -      if (dot(vec, lampvec) < 0.0) {
 -              inp = 0.0;
 -      }
 -      else {
 -              float intens = area_lamp_energy(area, co, vn);
 -
 -              inp = pow(intens * areasize, k);
 -      }
 -}
 -
 -void shade_diffuse_oren_nayer(float nl, vec3 n, vec3 l, vec3 v, float rough, out float is)
 -{
 -      vec3 h = normalize(v + l);
 -      float nh = max(dot(n, h), 0.0);
 -      float nv = max(dot(n, v), 0.0);
 -      float realnl = dot(n, l);
 -
 -      if (realnl < 0.0) {
 -              is = 0.0;
 -      }
 -      else if (nl < 0.0) {
 -              is = 0.0;
 -      }
 -      else {
 -              float vh = max(dot(v, h), 0.0);
 -              float Lit_A = acos(realnl);
 -              float View_A = acos(nv);
 -
 -              vec3 Lit_B = normalize(l - realnl * n);
 -              vec3 View_B = normalize(v - nv * n);
 -
 -              float t = max(dot(Lit_B, View_B), 0.0);
 -
 -              float a, b;
 -
 -              if (Lit_A > View_A) {
 -                      a = Lit_A;
 -                      b = View_A;
 -              }
 -              else {
 -                      a = View_A;
 -                      b = Lit_A;
 -              }
 -
 -              float A = 1.0 - (0.5 * ((rough * rough) / ((rough * rough) + 0.33)));
 -              float B = 0.45 * ((rough * rough) / ((rough * rough) + 0.09));
 -
 -              b *= 0.95;
 -              is = nl * (A + (B * t * sin(a) * tan(b)));
 -      }
 -}
 -
 -void shade_diffuse_toon(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out float is)
 -{
 -      float rslt = dot(n, l);
 -      float ang = acos(rslt);
 -
 -      if (ang < size) is = 1.0;
 -      else if (ang > (size + tsmooth) || tsmooth == 0.0) is = 0.0;
 -      else is = 1.0 - ((ang - size) / tsmooth);
 -}
 -
 -void shade_diffuse_minnaert(float nl, vec3 n, vec3 v, float darkness, out float is)
 -{
 -      if (nl <= 0.0) {
 -              is = 0.0;
 -      }
 -      else {
 -              float nv = max(dot(n, v), 0.0);
 -
 -              if (darkness <= 1.0)
 -                      is = nl * pow(max(nv * nl, 0.1), darkness - 1.0);
 -              else
 -                      is = nl * pow(1.0001 - nv, darkness - 1.0);
 -      }
 -}
 -
 -float fresnel_fac(vec3 view, vec3 vn, float grad, float fac)
 -{
 -      float t1, t2;
 -      float ffac;
 -
 -      if (fac == 0.0) {
 -              ffac = 1.0;
 -      }
 -      else {
 -              t1 = dot(view, vn);
 -              if (t1 > 0.0) t2 = 1.0 + t1;
 -              else t2 = 1.0 - t1;
 -
 -              t2 = grad + (1.0 - grad) * pow(t2, fac);
 -
 -              if (t2 < 0.0) ffac = 0.0;
 -              else if (t2 > 1.0) ffac = 1.0;
 -              else ffac = t2;
 -      }
 -
 -      return ffac;
 -}
 -
 -void shade_diffuse_fresnel(vec3 vn, vec3 lv, vec3 view, float fac_i, float fac, out float is)
 -{
 -      is = fresnel_fac(lv, vn, fac_i, fac);
 -}
 -
 -void shade_cubic(float is, out float outis)
 -{
 -      if (is > 0.0 && is < 1.0)
 -              outis = smoothstep(0.0, 1.0, is);
 -      else
 -              outis = is;
 -}
 -
 -void shade_visifac(float i, float visifac, float refl, out float outi)
 -{
 -      /*if (i > 0.0)*/
 -      outi = max(i * visifac * refl, 0.0);
 -      /*else
 -          outi = i;*/
 -}
 -
 -void shade_tangent_v_spec(vec3 tang, out vec3 vn)
 -{
 -      vn = tang;
 -}
 -
 -void shade_add_to_diffuse(float i, vec3 lampcol, vec3 col, out vec3 outcol)
 -{
 -      if (i > 0.0)
 -              outcol = i * lampcol * col;
 -      else
 -              outcol = vec3(0.0, 0.0, 0.0);
 -}
 -
 -void shade_hemi_spec(vec3 vn, vec3 lv, vec3 view, float spec, float hard, float visifac, out float t)
 -{
 -      lv += view;
 -      lv = normalize(lv);
 -
 -      t = dot(vn, lv);
 -      t = 0.5 * t + 0.5;
 -
 -      t = visifac * spec * pow(t, hard);
 -}
 -
 -void shade_phong_spec(vec3 n, vec3 l, vec3 v, float hard, out float specfac)
 -{
 -      vec3 h = normalize(l + v);
 -      float rslt = max(dot(h, n), 0.0);
 -
 -      specfac = pow(rslt, hard);
 -}
 -
 -void shade_cooktorr_spec(vec3 n, vec3 l, vec3 v, float hard, out float specfac)
 -{
 -      vec3 h = normalize(v + l);
 -      float nh = dot(n, h);
 -
 -      if (nh < 0.0) {
 -              specfac = 0.0;
 -      }
 -      else {
 -              float nv = max(dot(n, v), 0.0);
 -              float i = pow(nh, hard);
 -
 -              i = i / (0.1 + nv);
 -              specfac = i;
 -      }
 -}
 -
 -void shade_blinn_spec(vec3 n, vec3 l, vec3 v, float refrac, float spec_power, out float specfac)
 -{
 -      if (refrac < 1.0) {
 -              specfac = 0.0;
 -      }
 -      else if (spec_power == 0.0) {
 -              specfac = 0.0;
 -      }
 -      else {
 -              if (spec_power < 100.0)
 -                      spec_power = sqrt(1.0 / spec_power);
 -              else
 -                      spec_power = 10.0 / spec_power;
 -
 -              vec3 h = normalize(v + l);
 -              float nh = dot(n, h);
 -              if (nh < 0.0) {
 -                      specfac = 0.0;
 -              }
 -              else {
 -                      float nv = max(dot(n, v), 0.01);
 -                      float nl = dot(n, l);
 -                      if (nl <= 0.01) {
 -                              specfac = 0.0;
 -                      }
 -                      else {
 -                              float vh = max(dot(v, h), 0.01);
 -
 -                              float a = 1.0;
 -                              float b = (2.0 * nh * nv) / vh;
 -                              float c = (2.0 * nh * nl) / vh;
 -
 -                              float g = 0.0;
 -
 -                              if (a < b && a < c) g = a;
 -                              else if (b < a && b < c) g = b;
 -                              else if (c < a && c < b) g = c;
 -
 -                              float p = sqrt(((refrac * refrac) + (vh * vh) - 1.0));
 -                              float f = ((((p - vh) * (p - vh)) / ((p + vh) * (p + vh))) *
 -                                         (1.0 + ((((vh * (p + vh)) - 1.0) * ((vh * (p + vh)) - 1.0)) /
 -                                                 (((vh * (p - vh)) + 1.0) * ((vh * (p - vh)) + 1.0)))));
 -                              float ang = acos(nh);
 -
 -                              specfac = max(f * g * exp_blender((-(ang * ang) / (2.0 * spec_power * spec_power))), 0.0);
 -                      }
 -              }
 -      }
 -}
 -
 -void shade_wardiso_spec(vec3 n, vec3 l, vec3 v, float rms, out float specfac)
 -{
 -      vec3 h = normalize(l + v);
 -      float nh = max(dot(n, h), 0.001);
 -      float nv = max(dot(n, v), 0.001);
 -      float nl = max(dot(n, l), 0.001);
 -      float angle = tan(acos(nh));
 -      float alpha = max(rms, 0.001);
 -
 -      specfac = nl * (1.0 / (4.0 * M_PI * alpha * alpha)) * (exp_blender(-(angle * angle) / (alpha * alpha)) / (sqrt(nv * nl)));
 -}
 -
 -void shade_toon_spec(vec3 n, vec3 l, vec3 v, float size, float tsmooth, out float specfac)
 -{
 -      vec3 h = normalize(l + v);
 -      float rslt = dot(h, n);
 -      float ang = acos(rslt);
 -
 -      if (ang < size) rslt = 1.0;
 -      else if (ang >= (size + tsmooth) || tsmooth == 0.0) rslt = 0.0;
 -      else rslt = 1.0 - ((ang - size) / tsmooth);
 -
 -      specfac = rslt;
 -}
 -
 -void shade_spec_area_inp(float specfac, float inp, out float outspecfac)
 -{
 -      outspecfac = specfac * inp;
 -}
 -
 -void shade_spec_t(float shadfac, float spec, float visifac, float specfac, out float t)
 -{
 -      t = shadfac * spec * visifac * specfac;
 -}
 -
 -void shade_add_spec(float t, vec3 lampcol, vec3 speccol, out vec3 outcol)
 -{
 -      outcol = t * lampcol * speccol;
 -}
 -
 -void shade_add_mirror(vec3 mir, vec4 refcol, vec3 combined, out vec3 result)
 -{
 -      result = mir * refcol.gba + (vec3(1.0) - mir * refcol.rrr) * combined;
 -}
 -
 -void alpha_spec_correction(vec3 spec, float spectra, float alpha, out float outalpha)
 -{
 -      if (spectra > 0.0) {
 -              float t = clamp(max(max(spec.r, spec.g), spec.b) * spectra, 0.0, 1.0);
 -              outalpha = (1.0 - t) * alpha + t;
 -      }
 -      else {
 -              outalpha = alpha;
 -      }
 -}
 -
 -void shade_add(vec4 col1, vec4 col2, out vec4 outcol)
 -{
 -      outcol = col1 + col2;
 -}
 -
 -void shade_madd(vec4 col, vec4 col1, vec4 col2, out vec4 outcol)
 -{
 -      outcol = col + col1 * col2;
 -}
 -
 -void shade_add_clamped(vec4 col1, vec4 col2, out vec4 outcol)
 -{
 -      outcol = col1 + max(col2, vec4(0.0, 0.0, 0.0, 0.0));
 -}
 -
 -void shade_madd_clamped(vec4 col, vec4 col1, vec4 col2, out vec4 outcol)
 -{
 -      outcol = col + max(col1 * col2, vec4(0.0, 0.0, 0.0, 0.0));
 -}
 -
 -void env_apply(vec4 col, vec3 hor, vec3 zen, vec4 f, mat4 vm, vec3 vn, out vec4 outcol)
 -{
 -      vec3 vv = normalize(vm[2].xyz);
 -      float skyfac = 0.5 * (1.0 + dot(vn, -vv));
 -      outcol = col + f * vec4(mix(hor, zen, skyfac), 0);
 -}
 -
 -void shade_maddf(vec4 col, float f, vec4 col1, out vec4 outcol)
 -{
 -      outcol = col + f * col1;
 -}
 -
 -void shade_mul(vec4 col1, vec4 col2, out vec4 outcol)
 -{
 -      outcol = col1 * col2;
 -}
 -
 -void shade_mul_value(float fac, vec4 col, out vec4 outcol)
 -{
 -      outcol = col * fac;
 -}
 -
 -void shade_mul_value_v3(float fac, vec3 col, out vec3 outcol)
 -{
 -      outcol = col * fac;
 -}
 -
 -void shade_obcolor(vec4 col, vec4 obcol, out vec4 outcol)
 -{
 -      outcol = vec4(col.rgb * obcol.rgb, col.a);
 -}
 -
 -void ramp_rgbtobw(vec3 color, out float outval)
 -{
 -      outval = color.r * 0.3 + color.g * 0.58 + color.b * 0.12;
 -}
 -
 -void shade_only_shadow(float i, float shadfac, float energy, vec3 shadcol, out vec3 outshadrgb)
 -{
 -      outshadrgb = i * energy * (1.0 - shadfac) * (vec3(1.0) - shadcol);
 -}
 -
 -void shade_only_shadow_diffuse(vec3 shadrgb, vec3 rgb, vec4 diff, out vec4 outdiff)
 -{
 -      outdiff = diff - vec4(rgb * shadrgb, 0.0);
 -}
 -
 -void shade_only_shadow_specular(vec3 shadrgb, vec3 specrgb, vec4 spec, out vec4 outspec)
 -{
 -      outspec = spec - vec4(specrgb * shadrgb, 0.0);
 -}
 -
 -void shade_clamp_positive(vec4 col, out vec4 outcol)
 -{
 -      outcol = max(col, vec4(0.0));
 -}
 -
 -void test_shadowbuf(
 -        vec3 rco, sampler2DShadow shadowmap, mat4 shadowpersmat, float shadowbias, float inp,
 -        out float result)
 -{
 -      if (inp <= 0.0) {
 -              result = 0.0;
 -      }
 -      else {
 -              vec4 co = shadowpersmat * vec4(rco, 1.0);
 -
 -              //float bias = (1.5 - inp*inp)*shadowbias;
 -              co.z -= shadowbias * co.w;
 -
 -              if (co.w > 0.0 && co.x > 0.0 && co.x / co.w < 1.0 && co.y > 0.0 && co.y / co.w < 1.0)
 -                      result = shadow2DProj(shadowmap, co).x;
 -              else
 -                      result = 1.0;
 -      }
 -}
 -
 -void test_shadowbuf_vsm(
 -        vec3 rco, sampler2D shadowmap, mat4 shadowpersmat, float shadowbias, float bleedbias, float inp,
 -        out float result)
 -{
 -      if (inp <= 0.0) {
 -              result = 0.0;
 -      }
 -      else {
 -              vec4 co = shadowpersmat * vec4(rco, 1.0);
 -              if (co.w > 0.0 && co.x > 0.0 && co.x / co.w < 1.0 && co.y > 0.0 && co.y / co.w < 1.0) {
 -                      vec2 moments = texture2DProj(shadowmap, co).rg;
 -                      float dist = co.z / co.w;
 -                      float p = 0.0;
 -
 -                      if (dist <= moments.x)
 -                              p = 1.0;
 -
 -                      float variance = moments.y - (moments.x * moments.x);
 -                      variance = max(variance, shadowbias / 10.0);
 -
 -                      float d = moments.x - dist;
 -                      float p_max = variance / (variance + d * d);
 -
 -                      // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]
 -                      p_max = clamp((p_max - bleedbias) / (1.0 - bleedbias), 0.0, 1.0);
 -
 -                      result = max(p, p_max);
 -              }
 -              else {
 -                      result = 1.0;
 -              }
 -      }
 -}
 -
 -void shadows_only(
 -        vec3 rco, sampler2DShadow shadowmap, mat4 shadowpersmat,
 -        float shadowbias, vec3 shadowcolor, float inp,
 -        out vec3 result)
 -{
 -      result = vec3(1.0);
 -
 -      if (inp > 0.0) {
 -              float shadfac;
 -
 -              test_shadowbuf(rco, shadowmap, shadowpersmat, shadowbias, inp, shadfac);
 -              result -= (1.0 - shadfac) * (vec3(1.0) - shadowcolor);
 -      }
 -}
 -
 -void shadows_only_vsm(
 -        vec3 rco, sampler2D shadowmap, mat4 shadowpersmat,
 -        float shadowbias, float bleedbias, vec3 shadowcolor, float inp,
 -        out vec3 result)
 -{
 -      result = vec3(1.0);
 -
 -      if (inp > 0.0) {
 -              float shadfac;
 -
 -              test_shadowbuf_vsm(rco, shadowmap, shadowpersmat, shadowbias, bleedbias, inp, shadfac);
 -              result -= (1.0 - shadfac) * (vec3(1.0) - shadowcolor);
 -      }
 -}
 -
 -void shade_light_texture(vec3 rco, sampler2D cookie, mat4 shadowpersmat, out vec4 result)
 -{
 -
 -      vec4 co = shadowpersmat * vec4(rco, 1.0);
 -
 -      result = texture2DProj(cookie, co);
 -}
 -
 -void shade_exposure_correct(vec3 col, float linfac, float logfac, out vec3 outcol)
 -{
 -      outcol = linfac * (1.0 - exp(col * logfac));
 -}
 -
 -void shade_mist_factor(
 -        vec3 co, float enable, float miststa, float mistdist, float misttype, float misi,
 -        out float outfac)
 -{
 -      if (enable == 1.0) {
 -              float fac, zcor;
 -
 -              zcor = (gl_ProjectionMatrix[3][3] == 0.0) ? length(co) : -co[2];
 -
 -              fac = clamp((zcor - miststa) / mistdist, 0.0, 1.0);
 -              if (misttype == 0.0) fac *= fac;
 -              else if (misttype == 1.0) ;
 -              else fac = sqrt(fac);
 -
 -              outfac = 1.0 - (1.0 - fac) * (1.0 - misi);
 -      }
 -      else {
 -              outfac = 0.0;
 -      }
 -}
 -
 -void shade_world_mix(vec3 hor, vec4 col, out vec4 outcol)
 -{
 -      float fac = clamp(col.a, 0.0, 1.0);
 -      outcol = vec4(mix(hor, col.rgb, fac), col.a);
 -}
 -
 -void shade_alpha_opaque(vec4 col, out vec4 outcol)
 -{
 -      outcol = vec4(col.rgb, 1.0);
 -}
 -
 -void shade_alpha_obcolor(vec4 col, vec4 obcol, out vec4 outcol)
 -{
 -      outcol = vec4(col.rgb, col.a * obcol.a);
 -}
 -
 -/*********** NEW SHADER UTILITIES **************/
 -
 -float fresnel_dielectric_0(float eta)
 -{
 -      /* compute fresnel reflactance at normal incidence => cosi = 1.0 */
 -      float A = (eta - 1.0) / (eta + 1.0);
 -
 -      return A * A;
 -}
 -
 -float fresnel_dielectric_cos(float cosi, float eta)
 -{
 -      /* compute fresnel reflectance without explicitly computing
 -       * the refracted direction */
 -      float c = abs(cosi);
 -      float g = eta * eta - 1.0 + c * c;
 -      float result;
 -
 -      if (g > 0.0) {
 -              g = sqrt(g);
 -              float A = (g - c) / (g + c);
 -              float B = (c * (g + c) - 1.0) / (c * (g - c) + 1.0);
 -              result = 0.5 * A * A * (1.0 + B * B);
 -      }
 -      else {
 -              result = 1.0;  /* TIR (no refracted component) */
 -      }
 -
 -      return result;
 -}
 -
 -float fresnel_dielectric(vec3 Incoming, vec3 Normal, float eta)
 -{
 -      /* compute fresnel reflectance without explicitly computing
 -       * the refracted direction */
 -      return fresnel_dielectric_cos(dot(Incoming, Normal), eta);
 -}
 -
 -float hypot(float x, float y)
 -{
 -      return sqrt(x * x + y * y);
 -}
 -
 -void generated_from_orco(vec3 orco, out vec3 generated)
 -{
 -      generated = orco * 0.5 + vec3(0.5);
 -}
 -
 -int floor_to_int(float x)
 -{
 -      return int(floor(x));
 -}
 -
 -int quick_floor(float x)
 -{
 -      return int(x) - ((x < 0) ? 1 : 0);
 -}
 -
 -#ifdef BIT_OPERATIONS
 -float integer_noise(int n)
 -{
 -      int nn;
 -      n = (n + 1013) & 0x7fffffff;
 -      n = (n >> 13) ^ n;
 -      nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
 -      return 0.5 * (float(nn) / 1073741824.0);
 -}
 -
 -uint hash(uint kx, uint ky, uint kz)
 -{
 -#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
 -#define final(a, b, c) \
 -{ \
 -      c ^= b; c -= rot(b, 14); \
 -      a ^= c; a -= rot(c, 11); \
 -      b ^= a; b -= rot(a, 25); \
 -      c ^= b; c -= rot(b, 16); \
 -      a ^= c; a -= rot(c, 4);  \
 -      b ^= a; b -= rot(a, 14); \
 -      c ^= b; c -= rot(b, 24); \
 -}
 -      // now hash the data!
 -      uint a, b, c, len = 3u;
 -      a = b = c = 0xdeadbeefu + (len << 2u) + 13u;
 -
 -      c += kz;
 -      b += ky;
 -      a += kx;
 -      final (a, b, c);
 -
 -      return c;
 -#undef rot
 -#undef final
 -}
 -
 -uint hash(int kx, int ky, int kz)
 -{
 -      return hash(uint(kx), uint(ky), uint(kz));
 -}
 -
 -float bits_to_01(uint bits)
 -{
 -      return (float(bits) / 4294967295.0);
 -}
 -
 -float cellnoise(vec3 p)
 -{
 -      int ix = quick_floor(p.x);
 -      int iy = quick_floor(p.y);
 -      int iz = quick_floor(p.z);
 -
 -      return bits_to_01(hash(uint(ix), uint(iy), uint(iz)));
 -}
 -
 -vec3 cellnoise_color(vec3 p)
 -{
 -      float r = cellnoise(p);
 -      float g = cellnoise(vec3(p.y, p.x, p.z));
 -      float b = cellnoise(vec3(p.y, p.z, p.x));
 -
 -      return vec3(r, g, b);
 -}
 -#endif  // BIT_OPERATIONS
 -
 -float floorfrac(float x, out int i)
 -{
 -      i = floor_to_int(x);
 -      return x - i;
 -}
 -
 -
 -/* Principled BSDF operations */
 -
 -float sqr(float a)
 -{
 -      return a*a;
 -}
 -
 -float schlick_fresnel(float u)
 -{
 -      float m = clamp(1.0 - u, 0.0, 1.0);
 -      float m2 = m * m;
 -      return m2 * m2 * m; // pow(m,5)
 -}
 -
 -float GTR1(float NdotH, float a)
 -{
 -      if (a >= 1.0) {
 -              return M_1_PI;
 -      }
 -
 -      a = max(a, 0.001);
 -      float a2 = a*a;
 -      float t = 1.0 + (a2 - 1.0) * NdotH*NdotH;
 -      return (a2 - 1.0) / (M_PI * log(a2) * t);
 -}
 -
 -float GTR2(float NdotH, float a)
 -{
 -      float a2 = a*a;
 -      float t = 1.0 + (a2 - 1.0) * NdotH*NdotH;
 -      return a2 / (M_PI * t*t);
 -}
 -
 -float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
 -{
 -      return 1.0 / (M_PI * ax*ay * sqr(sqr(HdotX / ax) + sqr(HdotY / ay) + NdotH*NdotH));
 -}
 -
 -float smithG_GGX(float NdotV, float alphaG)
 -{
 -      float a = alphaG*alphaG;
 -      float b = NdotV*NdotV;
 -      return 1.0 / (NdotV + sqrt(a + b - a * b));
 -}
 -
 -vec3 rotate_vector(vec3 p, vec3 n, float theta) {
 -      return (
 -                 p * cos(theta) + cross(n, p) *
 -                 sin(theta) + n * dot(p, n) *
 -                 (1.0 - cos(theta))
 -             );
 -}
 -
 -
 -/*********** NEW SHADER NODES ***************/
 -
 -#define NUM_LIGHTS 3
 -
 -/* bsdfs */
 -
 -void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out vec4 result)
 -{
 -      /* ambient light */
 -      vec3 L = vec3(0.2);
 -
 -      /* directional lights */
 -      for (int i = 0; i < NUM_LIGHTS; i++) {
 -              vec3 light_position = gl_LightSource[i].position.xyz;
 -              vec3 light_diffuse = gl_LightSource[i].diffuse.rgb;
 -
 -              float bsdf = max(dot(N, light_position), 0.0);
 -              L += light_diffuse * bsdf;
 -      }
 -
 -      result = vec4(L * color.rgb, 1.0);
 -}
 -
 -void node_bsdf_glossy(vec4 color, float roughness, vec3 N, out vec4 result)
 -{
 -      /* ambient light */
 -      vec3 L = vec3(0.2);
 -
 -      /* directional lights */
 -      for (int i = 0; i < NUM_LIGHTS; i++) {
 -              vec3 light_position = gl_LightSource[i].position.xyz;
 -              vec3 H = gl_LightSource[i].halfVector.xyz;
 -              vec3 light_diffuse = gl_LightSource[i].diffuse.rgb;
 -              vec3 light_specular = gl_LightSource[i].specular.rgb;
 -
 -              /* we mix in some diffuse so low roughness still shows up */
 -              float r2 = roughness * roughness;
 -              float bsdf = 0.5 * pow(max(dot(N, H), 0.0), 1.0 / r2);
 -              bsdf += 0.5 * max(dot(N, light_position), 0.0);
 -              L += light_specular * bsdf;
 -      }
 -
 -      result = vec4(L * color.rgb, 1.0);
 -}
 -
 -void node_bsdf_anisotropic(
 -        vec4 color, float roughness, float anisotropy, float rotation, vec3 N, vec3 T,
 -        out vec4 result)
 -{
 -      node_bsdf_diffuse(color, 0.0, N, result);
 -}
 -
 -void node_bsdf_glass(vec4 color, float roughness, float ior, vec3 N, out vec4 result)
 -{
 -      node_bsdf_diffuse(color, 0.0, N, result);
 -}
 -
 -void node_bsdf_toon(vec4 color, float size, float tsmooth, vec3 N, out vec4 result)
 -{
 -      node_bsdf_diffuse(color, 0.0, N, result);
 -}
 -
 -void node_bsdf_principled(vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, float metallic, float specular,
 -      float specular_tint, float roughness, float anisotropic, float anisotropic_rotation, float sheen, float sheen_tint, float clearcoat,
 -      float clearcoat_roughness, float ior, float transmission, float transmission_roughness, vec3 N, vec3 CN, vec3 T, vec3 I, out vec4 result)
 -{
 -      /* ambient light */
 -      // TODO: set ambient light to an appropriate value
 -      vec3 L = mix(0.1, 0.03, metallic) * mix(base_color.rgb, subsurface_color.rgb, subsurface * (1.0 - metallic));
 -
 -      float eta = (2.0 / (1.0 - sqrt(0.08 * specular))) - 1.0;
 -
 -      /* set the viewing vector */
 -      vec3 V = (gl_ProjectionMatrix[3][3] == 0.0) ? -normalize(I) : vec3(0.0, 0.0, 1.0);
 -
 -      /* get the tangent */
 -      vec3 Tangent = T;
 -      if (T == vec3(0.0)) {
 -              // if no tangent is set, use a default tangent
 -              if(N.x != N.y || N.x != N.z) {
 -                      Tangent = vec3(N.z-N.y, N.x-N.z, N.y-N.x);  // (1,1,1) x N
 -              }
 -              else {
 -                      Tangent = vec3(N.z-N.y, N.x+N.z, -N.y-N.x);  // (-1,1,1) x N
 -              }
 -      }
 -
 -      /* rotate tangent */
 -      if (anisotropic_rotation != 0.0) {
 -              Tangent = rotate_vector(Tangent, N, anisotropic_rotation * 2.0 * M_PI);
 -      }
 -
 -      /* calculate the tangent and bitangent */
 -      vec3 Y = normalize(cross(N, Tangent));
 -      vec3 X = cross(Y, N);
 -
 -      /* fresnel normalization parameters */
 -      float F0 = fresnel_dielectric_0(eta);
 -      float F0_norm = 1.0 / (1.0 - F0);
 -
 -      /* directional lights */
 -      for (int i = 0; i < NUM_LIGHTS; i++) {
 -              vec3 light_position_world = gl_LightSource[i].position.xyz;
 -              vec3 light_position = normalize(light_position_world);
 -
 -              vec3 H = normalize(light_position + V);
 -
 -              vec3 light_diffuse = gl_LightSource[i].diffuse.rgb;
 -              vec3 light_specular = gl_LightSource[i].specular.rgb;
 -
 -              float NdotL = dot(N, light_position);
 -              float NdotV = dot(N, V);
 -              float LdotH = dot(light_position, H);
 -
 -              vec3 diffuse_and_specular_bsdf = vec3(0.0);
 -              if (NdotL >= 0.0 && NdotV >= 0.0) {
 -                      float NdotH = dot(N, H);
 -
 -                      float Cdlum = 0.3 * base_color.r + 0.6 * base_color.g + 0.1 * base_color.b; // luminance approx.
 -
 -                      vec3 Ctint = Cdlum > 0 ? base_color.rgb / Cdlum : vec3(1.0); // normalize lum. to isolate hue+sat
 -                      vec3 Cspec0 = mix(specular * 0.08 * mix(vec3(1.0), Ctint, specular_tint), base_color.rgb, metallic);
 -                      vec3 Csheen = mix(vec3(1.0), Ctint, sheen_tint);
 -
 -                      // Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
 -                      // and mix in diffuse retro-reflection based on roughness
 -
 -                      float FL = schlick_fresnel(NdotL), FV = schlick_fresnel(NdotV);
 -                      float Fd90 = 0.5 + 2.0 * LdotH*LdotH * roughness;
 -                      float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
 -
 -                      // Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf
 -                      // 1.25 scale is used to (roughly) preserve albedo
 -                      // Fss90 used to "flatten" retroreflection based on roughness
 -                      float Fss90 = LdotH*LdotH * roughness;
 -                      float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
 -                      float ss = 1.25 * (Fss * (1.0 / (NdotL + NdotV) - 0.5) + 0.5);
 -
 -                      // specular
 -                      float aspect = sqrt(1.0 - anisotropic * 0.9);
 -                      float a = sqr(roughness);
 -                      float ax = max(0.001, a / aspect);
 -                      float ay = max(0.001, a * aspect);
 -                      float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay); //GTR2(NdotH, a);
 -                      float FH = (fresnel_dielectric_cos(LdotH, eta) - F0) * F0_norm;
 -                      vec3 Fs = mix(Cspec0, vec3(1.0), FH);
 -                      float roughg = sqr(roughness * 0.5 + 0.5);
 -                      float Gs = smithG_GGX(NdotL, roughg) * smithG_GGX(NdotV, roughg);
 -
 -                      // sheen
 -                      vec3 Fsheen = schlick_fresnel(LdotH) * sheen * Csheen;
 -
 -                      vec3 diffuse_bsdf = (mix(Fd * base_color.rgb, ss * subsurface_color.rgb, subsurface) + Fsheen) * light_diffuse;
 -                      vec3 specular_bsdf = Gs * Fs * Ds * light_specular;
 -                      diffuse_and_specular_bsdf = diffuse_bsdf * (1.0 - metallic) + specular_bsdf;
 -              }
 -              diffuse_and_specular_bsdf *= max(NdotL, 0.0);
 -
 -              float CNdotL = dot(CN, light_position);
 -              float CNdotV = dot(CN, V);
 -
 -              vec3 clearcoat_bsdf = vec3(0.0);
 -              if (CNdotL >= 0.0 && CNdotV >= 0.0 && clearcoat > 0.0) {
 -                      float CNdotH = dot(CN, H);
 -                      //float FH = schlick_fresnel(LdotH);
 -
 -                      // clearcoat (ior = 1.5 -> F0 = 0.04)
 -                      float Dr = GTR1(CNdotH, sqr(clearcoat_roughness));
 -                      float Fr = fresnel_dielectric_cos(LdotH, 1.5); //mix(0.04, 1.0, FH);
 -                      float Gr = smithG_GGX(CNdotL, 0.25) * smithG_GGX(CNdotV, 0.25);
 -
 -                      clearcoat_bsdf = clearcoat * Gr * Fr * Dr * vec3(0.25) * light_specular;
 -              }
 -              clearcoat_bsdf *= max(CNdotL, 0.0);
 -
 -              L += diffuse_and_specular_bsdf + clearcoat_bsdf;
 -      }
 -
 -      result = vec4(L, 1.0);
 -}
 -
 -void node_bsdf_translucent(vec4 color, vec3 N, out vec4 result)
 -{
 -      node_bsdf_diffuse(color, 0.0, N, result);
 -}
 -
 -void node_bsdf_transparent(vec4 color, out vec4 result)
 -{
 -      /* this isn't right */
 -      result.r = color.r;
 -      result.g = color.g;
 -      result.b = color.b;
 -      result.a = 0.0;
 -}
 -
 -void node_bsdf_velvet(vec4 color, float sigma, vec3 N, out vec4 result)
 -{
 -      node_bsdf_diffuse(color, 0.0, N, result);
 -}
 -
 -void node_subsurface_scattering(
 -        vec4 color, float scale, vec3 radius, float sharpen, float texture_blur, vec3 N,
 -        out vec4 result)
 -{
 -      node_bsdf_diffuse(color, 0.0, N, result);
 -}
 -
 -void node_bsdf_hair(vec4 color, float offset, float roughnessu, float roughnessv, vec3 tangent, out vec4 result)
 -{
 -      result = color;
 -}
 -
 -void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out vec4 result)
 -{
 -      node_bsdf_diffuse(color, 0.0, N, result);
 -}
 -
 -void node_ambient_occlusion(vec4 color, float distance, vec3 normal, out vec4 result_color, out float result_ao)
 -{
 -      result_color = color;
 -      result_ao = 1.0;
 -}
 -
 -/* emission */
 -
 -void node_emission(vec4 color, float strength, vec3 N, out vec4 result)
 -{
 -      result = color * strength;
 -}
 -
 -/* background */
 -
 -void background_transform_to_world(vec3 viewvec, out vec3 worldvec)
 -{
 -      vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
 -      vec4 co_homogenous = (gl_ProjectionMatrixInverse * v);
 -
 -      vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
 -      worldvec = (gl_ModelViewMatrixInverse * co).xyz;
 -}
 -
 -void node_background(vec4 color, float strength, vec3 N, out vec4 result)
 -{
 -      result = color * strength;
 -}
 -
 -/* closures */
 -
 -void node_mix_shader(float fac, vec4 shader1, vec4 shader2, out vec4 shader)
 -{
 -      shader = mix(shader1, shader2, fac);
 -}
 -
 -void node_add_shader(vec4 shader1, vec4 shader2, out vec4 shader)
 -{
 -      shader = shader1 + shader2;
 +      shader = closure_add(shader1, shader2);
  }
  
  /* fresnel */
  
  void node_fresnel(float ior, vec3 N, vec3 I, out float result)
  {
 +      N = normalize(N);
        /* handle perspective/orthographic */
 -      vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
 +      vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
  
        float eta = max(ior, 0.00001);
        result = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? eta : 1.0 / eta);
  
  void node_layer_weight(float blend, vec3 N, vec3 I, out float fresnel, out float facing)
  {
 +      N = normalize(N);
 +
        /* fresnel */
        float eta = max(1.0 - blend, 0.00001);
 -      vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
 +      vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
  
        fresnel = fresnel_dielectric(I_view, N, (gl_FrontFacing) ? 1.0 / eta : eta);
  
@@@ -1654,67 -2893,11 +1654,67 @@@ void node_gamma(vec4 col, float gamma, 
  
  /* geometry */
  
 +void node_attribute_volume_density(sampler3D tex, out vec4 outcol, out vec3 outvec, out float outf)
 +{
 +#if defined(MESH_SHADER) && defined(VOLUMETRICS)
 +      vec3 cos = volumeObjectLocalCoord;
 +#else
 +      vec3 cos = vec3(0.0);
 +#endif
 +      outvec = texture(tex, cos).aaa;
 +      outcol = vec4(outvec, 1.0);
 +      outf = dot(vec3(1.0 / 3.0), outvec);
 +}
 +
 +void node_attribute_volume_color(sampler3D tex, out vec4 outcol, out vec3 outvec, out float outf)
 +{
 +#if defined(MESH_SHADER) && defined(VOLUMETRICS)
 +      vec3 cos = volumeObjectLocalCoord;
 +#else
 +      vec3 cos = vec3(0.0);
 +#endif
 +
 +      vec4 value = texture(tex, cos).rgba;
 +      /* Density is premultiplied for interpolation, divide it out here. */
 +      if (value.a > 1e-8)
 +              value.rgb /= value.a;
 +
 +      outvec = value.rgb;
 +      outcol = vec4(outvec, 1.0);
 +      outf = dot(vec3(1.0 / 3.0), outvec);
 +}
 +
 +void node_attribute_volume_flame(sampler3D tex, out vec4 outcol, out vec3 outvec, out float outf)
 +{
 +#if defined(MESH_SHADER) && defined(VOLUMETRICS)
 +      vec3 cos = volumeObjectLocalCoord;
 +#else
 +      vec3 cos = vec3(0.0);
 +#endif
 +      outf = texture(tex, cos).r;
 +      outvec = vec3(outf, outf, outf);
 +      outcol = vec4(outf, outf, outf, 1.0);
 +}
 +
 +void node_attribute_volume_temperature(sampler3D tex, vec2 temperature, out vec4 outcol, out vec3 outvec, out float outf)
 +{
 +#if defined(MESH_SHADER) && defined(VOLUMETRICS)
 +      vec3 cos = volumeObjectLocalCoord;
 +#else
 +      vec3 cos = vec3(0.0);
 +#endif
 +      float flame = texture(tex, cos).r;
 +
 +      outf = (flame > 0.01) ? temperature.x + flame * (temperature.y - temperature.x): 0.0;
 +      outvec = vec3(outf, outf, outf);
 +      outcol = vec4(outf, outf, outf, 1.0);
 +}
 +
  void node_attribute(vec3 attr, out vec4 outcol, out vec3 outvec, out float outf)
  {
        outcol = vec4(attr, 1.0);
        outvec = attr;
 -      outf = (attr.x + attr.y + attr.z) / 3.0;
 +      outf =  dot(vec3(1.0 / 3.0), attr);
  }
  
  void node_uvmap(vec3 attr_uv, out vec3 outvec)
        outvec = attr_uv;
  }
  
 +void tangent_orco_x(vec3 orco_in, out vec3 orco_out)
 +{
 +      orco_out = orco_in.xzy * vec3(0.0, -0.5, 0.5) + vec3(0.0, 0.25, -0.25);
 +}
 +
 +void tangent_orco_y(vec3 orco_in, out vec3 orco_out)
 +{
 +      orco_out = orco_in.zyx * vec3(-0.5, 0.0, 0.5) + vec3(0.25, 0.0, -0.25);
 +}
 +
 +void tangent_orco_z(vec3 orco_in, out vec3 orco_out)
 +{
 +      orco_out = orco_in.yxz * vec3(-0.5, 0.5, 0.0) + vec3(0.25, -0.25, 0.0);
 +}
 +
 +void node_tangentmap(vec4 attr_tangent, mat4 toworld, out vec3 tangent)
 +{
 +      tangent = (toworld * vec4(attr_tangent.xyz, 0.0)).xyz;
 +}
 +
 +void node_tangent(vec3 N, vec3 orco, mat4 objmat, mat4 toworld, out vec3 T)
 +{
 +#ifndef VOLUMETRICS
 +      N = normalize(worldNormal);
 +#else
 +      N = (toworld * vec4(N, 0.0)).xyz;
 +#endif
 +      T = (objmat * vec4(orco, 0.0)).xyz;
 +      T = cross(N, normalize(cross(T, N)));
 +}
 +
  void node_geometry(
 -        vec3 I, vec3 N, mat4 toworld,
 +        vec3 I, vec3 N, vec3 orco, mat4 objmat, mat4 toworld, vec2 barycentric,
          out vec3 position, out vec3 normal, out vec3 tangent,
          out vec3 true_normal, out vec3 incoming, out vec3 parametric,
          out float backfacing, out float pointiness)
  {
 -      position = (toworld * vec4(I, 1.0)).xyz;
 -      normal = (toworld * vec4(N, 0.0)).xyz;
 -      tangent = vec3(0.0);
 -      true_normal = normal;
 -
        /* handle perspective/orthographic */
 -      vec3 I_view = (gl_ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
 +      vec3 I_view = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
        incoming = -(toworld * vec4(I_view, 0.0)).xyz;
  
 -      parametric = vec3(0.0);
 +#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
 +      position = -incoming;
 +      true_normal = normal = incoming;
 +      tangent = parametric = vec3(0.0);
 +      vec3(0.0);
 +      backfacing = 0.0;
 +      pointiness = 0.0;
 +#else
 +
 +      position = worldPosition;
 +#  ifndef VOLUMETRICS
 +      normal = normalize(worldNormal);
 +
 +      vec3 B = dFdx(worldPosition);
 +      vec3 T = dFdy(worldPosition);
 +      true_normal = normalize(cross(B, T));
 +#  else
 +      normal = (toworld * vec4(N, 0.0)).xyz;
 +      true_normal = normal;
 +#  endif
 +      tangent_orco_z(orco, orco);
 +      node_tangent(N, orco, objmat, toworld, tangent);
 +
 +      parametric = vec3(barycentric, 0.0);
        backfacing = (gl_FrontFacing) ? 0.0 : 1.0;
        pointiness = 0.5;
 +#endif
 +}
 +
 +void generated_texco(vec3 I, vec3 attr_orco, out vec3 generated)
 +{
 +      vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
 +      vec4 co_homogenous = (ProjectionMatrixInverse * v);
 +      vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
 +      co.xyz = normalize(co.xyz);
 +#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
 +      generated = (ViewMatrixInverse * co).xyz;
 +#else
 +      generated_from_orco(attr_orco, generated);
 +#endif
  }
  
  void node_tex_coord(
          out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object,
          out vec3 camera, out vec3 window, out vec3 reflection)
  {
 -      generated = attr_orco * 0.5 + vec3(0.5);
 +      generated = attr_orco;
        normal = normalize((obinvmat * (viewinvmat * vec4(N, 0.0))).xyz);
        uv = attr_uv;
        object = (obinvmat * (viewinvmat * vec4(I, 1.0))).xyz;
        camera = vec3(I.xy, -I.z);
 -      vec4 projvec = gl_ProjectionMatrix * vec4(I, 1.0);
 +      vec4 projvec = ProjectionMatrix * vec4(I, 1.0);
        window = vec3(mtex_2d_mapping(projvec.xyz / projvec.w).xy * camerafac.xy + camerafac.zw, 0.0);
  
 -      vec3 shade_I;
 -      shade_view(I, shade_I);
 +      vec3 shade_I = (ProjectionMatrix[3][3] == 0.0) ? normalize(I) : vec3(0.0, 0.0, -1.0);
        vec3 view_reflection = reflect(shade_I, normalize(N));
        reflection = (viewinvmat * vec4(view_reflection, 0.0)).xyz;
  }
@@@ -1830,18 -2951,13 +1830,18 @@@ void node_tex_coord_background
          out vec3 generated, out vec3 normal, out vec3 uv, out vec3 object,
          out vec3 camera, out vec3 window, out vec3 reflection)
  {
 -      vec4 v = (gl_ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
 -      vec4 co_homogenous = (gl_ProjectionMatrixInverse * v);
 +      vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0);
 +      vec4 co_homogenous = (ProjectionMatrixInverse * v);
  
        vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0);
  
        co = normalize(co);
 -      vec3 coords = (gl_ModelViewMatrixInverse * co).xyz;
 +
 +#if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
 +      vec3 coords = (ViewMatrixInverse * co).xyz;
 +#else
 +      vec3 coords = (ModelViewMatrixInverse * co).xyz;
 +#endif
  
        generated = coords;
        normal = -coords;
        object = coords;
  
        camera = vec3(co.xy, -co.z);
 -      window = (gl_ProjectionMatrix[3][3] == 0.0) ?
 +      window = (ProjectionMatrix[3][3] == 0.0) ?
                 vec3(mtex_2d_mapping(I).xy * camerafac.xy + camerafac.zw, 0.0) :
                 vec3(vec2(0.5) * camerafac.xy + camerafac.zw, 0.0);
  
        reflection = -coords;
  }
  
 +#if defined(WORLD_BACKGROUND) || (defined(PROBE_CAPTURE) && !defined(MESH_SHADER))
 +#define node_tex_coord node_tex_coord_background
 +#endif
 +
  /* textures */
  
  float calc_gradient(vec3 p, int gradient_type)
@@@ -1915,7 -3027,9 +1915,7 @@@ void node_tex_checker(vec3 co, vec4 col
        vec3 p = co * scale;
  
        /* Prevent precision issues on unit coordinates. */
 -      p.x = (p.x + 0.000001) * 0.999999;
 -      p.y = (p.y + 0.000001) * 0.999999;
 -      p.z = (p.z + 0.000001) * 0.999999;
 +      p = (p + 0.000001) * 0.999999;
  
        int xi = int(abs(floor(p.x)));
        int yi = int(abs(floor(p.y)));
        fac = check ? 1.0 : 0.0;
  }
  
 -#ifdef BIT_OPERATIONS
  vec2 calc_brick_texture(vec3 p, float mortar_size, float mortar_smooth, float bias,
                          float brick_width, float row_height,
                          float offset_amount, int offset_frequency,
                return vec2(tint, smoothstep(0.0, mortar_smooth, min_dist));
        }
  }
 -#endif
  
  void node_tex_brick(vec3 co,
                      vec4 color1, vec4 color2,
                      float squash_amount, float squash_frequency,
                      out vec4 color, out float fac)
  {
 -#ifdef BIT_OPERATIONS
        vec2 f2 = calc_brick_texture(co * scale,
                                     mortar_size, mortar_smooth, bias,
                                     brick_width, row_height,
        }
        color = mix(color1, mortar, f);
        fac = f;
 -#else
 -      color = vec4(1.0);
 -      fac = 1.0;
 -#endif
  }
  
  void node_tex_clouds(vec3 co, float size, out vec4 color, out float fac)
        fac = 1.0;
  }
  
 -void node_tex_environment_equirectangular(vec3 co, sampler2D ima, out vec4 color)
 +void node_tex_environment_equirectangular(vec3 co, float clamp_size, sampler2D ima, out vec3 uv)
  {
        vec3 nco = normalize(co);
 -      float u = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5;
 -      float v = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5;
 +      uv.x = -atan(nco.y, nco.x) / (2.0 * M_PI) + 0.5;
 +      uv.y = atan(nco.z, hypot(nco.x, nco.y)) / M_PI + 0.5;
  
 -      color = texture2D(ima, vec2(u, v));
 +      /* Fix pole bleeding */
 +      float half_height = clamp_size / float(textureSize(ima, 0).y);
 +      uv.y = clamp(uv.y, half_height, 1.0 - half_height);
 +      uv.z = 0.0;
  }
  
 -void node_tex_environment_mirror_ball(vec3 co, sampler2D ima, out vec4 color)
 +void node_tex_environment_mirror_ball(vec3 co, out vec3 uv)
  {
        vec3 nco = normalize(co);
 -
        nco.y -= 1.0;
  
        float div = 2.0 * sqrt(max(-0.5 * nco.y, 0.0));
 -      if (div > 0.0)
 -              nco /= div;
 +      nco /= max(1e-8, div);
  
 -      float u = 0.5 * (nco.x + 1.0);
 -      float v = 0.5 * (nco.z + 1.0);
 -
 -      color = texture2D(ima, vec2(u, v));
 +      uv = 0.5 * nco.xzz + 0.5;
  }
  
  void node_tex_environment_empty(vec3 co, out vec4 color)
        color = vec4(1.0, 0.0, 1.0, 1.0);
  }
  
 -void node_tex_image(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 +void node_tex_image_linear(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 +{
 +      color = texture(ima, co.xy);
 +      alpha = color.a;
 +}
 +
 +void node_tex_image_linear_no_mip(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 +{
 +      color = textureLod(ima, co.xy, 0.0);
 +      alpha = color.a;
 +}
 +
 +void node_tex_image_nearest(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 +{
 +      ivec2 pix = ivec2(fract(co.xy) * textureSize(ima, 0).xy);
 +      color = texelFetch(ima, pix, 0);
 +      alpha = color.a;
 +}
 +
 +/* @arg f: signed distance to texel center. */
 +void cubic_bspline_coefs(vec2 f, out vec2 w0, out vec2 w1, out vec2 w2, out vec2 w3)
 +{
 +      vec2 f2 = f * f;
 +      vec2 f3 = f2 * f;
 +      /* Bspline coefs (optimized) */
 +      w3 =  f3 / 6.0;
 +      w0 = -w3       + f2 * 0.5 - f * 0.5 + 1.0 / 6.0;
 +      w1 =  f3 * 0.5 - f2 * 1.0           + 2.0 / 3.0;
 +      w2 = 1.0 - w0 - w1 - w3;
 +}
 +
 +void node_tex_image_cubic_ex(vec3 co, sampler2D ima, float do_extend, out vec4 color, out float alpha)
  {
 -      color = texture2D(ima, co.xy);
 +      vec2 tex_size = vec2(textureSize(ima, 0).xy);
 +
 +      co.xy *= tex_size;
 +      /* texel center */
 +      vec2 tc = floor(co.xy - 0.5) + 0.5;
 +      vec2 w0, w1, w2, w3;
 +      cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3);
 +
 +#if 1 /* Optimized version using 4 filtered tap. */
 +      vec2 s0 = w0 + w1;
 +      vec2 s1 = w2 + w3;
 +
 +      vec2 f0 = w1 / (w0 + w1);
 +      vec2 f1 = w3 / (w2 + w3);
 +
 +      vec4 final_co;
 +      final_co.xy = tc - 1.0 + f0;
 +      final_co.zw = tc + 1.0 + f1;
 +
 +      if (do_extend == 1.0) {
 +              final_co = clamp(final_co, vec4(0.5), tex_size.xyxy - 0.5);
 +      }
 +      final_co /= tex_size.xyxy;
 +
 +      color  = textureLod(ima, final_co.xy, 0.0) * s0.x * s0.y;
 +      color += textureLod(ima, final_co.zy, 0.0) * s1.x * s0.y;
 +      color += textureLod(ima, final_co.xw, 0.0) * s0.x * s1.y;
 +      color += textureLod(ima, final_co.zw, 0.0) * s1.x * s1.y;
 +
 +#else /* Reference bruteforce 16 tap. */
 +      color  = texelFetch(ima, ivec2(tc + vec2(-1.0, -1.0)), 0) * w0.x * w0.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 0.0, -1.0)), 0) * w1.x * w0.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 1.0, -1.0)), 0) * w2.x * w0.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 2.0, -1.0)), 0) * w3.x * w0.y;
 +
 +      color += texelFetch(ima, ivec2(tc + vec2(-1.0, 0.0)), 0) * w0.x * w1.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 0.0, 0.0)), 0) * w1.x * w1.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 1.0, 0.0)), 0) * w2.x * w1.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 2.0, 0.0)), 0) * w3.x * w1.y;
 +
 +      color += texelFetch(ima, ivec2(tc + vec2(-1.0, 1.0)), 0) * w0.x * w2.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 0.0, 1.0)), 0) * w1.x * w2.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 1.0, 1.0)), 0) * w2.x * w2.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 2.0, 1.0)), 0) * w3.x * w2.y;
 +
 +      color += texelFetch(ima, ivec2(tc + vec2(-1.0, 2.0)), 0) * w0.x * w3.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 0.0, 2.0)), 0) * w1.x * w3.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 1.0, 2.0)), 0) * w2.x * w3.y;
 +      color += texelFetch(ima, ivec2(tc + vec2( 2.0, 2.0)), 0) * w3.x * w3.y;
 +#endif
 +
        alpha = color.a;
  }
  
 +void node_tex_image_cubic(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 +{
 +      node_tex_image_cubic_ex(co, ima, 0.0, color, alpha);
 +}
 +
 +void node_tex_image_cubic_extend(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 +{
 +      node_tex_image_cubic_ex(co, ima, 1.0, color, alpha);
 +}
 +
 +void node_tex_image_smart(vec3 co, sampler2D ima, out vec4 color, out float alpha)
 +{
 +      /* use cubic for now */
 +      node_tex_image_cubic_ex(co, ima, 0.0, color, alpha);
 +}
 +
 +void tex_box_sample_linear(vec3 texco,
 +                    vec3 N,
 +                    sampler2D ima,
 +                    out vec4 color1,
 +                    out vec4 color2,
 +                    out vec4 color3)
 +{
 +      /* X projection */
 +      vec2 uv = texco.yz;
 +      if (N.x < 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      color1 = texture(ima, uv);
 +      /* Y projection */
 +      uv = texco.xz;
 +      if (N.y > 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      color2 = texture(ima, uv);
 +      /* Z projection */
 +      uv = texco.yx;
 +      if (N.z > 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      color3 = texture(ima, uv);
 +}
 +
 +void tex_box_sample_nearest(vec3 texco,
 +                    vec3 N,
 +                    sampler2D ima,
 +                    out vec4 color1,
 +                    out vec4 color2,
 +                    out vec4 color3)
 +{
 +      /* X projection */
 +      vec2 uv = texco.yz;
 +      if (N.x < 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      ivec2 pix = ivec2(uv.xy * textureSize(ima, 0).xy);
 +      color1 = texelFetch(ima, pix, 0);
 +      /* Y projection */
 +      uv = texco.xz;
 +      if (N.y > 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      pix = ivec2(uv.xy * textureSize(ima, 0).xy);
 +      color2 = texelFetch(ima, pix, 0);
 +      /* Z projection */
 +      uv = texco.yx;
 +      if (N.z > 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      pix = ivec2(uv.xy * textureSize(ima, 0).xy);
 +      color3 = texelFetch(ima, pix, 0);
 +}
 +
 +void tex_box_sample_cubic(vec3 texco,
 +                    vec3 N,
 +                    sampler2D ima,
 +                    out vec4 color1,
 +                    out vec4 color2,
 +                    out vec4 color3)
 +{
 +      float alpha;
 +      /* X projection */
 +      vec2 uv = texco.yz;
 +      if (N.x < 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      node_tex_image_cubic_ex(uv.xyy, ima, 0.0, color1, alpha);
 +      /* Y projection */
 +      uv = texco.xz;
 +      if (N.y > 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      node_tex_image_cubic_ex(uv.xyy, ima, 0.0, color2, alpha);
 +      /* Z projection */
 +      uv = texco.yx;
 +      if (N.z > 0.0) {
 +              uv.x = 1.0 - uv.x;
 +      }
 +      node_tex_image_cubic_ex(uv.xyy, ima, 0.0, color3, alpha);
 +}
 +
 +void tex_box_sample_smart(vec3 texco,
 +                    vec3 N,
 +                    sampler2D ima,
 +                    out vec4 color1,
 +                    out vec4 color2,
 +                    out vec4 color3)
 +{
 +      tex_box_sample_cubic(texco, N, ima, color1, color2, color3);
 +}
 +
  void node_tex_image_box(vec3 texco,
                          vec3 N,
 +                        vec4 color1,
 +                        vec4 color2,
 +                        vec4 color3,
                          sampler2D ima,
                          float blend,
                          out vec4 color,
                          out float alpha)
  {
 -      vec3 signed_N = N;
 -
        /* project from direction vector to barycentric coordinates in triangles */
 -      N = vec3(abs(N.x), abs(N.y), abs(N.z));
 -      N /= (N.x + N.y + N.z);
 +      N = abs(N);
 +      N /= dot(N, vec3(1.0));
  
        /* basic idea is to think of this as a triangle, each corner representing
         * one of the 3 faces of the cube. in the corners we have single textures,
         * the Nxyz values are the barycentric coordinates in an equilateral
         * triangle, which in case of blending, in the middle has a smaller
         * equilateral triangle where 3 textures blend. this divides things into
 -       * 7 zones, with an if () test for each zone */
 +       * 7 zones, with an if () test for each zone
 +       * EDIT: Now there is only 4 if's. */
  
 -      vec3 weight = vec3(0.0, 0.0, 0.0);
 -      float limit = 0.5 * (1.0 + blend);
 +      float limit = 0.5 + 0.5 * blend;
  
 -      /* first test for corners with single texture */
 -      if (N.x > limit * (N.x + N.y) && N.x > limit * (N.x + N.z)) {
 -              weight.x = 1.0;
 -      }
 -      else if (N.y > limit * (N.x + N.y) && N.y > limit * (N.y + N.z)) {
 -              weight.y = 1.0;
 +      vec3 weight;
 +      weight = N.xyz / (N.xyx + N.yzz);
 +      weight = clamp((weight - 0.5 * (1.0 - blend)) / max(1e-8, blend), 0.0, 1.0);
 +
 +      /* test for mixes between two textures */
 +      if (N.z < (1.0 - limit) * (N.y + N.x)) {
 +              weight.z = 0.0;
 +              weight.y = 1.0 - weight.x;
        }
 -      else if (N.z > limit * (N.x + N.z) && N.z > limit * (N.y + N.z)) {
 -              weight.z = 1.0;
 +      else if (N.x < (1.0 - limit) * (N.y + N.z)) {
 +              weight.x = 0.0;
 +              weight.z = 1.0 - weight.y;
        }
 -      else if (blend > 0.0) {
 -              /* in case of blending, test for mixes between two textures */
 -              if (N.z < (1.0 - limit) * (N.y + N.x)) {
 -                      weight.x = N.x / (N.x + N.y);
 -                      weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
 -                      weight.y = 1.0 - weight.x;
 -              }
 -              else if (N.x < (1.0 - limit) * (N.y + N.z)) {
 -                      weight.y = N.y / (N.y + N.z);
 -                      weight.y = clamp((weight.y - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
 -                      weight.z = 1.0 - weight.y;
 -              }
 -              else if (N.y < (1.0 - limit) * (N.x + N.z)) {
 -                      weight.x = N.x / (N.x + N.z);
 -                      weight.x = clamp((weight.x - 0.5 * (1.0 - blend)) / blend, 0.0, 1.0);
 -                      weight.z = 1.0 - weight.x;
 -              }
 -              else {
 -                      /* last case, we have a mix between three */
 -                      weight.x = ((2.0 - limit) * N.x + (limit - 1.0)) / (2.0 * limit - 1.0);
 -                      weight.y = ((2.0 - limit) * N.y + (limit - 1.0)) / (2.0 * limit - 1.0);
 -                      weight.z = ((2.0 - limit) * N.z + (limit - 1.0)) / (2.0 * limit - 1.0);
 -              }
 +      else if (N.y < (1.0 - limit) * (N.x + N.z)) {
 +              weight.y = 0.0;
 +              weight.x = 1.0 - weight.z;
        }
        else {
 -              /* Desperate mode, no valid choice anyway, fallback to one side.*/
 -              weight.x = 1.0;
 -      }
 -      color = vec4(0);
 -      if (weight.x > 0.0) {
 -              vec2 uv = texco.yz;
 -              if(signed_N.x < 0.0) {
 -                      uv.x = 1.0 - uv.x;
 -              }
 -              color += weight.x * texture2D(ima, uv);
 -      }
 -      if (weight.y > 0.0) {
 -              vec2 uv = texco.xz;
 -              if(signed_N.y > 0.0) {
 -                      uv.x = 1.0 - uv.x;
 -              }
 -              color += weight.y * texture2D(ima, uv);
 -      }
 -      if (weight.z > 0.0) {
 -              vec2 uv = texco.yx;
 -              if(signed_N.z > 0.0) {
 -                      uv.x = 1.0 - uv.x;
 -              }
 -              color += weight.z * texture2D(ima, uv);
 +              /* last case, we have a mix between three */
 +              weight = ((2.0 - limit) * N + (limit - 1.0)) / max(1e-8, 2.0 * limit - 1.0);
        }
  
 +      color = weight.x * color1 + weight.y * color2 + weight.z * color3;
 +      alpha = color.a;
 +}
 +
 +void tex_clip_linear(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
 +{
 +      vec2 tex_size = vec2(textureSize(ima, 0).xy);
 +      vec2 minco = min(co.xy, 1.0 - co.xy);
 +      minco = clamp(minco * tex_size + 0.5, 0.0, 1.0);
 +      float fac = minco.x * minco.y;
 +
 +      color = mix(vec4(0.0), icolor, fac);
 +      alpha = color.a;
 +}
 +
 +void tex_clip_nearest(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
 +{
 +      vec4 minco = vec4(co.xy, 1.0 - co.xy);
 +      color = (any(lessThan(minco, vec4(0.0)))) ? vec4(0.0) : icolor;
 +      alpha = color.a;
 +}
 +
 +void tex_clip_cubic(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
 +{
 +      vec2 tex_size = vec2(textureSize(ima, 0).xy);
 +
 +      co.xy *= tex_size;
 +      /* texel center */
 +      vec2 tc = floor(co.xy - 0.5) + 0.5;
 +      vec2 w0, w1, w2, w3;
 +      cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3);
 +
 +      /* TODO Optimize this part. I'm sure there is a smarter way to do that.
 +       * Could do that when sampling? */
 +#define CLIP_CUBIC_SAMPLE(samp, size) (float(all(greaterThan(samp, vec2(-0.5)))) * float(all(lessThan(ivec2(samp), itex_size))))
 +      ivec2 itex_size = textureSize(ima, 0).xy;
 +      float fac;
 +      fac  = CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, -1.0), itex_size) * w0.x * w0.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 0.0, -1.0), itex_size) * w1.x * w0.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 1.0, -1.0), itex_size) * w2.x * w0.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 2.0, -1.0), itex_size) * w3.x * w0.y;
 +
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0,  0.0), itex_size) * w0.x * w1.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 0.0,  0.0), itex_size) * w1.x * w1.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 1.0,  0.0), itex_size) * w2.x * w1.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 2.0,  0.0), itex_size) * w3.x * w1.y;
 +
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0,  1.0), itex_size) * w0.x * w2.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 0.0,  1.0), itex_size) * w1.x * w2.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 1.0,  1.0), itex_size) * w2.x * w2.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 2.0,  1.0), itex_size) * w3.x * w2.y;
 +
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0,  2.0), itex_size) * w0.x * w3.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 0.0,  2.0), itex_size) * w1.x * w3.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 1.0,  2.0), itex_size) * w2.x * w3.y;
 +      fac += CLIP_CUBIC_SAMPLE(tc + vec2( 2.0,  2.0), itex_size) * w3.x * w3.y;
 +#undef CLIP_CUBIC_SAMPLE
 +
 +      color = mix(vec4(0.0), icolor, fac);
        alpha = color.a;
  }
  
 +void tex_clip_smart(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
 +{
 +      tex_clip_cubic(co, ima, icolor, color, alpha);
 +}
 +
  void node_tex_image_empty(vec3 co, out vec4 color, out float alpha)
  {
        color = vec4(0.0);
@@@ -2402,6 -3307,7 +2402,6 @@@ void node_tex_magic(vec3 co, float scal
        fac = (color.x + color.y + color.z) / 3.0;
  }
  
 -#ifdef BIT_OPERATIONS
  float noise_fade(float t)
  {
        return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
@@@ -2476,9 -3382,10 +2476,9 @@@ float noise_turbulence(vec3 p, float oc
        float fscale = 1.0;
        float amp = 1.0;
        float sum = 0.0;
 -      int i, n;
        octaves = clamp(octaves, 0.0, 16.0);
 -      n = int(octaves);
 -      for (i = 0; i <= n; i++) {
 +      int n = int(octaves);
 +      for (int i = 0; i <= n; i++) {
                float t = noise(fscale * p);
                if (hard != 0) {
                        t = abs(2.0 * t - 1.0);
                fscale *= 2.0;
        }
        float rmd = octaves - floor(octaves);
 -      if  (rmd != 0.0) {
 +      if (rmd != 0.0) {
                float t = noise(fscale * p);
                if (hard != 0) {
                        t = abs(2.0 * t - 1.0);
                return sum;
        }
  }
 -#endif  // BIT_OPERATIONS
  
  void node_tex_noise(vec3 co, float scale, float detail, float distortion, out vec4 color, out float fac)
  {
 -#ifdef BIT_OPERATIONS
        vec3 p = co * scale;
        int hard = 0;
        if (distortion != 0.0) {
                     noise_turbulence(vec3(p.y, p.x, p.z), detail, hard),
                     noise_turbulence(vec3(p.y, p.z, p.x), detail, hard),
                     1);
 -#else  // BIT_OPERATIONS
 -      color = vec4(1.0);
 -      fac = 1.0;
 -#endif  // BIT_OPERATIONS
  }
  
 -
 -#ifdef BIT_OPERATIONS
 -
  /* Musgrave fBm
   *
   * H: fractal increment parameter
@@@ -2538,8 -3454,9 +2538,8 @@@ float noise_musgrave_fBm(vec3 p, float 
        float value = 0.0;
        float pwr = 1.0;
        float pwHL = pow(lacunarity, -H);
 -      int i;
  
 -      for (i = 0; i < int(octaves); i++) {
 +      for (int i = 0; i < int(octaves); i++) {
                value += snoise(p) * pwr;
                pwr *= pwHL;
                p *= lacunarity;
@@@ -2565,8 -3482,9 +2565,8 @@@ float noise_musgrave_multi_fractal(vec
        float value = 1.0;
        float pwr = 1.0;
        float pwHL = pow(lacunarity, -H);
 -      int i;
  
 -      for (i = 0; i < int(octaves); i++) {
 +      for (int i = 0; i < int(octaves); i++) {
                value *= (pwr * snoise(p) + 1.0);
                pwr *= pwHL;
                p *= lacunarity;
@@@ -2592,12 -3510,13 +2592,12 @@@ float noise_musgrave_hetero_terrain(vec
        float value, increment, rmd;
        float pwHL = pow(lacunarity, -H);
        float pwr = pwHL;
 -      int i;
  
        /* first unscaled octave of function; later octaves are scaled */
        value = offset + snoise(p);
        p *= lacunarity;
  
 -      for (i = 1; i < int(octaves); i++) {
 +      for (int i = 1; i < int(octaves); i++) {
                increment = (snoise(p) + offset) * pwr * value;
                value += increment;
                pwr *= pwHL;
@@@ -2626,12 -3545,13 +2626,12 @@@ float noise_musgrave_hybrid_multi_fract
        float result, signal, weight, rmd;
        float pwHL = pow(lacunarity, -H);
        float pwr = pwHL;
 -      int i;
  
        result = snoise(p) + offset;
        weight = gain * result;
        p *= lacunarity;
  
 -      for (i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
 +      for (int i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
                if (weight > 1.0)
                        weight = 1.0;
  
@@@ -2662,13 -3582,14 +2662,13 @@@ float noise_musgrave_ridged_multi_fract
        float result, signal, weight;
        float pwHL = pow(lacunarity, -H);
        float pwr = pwHL;
 -      int i;
  
        signal = offset - abs(snoise(p));
        signal *= signal;
        result = signal;
        weight = 1.0;
  
 -      for (i = 1; i < int(octaves); i++) {
 +      for (int i = 1; i < int(octaves); i++) {
                p *= lacunarity;
                weight = clamp(signal * gain, 0.0, 1.0);
                signal = offset - abs(snoise(p));
@@@ -2690,18 -3611,19 +2690,18 @@@ float svm_musgrave(int type
                     float gain,
                     vec3 p)
  {
 -      if (type == 0 /*NODE_MUSGRAVE_MULTIFRACTAL*/)
 +      if (type == 0 /* NODE_MUSGRAVE_MULTIFRACTAL */)
                return intensity * noise_musgrave_multi_fractal(p, dimension, lacunarity, octaves);
 -      else if (type == 1 /*NODE_MUSGRAVE_FBM*/)
 +      else if (type == 1 /* NODE_MUSGRAVE_FBM */)
                return intensity * noise_musgrave_fBm(p, dimension, lacunarity, octaves);
 -      else if (type == 2 /*NODE_MUSGRAVE_HYBRID_MULTIFRACTAL*/)
 +      else if (type == 2 /* NODE_MUSGRAVE_HYBRID_MULTIFRACTAL */)
                return intensity * noise_musgrave_hybrid_multi_fractal(p, dimension, lacunarity, octaves, offset, gain);
 -      else if (type == 3 /*NODE_MUSGRAVE_RIDGED_MULTIFRACTAL*/)
 +      else if (type == 3 /* NODE_MUSGRAVE_RIDGED_MULTIFRACTAL */)
                return intensity * noise_musgrave_ridged_multi_fractal(p, dimension, lacunarity, octaves, offset, gain);
 -      else if (type == 4 /*NODE_MUSGRAVE_HETERO_TERRAIN*/)
 +      else if (type == 4 /* NODE_MUSGRAVE_HETERO_TERRAIN */)
                return intensity * noise_musgrave_hetero_terrain(p, dimension, lacunarity, octaves, offset);
        return 0.0;
  }
 -#endif  // #ifdef BIT_OPERATIONS
  
  void node_tex_musgrave(vec3 co,
                         float scale,
                         out vec4 color,
                         out float fac)
  {
 -#ifdef BIT_OPERATIONS
        fac = svm_musgrave(int(type),
                           dimension,
                           lacunarity,
                           1.0,
                           gain,
                           co * scale);
 -#else
 -      fac = 1.0;
 -#endif
  
        color = vec4(fac, fac, fac, 1.0);
  }
@@@ -2731,8 -3657,9 +2731,8 @@@ void node_tex_sky(vec3 co, out vec4 col
        color = vec4(1.0);
  }
  
 -void node_tex_voronoi(vec3 co, float scale, float exponent, float coloring, out vec4 color, out float fac)
 +void node_tex_voronoi(vec3 co, float scale, float exponent, float coloring, float metric, float feature, out vec4 color, out float fac)
  {
 -#ifdef BIT_OPERATIONS
        vec3 p = co * scale;
        int xx, yy, zz, xi, yi, zi;
        float da[4];
                                vec3 ip = vec3(xx, yy, zz);
                                vec3 vp = cellnoise_color(ip);
                                vec3 pd = p - (vp + ip);
 -                              float d = dot(pd, pd);
 +
 +                              float d = 0.0;
 +                              if (metric == 0.0) { /* SHD_VORONOI_DISTANCE 0 */
 +                                      d = dot(pd, pd);
 +                              }
 +                              else if (metric == 1.0) { /* SHD_VORONOI_MANHATTAN 1 */
 +                                      d = abs(pd[0]) + abs(pd[1]) + abs(pd[2]);
 +                              }
 +                              else if (metric == 2.0) { /* SHD_VORONOI_CHEBYCHEV 2 */
 +                                      d = max(abs(pd[0]), max(abs(pd[1]), abs(pd[2])));
 +                              }
 +                              else if (metric == 3.0) { /* SHD_VORONOI_MINKOWSKI 3 */
 +                                      d = pow(pow(abs(pd[0]), exponent) + pow(abs(pd[1]), exponent) + pow(abs(pd[2]), exponent), 1.0/exponent);
 +                              }
 +
                                vp += vec3(xx, yy, zz);
                                if (d < da[0]) {
                                        da[3] = da[2];
        }
  
        if (coloring == 0.0) {
 -              fac = abs(da[0]);
 -              color = vec4(fac, fac, fac, 1);
 +              /* Intensity output */
 +              if (feature == 0.0) { /* F1 */
 +                      fac = abs(da[0]);
 +              }
 +              else if (feature == 1.0) { /* F2 */
 +                      fac = abs(da[1]);
 +              }
 +              else if (feature == 2.0) { /* F3 */
 +                      fac = abs(da[2]);
 +              }
 +              else if (feature == 3.0) { /* F4 */
 +                      fac = abs(da[3]);
 +              }
 +              else if (feature == 4.0) { /* F2F1 */
 +                      fac = abs(da[1] - da[0]);
 +              }
 +              color = vec4(fac, fac, fac, 1.0);
        }
        else {
 -              color = vec4(cellnoise_color(pa[0]), 1);
 +              /* Color output */
 +              vec3 col = vec3(fac, fac, fac);
 +              if (feature == 0.0) { /* F1 */
 +                      col = pa[0];
 +              }
 +              else if (feature == 1.0) { /* F2 */
 +                      col = pa[1];
 +              }
 +              else if (feature == 2.0) { /* F3 */
 +                      col = pa[2];
 +              }
 +              else if (feature == 3.0) { /* F4 */
 +                      col = pa[3];
 +              }
 +              else if (feature == 4.0) { /* F2F1 */
 +                      col = abs(pa[1] - pa[0]);
 +              }
 +
 +              color = vec4(cellnoise_color(col), 1.0);
                fac = (color.x + color.y + color.z) * (1.0 / 3.0);
        }
 -#else  // BIT_OPERATIONS
 -      color = vec4(1.0);
 -      fac = 1.0;
 -#endif  // BIT_OPERATIONS
  }
  
 -#ifdef BIT_OPERATIONS
  float calc_wave(vec3 p, float distortion, float detail, float detail_scale, int wave_type, int wave_profile)
  {
        float n;
                return (n < 0.0) ? n + 1.0 : n;
        }
  }
 -#endif  // BIT_OPERATIONS
  
  void node_tex_wave(
          vec3 co, float scale, float distortion, float detail, float detail_scale, float wave_type, float wave_profile,
          out vec4 color, out float fac)
  {
 -#ifdef BIT_OPERATIONS
        float f;
        f = calc_wave(co * scale, distortion, detail, detail_scale, int(wave_type), int(wave_profile));
  
        color = vec4(f, f, f, 1.0);
        fac = f;
 -#else  // BIT_OPERATIONS
 -      color = vec4(1.0);
 -      fac = 1;
 -#endif  // BIT_OPERATIONS
  }
  
  /* light path */
@@@ -2896,22 -3787,19 +2896,22 @@@ void node_light_path
        out float transparent_depth,
        out float transmission_depth)
  {
 -      is_camera_ray = 1.0;
 -      is_shadow_ray = 0.0;
 -      is_diffuse_ray = 0.0;
 -      is_glossy_ray = 0.0;
 -      is_singular_ray = 0.0;
 -      is_reflection_ray = 0.0;
 -      is_transmission_ray = 0.0;
 +      /* Supported. */
 +      is_camera_ray = (rayType == EEVEE_RAY_CAMERA) ? 1.0 : 0.0;
 +      is_shadow_ray = (rayType == EEVEE_RAY_SHADOW) ? 1.0 : 0.0;
 +      is_diffuse_ray = (rayType == EEVEE_RAY_DIFFUSE) ? 1.0 : 0.0;
 +      is_glossy_ray = (rayType == EEVEE_RAY_GLOSSY) ? 1.0 : 0.0;
 +      /* Kind of supported. */
 +      is_singular_ray = is_glossy_ray;
 +      is_reflection_ray = is_glossy_ray;
 +      is_transmission_ray = is_glossy_ray;
 +      ray_depth = rayDepth;
 +      diffuse_depth = (is_diffuse_ray == 1.0) ? rayDepth : 0.0;
 +      glossy_depth = (is_glossy_ray == 1.0) ? rayDepth : 0.0;
 +      transmission_depth = (is_transmission_ray == 1.0) ? glossy_depth : 0.0;
 +      /* Not supported. */
        ray_length = 1.0;
 -      ray_depth = 1.0;
 -      diffuse_depth = 1.0;
 -      glossy_depth = 1.0;
 -      transparent_depth = 1.0;
 -      transmission_depth = 1.0;
 +      transparent_depth = 0.0;
  }
  
  void node_light_falloff(float strength, float tsmooth, out float quadratic, out float linear, out float constant)
@@@ -2939,9 -3827,9 +2939,9 @@@ void node_normal_map(vec4 tangent, vec
  
  void node_bump(float strength, float dist, float height, vec3 N, vec3 surf_pos, float invert, out vec3 result)
  {
 -      if (invert != 0.0) {
 -              dist *= -1.0;
 -      }
 +      N = mat3(ViewMatrix) * normalize(N);
 +      dist *= invert;
 +
        vec3 dPdx = dFdx(surf_pos);
        vec3 dPdy = dFdy(surf_pos);
  
  
        /* Compute surface gradient and determinant. */
        float det = dot(dPdx, Rx);
 -      float absdet = abs(det);
  
        float dHdx = dFdx(height);
        float dHdy = dFdy(height);
  
        strength = max(strength, 0.0);
  
 -      result = normalize(absdet * N - dist * sign(det) * surfgrad);
 -      result = normalize(strength * result + (1.0 - strength) * N);
 +      result = normalize(abs(det) * N - dist * sign(det) * surfgrad);
 +      result = normalize(mix(N, result, strength));
 +
 +      result = mat3(ViewMatrixInverse) * result;
  }
  
  void node_bevel(float radius, vec3 N, out vec3 result)
        result = N;
  }
  
 +void node_hair_info(out float is_strand, out float intercept, out float thickness, out vec3 tangent, out float random)
 +{
 +#ifdef HAIR_SHADER
 +      is_strand = 1.0;
 +      intercept = hairTime;
 +      thickness = hairThickness;
 +      tangent = normalize(hairTangent);
 +      random = wang_hash_noise(uint(hairStrandID)); /* TODO: could be precomputed per strand instead. */
 +#else
 +      is_strand = 0.0;
 +      intercept = 0.0;
 +      thickness = 0.0;
 +      tangent = vec3(1.0);
 +      random = 0.0;
 +#endif
 +}
 +
  void node_displacement_object(float height, float midlevel, float scale, vec3 N, mat4 obmat, out vec3 result)
  {
        N = (vec4(N, 0.0) * obmat).xyz;
@@@ -3022,91 -3892,37 +3022,91 @@@ void node_vector_displacement_world(vec
  
  /* output */
  
 -void node_output_material(vec4 surface, vec4 volume, vec3 displacement, out vec4 result)
 +void node_output_material(Closure surface, Closure volume, vec3 displacement, out Closure result)
  {
 +#ifdef VOLUMETRICS
 +      result = volume;
 +#else
        result = surface;
 +#endif
  }
  
 -void node_output_world(vec4 surface, vec4 volume, out vec4 result)
 +uniform float backgroundAlpha;
 +
 +void node_output_world(Closure surface, Closure volume, out Closure result)
  {
 -      result = surface;
 +#ifndef VOLUMETRICS
 +      result.radiance = surface.radiance;
 +      result.opacity = backgroundAlpha;
 +#else
 +      result = volume;
 +#endif /* VOLUMETRICS */
  }
  
 -/* ********************** matcap style render ******************** */
 -
 -void material_preview_matcap(vec4 color, sampler2D ima, vec4 N, vec4 mask, out vec4 result)
 +#ifndef VOLUMETRICS
 +/* TODO : clean this ifdef mess */
 +/* EEVEE output */
 +void world_normals_get(out vec3 N)
  {
 -      vec3 normal;
 -      vec2 tex;
 -
 -#ifndef USE_OPENSUBDIV
 -      /* remap to 0.0 - 1.0 range. This is done because OpenGL 2.0 clamps colors
 -       * between shader stages and we want the full range of the normal */
 -      normal = vec3(2.0, 2.0, 2.0) * vec3(N.x, N.y, N.z) - vec3(1.0, 1.0, 1.0);
 -      if (normal.z < 0.0) {
 -              normal.z = 0.0;
 +#ifdef HAIR_SHADER
 +      vec3 B = normalize(cross(worldNormal, hairTangent));
 +      float cos_theta;
 +      if (hairThicknessRes == 1) {
 +              vec4 rand = texelFetch(utilTex, ivec3(ivec2(gl_FragCoord.xy) % LUT_SIZE, 2.0), 0);
 +              /* Random cosine normal distribution on the hair surface. */
 +              cos_theta = rand.x * 2.0 - 1.0;
        }
 -      normal = normalize(normal);
 +      else {
 +              /* Shade as a cylinder. */
 +              cos_theta = hairThickTime / hairThickness;
 +      }
 +      float sin_theta = sqrt(max(0.0, 1.0f - cos_theta*cos_theta));
 +      N = normalize(worldNormal * sin_theta + B * cos_theta);
  #else
 -      normal = inpt.v.normal;
 -      mask = vec4(1.0, 1.0, 1.0, 1.0);
 +      N = gl_FrontFacing ? worldNormal : -worldNormal;
  #endif
 +}
 +
 +void node_eevee_specular(
 +        vec4 diffuse, vec4 specular, float roughness, vec4 emissive, float transp, vec3 normal,
 +        float clearcoat, float clearcoat_roughness, vec3 clearcoat_normal,
 +        float occlusion, float ssr_id, out Closure result)
 +{
 +      vec3 out_diff, out_spec, ssr_spec;
 +      eevee_closure_default(normal, diffuse.rgb, specular.rgb, int(ssr_id), roughness, occlusion,
 +                            out_diff, out_spec, ssr_spec);
 +
 +      vec3 vN = normalize(mat3(ViewMatrix) * normal);
 +      result = CLOSURE_DEFAULT;
 +      result.radiance = out_diff * diffuse.rgb + out_spec + emissive.rgb;
 +      result.opacity = 1.0 - transp;
 +      result.ssr_data = vec4(ssr_spec, roughness);
 +      result.ssr_normal = normal_encode(vN, viewCameraVec);
 +      result.ssr_id = int(ssr_id);
 +}
 +
 +void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha)
 +{
 +      vec4 spec_accum = vec4(0.0);
 +      if (ssrToggle && cl.ssr_id == outputSsrId) {
 +              vec3 V = cameraVec;
 +              vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec);
 +              vec3 N = transform_direction(ViewMatrixInverse, vN);
 +              float roughness = cl.ssr_data.a;
 +              float roughnessSquared = max(1e-3, roughness * roughness);
 +              fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum);
 +      }
  
 -      tex.x = 0.5 + 0.49 * normal.x;
 -      tex.y = 0.5 + 0.49 * normal.y;
 -      result = texture2D(ima, tex) * mask;
 +      outalpha = cl.opacity;
 +      outcol = vec4((spec_accum.rgb * cl.ssr_data.rgb) + cl.radiance, 1.0);
 +
 +#   ifdef USE_SSS
 +#             ifdef USE_SSS_ALBEDO
 +      outcol.rgb += cl.sss_data.rgb * cl.sss_albedo;
 +#     else
 +      outcol.rgb += cl.sss_data.rgb;
 +#             endif
 +#     endif
  }
 +
 +#endif /* VOLUMETRICS */
@@@ -17,7 -17,8 +17,7 @@@
   * All rights reserved.
   */
  
 -/** \file blender/nodes/shader/nodes/node_shader_hueSatVal.c
 - *  \ingroup shdnodes
 +/** \file \ingroup shdnodes
   */
  
  
@@@ -31,11 -32,11 +31,11 @@@ static bNodeSocketTemplate sh_node_hue_
        {       SOCK_FLOAT, 1, N_("Value"),                     1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_NONE},
        {       SOCK_FLOAT, 1, N_("Fac"),                       1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
        {       SOCK_RGBA, 1, N_("Color"),                      0.8f, 0.8f, 0.8f, 1.0f},
 -      {       -1, 0, ""       }
 +      {       -1, 0, ""       },
  };
  static bNodeSocketTemplate sh_node_hue_sat_out[] = {
        {       SOCK_RGBA, 0, N_("Color")},
 -      {       -1, 0, ""       }
 +      {       -1, 0, ""       },
  };
  
  /* note: it would be possible to use CMP version for both nodes */
@@@ -46,7 -47,7 +46,7 @@@ static void do_hue_sat_fac(bNode *UNUSE
  
                rgb_to_hsv(in[0], in[1], in[2], hsv, hsv + 1, hsv + 2);
                hsv[0] = fmodf(hsv[0] + hue + 0.5f, 1.0f);
-               hsv[1] *= clamp_f(sat, 0.0f, 1.0f);
+               hsv[1] = clamp_f(hsv[1] * sat, 0.0f, 1.0f);
                hsv[2] *= val;
                hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col + 1, col + 2);
  
@@@ -72,9 -73,9 +72,9 @@@ static void node_shader_exec_hue_sat(vo
  }
  
  
 -static int gpu_shader_hue_sat(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
 +static int gpu_shader_hue_sat(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, GPUNodeStack *out)
  {
 -      return GPU_stack_link(mat, "hue_sat", in, out);
 +      return GPU_stack_link(mat, node, "hue_sat", in, out);
  }
  
  void register_node_type_sh_hue_sat(void)
@@@ -82,6 -83,7 +82,6 @@@
        static bNodeType ntype;
  
        sh_node_type_base(&ntype, SH_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0);
 -      node_type_compatibility(&ntype, NODE_OLD_SHADING | NODE_NEW_SHADING);
        node_type_socket_templates(&ntype, sh_node_hue_sat_in, sh_node_hue_sat_out);
        node_type_size_preset(&ntype, NODE_SIZE_MIDDLE);
        node_type_exec(&ntype, NULL, NULL, node_shader_exec_hue_sat);