uniform float far;
uniform vec3 cameraPosition;
uniform vec2 viewSize;

#include "../../BlockMappings.glsl"
#include "../../includes/Voxelization.glsl"

layout (r32ui) uniform uimage2D voxel_data_img;
layout (r32ui) uniform uimage2D voxel_data_img2;
layout (r32ui) uniform uimage2D colorimg3;

/**********************************************************************/
#if defined vsh

attribute vec4 at_tangent;
attribute vec3 at_midBlock;
attribute vec3 mc_Entity;
attribute vec2 mc_midTexCoord;

uniform sampler2D tex;

uniform mat4 gbufferModelView;
uniform mat4 gbufferModelViewInverse;
uniform mat4 gbufferProjection;

uniform vec3 previousCameraPosition;
ivec2 atlasSize = ivec2(textureSize(tex, 0).xy);

uniform float frameTimeCounter;
uniform int frameCounter;
uniform bool accum;

#include "../../includes/Random.glsl"


#define tanMat _tanMat
#define vertexColor _vertexColor
#define worldPos _worldPos
#define viewPos _viewPos
#define voxelPos _voxelPos
#define midTexcoord _midTexcoord
#define cornerTexcoord _cornerTexcoord
#define texcoord _texcoord
#define spriteSize _spriteSize
#define blockID _blockID

out mat3 tanMat;
out vec4 vertexColor;
out vec3 worldPos;
out vec3 viewPos;
out vec3 voxelPos;
flat out vec2 midTexcoord;
flat out vec2 cornerTexcoord;
out vec2 texcoord;
flat out ivec2 spriteSize;
flat out int blockID;
flat out ivec3 ivoxelPos;
flat out uint packed_tex_coord;
flat out uint packedVoxelData;
out vec2 lmcoord;

uint packedVoxelData2;

// Returns the tangent to world space matrix
mat3 GetTangentMat() {
    // World-space normal matrix
    mat3 normal_mat = mat3(gbufferModelViewInverse) * gl_NormalMatrix;
    
    vec3 tangent  = normalize(normal_mat * at_tangent.xyz);
    vec3 normal   = normalize(normal_mat *  gl_Normal.xyz);
    vec3 binormal = normalize(cross(tangent, normal));
    
    return mat3(tangent, binormal, normal);
}

