Nimbin[12]?Games / Entropie / entropie/src/world/daycycle.c

Entropie git · main

Entropie festival game; first steps

sdl c game entropie cmake · first commit 2026-10-10 · last commit 2026-11-02 (18 hours ago) · synced 10 hours ago

C 88.5% Markdown 9.1% GLSL 2.3%
git clone https://git.christianimmanuel.de/games/Entropie.gitwget https://git.christianimmanuel.de/games/Entropie/archive/Entropie.tar.gz
entropie/src/world/daycycle.c 4.5 KB · 107 lines raw
#include "world/daycycle.h"

#include <stdio.h>

#define SECONDS_PER_DAY   86400.0f
#define SUN_TILT          0.4f   /* tilts the sun path so noon shadows have a direction */
#define MIN_LIGHT_HEIGHT  0.3f   /* keeps shadows from stretching to infinity */

/* Painted palette for a time of day. */
typedef struct {
    float hour;
    Vec3  sky_top;
    Vec3  sky_horizon;
    Vec3  light;   /* lit areas */
    Vec3  shadow;  /* shaded areas */
} Mood;

static const Mood MOODS[] = {
    {  0.0f, { 0.06f, 0.07f, 0.20f }, { 0.20f, 0.16f, 0.36f }, { 0.35f, 0.42f, 0.70f }, { 0.22f, 0.20f, 0.42f } }, /* night      */
    {  5.5f, { 0.16f, 0.14f, 0.38f }, { 0.55f, 0.32f, 0.48f }, { 0.45f, 0.40f, 0.70f }, { 0.28f, 0.22f, 0.45f } }, /* pre-dawn   */
    {  7.0f, { 0.36f, 0.42f, 0.72f }, { 1.00f, 0.58f, 0.38f }, { 1.10f, 0.72f, 0.50f }, { 0.42f, 0.30f, 0.55f } }, /* sunrise    */
    { 10.0f, { 0.30f, 0.56f, 0.86f }, { 0.86f, 0.84f, 0.66f }, { 1.10f, 1.00f, 0.82f }, { 0.45f, 0.45f, 0.70f } }, /* morning    */
    { 14.0f, { 0.26f, 0.52f, 0.84f }, { 0.92f, 0.86f, 0.62f }, { 1.15f, 1.02f, 0.78f }, { 0.48f, 0.42f, 0.68f } }, /* afternoon  */
    { 19.0f, { 0.36f, 0.40f, 0.70f }, { 1.00f, 0.66f, 0.36f }, { 1.20f, 0.80f, 0.46f }, { 0.50f, 0.32f, 0.55f } }, /* golden     */
    { 21.0f, { 0.26f, 0.20f, 0.50f }, { 0.95f, 0.42f, 0.40f }, { 0.85f, 0.48f, 0.50f }, { 0.36f, 0.24f, 0.48f } }, /* sunset     */
    { 22.5f, { 0.06f, 0.07f, 0.20f }, { 0.20f, 0.16f, 0.36f }, { 0.35f, 0.42f, 0.70f }, { 0.22f, 0.20f, 0.42f } }, /* night      */
};
#define MOOD_COUNT ((int)(sizeof(MOODS) / sizeof(MOODS[0])))

/* Interpolates between the two moods around the given hour (wraps at midnight). */
static Mood moodAt(float hour) {
    int next = 0;
    while (next < MOOD_COUNT && MOODS[next].hour <= hour) next++;
    const Mood *a = &MOODS[(next + MOOD_COUNT - 1) % MOOD_COUNT];
    const Mood *b = &MOODS[next % MOOD_COUNT];

    float start = a->hour, end = b->hour;
    if (end <= start) end += 24.0f;
    float h = hour < start ? hour + 24.0f : hour;
    float t = smoothstepf(0.0f, 1.0f, (h - start) / (end - start));

    return (Mood){
        .hour        = hour,
        .sky_top     = vec3_lerp(a->sky_top, b->sky_top, t),
        .sky_horizon = vec3_lerp(a->sky_horizon, b->sky_horizon, t),
        .light       = vec3_lerp(a->light, b->light, t),
        .shadow      = vec3_lerp(a->shadow, b->shadow, t),
    };
}

static void updateLighting(DayCycle *dc) {
    const float hour         = dc->seconds / 3600.0f;
    const float day_length   = dc->day_end - dc->day_start;
    const float night_length = 24.0f - day_length;

    /* Sun travels a half circle during the day and the other half at night. */
    float angle;
    if (hour >= dc->day_start && hour <= dc->day_end) {
        angle = (hour - dc->day_start) / day_length * PI_F;
    } else {
        float since_sunset = hour < dc->day_start ? hour + 24.0f - dc->day_end
                                                  : hour - dc->day_end;
        angle = since_sunset / night_length * PI_F + PI_F;
    }

    Lighting *l = &dc->light;
    l->sun_direction = vec3_normalize(vec3(cosf(angle), sinf(angle), SUN_TILT));

    /* Key light: sun by day, moon (opposite the sun) by night, never too flat. */
    Vec3 key = l->sun_direction.y >= 0.0f ? l->sun_direction : vec3_scale(l->sun_direction, -1.0f);
    if (key.y < MIN_LIGHT_HEIGHT) {
        float flat = sqrtf(1.0f - MIN_LIGHT_HEIGHT * MIN_LIGHT_HEIGHT);
        Vec3 h = vec3_normalize(vec3(key.x, 0.0f, key.z));
        key = vec3(h.x * flat, MIN_LIGHT_HEIGHT, h.z * flat);
    }
    l->light_direction = key;

    /* Shadows fade out while sun and moon swap near the horizon. */
    l->shadow_strength = smoothstepf(0.0f, 0.2f, fabsf(l->sun_direction.y));
    l->day_factor      = smoothstepf(-0.1f, 0.3f, l->sun_direction.y);

    Mood m = moodAt(hour);
    l->sky_top      = m.sky_top;
    l->sky_horizon  = m.sky_horizon;
    l->light_color  = m.light;
    l->shadow_color = m.shadow;
}

void DayCycle_init(DayCycle *dc, float start_hour, float time_scale) {
    *dc = (DayCycle){
        .seconds    = start_hour * 3600.0f,
        .time_scale = time_scale,
        .day_start  = 7.0f,
        .day_end    = 21.0f,
    };
    updateLighting(dc);
}

void DayCycle_update(DayCycle *dc, float dt) {
    dc->seconds = fmodf(dc->seconds + dt * dc->time_scale, SECONDS_PER_DAY);
    updateLighting(dc);
}

void DayCycle_format(const DayCycle *dc, char *out, size_t out_size) {
    int total = (int)dc->seconds;
    snprintf(out, out_size, "%02d:%02d:%02d", total / 3600, (total / 60) % 60, total % 60);
}