/* COPYRIGHT Esri - https://js.arcgis.com/5.1.2/LICENSE.txt */
import{set as e}from"../core/libs/gl-matrix-2/math/vec2.js";import{create as a}from"../core/libs/gl-matrix-2/factories/vec2f64.js";import{create as i}from"../core/libs/gl-matrix-2/factories/vec3f64.js";import{RejectBySlicePass as n}from"../views/3d/webgl-engine/core/shaderLibrary/Slice.glsl.js";import{EvaluateSceneLighting as t,addAmbientBoostFactor as o,addLightingGlobalFactor as r}from"../views/3d/webgl-engine/core/shaderLibrary/shading/EvaluateSceneLighting.glsl.js";import{NormalUtils as s}from"../views/3d/webgl-engine/core/shaderLibrary/shading/NormalUtils.glsl.js";import{positionOutsideClipSpace as l}from"../views/3d/webgl-engine/core/shaderLibrary/shading/PositionOutsideClipSpace.js";import{ReadShadowMapPassFragment as c}from"../views/3d/webgl-engine/core/shaderLibrary/shading/ReadShadowMap.glsl.js";import{GaussianSplatCovariance as d}from"../views/3d/webgl-engine/core/shaderLibrary/util/GaussianSplatCovariance.glsl.js";import{GaussianSplatEvaluation as g}from"../views/3d/webgl-engine/core/shaderLibrary/util/GaussianSplatEvaluation.glsl.js";import{GaussianSplatTextureFetch as p}from"../views/3d/webgl-engine/core/shaderLibrary/util/GaussianSplatTextureFetch.glsl.js";import{GaussianSplatUnpackingPassParameters as m,GaussianSplatUnpacking as u}from"../views/3d/webgl-engine/core/shaderLibrary/util/GaussianSplatUnpacking.glsl.js";import{QuaternionToRotationMatrix as f}from"../views/3d/webgl-engine/core/shaderLibrary/util/QuaternionToRotationMatrix.glsl.js";import{RgbNormalizedDepthEncoding as v}from"../views/3d/webgl-engine/core/shaderLibrary/util/RgbNormalizedDepthEncoding.glsl.js";import{Float2BindUniform as h}from"../views/3d/webgl-engine/core/shaderModules/Float2BindUniform.js";import{Float2PassUniform as w}from"../views/3d/webgl-engine/core/shaderModules/Float2PassUniform.js";import{Float3BindUniform as x}from"../views/3d/webgl-engine/core/shaderModules/Float3BindUniform.js";import{Float3PassUniform as b}from"../views/3d/webgl-engine/core/shaderModules/Float3PassUniform.js";import{FloatPassUniform as y}from"../views/3d/webgl-engine/core/shaderModules/FloatPassUniform.js";import{glsl as P,If as C}from"../views/3d/webgl-engine/core/shaderModules/glsl.js";import{Matrix4BindUniform as S}from"../views/3d/webgl-engine/core/shaderModules/Matrix4BindUniform.js";import{Texture2DUintPassUniform as M}from"../views/3d/webgl-engine/core/shaderModules/Texture2DUintPassUniform.js";import{getGaussianSplatAlphaCutoffValue as R}from"../views/3d/webgl-engine/shaders/GaussianSplatTechniqueConfiguration.js";import{ShaderBuilder as j}from"../views/webgl/ShaderBuilder.js";class L extends m{constructor(){super(...arguments),this.clipMinCameraRelative=i(),this.clipMaxCameraRelative=i(),this.focalLength=-1,this.minSplatRadius=-1,this.tanFov=a(),this.origin=i()}}function F(a){const{clippingEnabled:i,hasSlicePlane:m,receiveShadows:L,spherical:F}=a,D=new j;D.varyings.add("vColor","vec4"),D.varyings.add("conicOpacity","vec4"),D.varyings.add("gaussianLogAlphaCutoff","float"),D.varyings.add("offsetFromCenter","vec2"),G(a)&&D.varyings.add("fragmentPositionCameraRelative","vec3"),D.vertex.uniforms.add(new M("splatOrderTexture",e=>e.splatOrder),new M("splatFadingTexture",e=>e.splatFading),new M("splatAtlasTexture",e=>e.splatAtlas),new y("focalLength",e=>e.focalLength),new y("minSplatRadius",e=>e.minSplatRadius),new w("tanFov",e=>e.tanFov),new h("inverseScreenSize",({camera:a})=>e(z,1/a.fullWidth,1/a.fullHeight)),new S("proj",e=>e.camera.projectionMatrix),new S("view",e=>e.camera.viewMatrix),new h("nearFar",e=>e.camera.nearFar),new x("cameraPosition",e=>e.camera.eye)),i&&(D.vertex.uniforms.add(new b("clipMin",e=>e.clipMinCameraRelative),new b("clipMax",e=>e.clipMaxCameraRelative)),D.fragment.uniforms.add(new b("clipMin",e=>e.clipMinCameraRelative),new b("clipMax",e=>e.clipMaxCameraRelative))),D.vertex.include(u),D.vertex.include(p),D.vertex.include(f),D.vertex.include(d),D.vertex.include(g),D.vertex.include(n,a),L?(D.fragment.uniforms.add(new x("cameraPosition",e=>e.camera.eye)),D.fragment.include(t,a)):D.vertex.include(t,a),D.include(s,a),o(L?D.fragment:D.vertex),r(L?D.fragment:D.vertex),L&&D.include(c,a),D.outputs.add("fragColor","vec4",0),D.outputs.add("fragDepthColor","vec4",1);const A=R(a.alphaCutoff),B=Math.log(A),T=P`float groundLightAlignment = dot(groundNormal, mainLightDirection);
float additionalAmbientScale = additionalDirectedAmbientLight(groundLightAlignment);
vec3 additionalLight = mainLightIntensity * additionalAmbientScale * ambientBoostFactor * lightingGlobalFactor;`,$=P`
    vec3 groundNormal = ${F?P`normalize(cameraRelativePosition + cameraPosition)`:P`vec3(0.0, 0.0, 1.0)`};
    ${T}
    float shadow = ${F?P`lightingGlobalFactor * (1.0 - additionalAmbientScale)`:P`0.0`};
    vColor.rgb = evaluateSceneLighting(groundNormal, vColor.rgb, shadow, 0.0, additionalLight);
  `,E=P`
    vec3 groundNormal = ${F?P`normalize(fragmentPositionCameraRelative + cameraPosition)`:P`vec3(0.0, 0.0, 1.0)`};
    ${T}
    float shadow = readShadow(additionalAmbientScale, fragmentPositionCameraRelative);
    shadedColor = evaluateSceneLighting(groundNormal, vColor.rgb, shadow, 0.0, additionalLight);
  `;return D.vertex.main.add(`\n    uint gaussianIndex = fetchOrderedGaussianIndex(uint(gl_InstanceID));\n    uvec4 packedGaussian = fetchPackedGaussian(gaussianIndex);\n    uint pageNum = fetchGaussianPageIndex(gaussianIndex);\n\n    // Unpack color first so very transparent splats can discard before fetching the page header.\n    vColor = unpackColor(packedGaussian);\n\n    // Apply per-page fading before the early alpha rejection.\n    ${C(a.fadingEnabled,"\n      uint fadingTextureWidth = uint(textureSize(splatFadingTexture, 0).x);\n      uint fadeX = pageNum  % fadingTextureWidth;\n      uint fadeY = pageNum  / fadingTextureWidth;\n      uint opacityModifierByte = texelFetch(splatFadingTexture, ivec2(fadeX , fadeY), 0).r;\n      float opacityModifier = float(opacityModifierByte) / 255.0;\n      vColor.a *= opacityModifier;\n      ")}\n\n    // Set default position outside clip space for early returns.\n    gl_Position = ${l};\n\n    if (vColor.a < ${A}) {\n      return;\n    }\n\n    // Delay the page header fetch until the splat survives the opacity reject.\n    vec3 cameraRelativePosition = fetchGaussianCameraRelativePosition(gaussianIndex, packedGaussian);\n\n    ${C(i,"\n      if (cameraRelativePosition.x < clipMin.x || cameraRelativePosition.y < clipMin.y || cameraRelativePosition.z < clipMin.z ||\n        cameraRelativePosition.x > clipMax.x || cameraRelativePosition.y > clipMax.y || cameraRelativePosition.z > clipMax.z) {\n        return;\n      }\n      ")}\n\n    ${C(m,"if (rejectBySlice(cameraRelativePosition)) {\n        return;\n      }")}\n\n    vec4 viewPos = vec4(mat3(view) * cameraRelativePosition, 1);\n\n    if (viewPos.z > -nearFar.x || viewPos.z < -nearFar.y) {\n      return;\n    }\n\n    vec3 covarianceA;\n    vec3 covarianceB;\n    computePackedGaussianCovariance3D(packedGaussian, covarianceA, covarianceB);\n\n    float covariance3D[6] = float[6](covarianceA.x, covarianceA.y, covarianceA.z, covarianceB.x, covarianceB.y, covarianceB.z);\n\n    vec3 covariance2D = computeGaussianCovariance2D(viewPos.xyz, focalLength, tanFov, covariance3D, view);\n\n    // Reject degenerate covariances before inverting them into conic coefficients.\n    float determinant = computeGaussianCovarianceDeterminant(covariance2D);\n    if (determinant <= 0.) {\n      return;\n    }\n\n    vec2 eigenvalues = computeGaussianCovarianceEigenvalues(covariance2D);\n\n    // Fold the per-splat opacity into the log cutoff so the vertex-side ellipse\n    // bound matches the fragment alpha discard after fading.\n    gaussianLogAlphaCutoff = ${B} - log(vColor.a);\n    float gaussianEllipseThreshold = computeGaussianEllipseThreshold(gaussianLogAlphaCutoff);\n    vec2 axisLengths = computeGaussianAxisLengths(eigenvalues, gaussianEllipseThreshold);\n    float maxRadius = max(axisLengths.x, axisLengths.y);\n\n    // Ignore Gaussians with very small contribution, with tolerance based on the quality profile.\n    if (rejectGaussianByMinimumRadius(maxRadius, vColor.a, minSplatRadius)) {\n      return;\n    }\n\n    vec4 projPos = proj * viewPos;\n    float invW = 1. / (projPos.w + 1e-7);\n    vec3 ndcPos = projPos.xyz * invW;\n    vec2 clipSpacePixelScale = 2.0 * inverseScreenSize;\n\n    // Cull splats whose screen-space ellipse cannot touch the viewport.\n    if (rejectGaussianByScreenBounds(ndcPos.xy, maxRadius, clipSpacePixelScale)) {\n      return;\n    }\n\n    offsetFromCenter = computeGaussianQuadOffset(covariance2D, eigenvalues, axisLengths, gl_VertexID);\n\n    ${C(G(a),"float viewSpacePixelScale = -viewPos.z / focalLength;\n      vec3 fragmentViewOffset = vec3(offsetFromCenter * viewSpacePixelScale, 0.0);\n      fragmentPositionCameraRelative = cameraRelativePosition + transpose(mat3(view)) * fragmentViewOffset;")}\n\n    // Store conic coefficients with opacity for fragment-side falloff.\n    vec3 conic = computeGaussianConic(covariance2D, determinant);\n    conicOpacity = vec4(conic, vColor.a);\n\n    // Handle environment lighting per vertex when no shadow map is active.\n    // When shadows are received, defer lighting to the fragment shader for crisp shadow borders.\n  ${C(L,"forwardLinearDepth(-viewPos.z);",$)}\n\n    // Place this quad corner in clip space around the projected splat center.\n    vec2 quadClipPosition = ndcPos.xy + offsetFromCenter * clipSpacePixelScale - inverseScreenSize;\n\n    gl_Position = vec4(quadClipPosition, ndcPos.z, 1.0);\n\n  `),D.fragment.include(v),D.fragment.include(g),D.fragment.include(n,a),D.fragment.main.add(`\n    ${C(i,"if (fragmentPositionCameraRelative.x < clipMin.x || fragmentPositionCameraRelative.y < clipMin.y || fragmentPositionCameraRelative.z < clipMin.z ||\n        fragmentPositionCameraRelative.x > clipMax.x || fragmentPositionCameraRelative.y > clipMax.y || fragmentPositionCameraRelative.z > clipMax.z) {\n        discard;\n      }")}\n    ${C(m,"if (rejectBySlice(fragmentPositionCameraRelative)) { discard; }")}\n\n    float gaussianExponent = evaluateGaussianExponent(conicOpacity.xyz, offsetFromCenter);\n\n    // A positive exponent indicates alpha > 1, which should not happen.\n    // We also early check the opacity-aware alpha cutoff to avoid unnecessary exp().\n    if (gaussianExponent > 0.0 || gaussianExponent < gaussianLogAlphaCutoff) {\n      discard;\n    }\n\n    float gaussianFalloff = exp(gaussianExponent);\n\n    // Cap at 0.99 to avoid blending issues, such as seams between overlapping Gaussians.\n    float alpha = min(.99f, conicOpacity.w * gaussianFalloff);\n\n    vec3 shadedColor = vColor.rgb;\n  ${C(L,E)}\n    fragColor = vec4(shadedColor * alpha, alpha);\n\n    // We simulate first hit based depth using 0.25 as the opacity threshold.\n    // This works because we render in front-to-back order,\n    // i.e. the first hit that counts completely saturates the alpha channel\n    // and further splats do not contribute.\n    float depthHit = step(0.25, alpha);\n    float normalizedDepth = gl_FragCoord.z;\n    fragDepthColor = vec4(encodeNormalizedDepthToRGB(normalizedDepth) * depthHit, depthHit);\n  `),D}function G(e){const{clippingEnabled:a,hasSlicePlane:i,receiveShadows:n}=e;return n||i||a}const z=a(),D=Object.freeze(Object.defineProperty({__proto__:null,GaussianSplatPassParameters:L,build:F},Symbol.toStringTag,{value:"Module"}));export{L as G,D as a,F as b};
