
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef PI
#define PI 3.14159265359
#endif

#if 0
#define float16_t float
#define f16vec2 vec2
#define f16vec3 vec3
#define f16vec4 vec4
#endif

// these are NOT bitmask. pixel can have only one attribute. 
// NOTE: maybe call them differently?
#define ATTR_NONE (0<<6)
#define ATTR_REFLECTIVE (1<<6)
#define ATTR_PARTICLE (2<<6)
#define ATTR_BACKGROUND (3<<6)

#define MATERIAL_ID_MASK_ID 0x3f
#define MATERIAL_ID_MASK_ATTR 0xc0

// These are for the builtin geometry attributes. Exposed in <vtx_builtin_buffers>
#define VTXBuiltinAttribute_Coords 1
#define VTXBuiltinAttribute_Normals 2
#define VTXBuiltinAttribute_Albedo 4
#define VTXBuiltinAttribute_UV0 8
#define VTXBuiltinAttribute_UV1 16


uint packSnorm2x12(vec2 v) { uvec2 d = uvec2(round(2047.5 + v*2047.5)); return d.x|(d.y<<12u); }
uint packSnorm2x15(vec2 v) { uvec2 d = uvec2(round(16383.5 + v*16383.5)); return d.x|(d.y<<15u); }
uint packSnorm2x11(vec2 v) { uvec2 d = uvec2(round(1023.5 + v*1023.5)); return d.x|(d.y<<11u); }
uint packSnorm2x8( vec2 v) { uvec2 d = uvec2(round( 127.5 + v* 127.5)); return d.x|(d.y<< 8u); }
vec2 unpackSnorm2x8( uint d) { return vec2(uvec2(d,d>> 8)& 255u)/ 127.5 - 1.0; }
vec2 unpackSnorm2x12(uint d) { return vec2(uvec2(d,d>>12)&4095u)/2047.5 - 1.0; }
vec2 unpackSnorm2x15(uint d) { return vec2(uvec2(d,d>>15)&32767u)/16383.5 - 1.0; }
vec2 unpackSnorm2x11(uint d) { return vec2(uvec2(d,d>>11)&2047u)/1023.5 - 1.0; }

uint encode_normal_23bit(vec3 n)
{
	vec2 v = n.xy;// * 0.5 + 0.5;
	uint s = n.z > 0.0 ? (1 << 22u) : 0;
	return s | packSnorm2x11(v);
}

uint encode_normal_31bit(vec3 n)
{
	vec2 v = n.xy;// * 0.5 + 0.5;
	uint s = n.z > 0.0 ? (1 << 30u) : 0;
	return s | packSnorm2x15(v);
}

uint encode_normal_32bit(vec3 n)
{
	vec2 v = n.xy * 0.5 + 0.5;
	uint s = n.z > 0.0 ? (1 << 31u) : 0;
	return s | packSnorm2x15(v);
}

uint encode_normal_material(vec3 n, int id)
{
	uint normal_material = encode_normal_31bit(n);
	normal_material = (uint(id) << 31u) | normal_material;		// id into msb to make it compaticle when we just encode normal
	return normal_material;
}

vec3 decode_normal(uint data)
{
	vec2 v = unpackSnorm2x15(data);
	uint s = data & (1 << 30);

	vec3 n;
	n.xy = v;// * 2.0 - 1.0;
	n.z = sqrt(clamp(1.0 - dot(n.xy, n.xy), 0.0, 1.0)) * (s > 0 ? 1.0 : -1.0);

	return n;
}

vec3 decode_normal_32bit(uint data)
{
	vec2 v = unpackSnorm2x15(data);
	uint s = data & (1 << 31);

	vec3 n;
	n.xy = v * 2.0 - 1.0;
	n.z = sqrt(max(0.0, 1.0 - dot(n.xy, n.xy))) * (s > 0 ? 1.0 : -1.0);

	return n;
}

uvec2 encode_metalness_roughness_material(float metalness, float roughness, uint material)
{
	return uvec2(
		(uint(min(1.0, metalness) * 255.0) << 8) | (uint(min(1.0, roughness) * 255.0) << 0),
		material); 
}

void decode_metalness_roughness_material(uvec2 mrm, out float metalness, out float roughness, out uint material)
{
	metalness = float((mrm.r >> 8)) * (1.0 / 255.0);
	roughness = float((mrm.r >> 0) & 0xff) * (1.0 / 255.0);
	material = mrm.g;
}

int decode_material(uint data)
{
	return int(data >> 24u);
}

#define MaterialFlag_Reflective 1
#define MaterialFlag_Raytrace 2
#define MaterialFlag_Reflect 4
#define MaterialFlag_ScreenspaceReflect 8
#define MaterialFlag_Voxelize 16
#define MaterialFlag_Transparent 32
#define MaterialFlag_Doublesided 64
#define MaterialFlag_Flat 128
#define MaterialFlag_ShadowCast 256
#define MaterialFlag_ShadowReceive 512
#define MaterialFlag_TriPlanarMapping 1024

