export declare const glslCore = "\n float imHash(vec3 p, float seed) {\n p = fract(p * 0.3183099 + vec3(0.1, 0.2, 0.3) + seed);\n p *= 17.0;\n return fract(p.x * p.y * p.z * (p.x + p.y + p.z));\n }\n\n vec3 imWindowEmissive(vec3 windowId, float litRatio, vec3 warm, vec3 cool,\n float coolChance, vec2 bright, vec3 dim) {\n float hLit = imHash(windowId, 3.71);\n float hTone = imHash(windowId, 9.27);\n float hBright = imHash(windowId, 5.43);\n if (hLit >= litRatio) return dim;\n vec3 tone = mix(warm, cool, step(1.0 - coolChance, hTone));\n return tone * (bright.x + bright.y * hBright);\n }\n\n vec2 imAtlasUV(vec2 cellUV, vec3 windowId, float cols, float rows, float seed) {\n float total = cols * rows;\n float idx = floor(imHash(windowId, seed) * total);\n float col = mod(idx, cols);\n float row = floor(idx / cols);\n vec2 cellSize = vec2(1.0 / cols, 1.0 / rows);\n vec2 inset = cellSize * 0.001;\n vec2 cellOrigin = vec2(col * cellSize.x, 1.0 - (row + 1.0) * cellSize.y) + inset;\n return cellOrigin + clamp(cellUV, 0.0, 1.0) * (cellSize - 2.0 * inset);\n }\n\n vec2 imRoomBoxUV(vec3 camLocal, vec2 localXY, float depth, float backScale) {\n vec3 origin = vec3(localXY, 0.0);\n vec3 dir = normalize(origin - camLocal);\n vec3 invDir = 1.0 / dir;\n vec3 tNear = (vec3(-0.5, -0.5, -depth) - origin) * invDir;\n vec3 tFar = (vec3(0.5, 0.5, 0.0) - origin) * invDir;\n vec3 tMax = max(tNear, tFar);\n float t = min(min(tMax.x, tMax.y), tMax.z);\n vec3 hit = origin + dir * t;\n float bs = clamp(backScale, 0.05, 0.999);\n float camDist = bs * depth / (1.0 - bs);\n float scale = camDist / (camDist - hit.z);\n return hit.xy * scale + 0.5;\n }\n"; export declare const vertexCommon = "\n varying vec2 vInteriorLocalXY;\n varying vec3 vInteriorCameraLocal;\n varying vec3 vImWindowId;\n varying float vImLod;\n varying float vImFade;\n uniform vec2 uPlaneSize;\n uniform vec3 uWindowId;\n uniform float uLod;\n #ifdef IM_INSTANCED\n // one InstancedMesh = thousands of windows in a single draw call;\n // per-window identity/LOD/fade live in instanced attributes\n attribute vec3 instanceWindowId;\n attribute float instanceLod;\n attribute float instanceFade;\n #endif\n"; export declare const vertexBody = "\n vInteriorLocalXY = position.xy / uPlaneSize;\n #ifdef IM_INSTANCED\n mat4 _imModel = modelMatrix * instanceMatrix;\n vImWindowId = instanceWindowId;\n vImLod = instanceLod;\n vImFade = instanceFade;\n #else\n mat4 _imModel = modelMatrix;\n vImWindowId = uWindowId;\n vImLod = uLod;\n vImFade = 1.0;\n #endif\n vec3 _imCamLocal = (inverse(_imModel) * vec4(cameraPosition, 1.0)).xyz;\n vInteriorCameraLocal = vec3(\n _imCamLocal.xy / uPlaneSize,\n _imCamLocal.z / max(uPlaneSize.x, uPlaneSize.y)\n );\n"; export declare const fragmentCommon = "\n varying vec2 vInteriorLocalXY;\n varying vec3 vInteriorCameraLocal;\n varying vec3 vImWindowId;\n varying float vImLod;\n varying float vImFade;\n\n uniform sampler2D uBackAtlas;\n uniform float uBackAtlasCols;\n uniform float uBackAtlasRows;\n uniform float uDepth;\n uniform float uBackScale;\n uniform vec3 uWindowId;\n uniform vec3 uInteriorEmissive;\n uniform float uFrontTransmission;\n uniform float uFrontAlphaBoost;\n uniform float uGlassThickness;\n uniform float uRefractionStrength;\n uniform float uGlassDirtStrength;\n uniform float uGlassFresnelStrength;\n uniform vec3 uGlassFresnelColor;\n uniform float uGlassSmudgeStrength;\n\n // Front cols/rows are always declared (cheap floats); samplers are gated by #define.\n uniform float uFrontAtlasCols;\n uniform float uFrontAtlasRows;\n #ifdef HAS_FRONT_ATLAS\n uniform sampler2D uFrontAtlas;\n #endif\n #ifdef HAS_FRONT_NORMAL\n uniform sampler2D uFrontNormalAtlas;\n uniform float uFrontNormalScale;\n #endif\n #ifdef HAS_FRONT_ROUGHNESS\n uniform sampler2D uFrontRoughnessAtlas;\n #endif\n #ifdef HAS_FRONT_METALNESS\n uniform sampler2D uFrontMetalnessAtlas;\n #endif\n #ifdef HAS_GLASS_DIRT\n uniform sampler2D uGlassDirtMap;\n #endif\n\n // LOD blend: 1 = full interior mapping + glass effects, 0 = flat impostor\n // (the back cell sampled straight onto the plane, no parallax/fresnel/\n // refraction). Drive by camera distance to crossfade against a cheap\n // far-LOD shader that samples the same atlas cell flat.\n uniform float uLod;\n\n // Optional GPU-side per-window emissive variation (lit/unlit, warm/cool,\n // brightness jitter) derived from uWindowId so an impostor shader can\n // reproduce the exact same look from the same id. Hash seeds are part of\n // the public contract: lit = 3.71, tone = 9.27, brightness = 5.43.\n uniform float uVarEnabled;\n uniform float uVarLitRatio;\n uniform vec3 uVarWarm;\n uniform vec3 uVarCool;\n uniform float uVarCoolChance;\n uniform vec2 uVarBright; // (min, range)\n uniform vec3 uVarDim;\n\n // thin varying-bound wrappers over the public glslCore (injected above by\n // InteriorMappingMaterial) \u2014 one implementation, every consumer\n float _imHash(vec3 p, float seed) { return imHash(p, seed); }\n\n vec3 _imWindowEmissive() {\n if (uVarEnabled < 0.5) return uInteriorEmissive;\n return imWindowEmissive(vImWindowId, uVarLitRatio, uVarWarm, uVarCool,\n uVarCoolChance, uVarBright, uVarDim);\n }\n\n vec2 _imAtlasUV(vec2 cellUV, float cols, float rows, float seed) {\n return imAtlasUV(cellUV, vImWindowId, cols, rows, seed);\n }\n\n vec2 _imFrontUVAt(vec2 localXY) {\n return _imAtlasUV(localXY + 0.5, uFrontAtlasCols, uFrontAtlasRows, 7.13);\n }\n vec2 _imFrontUV() { return _imFrontUVAt(vInteriorLocalXY); }\n\n // Parallax-shift the local-XY where we sample the front overlay so it appears\n // to sit on the INSIDE face of a thin glass pane of thickness uGlassThickness.\n // Without this the overlay reads like a sticker glued to the outside.\n vec2 _imFrontShiftedXY() {\n vec3 origin = vec3(vInteriorLocalXY, 0.0);\n vec3 dir = normalize(origin - vInteriorCameraLocal);\n // dir.z is negative when camera is in front of the plane (its expected case).\n // Step along the ray to depth -uGlassThickness.\n float dz = dir.z;\n float safe = (abs(dz) < 0.05) ? 0.05 * sign(dz + 1e-6) : dz;\n return vInteriorLocalXY + dir.xy * (-uGlassThickness * vImLod / safe);\n }\n\n vec3 _imInteriorRGB(vec2 refractOffset) {\n vec2 cellUV = imRoomBoxUV(vInteriorCameraLocal, vInteriorLocalXY, uDepth, uBackScale) + refractOffset;\n // uLod = 0 collapses the room to a flat cell sample (LOD impostor match)\n cellUV = mix(vInteriorLocalXY + 0.5, cellUV, vImLod);\n vec2 atlasUV = _imAtlasUV(cellUV, uBackAtlasCols, uBackAtlasRows, 0.0);\n return texture2D(uBackAtlas, atlasUV).rgb;\n }\n"; export declare const fragmentMapReplacement = "\n // Sample dirt once and reuse for refraction perturbation + roughness modulation.\n vec2 _imRefractOffset = vec2(0.0);\n float _imDirt = 0.0;\n #ifdef HAS_GLASS_DIRT\n vec3 _imDirtSample = texture2D(uGlassDirtMap, _imFrontUV()).rgb;\n _imDirt = _imDirtSample.r;\n _imRefractOffset = (_imDirtSample.rg - 0.5) * uRefractionStrength;\n #endif\n\n vec3 _imInterior = _imInteriorRGB(_imRefractOffset);\n float _imFrontA = 0.0;\n\n #ifdef HAS_FRONT_ATLAS\n // Sample the overlay at the parallax-shifted XY so it reads as INSIDE the glass.\n vec4 _imFront = texture2D(uFrontAtlas, _imFrontUVAt(_imFrontShiftedXY()));\n // alphaBoost > 1 makes semi-transparent pixels read as more opaque (pow with 1/boost\n // is a gamma-like curve on alpha). Useful for night mode with sheer curtain textures\n // that would otherwise leak a lot of interior light.\n _imFrontA = pow(clamp(_imFront.a, 0.0, 1.0), 1.0 / max(uFrontAlphaBoost, 0.001));\n diffuseColor.rgb *= _imFront.rgb * _imFrontA;\n #else\n diffuseColor.rgb = vec3(0.0);\n #endif\n\n diffuseColor.a = 1.0;\n\n // Interior light transmission through the front layer. Where front alpha = 0 (glass),\n // the interior passes through unchanged. Where alpha = 1 (curtain), a fraction\n // (uFrontTransmission) of the interior bleeds through, tinted by the front color \u2014\n // so a red curtain glows red against a lit room.\n vec3 _imLitRoom = _imInterior * _imWindowEmissive();\n #ifdef HAS_FRONT_ATLAS\n vec3 _imTransmitTint = mix(_imFront.rgb, vec3(1.0), 1.0 - _imFrontA);\n float _imTransmitAmt = mix(uFrontTransmission, 1.0, 1.0 - _imFrontA);\n vec3 _imInteriorEmissive = _imLitRoom * _imTransmitTint * _imTransmitAmt;\n #else\n vec3 _imInteriorEmissive = _imLitRoom;\n #endif\n"; export declare const fragmentRoughnessReplacement = "\n float roughnessFactor = roughness;\n #ifdef HAS_FRONT_ROUGHNESS\n roughnessFactor *= texture2D(uFrontRoughnessAtlas, _imFrontUV()).g;\n #endif\n #ifdef HAS_GLASS_DIRT\n // Dirt only roughens the glass area (where the front overlay alpha is ~0).\n // Centered noise around 0.5; positive contribution raises roughness, negative lowers.\n float _imGlassMask = 1.0 - _imFrontA;\n roughnessFactor += (_imDirt - 0.5) * uGlassDirtStrength * _imGlassMask;\n roughnessFactor = clamp(roughnessFactor, 0.0, 1.0);\n #endif\n"; export declare const fragmentMetalnessReplacement = "\n float metalnessFactor = metalness;\n #ifdef HAS_FRONT_METALNESS\n metalnessFactor *= texture2D(uFrontMetalnessAtlas, _imFrontUV()).b;\n #endif\n"; export declare const fragmentNormalReplacement = "\n #ifdef HAS_FRONT_NORMAL\n vec3 _imNTex = texture2D(uFrontNormalAtlas, _imFrontUV()).xyz * 2.0 - 1.0;\n _imNTex.xy *= uFrontNormalScale;\n vec3 _imN = normalize(normal);\n vec3 _imT = normalize(vec3(1.0, 0.0, 0.0) - dot(vec3(1.0, 0.0, 0.0), _imN) * _imN);\n vec3 _imB = cross(_imN, _imT);\n normal = normalize(mat3(_imT, _imB, _imN) * _imNTex);\n #endif\n"; export declare const fragmentOutput = "\n gl_FragColor.rgb += _imInteriorEmissive;\n gl_FragColor.a *= vImFade; // per-instance LOD fade (1.0 unless instanced)\n\n // \u2500\u2500 Glass surface pass \u2500\u2500\n // Compositing model now: bottom = interior (already added above), top = a\n // visible glass surface layer that exists only where _imFrontA is ~0.\n // Two cues sell the surface: (1) Schlick fresnel sheen so the pane catches\n // sky/light at grazing angles, (2) the dirt noise becomes a faint additive\n // smudge so you literally see particles ON the glass in front of the room.\n float _imGlassSurfaceMask = 1.0 - _imFrontA;\n vec3 _imViewLocal = normalize(vInteriorCameraLocal - vec3(vInteriorLocalXY, 0.0));\n float _imNdotV = clamp(_imViewLocal.z, 0.0, 1.0);\n float _imFresnel = pow(1.0 - _imNdotV, 5.0) * vImLod; // glass cues fade out with LOD\n gl_FragColor.rgb += uGlassFresnelColor * uGlassFresnelStrength * _imFresnel * _imGlassSurfaceMask;\n\n #ifdef HAS_GLASS_DIRT\n // Subtle bright dust/smudges sitting on the glass. View-angle weighted so\n // they catch most at grazing \u2014 same way real grime reads on a window.\n float _imSmudge = max(_imDirt - 0.45, 0.0) * (0.35 + 0.65 * _imFresnel);\n gl_FragColor.rgb += vec3(_imSmudge) * uGlassSmudgeStrength * _imGlassSurfaceMask * vImLod;\n #endif\n";