// Shader stolen here : https://godotshaders.com/shader/vertex-animation-texture-uses-openvat-output/
shader_type spatial;
render_mode cull_disabled; // Prevents GPU hardware culling so shader can handle it
/*
For use with models exported from Blender using the OpenVAT plugin.
Expects:
Vertex Normals - Packed (Math gets too complex to include a toggle. Making a separate shader recommended if normals are not needed.)
Use Single Row - On
Export Model - On
Model Format - glTF Binary. (Unsure why the otherse do not work)

Ensure .exr file is set to Lossless compression and MipMaps - Off.
*/

// --- Uniforms ---
group_uniforms openVAT_inputs;
/** OpenVAT texture exported from Blender plugin. */
uniform sampler2D vertex_animation_texture : repeat_disable;
/** Min values from the .json file included with OpenVAT exports. */
uniform vec3 min_bounds;
/** Max values from the .json file included with OpenVAT exports. */
uniform vec3 max_bounds;
/** Plays animation automatically. Turn off to control manually via keyframes. */
uniform bool autoplay = true;
/** 
Autoplay speed in frames per second. Defaults to 30. 
[b]Blender default is 24. May require adjustment either in Blender or Here to match speed.[b] 
*/
uniform float autoplay_speed = 30.0;
/** Frame to display if animating manually. */
uniform float frame = 1.0;

/** Enable this if the asset does not have 2 UV sets. */
uniform bool use_uv_1 = false; 
/** Cull Mode, editable here for flexibility.
0: Backface Cull
1: Frontface Cull
2: Cull disabled (double sided rendering)*/
uniform int cull_mode : hint_enum("Back:0", "Front:1", "Disabled:2") = 0;
group_uniforms;

group_uniforms Albedo_map;
uniform sampler2D albedo_map : source_color, hint_default_white;
uniform vec4 albedo_tint : source_color = vec4(1.0);
group_uniforms;

group_uniforms ORM_inputs;
/**
[b]ORM texture.[/b] Standardized detail maps for PBR materials. Ignore, remap, or remove as needed.
[b]O[/b]cclusion (Ambient)
[b]R[/b]oughness
[b]M[/b]etallic 
*/
uniform sampler2D orm_map : hint_default_white; 
/** Select channel used by Occlusion map. R G [b]or[/b] B, combining will have strange results */
uniform vec4 occlusion_channel : source_color = vec4(1.0, 0.0, 0.0, 1.0);
/** Strength of Occlusion*/
uniform float occlusion : hint_range(0.0, 1.0) = 1.0;
/** Select channel used by Roughness map. R G [b]or[/b] B, combining will have strange results */
uniform vec4 roughness_channel : source_color = vec4(0.0, 1.0, 0.0, 1.0);
/** Strength of Roughness*/
uniform float roughness : hint_range(0.0, 1.0) = 1.0;
/** Select channel used by Metallic map. R G [b]or[/b] B, combining will have strange results */
uniform vec4 metallic_channel : source_color = vec4(0.0, 0.0, 1.0, 1.0);
/** Strength of Metallic*/
uniform float metallic : hint_range(0.0, 1.0) = 1.0;
/** Strength of Specular (No Map Included)*/
uniform float specular : hint_range(0.0, 1.0) = 0.5;
group_uniforms;

group_uniforms Normal_map;
uniform sampler2D normal_map : hint_roughness_normal;
uniform float normal_map_scale : hint_range(-10.0, 10.0) = 1.0;

/** Passed data from INSTANCE_CUSTOM_VARIABLE :
x : Unique Id
y : Damage effect
z : Size
**/

varying flat float unique_id;
varying flat float damage_effect;
varying flat float size;

float remapFloat(float value, float input_min, float input_max, float output_min, float output_max){
	// Code to replicate the remap feature from Visual Shader.	
	float _input_range = input_max - input_min;
	float _output_range = output_max - output_min;
	return output_min + _output_range * ((value - input_min) / _input_range);
}