void main() {
    vertexColor    = gl_Color;
    texcoord       = gl_MultiTexCoord0.xy;
    tanMat         = GetTangentMat();
    //blockID        = backport_id(int(mc_Entity.x)) % 256;
    blockID        = backport_id(int(mc_Entity.x));  // mc_Entity.x <= 32767 in OptiFine
    midTexcoord    = mc_midTexCoord;
    cornerTexcoord = mc_midTexCoord.xy - abs(mc_midTexCoord.xy - texcoord);
    vec2 texDirection = sign(texcoord - mc_midTexCoord)*vec2(1,sign(at_tangent.w));
    worldPos    = (gbufferModelViewInverse * gl_ModelViewMatrix * gl_Vertex).xyz;
    vec3 triCentroid = worldPos.xyz - (tanMat * vec3(texDirection,0.0)) - tanMat[2] / 32.0;
    triCentroid = mix(worldPos, triCentroid, 0.5);
    voxelPos    = WorldToVoxelSpace(mix(worldPos, triCentroid, exp2(-11))) + tanMat[2] * exp2(-11);
    // worldPos = mix(worldPos, triCentroid, -0.2);
    viewPos     = (gbufferModelView * vec4(worldPos, 1.0)).xyz;

    vec3 centroidOffset = at_midBlock / 64.0;
    vec3 blockCentroid = worldPos + centroidOffset;
    
    #if (defined gbuffers_hand) || (defined gbuffers_hand_water)
    gl_Position = ftransform();
      // If gl_Position is calculated the same for the hand as for the others, the hand is buried in object that is close to the player.
      // To prioritize the drawing of the hand at the very front, it is necessary to use this method.
    #else
    gl_Position = gbufferProjection * vec4(viewPos, 1.0);
    #endif
    gl_Position.xy += TAAHash() * gl_Position.w;
    lmcoord  = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy;
    
    // spriteSize is required in fsh (referenced as the divisor). therefore, spriteSize must be set before return statement. if don't set spriteSize 
    // before vsh returns, fsh will cause undefined error.
    spriteSize = ivec2(exp2(round(log2(abs(midTexcoord - texcoord) * 2.0 * atlasSize))));
    //spriteSize = ivec2(round(abs(midTexcoord - texcoord) * 2.0 * atlasSize));
    // stop rounding to a power of 2, since there are blocks whose size is not a power of 2, such as slabs and layered snow.
    
    // ivoxelPos is required in fsh. therefore, ivoxelPos must be set before return statement.
    ivoxelPos = ivec3(WorldToVoxelSpace(triCentroid));
    
    if (!is_voxelized(blockID))
        return;
    
    if (((blockID == 3 ||
          blockID == 4 ||
          blockID == 200 ||  // water
          (blockID >=  6 && blockID <= 12))  // Snow layers
         && abs(tanMat[2].y) < 0.9) ||
        ((blockID >= 14 && blockID <= 21)
         && abs(tanMat[2].y) < 0.9)
    )
    //    return;
        {
            // in the case of slabs, and so on, voxel positions may be shifted by 1 depending on the face, so set correct voxel positions and pass them to fsh.
            ivoxelPos = ivec3(WorldToVoxelSpace(blockCentroid));
            return;
        }
    
    // Omit unnecessary voxel data
    //if (blockID == 5 && (abs(tanMat[2]).y < 0.9 || abs(fract(WorldToVoxelSpace(triCentroid).y) - 0.5) > 0.1 ))  // stairs
    //    return;
    if (blockID >= 1000 && blockID <= 1023 && (abs(tanMat[2]).y < 0.9 || abs(fract(WorldToVoxelSpace(triCentroid).y) - 0.5) > 0.1 ))  // stairs
    //    return;
        {
            ivoxelPos = ivec3(WorldToVoxelSpace(blockCentroid));
            return;
        }
    //if (blockID >= 1000 && blockID <= 1023 && abs(fract(WorldToVoxelSpace(triCentroid).x) - 0.5) > 0.5 )  // stairs
    //    return;
    //if (blockID >= 1000 && blockID <= 1023 && abs(fract(WorldToVoxelSpace(triCentroid).z) - 0.5) > 0.5 )  // stairs
    //    return;
    /*
    vec3 thin = abs(fract(WorldToVoxelSpace(triCentroid)) - 0.5);
    float defectiveVoxel = 0.0;
    defectiveVoxel = (blockID == 23 || blockID == 27 || blockID == 24 || blockID == 28) ? thin.x : defectiveVoxel;  // door  east/west
    defectiveVoxel = (blockID == 25 || blockID == 29 || blockID == 26 || blockID == 30) ? thin.z : defectiveVoxel;  // door  north/south
    defectiveVoxel = (blockID == 31 || blockID == 32 || blockID == 37 || blockID == 38) ? thin.y : defectiveVoxel;  // trapdoor  top/bottom
    defectiveVoxel = (blockID == 33 || blockID == 39 || blockID == 34 || blockID == 40) ? thin.x : defectiveVoxel;  // trapdoor  east/west
    defectiveVoxel = (blockID == 35 || blockID == 41 || blockID == 36 || blockID == 42) ? thin.z : defectiveVoxel;  // trapdoor  north/south
    defectiveVoxel = (blockID == 14 || blockID == 15) ? thin.z : defectiveVoxel;  // button(wall)  north/south
    defectiveVoxel = (blockID == 16 || blockID == 17) ? thin.x : defectiveVoxel;  // button(wall)  east/west
    if (defectiveVoxel > 0.1) return;
    */
    
    //spriteSize = ivec2(exp2(round(log2(abs(midTexcoord - texcoord) * 2.0 * atlasSize))));
        //if ((blockID == 3 || blockID == 4) && spriteSize.x == 0) spriteSize = ivec2(512, 512);  // 2^9=512
        //if (spriteSize.x == 0) spriteSize = ivec2(512, 512);  // 2^9=512
    
    //ivoxelPos = ivec3(WorldToVoxelSpace(triCentroid));

    // Omit unnecessary voxel data (to completely prevent voxel duplicated)
    if (ivoxelPos != ivec3(WorldToVoxelSpace(blockCentroid)))
    //    return;
        {
            ivoxelPos = ivec3(WorldToVoxelSpace(blockCentroid));
            return;
        }
    
    // Store into chunk bitmap so that this chunk will be allocated next frame.
    imageStore(colorimg3, old_get_sparse_chunk_coord(ivoxelPos), uvec4(1));
    imageStore(colorimg3, old_get_sparse_chunk_coord(ivoxelPos) + DUPLICATE0, uvec4(1));
    
    // Allocate extra chunks along the movement vector.
    // This prevents flickering when stepping over chunk borders.
    vec2 chunkGrow = 16.0 * sign(previousCameraPosition.xz - cameraPosition.xz);
    imageStore(colorimg3, old_get_sparse_chunk_coord(ivoxelPos + ivec3(chunkGrow.x, 0, 0)), uvec4(1));
    imageStore(colorimg3, old_get_sparse_chunk_coord(ivoxelPos + ivec3(chunkGrow.x, 0, 0)) + DUPLICATE0, uvec4(1));
    imageStore(colorimg3, old_get_sparse_chunk_coord(ivoxelPos + ivec3(0, 0, chunkGrow.y)), uvec4(1));
    imageStore(colorimg3, old_get_sparse_chunk_coord(ivoxelPos + ivec3(0, 0, chunkGrow.y)) + DUPLICATE0, uvec4(1));
    
    uint chunkAddr = imageLoad(colorimg3, old_get_sparse_chunk_coord(ivoxelPos) + SPARSE0).r;
    bool allocFlag = chunkAddr != 0;
    if (!allocFlag)
        return;
    
    // Store all of its data into the sparse texture
    packed_tex_coord = packUnorm2x16(cornerTexcoord);
    
    vec2 hueSat           = RGBtoHSV(vertexColor.rgb).rg;
    packedVoxelData = 0;
    //packedVoxelData |= blockID;
    packedVoxelData |= int(clamp(hueSat.r, 0.0, 1.0) * 255.0) << VBM_hue_start;
    packedVoxelData |= int(clamp(hueSat.g, 0.0, 1.0) * 255.0) << VBM_sat_start;
    packedVoxelData |= int(round(log2(spriteSize.x))) << VBM_sprite_size_start;
    if (is_sub_voxel(blockID)) packedVoxelData |= VBM_AABB_bit;

    packedVoxelData2 = uint(blockID);

    ivec2 chunkCoord = get_sparse_voxel_coord(chunkAddr, ivoxelPos, 0);
        uint sparseVoxelAddr = 2 * get_sparse_voxel_addr(chunkAddr, ivoxelPos, 0) + get_lod_base_addr(8) - get_lod_base_addr(5);  // debug
        imageAtomicMax(colorimg3, ivec2(1000, chunk_map_height + 1), sparseVoxelAddr);  // debug voxel_max_addr
        imageAtomicMax(colorimg3, ivec2(1001, chunk_map_height + 1), chunkCoord.x);  // debug voxel_max_x
        imageAtomicMax(colorimg3, ivec2(1002, chunk_map_height + 1), chunkCoord.y);  // debug voxel_max_y
    
    imageAtomicMax(voxel_data_img, chunkCoord, packed_tex_coord);
    imageAtomicMax(voxel_data_img, chunkCoord + DATA0, packedVoxelData);
    imageAtomicMax(voxel_data_img2, chunkCoord + DATA0, packedVoxelData2);
        //imageAtomicMax(voxel_data_img2, chunkCoord, floatBitsToUint(tanMat[2].y));  // debug
        //if ((gl_VertexID % 4) == 0) imageAtomicMax(voxel_data_img2, chunkCoord, floatBitsToUint(tanMat[2].y));  // debug
        //imageAtomicAdd(voxel_data_img2, chunkCoord, 1);  // debug
        imageAtomicMax(voxel_data_img2, chunkCoord, spriteSize.y);  // debug
    
    for (int lod = 1; lod <= 4; ++lod) {
        chunkCoord = get_sparse_voxel_coord(chunkAddr, ivoxelPos, lod);
        imageStore(voxel_data_img, chunkCoord + DATA0, uvec4(1));
    }
}

#endif
/**********************************************************************/

/**********************************************************************/
#if defined fsh

uniform sampler2D tex;
uniform usampler2D normals;
uniform sampler2D specular;
uniform sampler2D noisetex;

uniform sampler2D colortex7;
uniform usampler2D voxel_data_tex;

layout (r32ui) uniform uimage2D colorimg5;

#undef atlas_tex_n
#define atlas_tex_n normals

uniform int frameCounter;
uniform float frameTimeCounter;