struct MaterialPropertiesGPU
{
	vec3  diffuse;
	float transparency;
	vec3  emissive;
	float roughness;

	float refraction;
	float normal_factor;
	float emissive_factor;
	float temporal_accumulation_factor;
	float triplanar_factor;
	float shadowmap_bias;
	float metalness;
	int   albedo_sampler;
	int   emissive_sampler;
	int   normal_sampler;
	int   metalic_roughness_sampler;
	uint  flags;
};

// Keep them in sync with sceneplayer.h

#ifdef MATERIAL_PROPERTIES_BINDING
layout (std140) uniform MaterialPropertiesDataBuffer {
//layout (std140) readonly buffer MaterialPropertiesDataBuffer {
	MaterialPropertiesGPU material_properties[512];
} materials;

uniform sampler2D material_textures[64];

#endif

struct LightProperties
{
	vec4 diffuse;
	vec4 direction;		// world space
	vec4 position;		// world space
	vec4 up;			// ltc/area
	vec4 right;			// ltc/area
	vec2 dimensions;	// ltc/area
	vec2 _pad1;
	float intensity;	// ltc/area
	float range;
	float cutoff;
	float roughness_modifier;
	int is_area;
	int type;			// LightType_ bitmask
	
	int projector_sampler;
	int _pad0;
	int shadowmap_sampler0;		// for each cascade. 'unrolled' as we index manually anyway
	int shadowmap_sampler1;		//
	int shadowmap_sampler2;		//
	int shadowmap_sampler3;		//
	float cascade_distance0;	// 
	float cascade_distance1;	// 
	float cascade_distance2;	// 
	float cascade_distance3;	// 

	mat4 mat_shadow_mv;
	mat4 mat_shadow_p[4];
	mat4 mat_shadow_mvp[4];
};

#define LightType_Directional   1
#define LightType_Spot          2
#define LightType_Point         4
#define LightType_Shadowcasting 8
#define LightType_Projector     16
#define LightType_LTC           32
#define LightType_Volumetric    64
#define LightType_Attenuating   128

#ifdef LIGHT_PROPERTIES_BINDING

layout (std140, row_major) uniform LightPropertiesBuffer {
	LightProperties light_properties[16];
} lights;

uniform sampler2DShadow LightShadowmapCmpSamplers[16];
uniform sampler2D       LightShadowmapSamplers[16]; // TODO: Combine with all other textures...
uniform sampler2D       LightProjectorSamplers[16];
uniform int             g_lights_num;

#endif

// length as integer into alpha, then decode from that
uint color_convert_rgb_float3_uint(vec3 v)
{
	uint vi;
	float v_mag = ceil(length(v.xyz));
	v.xyz = v.xyz / v_mag;
	v_mag = min(255.0, v_mag);

	vi = uint(v.x * 255.0) | (uint(v.y * 255.0) << 8) |( uint(v.z * 255.0) << 16);
	vi |= uint(v_mag) * (1 << 24); 
	return vi;
}

vec3 color_convert_uint_to_float3(uint vi)
{
	vec3 v = vec3(0.0);
	uint v_mag = vi >> 24;
	float v_mag_rcp = (1.0 / 255.0) * float(v_mag);

	v.x = float((vi >> 0) & 0xff) * v_mag_rcp;
	v.y = float((vi >> 8) & 0xff) * v_mag_rcp;
	v.z = float((vi >> 16) & 0xff) * v_mag_rcp;
	return v;
}

vec4 color_convert_rgb_rgbm(vec3 v)
{
	v = sqrt(v);
	v = v / 128.0;
	vec4 rgbm;
	rgbm.a = clamp(max(max(v.r, v.g), max(v.b,1e-6)), 0.0, 1.0);
	rgbm.a = ceil(rgbm.a * 256.0) / 255.0;
	rgbm.rgb = v / rgbm.a;

	return rgbm;
}

vec3 color_convert_rgbm_rgb(vec4 v)
{
	v.rgb = v.rgb * v.a * 128.0;
	return v.rgb * v.rgb;
}

vec3 vector_rotate_by_quaternion(vec3 v, vec4 q)
{
    // Extract the vector part of the quaternion
    vec3 u = q.xyz;

    // Extract the scalar part of the quaternion
    float s = q.w;

    // Do the math
    return 2.0 * dot(u, v) * u
         + (s*s - dot(u, u)) * v
         + 2.0 * s * cross(u, v);
}

struct EntityTransformParams
{
	mat4 mModel;
	mat4 mView;
	mat4 mModelNormal;
	mat4 mModelView;
	mat4 mModelViewInvTrans;
	mat4 mProjection;
	mat4 mModelInv;
	vec3 vCameraPosition;
};

vec3 vector_transform_by_mat43(vec3 v, mat4 m)
{
	return (m * vec4(v, 1.0)).xyz;
}

vec3 vector_transform_by_mat33(vec3 v, mat4 m)
{
	return (m * vec4(v, 0.0)).xyz;
}