--# CubeCornerStretch
CubeCornerStretch = {
    name = "CubeCornerStretch",
    options = {},
    properties = {
        env        = {"cubeTexture", nil},
        useWorld   = {"float", 1.0},
        face       = {"float", 4.0},
        corner     = {"float", 2.0},
        sideFrac   = {"float", 0.33205676078796},
        offU       = {"float", 0.00},
        offV       = {"float", 0.00},
        padPix     = {"float", 0.75},   -- small inward pad to avoid literal edge texels
        facePx     = {"float", 256.0},
        mipBias    = {"float", 0.15},   -- tiny blur helps hide residual edges
        scale      = {"float", 1.0},
        edgeClamp  = {"float", 0.995},
        seamWidthPx= {"float", 2.0},    -- FEATHER width in pixels from patch edge
        seamMix    = {"float", 0.55},   -- how strongly to blend near edges (0..1)
    },
    
    pass = {
        base = "Surface",
        depthWrite = true, depthFunc = "lessEqual",
        cullFace = "back", renderQueue = "solid", colorMask = {"rgba"},
        
        vertex = [[
        uniform mat4 modelViewProjection;
        out vec3 vObjN;
        out vec3 vWldN;
        void vertex(inout Vertex v, out Input o){
            vec3 wN = normalize((modelMatrix * vec4(v.normal, 0.0)).xyz);
            o.normal = wN;
            vObjN = v.normal;
            vWldN = wN;
            gl_Position = modelViewProjection * vec4(v.position, 1.0);
        }]],

surface = [[
precision highp float;

uniform samplerCube env;
uniform float useWorld, face, corner, sideFrac, offU, offV, padPix, facePx, mipBias, scale, edgeClamp;
uniform float seamWidthPx, seamMix;

in vec3 vObjN; in vec3 vWldN;

vec3 faceUVToDir(int f, vec2 uv){
    if (f==0) return normalize(vec3( 1.0,  uv.y, -uv.x));
    if (f==1) return normalize(vec3(-1.0,  uv.y,  uv.x));
    if (f==2) return normalize(vec3( uv.x,  1.0, -uv.y));
    if (f==3) return normalize(vec3( uv.x, -1.0,  uv.y));
    if (f==4) return normalize(vec3( uv.x,  uv.y,  1.0));
    return        normalize(vec3(-uv.x,  uv.y, -1.0));
}
void dirToFaceUV(vec3 d, out int f, out vec2 uv){
    vec3 ad = abs(d);
    if(ad.x>=ad.y && ad.x>=ad.z){
        if(d.x>=0.0){ f=0; uv=vec2(-d.z,-d.y)/ad.x; } else { f=1; uv=vec2( d.z,-d.y)/ad.x; }
    }else if(ad.y>=ad.x && ad.y>=ad.z){
        if(d.y>=0.0){ f=2; uv=vec2( d.x,  d.z)/ad.y; } else { f=3; uv=vec2( d.x,-d.z)/ad.y; }
    }else{
        if(d.z>=0.0){ f=4; uv=vec2( d.x,-d.y)/ad.z; } else { f=5; uv=vec2(-d.x,-d.y)/ad.z; }
    }
}

void cornerBasis(int cid, out vec2 base, out vec2 stepU, out vec2 stepV){
    if (cid==0){ base=vec2(-1.0,-1.0); stepU=vec2( 1.0, 0.0); stepV=vec2(0.0,  1.0); }
    if (cid==1){ base=vec2( 1.0,-1.0); stepU=vec2(-1.0, 0.0); stepV=vec2(0.0,  1.0); }
    if (cid==2){ base=vec2(-1.0, 1.0); stepU=vec2( 1.0, 0.0); stepV=vec2(0.0, -1.0); }
    if (cid==3){ base=vec2( 1.0, 1.0); stepU=vec2(-1.0, 0.0); stepV=vec2(0.0, -1.0); }
}

vec3 tap(int f, vec2 uv, float lod){
    uv = clamp(uv, vec2(-edgeClamp), vec2(edgeClamp));
    return textureLod(env, faceUVToDir(f, uv), lod).rgb;
}

void surface(in Input IN, inout SurfaceOutput o){
    // which face, and its direction-space uv in [-1,1]
    int f; vec2 uvDir;
    vec3 d = normalize( (useWorld>=0.5) ? vWldN : vObjN );
    dirToFaceUV(d, f, uvDir);
    
    // patch sizing
    float span = clamp(sideFrac * scale * 2.0, 0.001, 0.999);
    float uvPad = max(0.0, padPix) / max(1.0, facePx);
    
    // corner frame
    int cid = int(clamp(corner, 0.0, 3.0));
    vec2 base, stepU, stepV; cornerBasis(cid, base, stepU, stepV);
    
    // offsets inside the corner (0..1-span)
    float maxOff = max(0.0, 1.0 - span);
    float u0 = clamp(offU, 0.0, maxOff);
    float v0 = clamp(offV, 0.0, maxOff);
    
    // map to [0,1] quad by direction, then into the chosen corner
        vec2 uv01 = clamp(uvDir * 0.5 + 0.5, 0.0, 1.0);
        vec2 uvFace = base
        + stepU * (u0 + uvPad + (span - 2.0*uvPad) * uv01.x)
        + stepV * (v0 + uvPad + (span - 2.0*uvPad) * uv01.y);
        
        // base color
        vec3 col = tap(f, uvFace, mipBias);
        
        // -------- seam feather (only near patch edge) --------
        float seamUV = max(1.0/facePx, 0.0005);            // 1 px in uv
        float halfW  = (seamWidthPx / facePx);             // feather region in uv units
        float edge   = min(min(uv01.x, 1.0-uv01.x), min(uv01.y, 1.0-uv01.y)); // distance to edge inside [0,0.5]
        float w = smoothstep(halfW, 0.0, edge) * clamp(seamMix, 0.0, 1.0);
        
        if (w > 0.0){
            // sample slightly inward along both patch axes
            vec3 cU = tap(f, uvFace + stepU * seamUV, mipBias);
            vec3 cV = tap(f, uvFace + stepV * seamUV, mipBias);
            vec3 cD = tap(f, uvFace + (stepU + stepV) * seamUV, mipBias);
            vec3 avg = (col + cU + cV + cD) / 4.0;
            col = mix(col, avg, w);
        }
        // ------------------------------------------------------
        
        o.diffuse  = vec3(0.0);
        o.emission = col;
        o.emissive = 1.0;
        o.opacity  = 1.0;
    }
]]
}}