shader_type spatial;

uniform sampler2D u_texture_top : hint_albedo;
uniform sampler2D u_texture_sides : hint_albedo;

// Bitmask telling which of the 6 faces of the block are bordered by a block of lower resolution
uniform int u_transition_mask;

// We'll need to pass data from the vertex shader to the fragment shader
varying vec3 v_world_pos;
varying vec3 v_world_normal;
varying vec4 v_weights;
varying vec4 v_indices;

// We'll use a utility function to decode components.
// It returns 4 values in the range [0..255].
vec4 decode_8bit_vec4(float v) {
    uint i = floatBitsToUint(v);
    return vec4(
        float(i & uint(0xff)),
        float((i >> uint(8)) & uint(0xff)),
        float((i >> uint(16)) & uint(0xff)),
        float((i >> uint(24)) & uint(0xff)));
}

// A voxel mesh can have overhangs in any direction,
// so we may have to use triplanar mapping functions.
vec3 get_triplanar_blend(vec3 world_normal) {
    vec3 blending = abs(world_normal);
    blending = normalize(max(blending, vec3(0.00001))); // Force weights to sum to 1.0
    float b = blending.x + blending.y + blending.z;
    return blending / vec3(b, b, b);
}

vec4 texture_triplanar(sampler2D tex, vec3 world_pos, vec3 blend) {
	vec4 xaxis = texture(tex, world_pos.yz);
	vec4 yaxis = texture(tex, world_pos.xz);
	vec4 zaxis = texture(tex, world_pos.xy);
	// blend the results of the 3 planar projections.
	return xaxis * blend.x + yaxis * blend.y + zaxis * blend.z;
}

float get_hash(vec2 c) {
	return fract(sin(dot(c.xy, vec2(12.9898,78.233))) * 43758.5453);
}

vec3 get_transvoxel_position(vec3 vertex_pos, vec4 vertex_col) {

	int border_mask = int(vertex_col.a);
	int cell_border_mask = border_mask & 63; // Which sides the cell is touching
	int vertex_border_mask = (border_mask >> 6) & 63; // Which sides the vertex is touching

	// If the vertex is near a side where there is a low-resolution neighbor,
	// move it to secondary position
	int m = u_transition_mask & (cell_border_mask & 63);
	float t = float(m != 0);

	// If the vertex lies on one or more sides, and at least one side has no low-resolution neighbor,
	// don't move the vertex.
	t *= float((vertex_border_mask & ~u_transition_mask) == 0);

	// Position to use when border mask matches
	vec3 secondary_position = vertex_col.rgb;
	return mix(vertex_pos, secondary_position, t);
}

void vertex() {
    // Indices are integer values so we can decode them as-is
    v_indices = decode_8bit_vec4(UV.x);

    // Weights must be in [0..1] so we divide them
    v_weights = decode_8bit_vec4(UV.y) / 255.0;

    //v_normal = NORMAL;
    vec3 world_pos = (WORLD_MATRIX * vec4(VERTEX, 1.0)).xyz;
    v_world_pos = world_pos;
    v_world_normal = NORMAL;

    VERTEX = get_transvoxel_position(VERTEX, COLOR);
}

void fragment() {
    vec3 normal = v_world_normal;//normalize(v_world_normal);
    vec3 wpos = v_world_pos * 0.2;
    // Sample the 4 blending textures, all with triplanar mapping.
    // We can re-use the same triplanar blending factors for all of them so separating that part
    // of the function improves performance a little.
    vec3 blending = get_triplanar_blend(v_world_normal);

    vec3 top_col = texture_triplanar(u_texture_top, wpos, blending).rgb;
    vec3 side_col = texture_triplanar(u_texture_sides, wpos, blending).rgb;

    // Get weights and make sure they are normalized.
    // We may add a tiny safety margin so we can afford some degree of error.
    vec4 weights = v_weights;
    weights /= (weights.x + weights.y + weights.z + weights.w + 0.00001);

    // Calculate albedo
    //vec3 col = 
//        col0 * weights.r + 
//        col1 * weights.g + 
//        col2 * weights.b + 
//        col3 * weights.a;
	float r = top_col.r;
	ALBEDO = mix(side_col, top_col, clamp(normal.y * 10.0 - 4.0 - 8.0*r, 0.0, 1.0));
}
