diff options
Diffstat (limited to 'env.c')
| -rw-r--r-- | env.c | 384 |
1 files changed, 384 insertions, 0 deletions
@@ -0,0 +1,384 @@ +#include "env.h" + +Environment env = {0}; + +static const Color SKY_NIGHT_HORIZON = { 14, 11, 28, 255 }; +static const Color SKY_NIGHT_ZENITH = { 1, 2, 6, 255 }; +static const Color SKY_DAY_HORIZON = { 158, 199, 235, 255 }; +static const Color SKY_DAY_ZENITH = { 20, 71, 158, 255 }; + +static f32 +sky_smoothstep(f32 edge_0, f32 edge_1, f32 value) { + f32 amount = (value - edge_0) / (edge_1 - edge_0); + + if (amount < 0.0f) { + amount = 0.0f; + } else if (amount > 1.0f) { + amount = 1.0f; + } + + return amount * amount * (3.0f - 2.0f * amount); +} + +static Vec3 +sky_get_sun_direction(f32 cycle) { + f32 angle = cycle * 2.0f * PI - PI * 0.5f; + + Vec3 dir = (Vec3) { + cosf(angle), + sinf(angle), + 0.0f + }; + + return Vector3Normalize(dir); +} + +static Vec3 +sky_get_moon_direction(Vec3 sun_dir) { + return Vector3Negate(sun_dir); +} + +static void +sky_draw_symbol(Texture tex, Vec3 dir, Camera3D cam, f32 angular_diameter, + Color tint, f32 visibility) { + if (tex.id == 0 || visibility <= 0.0f) { + return; + } + + Vec3 cam_fwd = Vector3Normalize( + Vector3Subtract(cam.target, cam.position)); + + f32 fwd_facing = Vector3DotProduct(cam_fwd, dir); + + // don't draw if behind the camera + if (fwd_facing <= 0.0f) { + return; + } + + // distance is arbitrary. Only the direction matters because result is + // projected into screen space. + Vec3 world_pos = Vector3Add(cam.position, Vector3Scale(dir, 1000.0f)); + Vec2 scr_pos = GetWorldToScreen(world_pos, cam); + + // Convert the requested angular diameter into pixels. This makes the + // symbols respond correctly when camera FOV changes. + f32 vfov = cam.fovy * DEG2RAD; + + f32 diameter = + (f32)GetScreenHeight() * + tanf(angular_diameter * DEG2RAD * 0.5f) / + tanf(vfov * 0.5f); + + if (diameter < 1.0f) { + return; + } + + tint.a = (u8)((f32)tint.a * visibility); + + Rect src = (Rect) { 0, 0, (f32)tex.width, (f32)tex.height }; + Rect dst = (Rect) { scr_pos.x, scr_pos.y, diameter, diameter }; + Vec2 origin = (Vec2) { diameter * 0.5f, diameter * 0.5f }; + + DrawTexturePro(tex, src, dst, origin, 0, tint); +} + +static Color +color_lerp(Color a, Color b, f32 t) { + t = Clamp(t, 0.0f, 1.0f); + + return (Color) { + (u8)Lerp(a.r, b.r, t), + (u8)Lerp(a.g, b.g, t), + (u8)Lerp(a.b, b.b, t), + (u8)Lerp(a.a, b.a, t), + }; +} + +static Vec3 +color_to_vec3(Color col) { + return (Vec3) { + (f32)col.r / 255.0f, + (f32)col.g / 255.0f, + (f32)col.b / 255.0f, + }; +} + +static void +sky_update_lighting(void) { + Vec3 sun_dir = sky_get_sun_direction(env.cycle); + Vec3 moon_dir = sky_get_moon_direction(sun_dir); + + f32 day_amount = sky_smoothstep(-0.08f, 0.15f, sun_dir.y); + f32 night_amount = 1.0f - day_amount; + + // Strongest when the sun is close to the horizon. + f32 twilight_amount = 1.0f - sky_smoothstep( + 0.02f, + 0.28f, + fabsf(sun_dir.y) + ); + + Color horizon_col = color_lerp( + SKY_NIGHT_HORIZON, + SKY_DAY_HORIZON, + day_amount + ); + + Color zenith_col = color_lerp( + SKY_NIGHT_ZENITH, + SKY_DAY_ZENITH, + day_amount + ); + + Color night_ambient = { 18, 22, 42, 255 }; + Color day_ambient = { 145, 165, 185, 255 }; + Color twilight_ambient = { 185, 90, 55, 255 }; + + Color ambient_col = color_lerp( + night_ambient, + day_ambient, + day_amount + ); + + ambient_col = color_lerp( + ambient_col, + twilight_ambient, + twilight_amount * 0.45f + ); + + Color sun_col = color_lerp( + (Color){ 255, 155, 95, 255 }, + (Color){ 255, 244, 215, 255 }, + day_amount + ); + + Color moon_col = { 135, 155, 205, 255 }; + + env.sun_dir = sun_dir; + env.moon_dir = moon_dir; + + env.sun_col = sun_col; + env.moon_col = moon_col; + env.ambient_col = ambient_col; + + env.horizon_col = horizon_col; + env.zenith_col = zenith_col; + + env.day_amount = day_amount; + env.twilight_amount = twilight_amount; + + env.sun_intensity = day_amount; + env.moon_intensity = night_amount * 0.12f; + env.ambient_intensity = Lerp(0.18f, 0.55f, day_amount); +} + +static void +env_update_mdl_shader(void) { + Vec3 sun_col = color_to_vec3(env.sun_col); + Vec3 moon_col = color_to_vec3(env.moon_col); + Vec3 ambient_col = color_to_vec3(env.ambient_col); + + Shader sdr = shader_get("model")->sdr; + SetTraceLogLevel(LOG_DEBUG); + SetShaderValue( + sdr, + fun_shader_uniform("model", "sun_direction"), + &env.sun_dir, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + sdr, + fun_shader_uniform("model", "sun_colour"), + &sun_col, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + sdr, + fun_shader_uniform("model", "sun_intensity"), + &env.sun_intensity, + SHADER_UNIFORM_FLOAT + ); + + SetShaderValue( + sdr, + fun_shader_uniform("model", "moon_direction"), + &env.moon_dir, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + sdr, + fun_shader_uniform("model", "moon_colour"), + &moon_col, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + sdr, + fun_shader_uniform("model", "moon_intensity"), + &env.moon_intensity, + SHADER_UNIFORM_FLOAT + ); + + SetShaderValue( + sdr, + fun_shader_uniform("model", "ambient_colour"), + &ambient_col, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + sdr, + fun_shader_uniform("model", "ambient_intensity"), + &env.ambient_intensity, + SHADER_UNIFORM_FLOAT + ); + + SetTraceLogLevel(LOG_WARNING); +} + +void +env_init(void) { + env.sky_sdr_key = "sky"; + env.sun_tex_key = "res/tex/sun.png"; + env.moon_tex_key = "res/tex/moon.png"; + env.cycle = 0.2; + env.day_len = 240; // 600s = 5 min day/night cycle + env.sun_sz = 14; + env.moon_sz = 4; + env.sun_tint = WHITE; + env.moon_tint = GRAY; + + sky_update_lighting(); +} + +void +env_exit(void) { +} + +void +env_update(void) { + // if (env.day_len > 0.0f) { + // env.cycle += GetFrameTime() / env.day_len; + // env.cycle -= floorf(env.cycle); + // } + sky_update_lighting(); + env_update_mdl_shader(); +} + + +void +env_draw_sky(Camera cam) { + int scr_w = GetScreenWidth(); + int scr_h = GetScreenHeight(); + + if (scr_w <= 0 || scr_h <= 0) { + return; + } + + Vec3 cam_fwd = + Vector3Normalize(Vector3Subtract(cam.target, cam.position)); + + Vec3 cam_right = + Vector3Normalize(Vector3CrossProduct(cam_fwd, cam.up)); + + Vec3 cam_up = + Vector3Normalize(Vector3CrossProduct(cam_right, cam_fwd)); + + Vec3 sun_dir = env.sun_dir; + Vec3 moon_dir = env.moon_dir; + + f32 scr_res[2] = { (f32)scr_w, (f32)scr_h }; + f32 aspect = scr_res[0] / scr_res[1]; + f32 tan_half_fovy = tanf(cam.fovy * DEG2RAD * 0.5f); + + Vec3 horizon_col = color_to_vec3(env.horizon_col); + Vec3 zenith_col = color_to_vec3(env.zenith_col); + + FunShader *shader = shader_get(env.sky_sdr_key); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "resolution"), + scr_res, + SHADER_UNIFORM_VEC2 + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "camera_forward"), + &cam_fwd, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "camera_right"), + &cam_right, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "camera_up"), + &cam_up, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "sun_direction"), + &env.sun_dir, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "horizon_colour"), + &horizon_col, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "zenith_colour"), + &zenith_col, + SHADER_UNIFORM_VEC3 + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "day_amount"), + &env.day_amount, + SHADER_UNIFORM_FLOAT + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "twilight_amount"), + &env.twilight_amount, + SHADER_UNIFORM_FLOAT + ); + + SetShaderValue( + shader->sdr, + fun_shader_uniform(env.sky_sdr_key, "tan_half_fovy"), + &tan_half_fovy, + SHADER_UNIFORM_FLOAT + ); + + BeginShaderMode(shader->sdr); + DrawRectangle(0, 0, scr_w, scr_h, WHITE); + EndShaderMode(); + + f32 sun_vis = sky_smoothstep(-0.05f, 0.25f, sun_dir.y); + f32 moon_vis = sky_smoothstep(-0.05f, 0.8f, moon_dir.y); + + sky_draw_symbol(asset_get(env.sun_tex_key)->tex, sun_dir, cam, + env.sun_sz, env.sun_tint, sun_vis); + + sky_draw_symbol(asset_get(env.moon_tex_key)->tex, moon_dir, cam, + env.moon_sz, env.moon_tint, moon_vis); +} |
