#include "spherical_harmonics.h"
#include "sk_math.h"
#include "asset_types/texture_.h"
#include "libraries/ferr_halffloat.h"
namespace sk {
void sh_add (spherical_harmonics_t &to, vec3 light_dir, vec3 light_color);
void sh_windowing(spherical_harmonics_t &harmonics, float window_width);
vec3 to_color_128(uint8_t *color) { return *(vec3 *)color; }
vec3 to_color_32 (uint8_t *color) { return vec3{color[0]/255.f,color[1]/255.f,color[2]/255.f}; }
vec3 to_color_32_linear(uint8_t* color) { return vec3{ powf(color[0] / 255.f,2.2f),powf(color[1] / 255.f,2.2f),powf(color[2] / 255.f,2.2f) }; }
vec3 to_color_64(uint8_t* color) { const uint16_t* c = (uint16_t*)color; vec4 r; fhf_f16_to_f32_x4(c, &r.x); return vec3{ r.x, r.y, r.z }; }
vec3 to_color_11_11_10f(uint8_t* color) { vec3 r; fhf_r11g11b10f_to_f32_x3(*(uint32_t*)color, &r.x); return vec3{ r.x, r.y, r.z }; }
void sh_windowing(spherical_harmonics_t &harmonics, float window_width) {
int i = 0;
for(int band = 0; band <= 2; band++) {
float s = 1.0f / (1.0f + window_width * band * band * (band + 1.0f) * (band + 1.0f));
for(int m = -band; m <= band; m++) {
harmonics.coefficients[i++] *= s;
}
}
}
spherical_harmonics_t sh_create(const sh_light_t* lights, int32_t light_count) {
spherical_harmonics_t result = {};
for (int32_t i = 0; i < light_count; i++) {
sh_add(result, vec3_normalize(lights[i].dir_to), { lights[i].color.r, lights[i].color.g, lights[i].color.b });
}
for (int32_t i = 0; i < 9; i++) {
result.coefficients[i] /= (float)light_count;
}
sh_windowing(result, .01f);
return result;
}
void sh_brightness(spherical_harmonics_t &harmonics, float scale) {
for (int32_t i = 0; i < 9; i++)
harmonics.coefficients[i] *= scale;
}
void sh_add(spherical_harmonics_t &to, vec3 light_dir, vec3 light_color) {
light_dir = { -light_dir.x, -light_dir.y, light_dir.z };
const float fCW0 = 0.25f;
const float fCW1 = 0.5f;
const float fRet = MATH_PI / (fCW0 + fCW1);
light_color = light_color * fRet;
const float z2 = light_dir.z*light_dir.z;
const float s1 = light_dir.y;
const float c1 = light_dir.x;
const float s2 = light_dir.x*s1 + light_dir.y*c1;
const float c2 = light_dir.x*c1 - light_dir.y*s1;
to.coefficients[0] += light_color * 0.282094791773878140f;
to.coefficients[2] += light_color * 0.488602511902919920f*light_dir.z;
to.coefficients[6] += light_color * (0.946174695757560080f*z2 + -0.315391565252520050f);
to.coefficients[1] += light_color * -0.488602511902919920f*s1;
to.coefficients[3] += light_color * -0.488602511902919920f*c1;
const float p_2_1 = -1.092548430592079200f*light_dir.z;
to.coefficients[5] += light_color * p_2_1*s1;
to.coefficients[7] += light_color * p_2_1*c1;
to.coefficients[4] += light_color * 0.546274215296039590f*s2;
to.coefficients[8] += light_color * 0.546274215296039590f*c2;
}
spherical_harmonics_t sh_calculate(void **env_map_data, tex_format_ format, int32_t face_size) {
spherical_harmonics_t result = {};
size_t col_size = 0;
vec3 (*convert)(uint8_t *) = nullptr;
switch (format) {
case tex_format_rgba128: convert = to_color_128; col_size = sizeof(float) * 4; break;
case tex_format_rgba64f: convert = to_color_64; col_size = sizeof(uint16_t) * 4; break;
case tex_format_rg11b10: convert = to_color_11_11_10f; col_size = sizeof(uint32_t); break;
case tex_format_rgba32: convert = to_color_32_linear; col_size = sizeof(color32); break;
case tex_format_rgba32_linear: convert = to_color_32; col_size = sizeof(color32); break;
default: return {};
}
float half_px = 0.5f / face_size;
for (int32_t i = 0; i < 6; i++) {
uint8_t *data = (uint8_t*)env_map_data[i];
vec3 p1 = math_cubemap_corner(i * 4);
vec3 p2 = math_cubemap_corner(i * 4+1);
vec3 p3 = math_cubemap_corner(i * 4+2);
vec3 p4 = math_cubemap_corner(i * 4+3);
for (int32_t y = 0; y < face_size; y++) {
float py = 1 - (y / (float)face_size + half_px);
if (i == 2) {
py = 1 - py;
}
for (int32_t x = 0; x < face_size; x++) {
float px = x / (float)face_size + half_px;
if (i == 2) {
px = 1 - px;
}
vec3 pl = vec3_lerp(p1, p4, py);
vec3 pr = vec3_lerp(p2, p3, py);
vec3 pt = vec3_lerp(pl, pr, px);
pt = vec3_normalize(pt);
vec3 color = convert(&data[(x + y * face_size) * col_size]);
sh_add(result, pt, color);
}
}
}
float count = face_size * face_size * 6.f;
for (int32_t i = 0; i < 9; i++)
result.coefficients[i] /= count;
sh_windowing(result, .01f);
return result;
}
color128 sh_lookup(const spherical_harmonics_t &harmonics, vec3 normal) {
vec3 result = {};
static const float Pi = 3.141592654f;
static const float CosineA0 = Pi;
static const float CosineA1 = (2.0f * Pi) / 3.0f;
static const float CosineA2 = Pi * 0.25f;
result += harmonics.coefficients[0] * (0.282095f * CosineA0);
result += harmonics.coefficients[1] * (0.488603f * normal.y * CosineA1);
result += harmonics.coefficients[2] * (0.488603f * normal.z * CosineA1);
result += harmonics.coefficients[3] * (0.488603f * normal.x * CosineA1);
result += harmonics.coefficients[4] * (1.092548f * normal.x * normal.y * CosineA2);
result += harmonics.coefficients[5] * (1.092548f * normal.y * normal.z * CosineA2);
result += harmonics.coefficients[6] * (0.315392f * (3.0f * normal.z * normal.z - 1.0f) * CosineA2);
result += harmonics.coefficients[7] * (1.092548f * normal.x * normal.z * CosineA2);
result += harmonics.coefficients[8] * (0.546274f * (normal.x * normal.x - normal.y * normal.y) * CosineA2);
return { result.x, result.y, result.z, 1 };
}
vec3 sh_dominant_dir(const sk_ref(spherical_harmonics_t) harmonics) {
vec3 dir = vec3_normalize({
harmonics.coefficients[3].x * 0.3f + harmonics.coefficients[3].y * 0.59f + harmonics.coefficients[3].z,
harmonics.coefficients[1].x * 0.3f + harmonics.coefficients[1].y * 0.59f + harmonics.coefficients[1].z,
harmonics.coefficients[2].x * 0.3f + harmonics.coefficients[2].y * 0.59f + harmonics.coefficients[2].z });
return -dir;
}
inline vec4 to_vec4(const vec3& vec) { return { vec.x, vec.y, vec.z, 0 }; }
void sh_to_fast(const spherical_harmonics_t& lookup, vec4* fast_9) {
static const float CosineA0 = 3.141592654f;
static const float CosineA1 = (2.0f * CosineA0) / 3.0f;
static const float CosineA2 = CosineA0 * 0.25f;
fast_9[0] = to_vec4(lookup.coefficients[0] * (0.282095f * CosineA0));
fast_9[1] = to_vec4(lookup.coefficients[1] * (0.488603f * CosineA1));
fast_9[2] = to_vec4(lookup.coefficients[2] * (0.488603f * CosineA1));
fast_9[3] = to_vec4(lookup.coefficients[3] * (0.488603f * CosineA1));
fast_9[4] = to_vec4(lookup.coefficients[4] * (1.092548f * CosineA2));
fast_9[5] = to_vec4(lookup.coefficients[5] * (1.092548f * CosineA2));
fast_9[6] = to_vec4(lookup.coefficients[6] * (0.315392f * CosineA2));
fast_9[7] = to_vec4(lookup.coefficients[7] * (1.092548f * CosineA2));
fast_9[8] = to_vec4(lookup.coefficients[8] * (0.546274f * CosineA2));
}
}