playcanvas 1.47.1 → 1.47.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * PlayCanvas Engine v1.47.1 revision 40ca6f004
3
+ * PlayCanvas Engine v1.47.2 revision b336e2bc9
4
4
  * Copyright 2011-2021 PlayCanvas Ltd. All rights reserved.
5
5
  */
6
6
  (function (global, factory) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * PlayCanvas Engine v1.47.1 revision 40ca6f004 (DEBUG PROFILER)
3
+ * PlayCanvas Engine v1.47.2 revision b336e2bc9 (DEBUG PROFILER)
4
4
  * Copyright 2011-2021 PlayCanvas Ltd. All rights reserved.
5
5
  */
6
6
  (function (global, factory) {
@@ -634,8 +634,8 @@
634
634
  return result;
635
635
  }();
636
636
 
637
- var version = "1.47.1";
638
- var revision = "40ca6f004";
637
+ var version = "1.47.2";
638
+ var revision = "b336e2bc9";
639
639
  var config = {};
640
640
  var common = {};
641
641
  var apps = {};
@@ -5984,7 +5984,7 @@
5984
5984
 
5985
5985
  var clusteredLightShadowsPS = "// Clustered Omni Sampling using atlas\n\n#ifdef GL2\n\nfloat getShadowOmniClusteredSingleSample(sampler2DShadow shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\treturn texture(shadowMap, vec3(uv, shadowZ));\n}\n\n\nfloat getShadowOmniClusteredPCF3x3(sampler2DShadow shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\tdShadowCoord = vec3(uv, shadowZ);\n\treturn getShadowPCF3x3(shadowMap, shadowParams.xyz);\n}\n\n#else\n\nfloat getShadowOmniClusteredSingleSample(sampler2D shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\n\t// no filter shadow sampling\n\tfloat depth = unpackFloat(texture2D(shadowMap, uv));\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\treturn depth > shadowZ ? 1.0 : 0.0;\n}\n\nfloat getShadowOmniClusteredPCF3x3(sampler2D shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\n\t// pcf3\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\tdShadowCoord = vec3(uv, shadowZ);\n\treturn getShadowPCF3x3(shadowMap, shadowParams.xyz);\n}\n\n#endif\n";
5986
5986
 
5987
- var clusteredLightPS = "uniform sampler2D clusterWorldTexture;\nuniform sampler2D lightsTexture8;\nuniform highp sampler2D lightsTextureFloat;\n\n#ifdef GL2\n\t// TODO: when VSM shadow is supported, it needs to use sampler2D in webgl2\n\tuniform sampler2DShadow shadowAtlasTexture;\n#else\n\tuniform sampler2D shadowAtlasTexture;\n#endif\n\nuniform sampler2D cookieAtlasTexture;\n\nuniform float clusterPixelsPerCell;\nuniform vec3 clusterCellsCountByBoundsSize;\nuniform vec4 lightsTextureInvSize;\nuniform vec3 clusterTextureSize;\nuniform vec3 clusterBoundsMin;\nuniform vec3 clusterBoundsDelta;\nuniform vec3 clusterCellsDot;\nuniform vec3 clusterCellsMax;\nuniform vec2 clusterCompressionLimit0;\nuniform vec2 shadowAtlasParams;\n\n// structure storing light properties of a clustered light\nstruct ClusterLightData {\n\n\t// v coordinate to look up the light textures\n\tfloat lightV;\n\n\t// true if spot light, false for omni light\n\tbool isSpot;\n\n\t// area light shape\n\tfloat shape;\n\n\t// light follow mode\n\tfloat falloffMode;\n\n\t// true if the light is shadow casting\n\tbool castShadows;\n\n\t// shadow bias values\n\tfloat shadowBias;\n\tfloat shadowNormalBias;\n\n\t// shadow (spot light only) / cookie projection matrix\n\tmat4 lightProjectionMatrix;\n\n\t// world space position\n\tvec3 position;\n\n\t// world space direction (spot light only)\n\tvec3 direction;\n\n\t// range of the light\n\tfloat range;\n\n\t// spot light inner and outer angle cosine\n\tfloat innerConeAngleCos;\n\tfloat outerConeAngleCos;\n\n\t// color\n\tvec3 color;\n\n\t// atlas viewport for omni light shadow and cookie (.xy is offset to the viewport slot, .z is size of the face in the atlas)\n\tvec3 omniAtlasViewport;\n\n\t// true if the light has a cookie texture\n\tbool isCookie;\n\n\t// true if cookie texture is rgb, false is using a single channel selectable by cookieChannelMask\n\tbool isCookieRgb;\n\n\t// invensity of the cookie\n\tfloat cookieIntensity;\n\n\t// channel mask - one of the channels has 1, the others are 0\n\tvec4 cookieChannelMask;\n};\n\nvec4 decodeClusterLowRange4Vec4(vec4 d0, vec4 d1, vec4 d2, vec4 d3) {\n\treturn vec4(\n\t\tbytes2floatRange4(d0, -2.0, 2.0),\n\t\tbytes2floatRange4(d1, -2.0, 2.0),\n\t\tbytes2floatRange4(d2, -2.0, 2.0),\n\t\tbytes2floatRange4(d3, -2.0, 2.0)\n\t);\n}\n\nvec4 sampleLightsTexture8(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTexture8, vec2(index * lightsTextureInvSize.z, clusterLightData.lightV));\n}\n\nvec4 sampleLightTextureF(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTextureFloat, vec2(index * lightsTextureInvSize.x, clusterLightData.lightV));\n}\n\nvoid decodeClusterLightCore(inout ClusterLightData clusterLightData, float lightIndex) {\n\n\t// read omni light properties\n\tclusterLightData.lightV = (lightIndex + 0.5) * lightsTextureInvSize.w;\n\n\t// shared data from 8bit texture\n\tvec4 lightInfo = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_FLAGS);\n\tclusterLightData.isSpot = lightInfo.x > 0.5;\n\tclusterLightData.shape = lightInfo.y;\n\tclusterLightData.falloffMode = lightInfo.z;\n\tclusterLightData.castShadows = lightInfo.w > 0.5;\n\n\t// color\n\tvec4 colorA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_A);\n\tvec4 colorB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_B);\n\tclusterLightData.color = vec3(bytes2float2(colorA.xy), bytes2float2(colorA.zw), bytes2float2(colorB.xy)) * clusterCompressionLimit0.y;\n\n\t// isCookie\n\tclusterLightData.isCookie = colorB.z > 0.5;\n\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\n\t\tvec4 lightPosRange = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_POSITION_RANGE);\n\t\tclusterLightData.position = lightPosRange.xyz;\n\t\tclusterLightData.range = lightPosRange.w;\n\n\t\t// spot light direction\n\t\tvec4 lightDir_Unused = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_SPOT_DIRECTION);\n\t\tclusterLightData.direction = lightDir_Unused.xyz;\n\n\t#else // 8bit\n\n\t\tvec4 encPosX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_X);\n\t\tvec4 encPosY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Y);\n\t\tvec4 encPosZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Z);\n\t\tclusterLightData.position = vec3(bytes2float4(encPosX), bytes2float4(encPosY), bytes2float4(encPosZ)) * clusterBoundsDelta + clusterBoundsMin;\n\n\t\tvec4 encRange = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_RANGE);\n\t\tclusterLightData.range = bytes2float4(encRange) * clusterCompressionLimit0.x;\n\n\t\t// spot light direction\n\t\tvec4 encDirX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_X);\n\t\tvec4 encDirY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Y);\n\t\tvec4 encDirZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Z);\n\t\tclusterLightData.direction = vec3(bytes2float4(encDirX), bytes2float4(encDirY), bytes2float4(encDirZ)) * 2.0 - 1.0;\n\n\t#endif\n}\n\nvoid decodeClusterLightSpot(inout ClusterLightData clusterLightData) {\n\n\t// spot light cos angles\n\tvec4 coneAngle = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_ANGLES);\n\tclusterLightData.innerConeAngleCos = bytes2float2(coneAngle.xy) * 2.0 - 1.0;\n\tclusterLightData.outerConeAngleCos = bytes2float2(coneAngle.zw) * 2.0 - 1.0;\n}\n\nvoid decodeClusterLightOmniAtlasViewport(inout ClusterLightData clusterLightData) {\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tclusterLightData.omniAtlasViewport = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0).xyz;\n\t#else\n\t\tvec4 viewportA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_A);\n\t\tvec4 viewportB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_B);\n\t\tclusterLightData.omniAtlasViewport = vec3(bytes2float2(viewportA.xy), bytes2float2(viewportA.zw), bytes2float2(viewportB.xy));\n\t#endif\n}\n\nvoid decodeClusterLightProjectionMatrixData(inout ClusterLightData clusterLightData) {\n\t\n\t// shadow matrix\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tvec4 m0 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0);\n\t\tvec4 m1 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_1);\n\t\tvec4 m2 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_2);\n\t\tvec4 m3 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_3);\n\t#else\n\t\tvec4 m00 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_00);\n\t\tvec4 m01 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_01);\n\t\tvec4 m02 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_02);\n\t\tvec4 m03 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_03);\n\t\tvec4 m0 = decodeClusterLowRange4Vec4(m00, m01, m02, m03);\n\n\t\tvec4 m10 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_10);\n\t\tvec4 m11 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_11);\n\t\tvec4 m12 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_12);\n\t\tvec4 m13 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_13);\n\t\tvec4 m1 = decodeClusterLowRange4Vec4(m10, m11, m12, m13);\n\n\t\tvec4 m20 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_20);\n\t\tvec4 m21 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_21);\n\t\tvec4 m22 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_22);\n\t\tvec4 m23 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_23);\n\t\tvec4 m2 = decodeClusterLowRange4Vec4(m20, m21, m22, m23);\n\n\t\tvec4 m30 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_30);\n\t\tvec4 m31 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_31);\n\t\tvec4 m32 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_32);\n\t\tvec4 m33 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_33);\n\t\tvec4 m3 = vec4(mantisaExponent2Float(m30), mantisaExponent2Float(m31), mantisaExponent2Float(m32), mantisaExponent2Float(m33));\n\t#endif\n\t\n\tclusterLightData.lightProjectionMatrix = mat4(m0, m1, m2, m3);\n}\n\nvoid decodeClusterLightShadowData(inout ClusterLightData clusterLightData) {\n\t\n\t// shadow biases\n\tvec4 biases = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SHADOW_BIAS);\n\tclusterLightData.shadowBias = bytes2floatRange2(biases.xy, -1.0, 20.0),\n\tclusterLightData.shadowNormalBias = bytes2float2(biases.zw);\n}\n\nvoid decodeClusterLightCookieData(inout ClusterLightData clusterLightData) {\n\n\tvec4 cookieA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_A);\n\tclusterLightData.cookieIntensity = cookieA.x;\n\tclusterLightData.isCookieRgb = cookieA.y > 0.5;\n\n\tclusterLightData.cookieChannelMask = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_B);\n}\n\nvoid evaluateLight(ClusterLightData light) {\n\n\tdAtten3 = vec3(1.0);\n\n\t// evaluate omni part of the light\n\tgetLightDirPoint(light.position);\n\tdAtten = getFalloffLinear(light.range);\n\tif (dAtten > 0.00001) {\n\n\t\tdAtten *= getLightDiffuse();\n\n\t\t// spot light falloff\n\t\tif (light.isSpot == true) {\n\t\t\tdecodeClusterLightSpot(light);\n\t\t\tdAtten *= getSpotEffect(light.direction, light.innerConeAngleCos, light.outerConeAngleCos);\n\t\t}\n\n\t\tif (dAtten > 0.00001) {\n\n\t\t\t// shadow / cookie\n\t\t\tif (light.castShadows == true || light.isCookie == true) {\n\n\t\t\t\t// shared shadow / cookie data depends on light type\n\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\tdecodeClusterLightProjectionMatrixData(light);\n\t\t\t\t} else {\n\t\t\t\t\tdecodeClusterLightOmniAtlasViewport(light);\n\t\t\t\t}\n\n\t\t\t\tfloat shadowTextureResolution = shadowAtlasParams.x;\n\t\t\t\tfloat shadowEdgePixels = shadowAtlasParams.y;\n\n\t\t\t\t// cookie\n\t\t\t\tif (light.isCookie == true) {\n\t\t\t\t\tdecodeClusterLightCookieData(light);\n\n\t\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\t\tdAtten3 = getCookie2DClustered(cookieAtlasTexture, light.lightProjectionMatrix, vPositionW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdAtten3 = getCookieCubeClustered(cookieAtlasTexture, dLightDirW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask, shadowTextureResolution, shadowEdgePixels, light.omniAtlasViewport);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// shadow\n\t\t\t\tif (light.castShadows== true) {\n\t\t\t\t\tdecodeClusterLightShadowData(light);\n\n\t\t\t\t\tvec4 shadowParams = vec4(shadowTextureResolution, light.shadowNormalBias, light.shadowBias, 1.0 / light.range);\n\n\t\t\t\t\tif (light.isSpot == true) {\n\n\t\t\t\t\t\t// spot shadow\n\t\t\t\t\t\tgetShadowCoordPerspZbufferNormalOffset(light.lightProjectionMatrix, shadowParams);\n\t\t\t\t\t\tdAtten *= getShadowSpotPCF3x3(shadowAtlasTexture, shadowParams);\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// omni shadow\n\t\t\t\t\t\tnormalOffsetPointShadow(shadowParams); // normalBias adjusted for distance\n\t\t\t\t\t\tdAtten *= getShadowOmniClusteredPCF3x3(shadowAtlasTexture, shadowParams, light.omniAtlasViewport, shadowEdgePixels, dLightDirW);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdDiffuseLight += dAtten * light.color * dAtten3;\n\t}\n}\n\nvoid evaluateClusterLight(float lightIndex) {\n\n\t// decode core light data from textures\n\tClusterLightData clusterLightData;\n\tdecodeClusterLightCore(clusterLightData, lightIndex);\n\n\t// evaluate light\n\tevaluateLight(clusterLightData);\n}\n\nvoid addClusteredLights() {\n\t// world space position to 3d integer cell cordinates in the cluster structure\n\tvec3 cellCoords = floor((vPositionW - clusterBoundsMin) * clusterCellsCountByBoundsSize);\n\n\t// no lighting when cell coordinate is out of range\n\tif (!(any(lessThan(cellCoords, vec3(0.0))) || any(greaterThanEqual(cellCoords, clusterCellsMax)))) {\n\n\t\t// cell index (mapping from 3d cell coordinates to linear memory)\n\t\tfloat cellIndex = dot(clusterCellsDot, cellCoords);\n\n\t\t// convert cell index to uv coordinates\n\t\tfloat clusterV = floor(cellIndex * clusterTextureSize.y);\n\t\tfloat clusterU = cellIndex - (clusterV * clusterTextureSize.x);\n\t\tclusterV = (clusterV + 0.5) * clusterTextureSize.z;\n\n\t\t// loop over maximum possible number of supported light cells\n\t\tconst float maxLightCells = 256.0 / 4.0; // 8 bit index, each stores 4 lights\n\t\tfor (float lightCellIndex = 0.5; lightCellIndex < maxLightCells; lightCellIndex++) {\n\n\t\t\tvec4 lightIndices = texture2D(clusterWorldTexture, vec2(clusterTextureSize.y * (clusterU + lightCellIndex), clusterV));\n\t\t\tvec4 indices = lightIndices * 255.0;\n\n\t\t\tif (indices.x <= 0.0)\n\t\t\t\tbreak;\n\n\t\t\tevaluateClusterLight(indices.x);\n\n\t\t\tif (indices.y <= 0.0)\n\t\t\t\tbreak;\n\n\t\t\tevaluateClusterLight(indices.y);\n\n\t\t\tif (indices.z <= 0.0)\n\t\t\t\tbreak;\n\n\t\t\tevaluateClusterLight(indices.z);\n\n\t\t\tif (indices.w <= 0.0)\n\t\t\t\tbreak;\n\n\t\t\tevaluateClusterLight(indices.w);\n\n\t\t\t// end of the cell array\n\t\t\tif (lightCellIndex > clusterPixelsPerCell) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n";
5987
+ var clusteredLightPS = "uniform sampler2D clusterWorldTexture;\nuniform sampler2D lightsTexture8;\nuniform highp sampler2D lightsTextureFloat;\n\n#ifdef GL2\n\t// TODO: when VSM shadow is supported, it needs to use sampler2D in webgl2\n\tuniform sampler2DShadow shadowAtlasTexture;\n#else\n\tuniform sampler2D shadowAtlasTexture;\n#endif\n\nuniform sampler2D cookieAtlasTexture;\n\nuniform float clusterPixelsPerCell;\nuniform vec3 clusterCellsCountByBoundsSize;\nuniform vec4 lightsTextureInvSize;\nuniform vec3 clusterTextureSize;\nuniform vec3 clusterBoundsMin;\nuniform vec3 clusterBoundsDelta;\nuniform vec3 clusterCellsDot;\nuniform vec3 clusterCellsMax;\nuniform vec2 clusterCompressionLimit0;\nuniform vec2 shadowAtlasParams;\n\n// structure storing light properties of a clustered light\nstruct ClusterLightData {\n\n\t// v coordinate to look up the light textures\n\tfloat lightV;\n\n\t// true if spot light, false for omni light\n\tbool isSpot;\n\n\t// area light shape\n\tfloat shape;\n\n\t// light follow mode\n\tfloat falloffMode;\n\n\t// true if the light is shadow casting\n\tbool castShadows;\n\n\t// shadow bias values\n\tfloat shadowBias;\n\tfloat shadowNormalBias;\n\n\t// shadow (spot light only) / cookie projection matrix\n\tmat4 lightProjectionMatrix;\n\n\t// world space position\n\tvec3 position;\n\n\t// world space direction (spot light only)\n\tvec3 direction;\n\n\t// range of the light\n\tfloat range;\n\n\t// spot light inner and outer angle cosine\n\tfloat innerConeAngleCos;\n\tfloat outerConeAngleCos;\n\n\t// color\n\tvec3 color;\n\n\t// atlas viewport for omni light shadow and cookie (.xy is offset to the viewport slot, .z is size of the face in the atlas)\n\tvec3 omniAtlasViewport;\n\n\t// true if the light has a cookie texture\n\tbool isCookie;\n\n\t// true if cookie texture is rgb, false is using a single channel selectable by cookieChannelMask\n\tbool isCookieRgb;\n\n\t// invensity of the cookie\n\tfloat cookieIntensity;\n\n\t// channel mask - one of the channels has 1, the others are 0\n\tvec4 cookieChannelMask;\n};\n\nvec4 decodeClusterLowRange4Vec4(vec4 d0, vec4 d1, vec4 d2, vec4 d3) {\n\treturn vec4(\n\t\tbytes2floatRange4(d0, -2.0, 2.0),\n\t\tbytes2floatRange4(d1, -2.0, 2.0),\n\t\tbytes2floatRange4(d2, -2.0, 2.0),\n\t\tbytes2floatRange4(d3, -2.0, 2.0)\n\t);\n}\n\nvec4 sampleLightsTexture8(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTexture8, vec2(index * lightsTextureInvSize.z, clusterLightData.lightV));\n}\n\nvec4 sampleLightTextureF(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTextureFloat, vec2(index * lightsTextureInvSize.x, clusterLightData.lightV));\n}\n\nvoid decodeClusterLightCore(inout ClusterLightData clusterLightData, float lightIndex) {\n\n\t// read omni light properties\n\tclusterLightData.lightV = (lightIndex + 0.5) * lightsTextureInvSize.w;\n\n\t// shared data from 8bit texture\n\tvec4 lightInfo = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_FLAGS);\n\tclusterLightData.isSpot = lightInfo.x > 0.5;\n\tclusterLightData.shape = lightInfo.y;\n\tclusterLightData.falloffMode = lightInfo.z;\n\tclusterLightData.castShadows = lightInfo.w > 0.5;\n\n\t// color\n\tvec4 colorA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_A);\n\tvec4 colorB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_B);\n\tclusterLightData.color = vec3(bytes2float2(colorA.xy), bytes2float2(colorA.zw), bytes2float2(colorB.xy)) * clusterCompressionLimit0.y;\n\n\t// isCookie\n\tclusterLightData.isCookie = colorB.z > 0.5;\n\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\n\t\tvec4 lightPosRange = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_POSITION_RANGE);\n\t\tclusterLightData.position = lightPosRange.xyz;\n\t\tclusterLightData.range = lightPosRange.w;\n\n\t\t// spot light direction\n\t\tvec4 lightDir_Unused = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_SPOT_DIRECTION);\n\t\tclusterLightData.direction = lightDir_Unused.xyz;\n\n\t#else // 8bit\n\n\t\tvec4 encPosX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_X);\n\t\tvec4 encPosY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Y);\n\t\tvec4 encPosZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Z);\n\t\tclusterLightData.position = vec3(bytes2float4(encPosX), bytes2float4(encPosY), bytes2float4(encPosZ)) * clusterBoundsDelta + clusterBoundsMin;\n\n\t\tvec4 encRange = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_RANGE);\n\t\tclusterLightData.range = bytes2float4(encRange) * clusterCompressionLimit0.x;\n\n\t\t// spot light direction\n\t\tvec4 encDirX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_X);\n\t\tvec4 encDirY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Y);\n\t\tvec4 encDirZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Z);\n\t\tclusterLightData.direction = vec3(bytes2float4(encDirX), bytes2float4(encDirY), bytes2float4(encDirZ)) * 2.0 - 1.0;\n\n\t#endif\n}\n\nvoid decodeClusterLightSpot(inout ClusterLightData clusterLightData) {\n\n\t// spot light cos angles\n\tvec4 coneAngle = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_ANGLES);\n\tclusterLightData.innerConeAngleCos = bytes2float2(coneAngle.xy) * 2.0 - 1.0;\n\tclusterLightData.outerConeAngleCos = bytes2float2(coneAngle.zw) * 2.0 - 1.0;\n}\n\nvoid decodeClusterLightOmniAtlasViewport(inout ClusterLightData clusterLightData) {\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tclusterLightData.omniAtlasViewport = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0).xyz;\n\t#else\n\t\tvec4 viewportA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_A);\n\t\tvec4 viewportB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_B);\n\t\tclusterLightData.omniAtlasViewport = vec3(bytes2float2(viewportA.xy), bytes2float2(viewportA.zw), bytes2float2(viewportB.xy));\n\t#endif\n}\n\nvoid decodeClusterLightProjectionMatrixData(inout ClusterLightData clusterLightData) {\n\t\n\t// shadow matrix\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tvec4 m0 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0);\n\t\tvec4 m1 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_1);\n\t\tvec4 m2 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_2);\n\t\tvec4 m3 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_3);\n\t#else\n\t\tvec4 m00 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_00);\n\t\tvec4 m01 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_01);\n\t\tvec4 m02 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_02);\n\t\tvec4 m03 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_03);\n\t\tvec4 m0 = decodeClusterLowRange4Vec4(m00, m01, m02, m03);\n\n\t\tvec4 m10 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_10);\n\t\tvec4 m11 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_11);\n\t\tvec4 m12 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_12);\n\t\tvec4 m13 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_13);\n\t\tvec4 m1 = decodeClusterLowRange4Vec4(m10, m11, m12, m13);\n\n\t\tvec4 m20 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_20);\n\t\tvec4 m21 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_21);\n\t\tvec4 m22 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_22);\n\t\tvec4 m23 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_23);\n\t\tvec4 m2 = decodeClusterLowRange4Vec4(m20, m21, m22, m23);\n\n\t\tvec4 m30 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_30);\n\t\tvec4 m31 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_31);\n\t\tvec4 m32 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_32);\n\t\tvec4 m33 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_33);\n\t\tvec4 m3 = vec4(mantisaExponent2Float(m30), mantisaExponent2Float(m31), mantisaExponent2Float(m32), mantisaExponent2Float(m33));\n\t#endif\n\t\n\tclusterLightData.lightProjectionMatrix = mat4(m0, m1, m2, m3);\n}\n\nvoid decodeClusterLightShadowData(inout ClusterLightData clusterLightData) {\n\t\n\t// shadow biases\n\tvec4 biases = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SHADOW_BIAS);\n\tclusterLightData.shadowBias = bytes2floatRange2(biases.xy, -1.0, 20.0),\n\tclusterLightData.shadowNormalBias = bytes2float2(biases.zw);\n}\n\nvoid decodeClusterLightCookieData(inout ClusterLightData clusterLightData) {\n\n\tvec4 cookieA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_A);\n\tclusterLightData.cookieIntensity = cookieA.x;\n\tclusterLightData.isCookieRgb = cookieA.y > 0.5;\n\n\tclusterLightData.cookieChannelMask = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_B);\n}\n\nvoid evaluateLight(ClusterLightData light) {\n\n\tdAtten3 = vec3(1.0);\n\n\t// evaluate omni part of the light\n\tgetLightDirPoint(light.position);\n\tdAtten = getFalloffLinear(light.range);\n\tif (dAtten > 0.00001) {\n\n\t\tdAtten *= getLightDiffuse();\n\n\t\t// spot light falloff\n\t\tif (light.isSpot == true) {\n\t\t\tdecodeClusterLightSpot(light);\n\t\t\tdAtten *= getSpotEffect(light.direction, light.innerConeAngleCos, light.outerConeAngleCos);\n\t\t}\n\n\t\tif (dAtten > 0.00001) {\n\n\t\t\t// shadow / cookie\n\t\t\tif (light.castShadows == true || light.isCookie == true) {\n\n\t\t\t\t// shared shadow / cookie data depends on light type\n\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\tdecodeClusterLightProjectionMatrixData(light);\n\t\t\t\t} else {\n\t\t\t\t\tdecodeClusterLightOmniAtlasViewport(light);\n\t\t\t\t}\n\n\t\t\t\tfloat shadowTextureResolution = shadowAtlasParams.x;\n\t\t\t\tfloat shadowEdgePixels = shadowAtlasParams.y;\n\n\t\t\t\t// cookie\n\t\t\t\tif (light.isCookie == true) {\n\t\t\t\t\tdecodeClusterLightCookieData(light);\n\n\t\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\t\tdAtten3 = getCookie2DClustered(cookieAtlasTexture, light.lightProjectionMatrix, vPositionW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdAtten3 = getCookieCubeClustered(cookieAtlasTexture, dLightDirW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask, shadowTextureResolution, shadowEdgePixels, light.omniAtlasViewport);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// shadow\n\t\t\t\tif (light.castShadows== true) {\n\t\t\t\t\tdecodeClusterLightShadowData(light);\n\n\t\t\t\t\tvec4 shadowParams = vec4(shadowTextureResolution, light.shadowNormalBias, light.shadowBias, 1.0 / light.range);\n\n\t\t\t\t\tif (light.isSpot == true) {\n\n\t\t\t\t\t\t// spot shadow\n\t\t\t\t\t\tgetShadowCoordPerspZbufferNormalOffset(light.lightProjectionMatrix, shadowParams);\n\t\t\t\t\t\tdAtten *= getShadowSpotPCF3x3(shadowAtlasTexture, shadowParams);\n\n\t\t\t\t\t} else {\n\n\t\t\t\t\t\t// omni shadow\n\t\t\t\t\t\tnormalOffsetPointShadow(shadowParams); // normalBias adjusted for distance\n\t\t\t\t\t\tdAtten *= getShadowOmniClusteredPCF3x3(shadowAtlasTexture, shadowParams, light.omniAtlasViewport, shadowEdgePixels, dLightDirW);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdDiffuseLight += dAtten * light.color * dAtten3;\n\t}\n}\n\nvoid evaluateClusterLight(float lightIndex) {\n\n\t// decode core light data from textures\n\tClusterLightData clusterLightData;\n\tdecodeClusterLightCore(clusterLightData, lightIndex);\n\n\t// evaluate light\n\tevaluateLight(clusterLightData);\n}\n\nconst vec4 channelSelector[4] = vec4[4] (\n\tvec4(1., 0., 0., 0.),\n\tvec4(0., 1., 0., 0.),\n\tvec4(0., 0., 1., 0.),\n\tvec4(0., 0., 0., 1.)\n);\n\nvoid addClusteredLights() {\n\t// world space position to 3d integer cell cordinates in the cluster structure\n\tvec3 cellCoords = floor((vPositionW - clusterBoundsMin) * clusterCellsCountByBoundsSize);\n\n\t// no lighting when cell coordinate is out of range\n\tif (!(any(lessThan(cellCoords, vec3(0.0))) || any(greaterThanEqual(cellCoords, clusterCellsMax)))) {\n\n\t\t// cell index (mapping from 3d cell coordinates to linear memory)\n\t\tfloat cellIndex = dot(clusterCellsDot, cellCoords);\n\n\t\t// convert cell index to uv coordinates\n\t\tfloat clusterV = floor(cellIndex * clusterTextureSize.y);\n\t\tfloat clusterU = cellIndex - (clusterV * clusterTextureSize.x);\n\t\tclusterV = (clusterV + 0.5) * clusterTextureSize.z;\n\n\t\t// loop over maximum possible number of supported light cells\n\t\tconst float maxLightCells = 256.0 / 4.0; // 8 bit index, each stores 4 lights\n\t\tfor (float lightCellIndex = 0.5; lightCellIndex < maxLightCells; lightCellIndex++) {\n\n\t\t\tvec4 lightIndices = texture2D(clusterWorldTexture, vec2(clusterTextureSize.y * (clusterU + lightCellIndex), clusterV));\n\t\t\tvec4 indices = lightIndices * 255.0;\n\n\t\t\t// evaluate up to 4 lights. This is written using a loop instead of manually unrolling to keep shader compile time smaller\n\t\t\tfor (int i = 0; i < 4; i++) {\n\t\t\t\tfloat index = dot(channelSelector[i], indices);\n\t\t\t\tif (index <= 0.0)\n\t\t\t\t\treturn;\n\n\t\t\t\tevaluateClusterLight(index); \n\t\t\t}\n\n\t\t\t// end of the cell array\n\t\t\tif (lightCellIndex > clusterPixelsPerCell) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n";
5988
5988
 
5989
5989
  var combineClearCoatPS = "vec3 combineColorCC() {\n\treturn combineColor()+(ccSpecularLight*ccSpecularity+ccReflection.rgb*ccSpecularity*ccReflection.a);\n}\n";
5990
5990
 
@@ -11675,6 +11675,7 @@
11675
11675
  var useVsm = false;
11676
11676
  var usePerspZbufferShadow = false;
11677
11677
  var light;
11678
+ var isClustered = LayerComposition.clusteredLightingEnabled;
11678
11679
  var hasAreaLights = options.lights.some(function (light) {
11679
11680
  return light._shape && light._shape !== LIGHTSHAPE_PUNCTUAL;
11680
11681
  });
@@ -11697,6 +11698,7 @@
11697
11698
  for (i = 0; i < options.lights.length; i++) {
11698
11699
  light = options.lights[i];
11699
11700
  lightType = light._type;
11701
+ if (isClustered && lightType !== LIGHTTYPE_DIRECTIONAL) continue;
11700
11702
 
11701
11703
  if (hasAreaLights && light._shape) {
11702
11704
  lightShape = light._shape;
@@ -38844,7 +38846,7 @@
38844
38846
  };
38845
38847
 
38846
38848
  _proto.readU64 = function readU64() {
38847
- return this.dataView.getBigUint64(this._inc(8), true);
38849
+ return this.readU32() + Math.pow(2, 32) * this.readU32();
38848
38850
  };
38849
38851
 
38850
38852
  _proto.readU32be = function readU32be() {
@@ -72175,12 +72177,11 @@
72175
72177
  }
72176
72178
  };
72177
72179
 
72178
- _proto.calculateLightmapSize = function calculateLightmapSize(bakeNode) {
72180
+ _proto.calculateLightmapSize = function calculateLightmapSize(node) {
72179
72181
  var data;
72180
72182
  var sizeMult = this.scene.lightmapSizeMultiplier || 16;
72181
72183
  var scale = tempVec;
72182
72184
  var srcArea, lightmapSizeMultiplier;
72183
- var node = bakeNode.node;
72184
72185
 
72185
72186
  if (node.model) {
72186
72187
  lightmapSizeMultiplier = node.model.lightmapSizeMultiplier;
@@ -72230,7 +72231,9 @@
72230
72231
  area.x *= areaMult;
72231
72232
  area.y *= areaMult;
72232
72233
  area.z *= areaMult;
72233
- scale.copy(bakeNode.bounds.halfExtents);
72234
+ var component = node.render || node.model;
72235
+ var bounds = this.computeNodeBounds(component.meshInstances);
72236
+ scale.copy(bounds.halfExtents);
72234
72237
  var totalArea = area.x * scale.y * scale.z + area.y * scale.x * scale.z + area.z * scale.x * scale.y;
72235
72238
  totalArea /= area.uv;
72236
72239
  totalArea = Math.sqrt(totalArea);
@@ -72335,7 +72338,7 @@
72335
72338
  _proto.allocateTextures = function allocateTextures(bakeNodes, passCount) {
72336
72339
  for (var i = 0; i < bakeNodes.length; i++) {
72337
72340
  var bakeNode = bakeNodes[i];
72338
- var size = this.calculateLightmapSize(bakeNode);
72341
+ var size = this.calculateLightmapSize(bakeNode.node);
72339
72342
 
72340
72343
  for (var pass = 0; pass < passCount; pass++) {
72341
72344
  var tex = this.createTexture(size, TEXTURETYPE_DEFAULT, "lightmapper_lightmap_" + i);
@@ -72416,21 +72419,24 @@
72416
72419
  }
72417
72420
  };
72418
72421
 
72419
- _proto.computeNodeBounds = function computeNodeBounds(nodes) {
72422
+ _proto.computeNodeBounds = function computeNodeBounds(meshInstances) {
72420
72423
  var bounds = new BoundingBox();
72421
72424
 
72422
- for (var i = 0; i < nodes.length; i++) {
72423
- var meshInstances = nodes[i].meshInstances;
72424
-
72425
- if (meshInstances.length > 0) {
72426
- bounds.copy(meshInstances[0].aabb);
72425
+ if (meshInstances.length > 0) {
72426
+ bounds.copy(meshInstances[0].aabb);
72427
72427
 
72428
- for (var m = 1; m < meshInstances.length; m++) {
72429
- bounds.add(meshInstances[m].aabb);
72430
- }
72428
+ for (var m = 1; m < meshInstances.length; m++) {
72429
+ bounds.add(meshInstances[m].aabb);
72431
72430
  }
72431
+ }
72432
72432
 
72433
- nodes[i].bounds = bounds.clone();
72433
+ return bounds;
72434
+ };
72435
+
72436
+ _proto.computeNodesBounds = function computeNodesBounds(nodes) {
72437
+ for (var i = 0; i < nodes.length; i++) {
72438
+ var meshInstances = nodes[i].meshInstances;
72439
+ nodes[i].bounds = this.computeNodeBounds(meshInstances);
72434
72440
  }
72435
72441
  };
72436
72442
 
@@ -72589,7 +72595,7 @@
72589
72595
 
72590
72596
  scene.layers._update();
72591
72597
 
72592
- this.computeNodeBounds(bakeNodes);
72598
+ this.computeNodesBounds(bakeNodes);
72593
72599
  this.allocateTextures(bakeNodes, passCount);
72594
72600
  var allLights = [],
72595
72601
  bakeLights = [];
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @license
3
- * PlayCanvas Engine v1.47.1 revision 40ca6f004
3
+ * PlayCanvas Engine v1.47.2 revision b336e2bc9
4
4
  * Copyright 2011-2021 PlayCanvas Ltd. All rights reserved.
5
5
  */
6
6
  (function (global, factory) {
@@ -634,8 +634,8 @@
634
634
  return result;
635
635
  }();
636
636
 
637
- var version = "1.47.1";
638
- var revision = "40ca6f004";
637
+ var version = "1.47.2";
638
+ var revision = "b336e2bc9";
639
639
  var config = {};
640
640
  var common = {};
641
641
  var apps = {};
@@ -5976,7 +5976,7 @@
5976
5976
 
5977
5977
  var clusteredLightShadowsPS = "\n#ifdef GL2\nfloat getShadowOmniClusteredSingleSample(sampler2DShadow shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\treturn texture(shadowMap, vec3(uv, shadowZ));\n}\nfloat getShadowOmniClusteredPCF3x3(sampler2DShadow shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\tdShadowCoord = vec3(uv, shadowZ);\n\treturn getShadowPCF3x3(shadowMap, shadowParams.xyz);\n}\n#else\nfloat getShadowOmniClusteredSingleSample(sampler2D shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\tfloat depth = unpackFloat(texture2D(shadowMap, uv));\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\treturn depth > shadowZ ? 1.0 : 0.0;\n}\nfloat getShadowOmniClusteredPCF3x3(sampler2D shadowMap, vec4 shadowParams, vec3 omniAtlasViewport, float shadowEdgePixels, vec3 dir) {\n\tfloat shadowTextureResolution = shadowParams.x;\n\tvec2 uv = getCubemapAtlasCoordinates(omniAtlasViewport, shadowEdgePixels, shadowTextureResolution, dir);\n\tfloat shadowZ = length(dir) * shadowParams.w + shadowParams.z;\n\tdShadowCoord = vec3(uv, shadowZ);\n\treturn getShadowPCF3x3(shadowMap, shadowParams.xyz);\n}\n#endif\n";
5978
5978
 
5979
- var clusteredLightPS = "uniform sampler2D clusterWorldTexture;\nuniform sampler2D lightsTexture8;\nuniform highp sampler2D lightsTextureFloat;\n#ifdef GL2\n\tuniform sampler2DShadow shadowAtlasTexture;\n#else\n\tuniform sampler2D shadowAtlasTexture;\n#endif\nuniform sampler2D cookieAtlasTexture;\nuniform float clusterPixelsPerCell;\nuniform vec3 clusterCellsCountByBoundsSize;\nuniform vec4 lightsTextureInvSize;\nuniform vec3 clusterTextureSize;\nuniform vec3 clusterBoundsMin;\nuniform vec3 clusterBoundsDelta;\nuniform vec3 clusterCellsDot;\nuniform vec3 clusterCellsMax;\nuniform vec2 clusterCompressionLimit0;\nuniform vec2 shadowAtlasParams;\nstruct ClusterLightData {\n\tfloat lightV;\n\tbool isSpot;\n\tfloat shape;\n\tfloat falloffMode;\n\tbool castShadows;\n\tfloat shadowBias;\n\tfloat shadowNormalBias;\n\tmat4 lightProjectionMatrix;\n\tvec3 position;\n\tvec3 direction;\n\tfloat range;\n\tfloat innerConeAngleCos;\n\tfloat outerConeAngleCos;\n\tvec3 color;\n\tvec3 omniAtlasViewport;\n\tbool isCookie;\n\tbool isCookieRgb;\n\tfloat cookieIntensity;\n\tvec4 cookieChannelMask;\n};\nvec4 decodeClusterLowRange4Vec4(vec4 d0, vec4 d1, vec4 d2, vec4 d3) {\n\treturn vec4(\n\t\tbytes2floatRange4(d0, -2.0, 2.0),\n\t\tbytes2floatRange4(d1, -2.0, 2.0),\n\t\tbytes2floatRange4(d2, -2.0, 2.0),\n\t\tbytes2floatRange4(d3, -2.0, 2.0)\n\t);\n}\nvec4 sampleLightsTexture8(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTexture8, vec2(index * lightsTextureInvSize.z, clusterLightData.lightV));\n}\nvec4 sampleLightTextureF(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTextureFloat, vec2(index * lightsTextureInvSize.x, clusterLightData.lightV));\n}\nvoid decodeClusterLightCore(inout ClusterLightData clusterLightData, float lightIndex) {\n\tclusterLightData.lightV = (lightIndex + 0.5) * lightsTextureInvSize.w;\n\tvec4 lightInfo = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_FLAGS);\n\tclusterLightData.isSpot = lightInfo.x > 0.5;\n\tclusterLightData.shape = lightInfo.y;\n\tclusterLightData.falloffMode = lightInfo.z;\n\tclusterLightData.castShadows = lightInfo.w > 0.5;\n\tvec4 colorA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_A);\n\tvec4 colorB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_B);\n\tclusterLightData.color = vec3(bytes2float2(colorA.xy), bytes2float2(colorA.zw), bytes2float2(colorB.xy)) * clusterCompressionLimit0.y;\n\tclusterLightData.isCookie = colorB.z > 0.5;\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tvec4 lightPosRange = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_POSITION_RANGE);\n\t\tclusterLightData.position = lightPosRange.xyz;\n\t\tclusterLightData.range = lightPosRange.w;\n\t\tvec4 lightDir_Unused = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_SPOT_DIRECTION);\n\t\tclusterLightData.direction = lightDir_Unused.xyz;\n\t#else\n\t\tvec4 encPosX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_X);\n\t\tvec4 encPosY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Y);\n\t\tvec4 encPosZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Z);\n\t\tclusterLightData.position = vec3(bytes2float4(encPosX), bytes2float4(encPosY), bytes2float4(encPosZ)) * clusterBoundsDelta + clusterBoundsMin;\n\t\tvec4 encRange = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_RANGE);\n\t\tclusterLightData.range = bytes2float4(encRange) * clusterCompressionLimit0.x;\n\t\tvec4 encDirX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_X);\n\t\tvec4 encDirY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Y);\n\t\tvec4 encDirZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Z);\n\t\tclusterLightData.direction = vec3(bytes2float4(encDirX), bytes2float4(encDirY), bytes2float4(encDirZ)) * 2.0 - 1.0;\n\t#endif\n}\nvoid decodeClusterLightSpot(inout ClusterLightData clusterLightData) {\n\tvec4 coneAngle = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_ANGLES);\n\tclusterLightData.innerConeAngleCos = bytes2float2(coneAngle.xy) * 2.0 - 1.0;\n\tclusterLightData.outerConeAngleCos = bytes2float2(coneAngle.zw) * 2.0 - 1.0;\n}\nvoid decodeClusterLightOmniAtlasViewport(inout ClusterLightData clusterLightData) {\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tclusterLightData.omniAtlasViewport = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0).xyz;\n\t#else\n\t\tvec4 viewportA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_A);\n\t\tvec4 viewportB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_B);\n\t\tclusterLightData.omniAtlasViewport = vec3(bytes2float2(viewportA.xy), bytes2float2(viewportA.zw), bytes2float2(viewportB.xy));\n\t#endif\n}\nvoid decodeClusterLightProjectionMatrixData(inout ClusterLightData clusterLightData) {\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tvec4 m0 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0);\n\t\tvec4 m1 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_1);\n\t\tvec4 m2 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_2);\n\t\tvec4 m3 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_3);\n\t#else\n\t\tvec4 m00 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_00);\n\t\tvec4 m01 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_01);\n\t\tvec4 m02 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_02);\n\t\tvec4 m03 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_03);\n\t\tvec4 m0 = decodeClusterLowRange4Vec4(m00, m01, m02, m03);\n\t\tvec4 m10 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_10);\n\t\tvec4 m11 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_11);\n\t\tvec4 m12 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_12);\n\t\tvec4 m13 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_13);\n\t\tvec4 m1 = decodeClusterLowRange4Vec4(m10, m11, m12, m13);\n\t\tvec4 m20 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_20);\n\t\tvec4 m21 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_21);\n\t\tvec4 m22 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_22);\n\t\tvec4 m23 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_23);\n\t\tvec4 m2 = decodeClusterLowRange4Vec4(m20, m21, m22, m23);\n\t\tvec4 m30 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_30);\n\t\tvec4 m31 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_31);\n\t\tvec4 m32 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_32);\n\t\tvec4 m33 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_33);\n\t\tvec4 m3 = vec4(mantisaExponent2Float(m30), mantisaExponent2Float(m31), mantisaExponent2Float(m32), mantisaExponent2Float(m33));\n\t#endif\n\tclusterLightData.lightProjectionMatrix = mat4(m0, m1, m2, m3);\n}\nvoid decodeClusterLightShadowData(inout ClusterLightData clusterLightData) {\n\tvec4 biases = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SHADOW_BIAS);\n\tclusterLightData.shadowBias = bytes2floatRange2(biases.xy, -1.0, 20.0),\n\tclusterLightData.shadowNormalBias = bytes2float2(biases.zw);\n}\nvoid decodeClusterLightCookieData(inout ClusterLightData clusterLightData) {\n\tvec4 cookieA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_A);\n\tclusterLightData.cookieIntensity = cookieA.x;\n\tclusterLightData.isCookieRgb = cookieA.y > 0.5;\n\tclusterLightData.cookieChannelMask = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_B);\n}\nvoid evaluateLight(ClusterLightData light) {\n\tdAtten3 = vec3(1.0);\n\tgetLightDirPoint(light.position);\n\tdAtten = getFalloffLinear(light.range);\n\tif (dAtten > 0.00001) {\n\t\tdAtten *= getLightDiffuse();\n\t\tif (light.isSpot == true) {\n\t\t\tdecodeClusterLightSpot(light);\n\t\t\tdAtten *= getSpotEffect(light.direction, light.innerConeAngleCos, light.outerConeAngleCos);\n\t\t}\n\t\tif (dAtten > 0.00001) {\n\t\t\tif (light.castShadows == true || light.isCookie == true) {\n\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\tdecodeClusterLightProjectionMatrixData(light);\n\t\t\t\t} else {\n\t\t\t\t\tdecodeClusterLightOmniAtlasViewport(light);\n\t\t\t\t}\n\t\t\t\tfloat shadowTextureResolution = shadowAtlasParams.x;\n\t\t\t\tfloat shadowEdgePixels = shadowAtlasParams.y;\n\t\t\t\tif (light.isCookie == true) {\n\t\t\t\t\tdecodeClusterLightCookieData(light);\n\t\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\t\tdAtten3 = getCookie2DClustered(cookieAtlasTexture, light.lightProjectionMatrix, vPositionW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdAtten3 = getCookieCubeClustered(cookieAtlasTexture, dLightDirW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask, shadowTextureResolution, shadowEdgePixels, light.omniAtlasViewport);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (light.castShadows== true) {\n\t\t\t\t\tdecodeClusterLightShadowData(light);\n\t\t\t\t\tvec4 shadowParams = vec4(shadowTextureResolution, light.shadowNormalBias, light.shadowBias, 1.0 / light.range);\n\t\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\t\tgetShadowCoordPerspZbufferNormalOffset(light.lightProjectionMatrix, shadowParams);\n\t\t\t\t\t\tdAtten *= getShadowSpotPCF3x3(shadowAtlasTexture, shadowParams);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnormalOffsetPointShadow(shadowParams);\n\t\t\t\t\t\tdAtten *= getShadowOmniClusteredPCF3x3(shadowAtlasTexture, shadowParams, light.omniAtlasViewport, shadowEdgePixels, dLightDirW);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdDiffuseLight += dAtten * light.color * dAtten3;\n\t}\n}\nvoid evaluateClusterLight(float lightIndex) {\n\tClusterLightData clusterLightData;\n\tdecodeClusterLightCore(clusterLightData, lightIndex);\n\tevaluateLight(clusterLightData);\n}\nvoid addClusteredLights() {\n\tvec3 cellCoords = floor((vPositionW - clusterBoundsMin) * clusterCellsCountByBoundsSize);\n\tif (!(any(lessThan(cellCoords, vec3(0.0))) || any(greaterThanEqual(cellCoords, clusterCellsMax)))) {\n\t\tfloat cellIndex = dot(clusterCellsDot, cellCoords);\n\t\tfloat clusterV = floor(cellIndex * clusterTextureSize.y);\n\t\tfloat clusterU = cellIndex - (clusterV * clusterTextureSize.x);\n\t\tclusterV = (clusterV + 0.5) * clusterTextureSize.z;\n\t\tconst float maxLightCells = 256.0 / 4.0;\n\t\tfor (float lightCellIndex = 0.5; lightCellIndex < maxLightCells; lightCellIndex++) {\n\t\t\tvec4 lightIndices = texture2D(clusterWorldTexture, vec2(clusterTextureSize.y * (clusterU + lightCellIndex), clusterV));\n\t\t\tvec4 indices = lightIndices * 255.0;\n\t\t\tif (indices.x <= 0.0)\n\t\t\t\tbreak;\n\t\t\tevaluateClusterLight(indices.x);\n\t\t\tif (indices.y <= 0.0)\n\t\t\t\tbreak;\n\t\t\tevaluateClusterLight(indices.y);\n\t\t\tif (indices.z <= 0.0)\n\t\t\t\tbreak;\n\t\t\tevaluateClusterLight(indices.z);\n\t\t\tif (indices.w <= 0.0)\n\t\t\t\tbreak;\n\t\t\tevaluateClusterLight(indices.w);\n\t\t\tif (lightCellIndex > clusterPixelsPerCell) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n";
5979
+ var clusteredLightPS = "uniform sampler2D clusterWorldTexture;\nuniform sampler2D lightsTexture8;\nuniform highp sampler2D lightsTextureFloat;\n#ifdef GL2\n\tuniform sampler2DShadow shadowAtlasTexture;\n#else\n\tuniform sampler2D shadowAtlasTexture;\n#endif\nuniform sampler2D cookieAtlasTexture;\nuniform float clusterPixelsPerCell;\nuniform vec3 clusterCellsCountByBoundsSize;\nuniform vec4 lightsTextureInvSize;\nuniform vec3 clusterTextureSize;\nuniform vec3 clusterBoundsMin;\nuniform vec3 clusterBoundsDelta;\nuniform vec3 clusterCellsDot;\nuniform vec3 clusterCellsMax;\nuniform vec2 clusterCompressionLimit0;\nuniform vec2 shadowAtlasParams;\nstruct ClusterLightData {\n\tfloat lightV;\n\tbool isSpot;\n\tfloat shape;\n\tfloat falloffMode;\n\tbool castShadows;\n\tfloat shadowBias;\n\tfloat shadowNormalBias;\n\tmat4 lightProjectionMatrix;\n\tvec3 position;\n\tvec3 direction;\n\tfloat range;\n\tfloat innerConeAngleCos;\n\tfloat outerConeAngleCos;\n\tvec3 color;\n\tvec3 omniAtlasViewport;\n\tbool isCookie;\n\tbool isCookieRgb;\n\tfloat cookieIntensity;\n\tvec4 cookieChannelMask;\n};\nvec4 decodeClusterLowRange4Vec4(vec4 d0, vec4 d1, vec4 d2, vec4 d3) {\n\treturn vec4(\n\t\tbytes2floatRange4(d0, -2.0, 2.0),\n\t\tbytes2floatRange4(d1, -2.0, 2.0),\n\t\tbytes2floatRange4(d2, -2.0, 2.0),\n\t\tbytes2floatRange4(d3, -2.0, 2.0)\n\t);\n}\nvec4 sampleLightsTexture8(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTexture8, vec2(index * lightsTextureInvSize.z, clusterLightData.lightV));\n}\nvec4 sampleLightTextureF(const ClusterLightData clusterLightData, float index) {\n\treturn texture2D(lightsTextureFloat, vec2(index * lightsTextureInvSize.x, clusterLightData.lightV));\n}\nvoid decodeClusterLightCore(inout ClusterLightData clusterLightData, float lightIndex) {\n\tclusterLightData.lightV = (lightIndex + 0.5) * lightsTextureInvSize.w;\n\tvec4 lightInfo = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_FLAGS);\n\tclusterLightData.isSpot = lightInfo.x > 0.5;\n\tclusterLightData.shape = lightInfo.y;\n\tclusterLightData.falloffMode = lightInfo.z;\n\tclusterLightData.castShadows = lightInfo.w > 0.5;\n\tvec4 colorA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_A);\n\tvec4 colorB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COLOR_B);\n\tclusterLightData.color = vec3(bytes2float2(colorA.xy), bytes2float2(colorA.zw), bytes2float2(colorB.xy)) * clusterCompressionLimit0.y;\n\tclusterLightData.isCookie = colorB.z > 0.5;\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tvec4 lightPosRange = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_POSITION_RANGE);\n\t\tclusterLightData.position = lightPosRange.xyz;\n\t\tclusterLightData.range = lightPosRange.w;\n\t\tvec4 lightDir_Unused = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_SPOT_DIRECTION);\n\t\tclusterLightData.direction = lightDir_Unused.xyz;\n\t#else\n\t\tvec4 encPosX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_X);\n\t\tvec4 encPosY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Y);\n\t\tvec4 encPosZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_POSITION_Z);\n\t\tclusterLightData.position = vec3(bytes2float4(encPosX), bytes2float4(encPosY), bytes2float4(encPosZ)) * clusterBoundsDelta + clusterBoundsMin;\n\t\tvec4 encRange = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_RANGE);\n\t\tclusterLightData.range = bytes2float4(encRange) * clusterCompressionLimit0.x;\n\t\tvec4 encDirX = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_X);\n\t\tvec4 encDirY = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Y);\n\t\tvec4 encDirZ = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_DIRECTION_Z);\n\t\tclusterLightData.direction = vec3(bytes2float4(encDirX), bytes2float4(encDirY), bytes2float4(encDirZ)) * 2.0 - 1.0;\n\t#endif\n}\nvoid decodeClusterLightSpot(inout ClusterLightData clusterLightData) {\n\tvec4 coneAngle = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SPOT_ANGLES);\n\tclusterLightData.innerConeAngleCos = bytes2float2(coneAngle.xy) * 2.0 - 1.0;\n\tclusterLightData.outerConeAngleCos = bytes2float2(coneAngle.zw) * 2.0 - 1.0;\n}\nvoid decodeClusterLightOmniAtlasViewport(inout ClusterLightData clusterLightData) {\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tclusterLightData.omniAtlasViewport = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0).xyz;\n\t#else\n\t\tvec4 viewportA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_A);\n\t\tvec4 viewportB = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_ATLAS_VIEWPORT_B);\n\t\tclusterLightData.omniAtlasViewport = vec3(bytes2float2(viewportA.xy), bytes2float2(viewportA.zw), bytes2float2(viewportB.xy));\n\t#endif\n}\nvoid decodeClusterLightProjectionMatrixData(inout ClusterLightData clusterLightData) {\n\t#ifdef CLUSTER_TEXTURE_FLOAT\n\t\tvec4 m0 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_0);\n\t\tvec4 m1 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_1);\n\t\tvec4 m2 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_2);\n\t\tvec4 m3 = sampleLightTextureF(clusterLightData, CLUSTER_TEXTURE_F_PROJ_MAT_3);\n\t#else\n\t\tvec4 m00 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_00);\n\t\tvec4 m01 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_01);\n\t\tvec4 m02 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_02);\n\t\tvec4 m03 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_03);\n\t\tvec4 m0 = decodeClusterLowRange4Vec4(m00, m01, m02, m03);\n\t\tvec4 m10 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_10);\n\t\tvec4 m11 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_11);\n\t\tvec4 m12 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_12);\n\t\tvec4 m13 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_13);\n\t\tvec4 m1 = decodeClusterLowRange4Vec4(m10, m11, m12, m13);\n\t\tvec4 m20 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_20);\n\t\tvec4 m21 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_21);\n\t\tvec4 m22 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_22);\n\t\tvec4 m23 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_23);\n\t\tvec4 m2 = decodeClusterLowRange4Vec4(m20, m21, m22, m23);\n\t\tvec4 m30 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_30);\n\t\tvec4 m31 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_31);\n\t\tvec4 m32 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_32);\n\t\tvec4 m33 = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_PROJ_MAT_33);\n\t\tvec4 m3 = vec4(mantisaExponent2Float(m30), mantisaExponent2Float(m31), mantisaExponent2Float(m32), mantisaExponent2Float(m33));\n\t#endif\n\tclusterLightData.lightProjectionMatrix = mat4(m0, m1, m2, m3);\n}\nvoid decodeClusterLightShadowData(inout ClusterLightData clusterLightData) {\n\tvec4 biases = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_SHADOW_BIAS);\n\tclusterLightData.shadowBias = bytes2floatRange2(biases.xy, -1.0, 20.0),\n\tclusterLightData.shadowNormalBias = bytes2float2(biases.zw);\n}\nvoid decodeClusterLightCookieData(inout ClusterLightData clusterLightData) {\n\tvec4 cookieA = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_A);\n\tclusterLightData.cookieIntensity = cookieA.x;\n\tclusterLightData.isCookieRgb = cookieA.y > 0.5;\n\tclusterLightData.cookieChannelMask = sampleLightsTexture8(clusterLightData, CLUSTER_TEXTURE_8_COOKIE_B);\n}\nvoid evaluateLight(ClusterLightData light) {\n\tdAtten3 = vec3(1.0);\n\tgetLightDirPoint(light.position);\n\tdAtten = getFalloffLinear(light.range);\n\tif (dAtten > 0.00001) {\n\t\tdAtten *= getLightDiffuse();\n\t\tif (light.isSpot == true) {\n\t\t\tdecodeClusterLightSpot(light);\n\t\t\tdAtten *= getSpotEffect(light.direction, light.innerConeAngleCos, light.outerConeAngleCos);\n\t\t}\n\t\tif (dAtten > 0.00001) {\n\t\t\tif (light.castShadows == true || light.isCookie == true) {\n\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\tdecodeClusterLightProjectionMatrixData(light);\n\t\t\t\t} else {\n\t\t\t\t\tdecodeClusterLightOmniAtlasViewport(light);\n\t\t\t\t}\n\t\t\t\tfloat shadowTextureResolution = shadowAtlasParams.x;\n\t\t\t\tfloat shadowEdgePixels = shadowAtlasParams.y;\n\t\t\t\tif (light.isCookie == true) {\n\t\t\t\t\tdecodeClusterLightCookieData(light);\n\t\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\t\tdAtten3 = getCookie2DClustered(cookieAtlasTexture, light.lightProjectionMatrix, vPositionW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tdAtten3 = getCookieCubeClustered(cookieAtlasTexture, dLightDirW, light.cookieIntensity, light.isCookieRgb, light.cookieChannelMask, shadowTextureResolution, shadowEdgePixels, light.omniAtlasViewport);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (light.castShadows== true) {\n\t\t\t\t\tdecodeClusterLightShadowData(light);\n\t\t\t\t\tvec4 shadowParams = vec4(shadowTextureResolution, light.shadowNormalBias, light.shadowBias, 1.0 / light.range);\n\t\t\t\t\tif (light.isSpot == true) {\n\t\t\t\t\t\tgetShadowCoordPerspZbufferNormalOffset(light.lightProjectionMatrix, shadowParams);\n\t\t\t\t\t\tdAtten *= getShadowSpotPCF3x3(shadowAtlasTexture, shadowParams);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnormalOffsetPointShadow(shadowParams);\n\t\t\t\t\t\tdAtten *= getShadowOmniClusteredPCF3x3(shadowAtlasTexture, shadowParams, light.omniAtlasViewport, shadowEdgePixels, dLightDirW);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdDiffuseLight += dAtten * light.color * dAtten3;\n\t}\n}\nvoid evaluateClusterLight(float lightIndex) {\n\tClusterLightData clusterLightData;\n\tdecodeClusterLightCore(clusterLightData, lightIndex);\n\tevaluateLight(clusterLightData);\n}\nconst vec4 channelSelector[4] = vec4[4] (\n\tvec4(1., 0., 0., 0.),\n\tvec4(0., 1., 0., 0.),\n\tvec4(0., 0., 1., 0.),\n\tvec4(0., 0., 0., 1.)\n);\nvoid addClusteredLights() {\n\tvec3 cellCoords = floor((vPositionW - clusterBoundsMin) * clusterCellsCountByBoundsSize);\n\tif (!(any(lessThan(cellCoords, vec3(0.0))) || any(greaterThanEqual(cellCoords, clusterCellsMax)))) {\n\t\tfloat cellIndex = dot(clusterCellsDot, cellCoords);\n\t\tfloat clusterV = floor(cellIndex * clusterTextureSize.y);\n\t\tfloat clusterU = cellIndex - (clusterV * clusterTextureSize.x);\n\t\tclusterV = (clusterV + 0.5) * clusterTextureSize.z;\n\t\tconst float maxLightCells = 256.0 / 4.0;\n\t\tfor (float lightCellIndex = 0.5; lightCellIndex < maxLightCells; lightCellIndex++) {\n\t\t\tvec4 lightIndices = texture2D(clusterWorldTexture, vec2(clusterTextureSize.y * (clusterU + lightCellIndex), clusterV));\n\t\t\tvec4 indices = lightIndices * 255.0;\n\t\t\tfor (int i = 0; i < 4; i++) {\n\t\t\t\tfloat index = dot(channelSelector[i], indices);\n\t\t\t\tif (index <= 0.0)\n\t\t\t\t\treturn;\n\t\t\t\tevaluateClusterLight(index);\n\t\t\t}\n\t\t\tif (lightCellIndex > clusterPixelsPerCell) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n";
5980
5980
 
5981
5981
  var combineClearCoatPS = "vec3 combineColorCC() {\n\treturn combineColor()+(ccSpecularLight*ccSpecularity+ccReflection.rgb*ccSpecularity*ccReflection.a);\n}\n";
5982
5982
 
@@ -11580,6 +11580,7 @@
11580
11580
  var useVsm = false;
11581
11581
  var usePerspZbufferShadow = false;
11582
11582
  var light;
11583
+ var isClustered = LayerComposition.clusteredLightingEnabled;
11583
11584
  var hasAreaLights = options.lights.some(function (light) {
11584
11585
  return light._shape && light._shape !== LIGHTSHAPE_PUNCTUAL;
11585
11586
  });
@@ -11602,6 +11603,7 @@
11602
11603
  for (i = 0; i < options.lights.length; i++) {
11603
11604
  light = options.lights[i];
11604
11605
  lightType = light._type;
11606
+ if (isClustered && lightType !== LIGHTTYPE_DIRECTIONAL) continue;
11605
11607
 
11606
11608
  if (hasAreaLights && light._shape) {
11607
11609
  lightShape = light._shape;
@@ -38354,7 +38356,7 @@
38354
38356
  };
38355
38357
 
38356
38358
  _proto.readU64 = function readU64() {
38357
- return this.dataView.getBigUint64(this._inc(8), true);
38359
+ return this.readU32() + Math.pow(2, 32) * this.readU32();
38358
38360
  };
38359
38361
 
38360
38362
  _proto.readU32be = function readU32be() {
@@ -71540,12 +71542,11 @@
71540
71542
  }
71541
71543
  };
71542
71544
 
71543
- _proto.calculateLightmapSize = function calculateLightmapSize(bakeNode) {
71545
+ _proto.calculateLightmapSize = function calculateLightmapSize(node) {
71544
71546
  var data;
71545
71547
  var sizeMult = this.scene.lightmapSizeMultiplier || 16;
71546
71548
  var scale = tempVec;
71547
71549
  var srcArea, lightmapSizeMultiplier;
71548
- var node = bakeNode.node;
71549
71550
 
71550
71551
  if (node.model) {
71551
71552
  lightmapSizeMultiplier = node.model.lightmapSizeMultiplier;
@@ -71595,7 +71596,9 @@
71595
71596
  area.x *= areaMult;
71596
71597
  area.y *= areaMult;
71597
71598
  area.z *= areaMult;
71598
- scale.copy(bakeNode.bounds.halfExtents);
71599
+ var component = node.render || node.model;
71600
+ var bounds = this.computeNodeBounds(component.meshInstances);
71601
+ scale.copy(bounds.halfExtents);
71599
71602
  var totalArea = area.x * scale.y * scale.z + area.y * scale.x * scale.z + area.z * scale.x * scale.y;
71600
71603
  totalArea /= area.uv;
71601
71604
  totalArea = Math.sqrt(totalArea);
@@ -71689,7 +71692,7 @@
71689
71692
  _proto.allocateTextures = function allocateTextures(bakeNodes, passCount) {
71690
71693
  for (var i = 0; i < bakeNodes.length; i++) {
71691
71694
  var bakeNode = bakeNodes[i];
71692
- var size = this.calculateLightmapSize(bakeNode);
71695
+ var size = this.calculateLightmapSize(bakeNode.node);
71693
71696
 
71694
71697
  for (var pass = 0; pass < passCount; pass++) {
71695
71698
  var tex = this.createTexture(size, TEXTURETYPE_DEFAULT, "lightmapper_lightmap_" + i);
@@ -71770,21 +71773,24 @@
71770
71773
  }
71771
71774
  };
71772
71775
 
71773
- _proto.computeNodeBounds = function computeNodeBounds(nodes) {
71776
+ _proto.computeNodeBounds = function computeNodeBounds(meshInstances) {
71774
71777
  var bounds = new BoundingBox();
71775
71778
 
71776
- for (var i = 0; i < nodes.length; i++) {
71777
- var meshInstances = nodes[i].meshInstances;
71778
-
71779
- if (meshInstances.length > 0) {
71780
- bounds.copy(meshInstances[0].aabb);
71779
+ if (meshInstances.length > 0) {
71780
+ bounds.copy(meshInstances[0].aabb);
71781
71781
 
71782
- for (var m = 1; m < meshInstances.length; m++) {
71783
- bounds.add(meshInstances[m].aabb);
71784
- }
71782
+ for (var m = 1; m < meshInstances.length; m++) {
71783
+ bounds.add(meshInstances[m].aabb);
71785
71784
  }
71785
+ }
71786
71786
 
71787
- nodes[i].bounds = bounds.clone();
71787
+ return bounds;
71788
+ };
71789
+
71790
+ _proto.computeNodesBounds = function computeNodesBounds(nodes) {
71791
+ for (var i = 0; i < nodes.length; i++) {
71792
+ var meshInstances = nodes[i].meshInstances;
71793
+ nodes[i].bounds = this.computeNodeBounds(meshInstances);
71788
71794
  }
71789
71795
  };
71790
71796
 
@@ -71940,7 +71946,7 @@
71940
71946
 
71941
71947
  scene.layers._update();
71942
71948
 
71943
- this.computeNodeBounds(bakeNodes);
71949
+ this.computeNodesBounds(bakeNodes);
71944
71950
  this.allocateTextures(bakeNodes, passCount);
71945
71951
  var allLights = [],
71946
71952
  bakeLights = [];