Entropie git · main
Entropie festival game; first steps
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.gzentropie/src/world/daycycle.c 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);
}