uniform mat4 gbufferModelView;
uniform mat4 gbufferProjection;

ivec2 atlasSize = ivec2(textureSize(tex, 0).xy);

in mat3 _tanMat;
in vec4 _vertexColor;
in vec3 _worldPos;
in vec3 _viewPos;
in vec3 _voxelPos;
in vec2 _texcoord;
flat in vec2 _cornerTexcoord;
flat in ivec2 _spriteSize;
flat in int _blockID;
flat in ivec3 ivoxelPos;
flat in uint packed_tex_coord;
flat in uint packedVoxelData;
in vec2 lmcoord;
uniform vec4 entityColor;

#include "../../includes/debug.glsl"

#include "/lib/Pack.glsl"

float EncodeNormal(vec3 normal) {
    const float bits = 11.0;
    
	normal    = clamp(normal, -1.0, 1.0);
	normal.xy = vec2(atan(normal.x, normal.z), acos(normal.y)) / 3.14159;
	normal.x += 1.0;
	normal.xy = round(normal.xy * exp2(bits));
	
	return normal.x + normal.y * exp2(bits + 2.0);
}

#include "../../includes/Parallax.glsl"
#include "/includes/PomException.glsl"

#ifdef PARALLAX
/* RENDERTARGETS:6,7,12 */
#else
/* RENDERTARGETS:6,7 */
#endif


uint GetVoxelData(ivec3 voxelPos) {
    uint chunkAddr = imageLoad(colorimg3, old_get_sparse_chunk_coord(voxelPos) + SPARSE0).r;
    
    //if (chunkAddr == 0)
    //    return -1;
    
    ivec2 voxelCoord = get_sparse_voxel_coord(chunkAddr, voxelPos, 0);
    //return imageLoad(voxel_data_img, voxelCoord).x;
    uint ret = (chunkAddr == 0) ? 0 : imageLoad(voxel_data_img, voxelCoord).x;
    return ret;
}

uint GetPrevVoxelData(ivec3 voxelPos) {
    uint chunkAddr = imageLoad(colorimg3, old_get_sparse_chunk_coord(voxelPos) + SPARSE0).r;
    
    //if (chunkAddr == 0)
    //    return -1;
    
    ivec2 voxelCoord = get_sparse_voxel_coord(chunkAddr, voxelPos, 0);
    //return imageLoad(voxel_data_img2, voxelCoord).x;
    uint ret = (chunkAddr == 0) ? 0 : imageLoad(colorimg5, voxelCoord).x;
    return ret;
}

bool VoxelDataExists(ivec3 voxelPos) {
    uint chunkAddr = imageLoad(colorimg3, old_get_sparse_chunk_coord(voxelPos) + SPARSE0).r;
    
    //if (chunkAddr == 0)
    //    return false;
    
    ivec2 voxelCoord = get_sparse_voxel_coord(chunkAddr, voxelPos, 0);
    //return (imageLoad(voxel_data_img, voxelCoord).x != 0 || imageLoad(voxel_data_img2, voxelCoord).x != 0);
    bool ret = (chunkAddr == 0) ? false : (imageLoad(voxel_data_img, voxelCoord).x != 0 || imageLoad(colorimg5, voxelCoord).x != 0);
    return ret;
}

ivec2 GetVoxelCoord(ivec3 voxelPos) {
    uint chunkAddr = imageLoad(colorimg3, old_get_sparse_chunk_coord(voxelPos) + SPARSE0).r;
    return get_sparse_voxel_coord(chunkAddr, voxelPos, 0);
}


#define TIME 0.0
#define WAVE_SPEED 1.0
#define WAVE_MULT 1.0

const int noiseTextureResolution = 64; // [16 32 64 128 256 512 1024]
const float noiseRes = float(noiseTextureResolution);
const float noiseResInverse = 1.0 / noiseRes;
const float noiseScale = 64.0 / noiseRes;


#define cubesmooth(x) ((x) * (x) * (3.0 - (x)*2.0))

float GetWaveCoord(float coord) {
    const float madd = 0.5 * noiseResInverse;
    float whole = floor(coord);
    coord = whole + cubesmooth(coord - whole);
    
    return coord * noiseResInverse + madd;
}

vec2 GetWaveCoord(vec2 coord) {
    const vec2 madd = vec2(0.5 * noiseResInverse);
    vec2 whole = floor(coord);
    coord = whole + cubesmooth(coord - whole);
    
    return coord * noiseResInverse + madd;
}

float SharpenWave(float wave) {
    wave = 1.0 - abs(wave * 2.0 - 1.0);
    
    return wave < 0.78 ? wave : (wave * -2.5 + 5.0) * wave - 1.6;
}

const vec4 heights = vec4(29.0, 15.0, 17.0, 4.0);
const vec4 height = heights * WAVE_MULT / (heights.x + heights.y + heights.z + heights.w);

const vec2[4] scale = vec2[4](
    vec2(0.0065, 0.0052  ) * noiseRes * noiseScale,
    vec2(0.013 , 0.00975 ) * noiseRes * noiseScale,
    vec2(0.0195, 0.014625) * noiseRes * noiseScale,
    vec2(0.0585, 0.04095 ) * noiseRes * noiseScale);

const vec4 stretch = vec4(
    scale[0].x * -1.7 ,
    scale[1].x * -1.7 ,
    scale[2].x *  1.1 ,
    scale[3].x * -1.05);

mat4x2 waveTime = mat4x2(0.0);

void SetupWaveFBM() {
    const vec2 disp1 = vec2(0.04155, -0.0165   ) * noiseRes * noiseScale;
    const vec2 disp2 = vec2(0.017  , -0.0469   ) * noiseRes * noiseScale;
    const vec2 disp3 = vec2(0.0555 ,  0.03405  ) * noiseRes * noiseScale;
    const vec2 disp4 = vec2(0.00825, -0.0491625) * noiseRes * noiseScale;
    
    float w = TIME * WAVE_SPEED * 0.6;
    
    waveTime[0] = w * disp1;
    waveTime[1] = w * disp2;
    waveTime[2] = w * disp3;
    waveTime[3] = w * disp4;
}

