#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.35; env.day_len = 240; // 600s = 5 min day/night cycle env.sun_sz = 14; env.moon_sz = 4; // env.sun_tint = (Color) { 255, 255, 255, 40 }; 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); }