     ao *= texture2D(uAO, vUV).r;
 #endif
 
-    //TODO: No kd? so not really energy conserving
-    //we could use disney brdf for irradiance map to compensate for that like in Frostbite
-#ifdef USE_NORMALS
+//TODO: No kd? so not really energy conserving
+//we could use disney brdf for irradiance map to compensate for that like in Frostbite
+
 #ifdef USE_REFLECTION_PROBES
-    float NdotV = saturate( dot( normalWorld, eyeDirWorld ) );
-    vec3 reflectance = EnvBRDFApprox( F0, roughness, NdotV );
-    vec3 irradianceColor = getIrradiance(eyeDirWorld, normalWorld);
-    vec3 reflectionColor = getPrefilteredReflection(eyeDirWorld, normalWorld, roughness);
-    vec3 kS = F0;
-    vec3 kD = vec3(1.0) - kS;
-    kD *= 1.0 - metallic;
-    indirectDiffuse = kD * baseColor * irradianceColor;
-    indirectSpecular = reflectionColor * reflectance;
-#endif
-
-    //lights
+  EvaluateLightProbe(data);
+#endif
+
+#if NUM_AMBIENT_LIGHTS > 0
+  for(int i=0; i<NUM_AMBIENT_LIGHTS; i++) {
+    AmbientLight light = uAmbientLights[i];
+    EvaluateAmbientLight(data, light, i);    
+  }
+#endif
 #if NUM_DIRECTIONAL_LIGHTS > 0