float GetWaves(vec2 coord, inout mat4x2 c) {
	float waves = 0.0;
	vec2 ebin;
	
	c[0].xy = coord * scale[0] + waveTime[0];
	c[0].y = coord.x * stretch[0] + c[0].y;
	ebin = GetWaveCoord(c[0].xy);
	c[0].x = ebin.x;
	
	waves += SharpenWave(texture2D(noisetex, ebin).x) * height.x;
	
	c[1].xy = coord * scale[1] + waveTime[1];
	c[1].y = coord.x * stretch[1] + c[1].y;
	ebin = GetWaveCoord(c[1].xy);
	c[1].x = ebin.x;
	
	waves += texture2D(noisetex, ebin).x * height.y;
	
	c[2].xy = coord * scale[2] + waveTime[2];
	c[2].y = coord.x * stretch[2] + c[2].y;
	ebin = GetWaveCoord(c[2].xy);
	c[2].x = ebin.x;
	
	waves += texture2D(noisetex, ebin).x * height.z;
	
	c[3].xy = coord * scale[3] + waveTime[3];
	c[3].y = coord.x * stretch[3] + c[3].y;
	ebin = GetWaveCoord(c[3].xy);
	c[3].x = ebin.x;
	
	waves += texture2D(noisetex, ebin).x * height.w;
	
	return waves;
}

float GetWaves(vec2 coord) {
	mat4x2 c;
	
	return GetWaves(coord, c);
}

float GetWaves(mat4x2 c, float offset) {
	float waves = 0.0;
	
	c[0].y = GetWaveCoord(offset * scale[0].y + c[0].y);
	
	waves += SharpenWave(texture2D(noisetex, c[0].xy).x) * height.x;
	
	c[1].y = GetWaveCoord(offset * scale[1].y + c[1].y);
	
	waves += texture2D(noisetex, c[1].xy).x * height.y;
	
	c[2].y = GetWaveCoord(offset * scale[2].y + c[2].y);
	
	waves += texture2D(noisetex, c[2].xy).x * height.z;
	
	c[3].y = GetWaveCoord(offset * scale[3].y + c[3].y);
	
	waves += texture2D(noisetex, c[3].xy).x * height.w;
	
	return waves;
}

vec2 GetWaveDifferentials(vec2 coord, float scale) { // Get finite wave differentials for the world-space X and Z coordinates
	mat4x2 c;
	
	float a  = GetWaves(coord, c);
	float aX = GetWaves(coord + vec2(scale,   0.0));
	float aY = GetWaves(c, scale);
	
	return a - vec2(aX, aY);
}

vec3 ComputeWaveNormals(vec3 worldSpacePosition, vec3 flatWorldNormal) {
	if (WAVE_MULT == 0.0) return vec3(0.0, 0.0, 1.0);
	
	SetupWaveFBM();
	
	float angleCoeff  = dot(normalize(-_worldPos.xyz), normalize(flatWorldNormal));
	      angleCoeff /= clamp(length(_worldPos) * 0.05, 1.0, 10.0);
	      angleCoeff  = clamp(angleCoeff * 2.5, 0.0, 1.0);
	      angleCoeff  = sqrt(angleCoeff);
	
	vec3 worldPos    = _worldPos + cameraPosition;
	     worldPos.xz = worldPos.xz + worldPos.y;
	
	vec2 diff = GetWaveDifferentials(worldPos.xz, 0.1) * angleCoeff;
	
	return vec3(diff, sqrt(1.0 - dot(diff,diff)));
}