void vertex() {
    unique_id = INSTANCE_CUSTOM.x;
    damage_effect = INSTANCE_CUSTOM.y;
    size = INSTANCE_CUSTOM.z;
    
    // Animation Timing
	// ------
	// Divided by 2. since bottom half of texture is the normal data. 
	// Second argument (0) is MipMap level. These VATs use no mipmaps so always 0.
	int total_frames = textureSize(vertex_animation_texture, 0).y / 2; 
	// Checks if Autoplaying is checked
	float play_frame = autoplay ? (TIME * autoplay_speed / size) : frame;
	
    
    // Loops the current frame counter so it stays between 0 and total_frames - 1
	float current_frame = mod(play_frame + unique_id, float(total_frames));

	// Compute UV Coordinates
	// ------
	// samples pixel from the center, not the edge. Avoid unwanted bleeding into normal data pixels at the end of the loop.
	float center_pixel = current_frame - 0.5;
	float vat_y = center_pixel / float(total_frames);
	vat_y = clamp(vat_y * 0.5, 0.0, 0.5);
	
	vec2 vat_uv = vec2((use_uv_1 ? UV.x : UV2.x), vat_y);
	
	// Vertex Position Displacement
	// ------
	vec4 vat_pos_raw = texture(vertex_animation_texture, vat_uv);
	// Must go through some remapping to convert Blender to Godot coordinates
	vec3 offset_values = vec3(
		remapFloat(vat_pos_raw.r, 0.0, 1.0, min_bounds.x, max_bounds.x),
		remapFloat(vat_pos_raw.b, 0.0, 1.0, min_bounds.z, max_bounds.z),
		remapFloat(vat_pos_raw.g, 1.0, 0.0, min_bounds.y, max_bounds.y)
	);
	VERTEX += offset_values;

	// Normals. 0.5 added to UV.y to shift it down by 1/2 the map - to the normals section.
	vec4 vat_norm_raw = texture(vertex_animation_texture, vat_uv + vec2(0.0, 0.5)); 
	vec3 decoded_normals = vec3(
		remapFloat(vat_norm_raw.r, 0.0, 1.0, -1.0, 1.0),
		remapFloat(vat_norm_raw.b, 0.0, 1.0, -1.0, 1.0),
		remapFloat(vat_norm_raw.g, 1.0, 0.0, -1.0, 1.0)
	);
	NORMAL = normalize(decoded_normals);

	// Calculate Tangent and Binormal from computed Normal
	TANGENT = normalize(vec3(abs(NORMAL.y) + abs(NORMAL.z), 0.0, -abs(NORMAL.x)));
	BINORMAL = normalize(vec3(0.0, abs(NORMAL.x) + abs(NORMAL.z), -abs(NORMAL.y)));
}

void fragment() {
	// Cull Mode
	if (cull_mode == 0 && !FRONT_FACING) {
		discard; // Cull back faces
	} 
	else if (cull_mode == 1 && FRONT_FACING) {
		discard; // Cull front faces
	}
	
	// Albedo
	ALBEDO = texture(albedo_map, UV).rgb * albedo_tint.rgb;

    EMISSION = vec3(1.,1.,1.) * damage_effect;

	// ORM maps (Occlusion, Roughness, Metallic) Packing
	vec4 orm_tex = texture(orm_map, UV);
	AO = occlusion * dot(orm_tex.rgb, occlusion_channel.rgb);
	ROUGHNESS = roughness * dot(orm_tex.rgb, roughness_channel.rgb);
	METALLIC = metallic * dot(orm_tex.rgb, metallic_channel.rgb);

	// Specular
	SPECULAR = specular;

	// Normal Map
	NORMAL_MAP = texture(normal_map, UV).rgb;
	NORMAL_MAP_DEPTH = normal_map_scale;
}