-    for(int i=0; i<NUM_DIRECTIONAL_LIGHTS; i++) {
-        DirectionalLight light = uDirectionalLights[i];
-
-        //shadows
-        vec4 lightViewPosition = light.viewMatrix * vec4(vPositionWorld, 1.0);
-        float lightDistView = -lightViewPosition.z;
-        vec4 lightDeviceCoordsPosition = light.projectionMatrix * lightViewPosition;
-        vec2 lightDeviceCoordsPositionNormalized = lightDeviceCoordsPosition.xy / lightDeviceCoordsPosition.w;
-        float lightDeviceCoordsZ = lightDeviceCoordsPosition.z / lightDeviceCoordsPosition.w;
-        vec2 lightUV = lightDeviceCoordsPositionNormalized.xy * 0.5 + 0.5;
-
-        float illuminated = 0.0;
-        if (i == 0) illuminated += directionalShadow(uDirectionalLightShadowMaps[0], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#if NUM_DIRECTIONAL_LIGHTS >= 2
-        if (i == 1) illuminated += directionalShadow(uDirectionalLightShadowMaps[1], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#endif
-#if NUM_DIRECTIONAL_LIGHTS >= 3
-        if (i == 2) illuminated += directionalShadow(uDirectionalLightShadowMaps[2], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#endif
-#if NUM_DIRECTIONAL_LIGHTS >= 4
-        if (i == 3) illuminated += directionalShadow(uDirectionalLightShadowMaps[3], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#endif
-#if NUM_DIRECTIONAL_LIGHTS >= 5
-        if (i == 4) illuminated += directionalShadow(uDirectionalLightShadowMaps[4], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#endif
-#if NUM_DIRECTIONAL_LIGHTS >= 6
-        if (i == 5) illuminated += directionalShadow(uDirectionalLightShadowMaps[5], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#endif
-#if NUM_DIRECTIONAL_LIGHTS >= 7
-        if (i == 6) illuminated += directionalShadow(uDirectionalLightShadowMaps[6], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#endif
-#if NUM_DIRECTIONAL_LIGHTS >= 8
-        if (i == 7) illuminated += directionalShadow(uDirectionalLightShadowMaps[7], light.shadowMapSize, lightUV, lightDistView - light.bias, light.near, light.far);
-#endif
-
-        if (illuminated > 0.0) {
-            vec3 L = normalize(-light.direction);
-            vec3 N = normalWorld;
-            vec3 H = normalize(V + L);
-            float NdotL = max(0.0, dot(N, L));
-            float HdotV = max(0.0, dot(H, V));
-            float NdotV = max(0.0, dot(N, V));
-
-            vec3 F = FresnelSchlick(HdotV, F0);
-
-            vec3 kS = F;
-            vec3 kD = vec3(1.0) - kS;
-
-            kD *= 1.0 - metallic;
-
-            float NDF = GGX(N, H, roughness * roughness); // TODO: roughtness^2 or not?
-
-            float G = GeometrySmith(N, V, L, roughness * roughness); // TODO: roughtness^2 or not?
-
-            vec3 nominator = NDF * G * F;
-            float denominator = 4.0 * NdotV * NdotL + 0.001;
-            vec3 brdf = nominator / denominator;
-
-            vec3 lightColor = decode(light.color, 3).rgb;
-            lightColor *= light.color.a; // intensity
-            vec3 finalLight = NdotL * lightColor * illuminated;
-
-            directDiffuse += kD * baseColor / PI * finalLight;
-            directSpecular += brdf * finalLight;
-        }
-    }
-#endif
-
-#if NUM_POINT_LIGHTS > 0
-    for(int i=0; i<NUM_POINT_LIGHTS; i++) {
-        PointLight light = uPointLights[i];
-
-        vec3 L = light.position - vPositionWorld;
-        float dist = length(L);
-        L /= dist;
-
-        float dotNL = max(0.0, dot(normalWorld, L));
-
-        // Based on UE4
-        float distanceRatio = clamp(1.0 - pow(dist/light.radius, 4.0), 0.0, 1.0);
-        float falloff = (distanceRatio * distanceRatio) / (dist * dist + 1.0);
-
-        vec3 lightColor = decode(light.color, 3).rgb;
-        lightColor *= light.color.a;
-        //TODO: specular light conservation
-        // directDiffuse += baseColor * dotNL * lightColor * falloff;
-        // directSpecular += directSpecularGGX(normalWorld, eyeDirWorld, L, roughness, F0) * light.color.rgb * falloff;
-
-        vec3 N = normalWorld;
-        vec3 H = normalize(V + L);
-        float NdotL = max(0.0, dot(N, L));
-        float HdotV = max(0.0, dot(H, V));
-        float NdotV = max(0.0, dot(N, V));
-
-        vec3 F = FresnelSchlick(HdotV, F0);
-
-        vec3 kS = F;
-        vec3 kD = vec3(1.0) - kS;
-
-        kD *= 1.0 - metallic;
-
-        float NDF = GGX(N, H, roughness);
-
-        float G = GeometrySmith(N, V, L, roughness * roughness);
-
-        vec3 nominator = NDF * G * F;
-        float denominator = 4.0 * NdotV * NdotL + 0.001;
-        vec3 brdf = nominator / denominator;
-
-        vec3 radiance = lightColor * falloff;
-
-        directDiffuse += kD * baseColor / PI * NdotL * radiance;
-        directSpecular += brdf * NdotL * radiance;
-    }
-#endif
-
-#if NUM_SPOT_LIGHTS > 0
-    for(int i=0; i<NUM_SPOT_LIGHTS; i++) {
-        SpotLight light = uSpotLights[i];
-
-        const float spotlightLinearAtt = 1.0;
-        float dist = distance(vPositionWorld, light.position);
-        vec3 vDir = (vPositionWorld - light.position) / dist;
-        float distAttenuation = 1.0/(dist * spotlightLinearAtt);
-
-        float fCosine = dot(light.direction, vDir);
-        float cutOff = cos(light.angle);
-
-        float fDif = 1.0 - cutOff;
-        float fFactor = clamp((fCosine - cutOff)/fDif, 0.0, 1.0);
-
-        fFactor = pow(fFactor, 2.0);
-
-        if (fCosine > cutOff) {
-          vec3 L = normalize(-light.direction);
-
-          float intensity = max(dot(normalWorld, L), 0.0);
-          //vec4 spec = vec4(0.0);
-          //if (intensity > 0.0) {
-          //  vec3 eye = vec3(0.0, 0.0, 1.0);
-          //  vec3 h = normalize(ecLightDir + eye);
-          //  float intSpec = max(dot(h, ecN), 0.0);
-          //  spec = vec4(1.0) * pow(intSpec, 128.0);
-          //}
-
-          const float distAttenuation = 1.0;
-          //return fFactor * distAttenuation * intensity * spotlightColor + spec;
-          directDiffuse += baseColor * fFactor * distAttenuation * intensity * light.color.rgb;
-
-        }
-
-        /*
-        // Based on UE4
-        float distanceRatio = clamp(1.0 - pow(dist/light.radius, 4.0), 0.0, 1.0);
-        float falloff = (distanceRatio * distanceRatio) / (dist * dist + 1.0);
-
-        vec3 lightColor = decode(light.color, 3).rgb;
-        lightColor *= light.color.a;
-        //TODO: specular light conservation
-        // directDiffuse += baseColor * dotNL * lightColor * falloff;
-        // directSpecular += directSpecularGGX(normalWorld, eyeDirWorld, L, roughness, F0) * light.color.rgb * falloff;
-
-        vec3 N = normalWorld;
-        vec3 H = normalize(V + L);
-        float NdotL = max(0.0, dot(N, L));
-        float HdotV = max(0.0, dot(H, V));
-        float NdotV = max(0.0, dot(N, V));
-
-        vec3 F = FresnelSchlick(HdotV, F0);
-
-        vec3 kS = F;
-        vec3 kD = vec3(1.0) - kS;
-
-        kD *= 1.0 - metallic;
-
-        float NDF = GGX(N, H, roughness);
-
-        float G = GeometrySmith(N, V, L, roughness);
-
-        vec3 nominator = NDF * G * F;
-        float denominator = 4.0 * NdotV * NdotL + 0.001;
-        vec3 brdf = nominator / denominator;
-
-        vec3 radiance = lightColor * falloff;
-
-        directDiffuse += kD * baseColor / PI * NdotL * radiance;
-        directSpecular += brdf * NdotL * radiance;
-        */
-    }
-#endif
-
-    vec3 indirectArea = vec3(0.0);
-
-#if NUM_AREA_LIGHTS > 0
-    for(int i=0; i<NUM_AREA_LIGHTS; i++) {
-        AreaLight light = uAreaLights[i];
-
-        // TODO: kD, kS
-        //if (length(emissiveColor) == 0.0) {
-            indirectArea += evalAreaLight(light, vPositionWorld, normalWorld, baseColor, specularColor, roughness); //TEMP: fix roughness
-            //indirectArea = evalAreaLight(light, vPositionWorld, normalWorld,roughness); //TEMP: fix roughness
-            //indirectArea = evalAreaLight(light, vPositionView, (uNormalMatrix*normalWorld).xyz, diffuseColor, specularColor, roughness); //TEMP: fix roughness
-        //}
-    }
-#endif
-    vec3 color = emissiveColor + ao * indirectDiffuse + ao * indirectSpecular + directDiffuse + directSpecular + indirectArea;
-    // color = vec3(metallic);
-#else  // USE_NORMALS
-    vec3 color = ao * baseColor;
-#endif // USE_NORMALS
-    gl_FragData[0] = encode(vec4(color, 1.0), uOutputEncoding);
-    gl_FragData[1] = encode(vec4(emissiveColor, 1.0), uOutputEncoding);
-}
+  for(int i=0; i<NUM_DIRECTIONAL_LIGHTS; i++) {
+    DirectionalLight light = uDirectionalLights[i];
+    EvaluateDirectionalLight(data, light, i);    
+  }
+#endif
+#if NUM_POINT_LIGHTS > 0  
+  for(int i=0; i<NUM_POINT_LIGHTS; i++) {
+    PointLight light = uPointLights[i];
+    EvaluatePointLight(data, light, i);   
+  }    
+#endif  
+#if NUM_SPOT_LIGHTS > 0  
+  for(int i=0; i<NUM_SPOT_LIGHTS; i++) {
+    SpotLight light = uSpotLights[i];
+    EvaluateSpotLight(data, light, i);   
+  }    
+#endif 
+#if NUM_AREA_LIGHTS > 0  
+  for(int i=0; i<NUM_AREA_LIGHTS; i++) {
+    AreaLight light = uAreaLights[i];
+    EvaluateAreaLight(data, light, i);   
+  }    
+#endif   
+  vec3 color = data.emissiveColor + ao * data.indirectDiffuse + ao * data.indirectSpecular + data.directDiffuse + data.directSpecular;  
+    
+    
+    
+    
+    
+    
+   
+   
+   
+   
+#endif // USE_USE_UNLIT_WORKFLOW
+  //gl_FragData[0] = encode(vec4(color, 1.0), uOutputEncoding);
+  gl_FragData[0] = encode(vec4(color, 1.0), uOutputEncoding);
+  gl_FragData[1] = encode(vec4(data.emissiveColor, 1.0), uOutputEncoding);
+  //gl_FragData[0] = vec4(toGamma(color), 1.0);
+  #ifdef USE_BLEND
+  gl_FragData[0].a = data.opacity;
+  #endif
+}

varying vec3 vPositionWorld;
varying vec3 vPositionView;

#ifdef USE_VERTEX_COLORS
varying vec4 vColor;
#endif
#ifdef USE_INSTANCED_COLOR
varying vec4 vColor;
#endif


uniform int uOutputEncoding;

#define PI 3.14159265359
          

vec2 envMapOctahedral(vec3 dir) {
  dir /= dot(vec3(1.0), abs(dir));
  if (dir.y < 0.0) {
    dir.xy = vec2(1.0 - abs(dir.zx)) * sign(dir.xz);
  }
  else {
    dir.xy = dir.xz;
  }
  dir.xy = dir.xy * 0.5;
  dir.xy += 0.5; // move to center
  // dir.xy = (dir.xy * 64.0 + 1.0) / 66.0;
  return dir.xy;
}

vec2 envMapOctahedral(vec3 dir, float textureSize) {
  dir /= dot(vec3(1.0), abs(dir));
  if (dir.y < 0.0) {
    dir.xy = vec2(1.0 - abs(dir.zx)) * sign(dir.xz);
  }
  else {
    dir.xy = dir.xz;
  }
  dir.xy = dir.xy * 0.5;
  dir.xy += 0.5; // move to center

  // center on texels
  dir.xy += 0.5 / textureSize;
  dir.xy /= textureSize / (textureSize - 1.0);

  return dir.xy;
}

vec2 envMapOctahedral(vec3 dir, float mipmapLevel, float roughnessLevel) {
  float width = 2048.0;
  float maxLevel = 11.0; // this should come from log of size
  float levelSizeInPixels = pow(2.0, 1.0 + mipmapLevel + roughnessLevel);
  float levelSize = max(64.0, width / levelSizeInPixels);
  float roughnessLevelWidth = width / pow(2.0, 1.0 + roughnessLevel);
  float vOffset = (width - pow(2.0, maxLevel - roughnessLevel));
  float hOffset = 2.0 * roughnessLevelWidth - pow(2.0, log2(2.0 * roughnessLevelWidth) - mipmapLevel);
  vec2 uv = envMapOctahedral(dir, levelSize);
  uv *= levelSize;

  return (uv + vec2(hOffset, vOffset)) / width;
}
    

#define LINEAR 1
#define GAMMA 2
#define SRGB 3
#define RGBM 4

const float gamma_1 = 2.2;

float toLinear(float v) {
  return pow(v, gamma_1);
}

vec2 toLinear(vec2 v) {
  return pow(v, vec2(gamma_1));
}

vec3 toLinear(vec3 v) {
  return pow(v, vec3(gamma_1));
}

vec4 toLinear(vec4 v) {
  return vec4(toLinear(v.rgb), v.a);
}

// source http://webglinsights.github.io/downloads/WebGL-Insights-Chapter-16.pdf
vec3 decodeRGBM (vec4 rgbm) {
  vec3 r = rgbm.rgb * (7.0 * rgbm.a);
  return r * r;
}

vec4 decode(vec4 pixel, int encoding) {
  if (encoding == LINEAR) return pixel;
  if (encoding == GAMMA) return toLinear(pixel);
  if (encoding == SRGB) return toLinear(pixel);
  if (encoding == RGBM) return vec4(decodeRGBM(pixel), 1.0);
  return pixel;
}

#define LINEAR 1
#define GAMMA 2
#define SRGB 3
#define RGBM 4

const float gamma_0 = 2.2;

float toGamma(float v) {
  return pow(v, 1.0 / gamma_0);
}

vec2 toGamma(vec2 v) {
  return pow(v, vec2(1.0 / gamma_0));
}

vec3 toGamma(vec3 v) {
  return pow(v, vec3(1.0 / gamma_0));
}

vec4 toGamma(vec4 v) {
  return vec4(toGamma(v.rgb), v.a);
}

// source http://webglinsights.github.io/downloads/WebGL-Insights-Chapter-16.pdf
vec4 encodeRGBM (vec3 rgb_0) {
  vec4 r;
  r.xyz = (1.0 / 7.0) * sqrt(rgb_0);
  r.a = max(max(r.x, r.y), r.z);
  r.a = clamp(r.a, 1.0 / 255.0, 1.0);
  r.a = ceil(r.a * 255.0) / 255.0;
  r.xyz /= r.a;
  return r;
}

vec4 encode(vec4 pixel, int encoding) {
  if (encoding == LINEAR) return pixel;
  if (encoding == GAMMA) return toGamma(pixel);
  if (encoding == SRGB) return toGamma(pixel);
  if (encoding == RGBM) return encodeRGBM(pixel.rgb);
  return pixel;
}
    
    
struct PBRData {
  mat4 inverseViewMatrix;
  vec2 texCoord0;
  vec3 normalView;
  vec3 positionWorld;
  vec3 positionView;
  vec3 eyeDirView;
  vec3 eyeDirWorld;
  vec3 normalWorld; //N, world space
  vec3 viewWorld; //V, view vector from position to camera, world space
  vec3 lightWorld; //L, light vector from position to light, world space
  float NdotV;
  float NdotL;
  float NdotH;
  float LdotH;
  float HdotV;
  vec3 baseColor;
  vec3 emissiveColor;
  float opacity;
  float roughness;    // roughness value, as authored by the model creator (input to shader)
  float metallic;              // metallic value at the surface
  float alphaRoughness;         // roughness mapped to a more linear change in the roughness (proposed by [2])
  vec3 diffuseColor;            // color contribution from diffuse lighting
  vec3 specularColor;           // color contribution from specular lighting
  vec3 indirectDiffuse;  // contribution from IBL light probe
  vec3 indirectSpecular; // contribution from IBL light probe
  vec3 directDiffuse;  // contribution from light sources
  vec3 directSpecular; // contribution from light sources
  float clearCoat;
  float clearCoatRoughness;
};

#ifndef USE_USE_UNLIT_WORKFLOW     


//fron depth buf normalized z to linear (eye space) z
//http://stackoverflow.com/questions/6652253/getting-the-true-z-value-from-the-depth-buffer
float ndcDepthToEyeSpaceProj(float ndcDepth, float near, float far) {