void main() {
    vec3 plane = vec3(0,0,1);
    ivec2 corner = ivec2(_cornerTexcoord*atlasSize);
    float LOD = max(0.0, textureQueryLod(tex, _texcoord).y)*0;
    vec3 tangent_pos = vec3((_texcoord - _cornerTexcoord)*atlasSize, 1.0);
    
    vec3 tangent_ray = normalize(_worldPos * _tanMat);
    tangent_pos.xy += 0.5 / _spriteSize;
    
    vec2 tCoord = _texcoord;
    
    if (textureLod(tex, tCoord, LOD).a < 0.1) {
        discard;
    }
    
    vec4 tex_n = uintBitsToFloat(texture(normals, tCoord));
    
    vec3 normal;
    
    vec2 edge = sign(tangent_ray.xy) * 0.5 + 0.5;
        //if (tangent_ray.x == 0.0) tangent_ray.x = 0.00001;
        //if (tangent_ray.y == 0.0) tangent_ray.y = 0.00001;
    /**/
    vec3 cornerPlane = vec3(MinComponent2((edge - tangent_pos.xy / _spriteSize) / tangent_ray.xy), 0.0);
    /**/
        //ivec2 ss = _spriteSize;
        //if (ss.x == 0) ss.x = 1;
        //if (ss.y == 0) ss.y = 1;
        //if (_blockID == 10 || _blockID == 10) ss.y = int(float(ss.x) * 0.625);  // snow:layers=5
        //if (_blockID == 10 || _blockID == 10) ss.y = int(float(ss.y) * 1.05);  // snow:layers=5
        //if (_blockID == 10 && ss.y < 256) ss.y = int(float(ss.y) * 2.0);  // snow:layers=5
        //vec3 cornerPlane = vec3(MinComponent2((edge - tangent_pos.xy / ss) / tangent_ray.xy), 0.0);
        //if (_blockID == 10 && tangent_pos.y > 256.0 && tangent_pos.x < 20.0) cornerPlane = vec3(1.0, 0.0, 0.0);  // snow:layers=5
    ivec3 edgePlane = ivec3(round(abs(_tanMat * cornerPlane) * sign(_worldPos)));
    ivec3 edgeBackPlane = ivec3(round(abs(_tanMat * vec3(1-cornerPlane.xy,0)) * sign(_worldPos)));
    
    // -1 = air, 0 = tiled, 1 = (different block) or (any corner block)
    
    ivec3 ebinPos = ivoxelPos + ivec3(round(_tanMat[2])) + edgePlane;
    int adjStatus = 0; // 0 = occupied by different block. 1 = occupied by same block. -1 = occupied by air
    
    #ifdef PARALLAX
        //uint test001 = EncodePlane(ivec3(round(_tanMat[2])));
        //uint test001 = EncodePlane(edgePlane);
        /*
        //uint test001 = uint(ivec3(_voxelPos + _tanMat[2] * exp2(-11)) == ivoxelPos);
        ivec3 ctex7_vpos = ivec3(_voxelPos + _tanMat[2] * exp2(-11));
        //uint ctex7_vp_x = uint(ctex7_vpos.x == ivoxelPos.x);
        //uint ctex7_vp_y = uint(ctex7_vpos.y == (ivoxelPos.y + 1));
        //uint ctex7_vp_z = uint(ctex7_vpos.z == ivoxelPos.z);
        //uint test001 = (ctex7_vp_x << 2) | (ctex7_vp_y << 1) | ctex7_vp_z;
        ivec3 xpos = ivoxelPos + ivec3(round(_tanMat[2]));
        xpos.y -= (_blockID == 3 && ivec3(round(_tanMat[2])).y > 0) ? ivec3(round(_tanMat[2])).y : 0;
        uint vp_x = uint(ctex7_vpos.x == xpos.x);
        uint vp_y = uint(ctex7_vpos.y == xpos.y);
        uint vp_z = uint(ctex7_vpos.z == xpos.z);
        uint test001 = (vp_x << 2) | (vp_y << 1) | vp_z;
        */
        //uint test001 = uint(_spriteSize.y);
        //uint test001 = int(VoxelDataExists(ebinPos));
        //uint test001 = (uint(ebinPos.x % 256) << 12) | (uint(ebinPos.z % 256) << 4) | uint(ebinPos.y % 16);
        //uint test001 = (uint(edgePlane.x % 256) << 12) | (uint(edgePlane.z % 256) << 4) | uint(edgePlane.y % 16);
        //uint test001 = (uint(ivoxelPos.x % 256) << 12) | (uint(ivoxelPos.z % 256) << 4) | uint(ivoxelPos.y % 16);
            //vec2 test_corner = MinComponent2((edge - tangent_pos.xy / _spriteSize) / tangent_ray.xy);
            //if (_blockID == 10 && ss.y > 256) test_corner = vec2(1.0, 0.0);  // snow:layers=5
            //uint test001 = uint(test_corner.x * 100.0 + test_corner.y);
            //uint test001 = uint(cornerPlane.x * 100.0 + cornerPlane.y * 10.0 + cornerPlane.z);
        //uint test001 = 0;
        /*
        float verticalPosition = fract(vec3(_voxelPos + _tanMat[2] * exp2(-11)).y);
        */
    /*
        float horizontalPositionEW = fract(vec3(_voxelPos + _tanMat[2] * exp2(-11)).x);  // west 0.0 ... 1.0 east
        float horizontalPositionNS = fract(vec3(_voxelPos + _tanMat[2] * exp2(-11)).z);  // north 0.0 ... 1.0 south
        float verticalPosition = fract(vec3(_voxelPos + _tanMat[2] * exp2(-11)).y);  // bottom 0.0 ... 1.0 top
        ivec3 cubeFace = ivec3(round(_tanMat[2]));
        // 1004-1007 : stairs shapes inside the corner
        bool insideFaceL1 = ((_blockID >= 1004 && _blockID <= 1007) && (cubeFace.x != 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53));
        bool insideFaceL2 = ((_blockID >= 1004 && _blockID <= 1007) && (cubeFace.z != 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53));
        bool insideFaceR1 = ((_blockID >= 1004 && _blockID <= 1007) && (cubeFace.z > 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53 && horizontalPositionEW > 0.5));
        bool insideFaceR2 = ((_blockID >= 1004 && _blockID <= 1007) && (cubeFace.z < 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53 && horizontalPositionEW < 0.5));
        bool insideFaceR3 = ((_blockID >= 1004 && _blockID <= 1007) && (cubeFace.x > 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53 && horizontalPositionNS < 0.5));
        bool insideFaceR4 = ((_blockID >= 1004 && _blockID <= 1007) && (cubeFace.x < 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53 && horizontalPositionNS > 0.5));
        bool insideFace01 = ((_blockID >= 1000 && _blockID <= 1003) && (cubeFace.x != 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53));
        bool insideFace02 = ((_blockID >= 1000 && _blockID <= 1003) && (cubeFace.z != 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53));
        // 1012-1015 : stairs upside down shapes normal
        bool insideFaceLower1 = ((_blockID >= 1012 && _blockID <= 1015) && (cubeFace.x != 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53));
        bool insideFaceLower2 = ((_blockID >= 1012 && _blockID <= 1015) && (cubeFace.z != 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53));
        // 1016-1019 : stairs upside down shapes inside the corner
        bool insideFaceLowerR1 = ((_blockID >= 1016 && _blockID <= 1019) && (cubeFace.x != 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53));
        bool insideFaceLowerR2 = ((_blockID >= 1016 && _blockID <= 1019) && (cubeFace.z != 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53));
        bool insideFaceLowerL1 = ((_blockID >= 1016 && _blockID <= 1019) && (cubeFace.z > 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53 && horizontalPositionEW < 0.5));
        bool insideFaceLowerL2 = ((_blockID >= 1016 && _blockID <= 1019) && (cubeFace.z < 0 && horizontalPositionNS > 0.47 && horizontalPositionNS < 0.53 && horizontalPositionEW > 0.5));
        bool insideFaceLowerL3 = ((_blockID >= 1016 && _blockID <= 1019) && (cubeFace.x > 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53 && horizontalPositionNS > 0.5));
        bool insideFaceLowerL4 = ((_blockID >= 1016 && _blockID <= 1019) && (cubeFace.x < 0 && horizontalPositionEW > 0.47 && horizontalPositionEW < 0.53 && horizontalPositionNS < 0.5));
        
        bool outsideFace1 = (((_blockID == 1000 && cubeFace.z < 0) || (_blockID == 1001 && cubeFace.z > 0) ||
                              (_blockID == 1002 && cubeFace.x > 0) || (_blockID == 1003 && cubeFace.x < 0)) && 
                             verticalPosition > 0.5);
        bool outsideFace2 = ((_blockID >= 1000 && _blockID <= 1011) && (cubeFace.x != 0 || cubeFace.z != 0) && verticalPosition < 0.5);
        // 1008-1011 : stairs shapes outside the corner
        bool outsideFaceL1 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.z > 0 && horizontalPositionEW < 0.5 && verticalPosition > 0.5));
        bool outsideFaceL2 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.z < 0 && horizontalPositionEW > 0.5 && verticalPosition > 0.5));
        bool outsideFaceL3 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.x > 0 && horizontalPositionNS > 0.5 && verticalPosition > 0.5));
        bool outsideFaceL4 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.x < 0 && horizontalPositionNS < 0.5 && verticalPosition > 0.5));
        bool outsideFaceR1 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.z > 0 && horizontalPositionEW > 0.5 && verticalPosition > 0.5));
        bool outsideFaceR2 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.z < 0 && horizontalPositionEW < 0.5 && verticalPosition > 0.5));
        bool outsideFaceR3 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.x > 0 && horizontalPositionNS < 0.5 && verticalPosition > 0.5));
        bool outsideFaceR4 = ((_blockID >= 1008 && _blockID <= 1011) && (cubeFace.x < 0 && horizontalPositionNS > 0.5 && verticalPosition > 0.5));
        // 1012-1015 : stairs upside down shapes normal
        bool outsideFaceUSD1 = (((_blockID == 1012 && cubeFace.z < 0) || (_blockID == 1013 && cubeFace.z > 0) ||
                                   (_blockID == 1014 && cubeFace.x > 0) || (_blockID == 1015 && cubeFace.x < 0)) && 
                                  verticalPosition < 0.5);
        bool outsideFaceUSD2 = ((_blockID >= 1012 && _blockID <= 1023) && (cubeFace.x != 0 || cubeFace.z != 0) && verticalPosition > 0.5);
        // 1020-1023 : stairs upside down shapes outside the corner
        bool outsideFaceLowerL1 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.z > 0 && horizontalPositionEW < 0.5 && verticalPosition < 0.5));
        bool outsideFaceLowerL2 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.z < 0 && horizontalPositionEW > 0.5 && verticalPosition < 0.5));
        bool outsideFaceLowerL3 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.x > 0 && horizontalPositionNS > 0.5 && verticalPosition < 0.5));
        bool outsideFaceLowerL4 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.x < 0 && horizontalPositionNS < 0.5 && verticalPosition < 0.5));
        bool outsideFaceLowerR1 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.z > 0 && horizontalPositionEW > 0.5 && verticalPosition < 0.5));
        bool outsideFaceLowerR2 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.z < 0 && horizontalPositionEW < 0.5 && verticalPosition < 0.5));
        bool outsideFaceLowerR3 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.x > 0 && horizontalPositionNS < 0.5 && verticalPosition < 0.5));
        bool outsideFaceLowerR4 = ((_blockID >= 1020 && _blockID <= 1023) && (cubeFace.x < 0 && horizontalPositionNS > 0.5 && verticalPosition < 0.5));
    */
        /*
        bool verticalMiddle = (_blockID >= 1000 && _blockID <= 1023 && edgePlane.y != 0 && verticalPosition > 0.25 && verticalPosition < 0.75);  // central boundary of the stair block
          // sides of stairs are rendered with two textures: upper half / lower half.  applying Pom Silhouette to each of these would also apply it to the boundary between upper half and lower half of the stair block, so prevent it in that area.
        
        //                                                                     back of stair      side of stair      flat part of side
        bool horizontalFlatPart1 = ((_blockID == 1000 || _blockID == 1012) && (cubeFace.z < 0 || (cubeFace.x != 0 && horizontalPositionNS < 0.53)));  //stairs-north-straight-(top/bottom)
        bool horizontalFlatPart2 = ((_blockID == 1001 || _blockID == 1013) && (cubeFace.z > 0 || (cubeFace.x != 0 && horizontalPositionNS > 0.47)));  //stairs-south-straight-(top/bottom)
        bool horizontalFlatPart3 = ((_blockID == 1002 || _blockID == 1014) && (cubeFace.x > 0 || (cubeFace.z != 0 && horizontalPositionEW > 0.47)));  //stairs-east-straight-(top/bottom)
        bool horizontalFlatPart4 = ((_blockID == 1003 || _blockID == 1015) && (cubeFace.x < 0 || (cubeFace.z != 0 && horizontalPositionEW < 0.53)));  //stairs-west-straight-(top/bottom)
        //bool preventPomSilhouette = verticalMiddle && (horizontalFlatPart1 || horizontalFlatPart2 || horizontalFlatPart3 || horizontalFlatPart4);
        */
        bool preventPomSilhouette = preventPomSil(_blockID, edgePlane, ivec3(round(_tanMat[2])), fract(_voxelPos + _tanMat[2] * exp2(-11)));
    //if (GetVoxelData(ebinPos) == 0) {
    //if (!VoxelDataExists(ebinPos)) {
    if (!VoxelDataExists(ebinPos) && !preventPomSilhouette) {
        ivec3 ebinPos = ivoxelPos + edgePlane;
        //uint chunkAddr = imageLoad(colorimg3, old_get_sparse_chunk_coord(ebinPos) + SPARSE0).r;
        //ivec2 chunkCoord = get_sparse_voxel_coord(chunkAddr, ebinPos, 0);
            //test001 = (uint(ebinPos.x % 256) << 12) | (uint(ebinPos.z % 256) << 4) | uint(ebinPos.y % 16);
        uint data = GetVoxelData(ebinPos);
        uint data2 = GetPrevVoxelData(ebinPos);
        //if (data == 0) {
        if (data == 0 && data2 == 0) {
            adjStatus = -1;
        }
        else if (data == packed_tex_coord) {
            adjStatus = 1;
            //if (GetVoxelData(ivoxelPos + edgePlane + edgeBackPlane + ivec3(round(_tanMat[2]))) == 0
            // && GetVoxelData(ivoxelPos + edgePlane + edgeBackPlane) == 0) adjStatus = -2;
            ivec3 tipPos1 = ivoxelPos + edgePlane + edgeBackPlane + ivec3(round(_tanMat[2]));
            ivec3 tipPos2 = ivoxelPos + edgePlane + edgeBackPlane;
            if (!VoxelDataExists(tipPos1) && !VoxelDataExists(tipPos2)) adjStatus = -2;
        }
    }
        //test001 = uint(adjStatus);
    #endif
    
    
    if (bool_parallax && tex_n.a < 1.0 && is_voxelized(_blockID) && length(_worldPos) < 1600.0) {
    //if (bool_parallax && tex_n.a < 1.0 && is_voxelized(_blockID) && length(_worldPos) < 1600.0 && tangent_ray.z != 0.0) {
    //if (false) {
        ivec2 pCoord = Parallax(tangent_pos, tangent_ray, normal, corner, _spriteSize, int(0), adjStatus == -10);
        
        tCoord = vec2(pCoord) / atlasSize;
            //if (pCoord.x < 0) tCoord = _texcoord;
            //if ((_blockID == 3 || _blockID == 4) && _tanMat[2].y < 0.5) tCoord.y -= tangent_pos.y * 0.5;
        
        #ifdef POM_SILHOUETTE
      //if (_spriteSize.x != 0 && _spriteSize.y != 0) {
        //if (_blockID != 3 && adjStatus == -1 &&
        if (adjStatus == -1 &&
            any(greaterThanEqual(abs((tangent_pos.xy/_spriteSize - normal.xy * exp2(-8)) - vec2(0.5)), vec2(0.5)))) discard;
                //any(greaterThanEqual(abs((tangent_pos.xy/_spriteSize - normal.xy * exp2(-8)) - vec2(0.5)), vec2(0.5, 0.7)))) discard;
        
        if (adjStatus == -2 &&
            abs(dot(1.0 - cornerPlane.xy, tangent_pos.xy/_spriteSize - normal.xy * exp2(-8)) - 0.5) >= 0.5
            ) discard;
      //}
        #endif
        
        if (normal.z > 0.5) {
            tex_n = uintBitsToFloat(texture(normals, tCoord));
            normal.xy = tex_n.xy * 2.0 - 1.0;
            normal.z = sqrt(max(1.0 - dot(normal.xy, normal.xy), 0.0));
            normal = normalize(normal);
        }
    } else {
        normal.xy = tex_n.xy * 2.0 - 1.0;
        normal.z = sqrt(max(1.0 - dot(normal.xy, normal.xy), 0.0));
        normal = normalize(normal);
    }
    
    vec4 spec = texture(specular, tCoord) * 255.0 / 256.0;
    
    vec4 diffuse = textureLod(tex, tCoord, LOD);
    #if (defined gbuffers_entities)
        diffuse.rgb = mix(diffuse.rgb, entityColor.rgb, entityColor.a);  // entity color when injured
    #endif
    
    diffuse.rgb *= pow(_vertexColor.rgb, vec3(1.0 / 2.2));
    
    vec3 surfaceNormal = _tanMat * normal;
    
    if (is_iron_block(_blockID)) {
        diffuse.rgb *= pow(0.65 / 5.0, 1.0/2.2);
    }
    
    if (is_water(_blockID)) {
        spec.r = 1.0;
        spec.g = 230.0 / 255.0;
        
        surfaceNormal = _tanMat * ComputeWaveNormals(_worldPos, _tanMat[2]);
        diffuse.rgb = vec3(pow(0.65 / 5.0, 1.0/2.2));
    }
    
    #if (defined gbuffers_water) || (defined gbuffers_hand_water)
        if (!is_water(_blockID)) {
            // include alpha values ​​in RGB. (color of stained glass has the same RGB color code all over, and the pattern is expressed by differences in alpha values.)
            //diffuse.rgb *= (1.0 - diffuse.a / 2.5);
            diffuse.rgb *= (1.0 - diffuse.a / 2.0);
        }
    #endif

    #if (defined gbuffers_entities) || (defined gbuffers_hand)
        diffuse.a = 1.0;
    #else
        diffuse.a = 0.0;
    #endif
    
    float gbuffer_normal = EncodeNormal(surfaceNormal);
    #if (defined gbuffers_water) || (defined gbuffers_hand_water)
      // in deferred, normals (without translucency) are stored in colortex6.g and colortex7.a.
      // in composite, normals of translucent blocks themselves are stored in colortex6.g, but normals of geometry without translucency behind the translucent blocks are still stored in colortex7.a.
        ivec2 fragCoord = ivec2(gl_FragCoord.xy);
        vec4 ct7 = texelFetch(colortex7, fragCoord, 0);
        float gbuffer_normal_bak = ct7.a;
    #else
        float gbuffer_normal_bak = gbuffer_normal;
    #endif
    uvec2 lm = uvec2(clamp(lmcoord, 0.0, 1.0) * 255.0);
    int lightmapPack = int(Pack2x8uiTo16ui(lm));
    //gl_FragData[0] = vec4(uintBitsToFloat(packUnorm4x8(vec4(diffuse.rgb * 255.0 / 256.0, diffuse.a))), EncodeNormal(surfaceNormal), uintBitsToFloat(packUnorm4x8(spec)), _blockID);
    gl_FragData[0] = vec4(uintBitsToFloat(packUnorm4x8(vec4(diffuse.rgb * 255.0 / 256.0, diffuse.a))), gbuffer_normal, uintBitsToFloat(packUnorm4x8(spec)), Pack2x16Int(ivec2(_blockID, lightmapPack)));
        // Please note that 32-bit float does not strictly have 32 bits (2^32) of resolution.
        // In this case, if the most significant bit of the result of Pack2x16Int is 1, an error (Nan?) will occur and subsequent processing will be ignored.
        // Therefore, in the above formula, the 16-bit _blockID cannot have a value up to 65535, so it must be limited to 32767.
    //gl_FragData[1].rgb = _voxelPos + _tanMat[2] * exp2(-11);
    gl_FragData[1] = vec4(_voxelPos + _tanMat[2] * exp2(-11), gbuffer_normal_bak);
    
    #ifdef PARALLAX
    //gl_FragData[2] = vec4(EncodeTangentPos(tangent_pos.xy, _spriteSize), uintBitsToFloat(packUnorm2x16(_cornerTexcoord)), tangent_pos.z, uintBitsToFloat(EncodePlane(_tanMat[2]) + 8*uint(tex_n.a < 1.0)));
        uint pdata_a = EncodePlane(_tanMat[2]) + 8*uint(tex_n.a < 1.0);
        /*
        uint spritesizex_packed = uint(round(log2(_spriteSize.x)));
        uint spritesizey_packed = uint(round(log2(_spriteSize.y)));
        pdata_a |= (spritesizey_packed << 4);
        pdata_a |= (spritesizex_packed << 8);
        */
        /*
        pdata_a |= ((_spriteSize.y % 4096) << 4);
        pdata_a |= ((_spriteSize.x % 4096) << 16);
        */
            /**/
            ivec2 sp = _spriteSize;
            vec2 ctc = _cornerTexcoord;
            vec3 tpos = tangent_pos;
            deceiveStairsTexture(_blockID, ivec3(round(_tanMat[2])), fract(vec3(_voxelPos + _tanMat[2] * exp2(-11))), sp, ctc, tpos);
            //if (insideFace1 && sp.x == 512) sp.x = 256;
            //if (insideFace2 && sp.x == 512) sp.x = 256;
            //if (insideFace1 || insideFace2) sp = ivec2(512, 512);
            //if ((insideFace1 || insideFace2) && sp.x == 512 && sp.y == 256) sp = ivec2(512, 512);
            //if ((insideFace1 || insideFace2) && sp.x == 256 && sp.y == 256) sp = ivec2(256, 512);
        /*
            if (insideFaceL1 || insideFaceL2 || insideFaceR1 || insideFaceR2 || insideFaceR3 || insideFaceR4 || insideFace01 || insideFace02 || 
                outsideFace1 || outsideFace2 || 
                outsideFaceR1 || outsideFaceR2 || outsideFaceR3 || outsideFaceR4 || 
                outsideFaceL1 || outsideFaceL2 || outsideFaceL3 || outsideFaceL4 || 
                insideFaceLower1 || insideFaceLower2 || insideFaceLowerR1 || insideFaceLowerR2 || 
                insideFaceLowerL1 || insideFaceLowerL2 || insideFaceLowerL3 || insideFaceLowerL4 || 
                outsideFaceUSD1 || outsideFaceUSD2 || 
                outsideFaceLowerL1 || outsideFaceLowerL2 || outsideFaceLowerL3 || outsideFaceLowerL4 || 
                outsideFaceLowerR1 || outsideFaceLowerR2 || outsideFaceLowerR3 || outsideFaceLowerR4) sp.y *= 2;
            //if (insideFace2) sp.y *= 2;
            if (insideFaceR1 || insideFaceR2 || insideFaceR3 || insideFaceR4 || 
                outsideFaceR1 || outsideFaceR2 || outsideFaceR3 || outsideFaceR4 || 
                outsideFaceL1 || outsideFaceL2 || outsideFaceL3 || outsideFaceL4 || 
                insideFaceLowerL1 || insideFaceLowerL2 || insideFaceLowerL3 || insideFaceLowerL4 || 
                outsideFaceLowerL1 || outsideFaceLowerL2 || outsideFaceLowerL3 || outsideFaceLowerL4 || 
                outsideFaceLowerR1 || outsideFaceLowerR2 || outsideFaceLowerR3 || outsideFaceLowerR4) sp.x *= 2;
        */
            //pdata_a |= ((sp.y % 4096) << 4);
            //pdata_a |= ((sp.x % 4096) << 16);
            pdata_a |= ((sp.y % 8192) << 4);
            pdata_a |= ((sp.x % 8192) << 17);
            /**/
            //pdata_a |= (uint(horizontalPositionEW * 10.0) << 28);
            //pdata_a |= (uint(horizontalPositionNS * 10.0) << 28);
            //uint cornerplanex_packed = uint(cornerPlane.x > 0.5);
            //pdata_a |= (cornerplanex_packed << 12);
        //uint edgeplane_packed = EncodePlane(vec3(edgePlane));
        //pdata_a |= (edgeplane_packed << 12);
        //uint tanmat2_packed = uint(abs(_tanMat[2].x * 100.0)) % 1000;
        //pdata_a |= (tanmat2_packed << 4);
        //pdata_a |= (test001 << 12);
            //uint test002 = uint(tex_n.a * 10.0) % 10;
            //uint test002 = EncodePlane(_tanMat[2]);
        //uint test002 = uint(insideFaceR1) + 2 * uint(insideFaceR2) + 4 * uint(insideFaceR3);
        //uint test002 = uint(insideFace01) + 2 * uint(insideFace02);
        //pdata_a |= (test002 << 28);
        /*
        gl_FragData[2] = vec4(EncodeTangentPos(tangent_pos.xy, _spriteSize), uintBitsToFloat(packUnorm2x16(_cornerTexcoord)), tangent_pos.z, uintBitsToFloat(pdata_a));
        */
            //uint recov_chunkAddr = imageLoad(colorimg3, old_get_sparse_chunk_coord(ivoxelPos) + SPARSE0).r;
            //ivec2 recov_voxel_coord = get_sparse_voxel_coord(recov_chunkAddr, ivoxelPos, 0);
            //vec2 recov_cornerTexcoord = floor(unpackUnorm2x16(texelFetch(voxel_data_tex, recov_voxel_coord, 0).r) * atlasSize) / atlasSize;
            /*
            vec2 ctc = _cornerTexcoord;
            vec3 tpos = tangent_pos;
            */
            //if (insideFace2) ctc = recov_cornerTexcoord;
            //if (insideFaceR1 || insideFaceR2 || insideFaceR3 || insideFaceR4) ctc.x -= float(_spriteSize.x) / float(atlasSize.x);  // inside vertical face of stairs-inner blocks
        /*
            if (insideFaceR1 || insideFaceR2 || insideFaceR3 || insideFaceR4 || 
                outsideFaceR1 || outsideFaceR2 || outsideFaceR3 || outsideFaceR4 || 
                outsideFaceLowerR1 || outsideFaceLowerR2 || outsideFaceLowerR3 || outsideFaceLowerR4) {
                ctc.x -= float(_spriteSize.x) / float(atlasSize.x);
                tpos.x = _spriteSize.x + tpos.x;
            }
            //if (insideFaceR1 || insideFaceR2 || insideFaceR3 || insideFaceR4 || 
            //    outsideFaceR1 || outsideFaceR2 || outsideFaceR3 || outsideFaceR4) {
            //    tpos.x = _spriteSize.x + tpos.x;
            //}
            if (outsideFace2 || 
                insideFaceLower1 || insideFaceLower2 || 
                insideFaceLowerR1 || insideFaceLowerR2 || insideFaceLowerL1 || insideFaceLowerL2 || insideFaceLowerL3 || insideFaceLowerL4 || 
                outsideFaceUSD1 || 
                outsideFaceLowerL1 || outsideFaceLowerL2 || outsideFaceLowerL3 || outsideFaceLowerL4 || 
                outsideFaceLowerR1 || outsideFaceLowerR2 || outsideFaceLowerR3 || outsideFaceLowerR4) {
                ctc.y -= float(_spriteSize.y) / float(atlasSize.y);
                tpos.y = _spriteSize.y + tpos.y;
            }
        */
            //if (outsideFace2 || 
            //    insideFaceLower1 || insideFaceLower2 || 
            //    insideFaceLowerR1 || insideFaceLowerR2 || insideFaceLowerL3) {
            //    tpos.y = _spriteSize.y + tpos.y;
            //}
            /*
            vec3 surface_pos = fract(vec3(_voxelPos + _tanMat[2] * exp2(-11)));
            //if (surface_pos.x < 0.1 || surface_pos.x > 0.9 || surface_pos.y < 0.1 || surface_pos.y > 0.9 || surface_pos.z < 0.1 || surface_pos.z > 0.9) {
            if (surface_pos.y < 0.1) {
                pdata_a |= (1 << 30);
            }
            */
            gl_FragData[2] = vec4(EncodeTangentPos(tpos.xy, sp), uintBitsToFloat(packUnorm2x16(ctc)), tpos.z, uintBitsToFloat(pdata_a));
    #endif
    
    exit();
}

#endif
/**********************************************************************/