Nimbin[12]?Games / Entropie / entropie/src/people/body_internal.h

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/people/body_internal.h 17.4 KB · 323 lines raw
#ifndef PEOPLE_BODY_INTERNAL_H
#define PEOPLE_BODY_INTERNAL_H

/*
 * Inside the body (body.c, body_pose.c): proportions, timings and the
 * little helpers both halves share. Not for use outside the body.
 */

#include "people/body.h"
#include "render/shapes.h"

/* Proportions of a 1.75 m figure (scaled to the actual height). */
#define REF_HEIGHT    1.75f
#define PELVIS_Y      0.97f
#define HIP_X         0.075f
#define HIP_DROP      0.05f
#define THIGH         0.44f
#define SHIN          0.42f
#define ANKLE_Y       BODY_ANKLE_Y
#define FOOT_LENGTH   0.15f
#define FOOT_X        0.085f
#define SPINE         0.36f
#define NECK          0.17f
#define HEAD_UP       0.12f
#define SHOULDER_X    0.19f
#define SHOULDER_UP   0.07f
#define UPPER_ARM     0.29f
#define FOREARM       0.26f
#define HAND          0.08f

/* Stepping */
#define STEP_UP           0.6f   /* how high a foot may step up (scaled) */
#define MAX_DROP          0.45f  /* ... and down */
#define STEP_THRESHOLD    0.09f  /* drift that triggers a step while moving */
#define IDLE_THRESHOLD    0.14f  /* ... while standing (avoids fidgeting) */
#define OVER_CLEAR        0.07f  /* a foot clears what it steps over by this much (scaled) */
#define OVER_RISE         4.0f   /* m/s the lift for it may grow mid-step (scaled) */
#define OVER_STRIDE       0.95f  /* a step over something lands at most this far from the other foot (scaled) */
#define OVER_LEAP         1.3f   /* running: up to this many step lengths */
#define OVER_LEFT         0.2f   /* the aim ran this far past a step waiting in front of something: go past it */
#define OVER_LEAP_LATE    0.6f   /* ... while the foot is no further into its step than this */
#define LEAP_SPEED        2.5f   /* m/s: from here on ... */
#define LEAP_BEHIND       0.8f   /* ... a foot this far behind leaves before the other one is over (both in the air) */
#define LATE_QUICK        3.0f   /* running: a step held back this much quicker per meter it lands behind the hips */
#define LIMB_R            0.05f  /* a shin's half thickness (kept clear of things) */
#define LEG_NEAR          1.3f   /* only a foot this near the body minds its leg (scaled) */
#define LEG_AHEAD         0.25f  /* s: a standing leg is checked where the body will be this soon */
#define LEG_FORGET        0.5f   /* m/s: ... it lets go of that slowly */
#define LIFT_DROP         3.0f   /* m/s a lifted foot comes down at most */
#define LEG_MOVING        0.3f   /* m/s: slower, a swinging foot doesn't lift its leg over things */
#define LEG_RISE          4.0f   /* m/s a foot lifts when its leg would go into something */
#define LEG_HOLD          0.85f  /* until this far into a step, a foot waits for its leg to clear */
#define LEG_TOUCH         0.015f /* (let off) */
#define LIFT_MOST         0.62f  /* a foot lifts at most this high over what it passes */
#define STEP_ON_SMALL     0.12f  /* things this low (scaled) a careless foot just steps on */
#define OVER_SWING_OUT    0.12f  /* a foot stepping over something swings out this far to the side (scaled) ... */
#define OVER_SWING_FULL   0.35f  /* ... over things this high (less over lower ones) */
#define PLAN_FIT          0.3f   /* sportier than this, steps are fitted to what's coming (fully from +0.4) ... */
#define PLAN_AHEAD        3.0f   /* ... looking this far ahead (scaled) ... */
#define PLAN_TAKE_OFF     0.25f  /* ... for a take-off this far before it (+ a bit per m/s) */
#define OVER_SLOW         1.0f   /* a step over something takes this much longer per meter of its height */
#define LIFT_RISE         2.5f   /* m/s a foot is lifted at most to keep clear of something (scaled) */
#define STAND_ON_MOST     0.42f  /* a foot comes down on top of something at most this high (scaled) */
#define AIM_GLIDE         0.6f   /* m/s a stepping foot's aim may shift on its own (not carried by the body) */
#define TURN_THRESHOLD    0.82f  /* cos of how far the body may turn before a foot follows */
#define FOOT_GAP          0.12f  /* feet never come closer sideways than this */
#define HEEL_LIFT         0.09f  /* rear foot rolls onto its toes (unscaled) */
#define STEP_LIFT         0.10f
#define MIN_STEP_TIME     0.16f
#define MAX_STEP_TIME     0.42f
#define RUN_SPEED         3.5f   /* from here on both feet may be in the air */
#define MAX_LEAD          0.48f  /* how far ahead of the hips a foot lands */
#define TELEPORT_SPEED    40.0f  /* faster than this: replant the feet */
#define AIR_GRACE         0.15f  /* brief bumps off the ground don't count as flying */
#define LANDING_STEP_TIME 0.12f
#define STUMBLE_TIME      0.8f
#define CATCH_STEP_TIME   0.16f  /* quick step to catch yourself */
#define TOE_OUT           0.12f  /* radians feet point outwards */

/* Falling down and getting up (seconds) */
#define FALL_TIME         0.6f
#define LIE_DOWN_TIME     2.0f
#define GET_UP_TIME       1.7f
#define LIE_HEIGHT        0.12f  /* torso middle above the ground while lying */
#define FALL_REACH        0.95f  /* hips land this far from the feet after a fall */

/* Expression */
#define BURP_TIME         0.9f
#define VOMIT_TIME        2.6f
#define VOMIT_SPLASH      1.1f   /* seconds into it */
#define LIE_SAMPLES       9
#define LIE_STEP          0.3f   /* meters between ground samples under a lying body */
#define BLINK_TIME        0.13f

/* Ways to fall */
enum { FALL_PLANK, FALL_CRUMPLE, FALL_TWIST, FALL_DIVE };

/* Drop and roll */
#define DIVE_TIME         0.38f  /* down to the ground, quick */
#define ROLL_REACH        (1.6f * PI_F) /* radians at the furthest */
#define ROLL_PACE         2.2f   /* radians per second of the to and fro */
#define ROLL_TIME         (2.0f * PI_F / ROLL_PACE) /* one way, back, the other way and back */
#define ROLL_REST         0.4f   /* lying a moment after rolling */
#define ROLL_RADIUS       0.14f  /* the body rolls sideways like a log this thick */
#define ROLL_OVER_TIME    1.1f   /* s rolling over once (the roll way up) */

/* Jumping */
#define TAKE_OFF_SPEED    1.0f   /* rising this fast off the ground is a jump */
#define PUSH_OFF_TIME     0.08f  /* the push-off foot leaves the ground this much later */
#define TRAIL_LAND_DELAY  0.09f  /* running jump: the trailing foot comes down after the leading one */

/* Idle gestures with one hand */
enum { GESTURE_NONE, GESTURE_HIP, GESTURE_HEAD, GESTURE_CHIN, GESTURE_POCKET,
       GESTURE_NECK,   /* rubbing the back of the neck (tired, shy) */
       GESTURE_NOSE,   /* rubbing the nose (nervous) */
       GESTURE_EAR,    /* at the earlobe (nervous, shy) */
       GESTURE_HAIR,   /* a hand back through the hair (proud, lively) */
       GESTURE_THUMB,  /* thumb hooked in the belt (proud, at ease) */
       GESTURE_CHEST,  /* hand on the chest (shy) */
       GESTURE_SHADE,  /* shading the eyes, looking out (lively) */
       GESTURE_WAVE,   /* talking: waving hello or goodbye (Body_talkGesture) */
       GESTURE_SHRUG,  /* ... a palm up and out: "no idea", "never mind" */
       GESTURE_POINT,  /* ... pointing ahead: "over there" */
       GESTURE_COUNT };
#define GESTURE_FADE      0.4f

/* Standing about, the whole body: nobody stands quite still for long */
enum {
    FIDGET_NONE,
    FIDGET_SHAKE_LEG,   /* weight on one leg, the other foot up and shaken loose */
    FIDGET_ROCK,        /* up on the toes and back down, twice */
    FIDGET_STRETCH,     /* arms up over the head, chest out */
    FIDGET_SHOULDERS,   /* rolling the shoulders */
    FIDGET_NECK,        /* head over to one side, then the other */
    FIDGET_CROSS_ARMS,  /* arms folded for a while */
    FIDGET_TAP_FOOT,    /* the toes of one foot tapping */
    FIDGET_RUB_HANDS,   /* rubbing the hands together (nervous) */
    FIDGET_HEAD_BEHIND, /* hands clasped behind the head, elbows out (at ease, proud) */
    FIDGET_AKIMBO,      /* both hands on the hips (proud) */
    FIDGET_KNEES,       /* out of breath: bent over, hands on the knees (the unfit) */
    FIDGET_ON_HEAD,     /* out of breath: hands on top of the head, chest open (the sporty) */
    FIDGET_COUNT
};
#define FIDGET_FADE       0.35f
#define STANCE_STEP       0.17f  /* a new stance is at least a step away (see IDLE_THRESHOLD) */

/* Body volume that arms keep out of (unscaled; see figure.frag) */
#define TORSO_HALF_WIDTH  0.145f
#define TORSO_HALF_DEPTH  0.10f
#define PELVIS_RADIUS     0.10f
#define THIGH_RADIUS      0.08f
#define HEAD_RADIUS       0.11f
#define ELBOW_RADIUS      0.05f
#define WRIST_RADIUS      0.04f
#define PALM_RADIUS       0.035f
#define LEG_RADIUS        0.05f  /* thigh and shin, kept clear of things */
#define KNEE_FOLLOWS_FOOT 0.45f  /* the knee points this much the way the foot does, the rest straight ahead ... */
#define KNEE_OUT          -0.12f /* ... and this much out (+) or in (a little in: no bow legs) */
#define KNEE_OUT_MOST     1.4f   /* radians a knee may swing out round the leg ... */
#define KNEE_OUT_STEPS    7
#define KNEE_IN_MOST      0.5f   /* ... or in (radians) */
#define KNEE_IN_STEPS     2
#define KNEE_OFF_SPEED    1.6f   /* ... the knee moving there at most this fast (m/s) */
#define LEG_LET_OFF       0.01f  /* (a leg this little into something is let be) */
#define LEG_RAISE_BELOW_HIP 0.35f /* a raised foot stays at least this far below the hip */
#define LEG_RAISE_SPEED   2.5f   /* m/s a foot comes up off the ground when its leg must */
#define OVER_LEAN         0.22f  /* radians the body leans forward stepping over something high */
#define BOB_OMEGA         28.0f  /* the running wave follows the strides this quickly */
#define RUN_TUCK_BACK     -0.30f /* running, the swing foot's curve: pulled up behind the seat ... */
#define RUN_TUCK_UP       0.85f
#define RUN_DRIVE_AHEAD   0.45f  /* ... then through in front, the knee up */
#define RUN_DRIVE_UP      0.55f
#define ARM_SWING         1.25f  /* rad the arms swing per m the opposite foot is ahead (walking; running more) */
#define SWING_RUN_EASE    2.5f   /* per s a running swing turns into a walking one, slowing down */
#define STANCE_RUN        0.11f  /* s a foot stays down at a full run (bending, then pushing off) */
#define BOB_RUN           0.8f   /* running: the wave this much deeper (down on the bent leg, up in the flight) */
#define BOB_FLIGHT        1.6f   /* ... in the flight the body goes up past standing height (the wave's top) */
#define SWING_CHEST   0.8f   /* a two-handed swing off to one side: the shoulders turn this much with it ... */
#define SWING_HIPS    0.3f   /* ... the hips ... */
#define SWING_HEAD    0.3f   /* ... the head ... */
#define SWING_ARCH    0.15f  /* ... and the back arches winding up, bends into the hit (rad) */
#define WALK_BOB          0.022f /* m walking: up mid-step, down as a foot lands (either way) */
#define BOB_FROM          1.8f   /* m/s: from a brisk walk on ... */
#define BOB_FULL          3.6f   /* ... to a run: the body rises and falls with each stride */
#define BOB_FIT           0.035f /* m down onto each step, sporty ... */
#define BOB_UNFIT         0.04f  /* ... and this much more for the unsporty */
#define BOB_NOD           0.12f  /* radians the unsporty nod forward with it */
#define JOINT_TOP_HAND    14.0f  /* m/s (scaled, relative to the body) a hand can move at most (a throw) ... */
#define JOINT_TOP_FOOT    12.0f  /* ... a foot (sprinting) ... */
#define JOINT_TOP_MIDDLE  9.0f   /* ... a knee ... */
#define JOINT_TOP_ELBOW   5.5f   /* ... an elbow (it swings round the arm, never flips over) ... */
#define JOINT_TOP_TRUNK   5.0f   /* ... hips, chest, head, shoulders */
#define KNEE_GAP          0.13f  /* knees never come closer than this */

/* Easing rates (per second) */
#define EASE_VELOCITY 10.0f
#define EASE_POSE     6.0f
#define EASE_PELVIS   10.0f
#define EASE_MOOD     1.5f
#define PELVIS_SPEED  1.2f   /* max vertical hip speed (m/s, unscaled) */
#define PELVIS_OMEGA  14.0f  /* springs (rad/s): how quickly the hips follow up and down ... */
#define SWAY_OMEGA    13.0f  /* ... and from foot to foot */
#define HAND_OMEGA    26.0f  /* ... and the hands to what they reach for */
#define HAND_WEIGHT_OMEGA 11.0f /* ... and a hand taking up (or giving up) what it does */
#define SWAY_MOST     0.045f /* m the hips go over the standing foot at most (scaled out: unscaled body) */
#define ROOT_OMEGA    12.0f  /* the hips follow the character's position on a spring ... */
#define ROOT_LAG      0.08f  /* ... never further behind than this (meters) */
#define HAND_FOLLOW   4.0f   /* a hand goal moving faster than this (m/s) jumped: the hand eases there */
#define POSE_OMEGA    9.0f  /* ... and bending down, leaning in */
#define POLE_TURN     9.0f  /* radians a second a knee or elbow may turn round its limb */

/* Arm swing springs: a little lag and overshoot gives the follow-through */
#define ARM_STIFFNESS  80.0f
#define ARM_DAMPING    11.0f
#define FORE_STIFFNESS 50.0f
#define FORE_DAMPING    8.0f

#define UP vec3(0.0f, 1.0f, 0.0f)

static inline float ease(float value, float goal, float rate, float dt) {
    return value + (goal - value) * fminf(1.0f, rate * dt);
}

static inline Vec3 flat(Vec3 v) { return vec3(v.x, 0.0f, v.z); }

static inline float shyness(const Body *b) { return fmaxf(-b->shown.pride, 0.0f); }

static inline float proudness(const Body *b) { return fmaxf(b->shown.pride, 0.0f); }

/* 0 = walking .. 1 = running (longer swings with both feet in the air) */
static inline float runAmount(float speed) {
    return clampf((speed - RUN_SPEED) / 2.5f, 0.0f, 1.0f);
}

/* Smoking (body.c) */
#define ROLL_CIG_TIME 7.0f    /* rolling one */
#define LIGHT_TIME    1.8f    /* the flame to it, the first drag */
#define DRAG_TIME     1.4f

/* Peeing: 0..1, hands at the belt, head down a little. */
static inline float peeing(const Body *b) {
    if (b->pee <= 0.0f) return 0.0f;
    float p = 1.0f - b->pee / fmaxf(b->pee_total, 1.0f);
    return smoothstepf(0.0f, 0.12f, p) * (1.0f - smoothstepf(0.9f, 1.0f, p));
}

/* Ground under a lying body at a distance along fall_dir from the hips (meters). */
static inline float lyingGround(const Body *b, float along) {
    float t = clampf((along + LIE_STEP * 4.0f) / LIE_STEP, 0.0f, (float)(LIE_SAMPLES - 1));
    int i = (int)t;
    if (i >= LIE_SAMPLES - 1) return b->lie_ground[LIE_SAMPLES - 1];
    float f = t - (float)i;
    return b->lie_ground[i] * (1.0f - f) + b->lie_ground[i + 1] * f;
}

/* Being sick: 0..1 into it; bent over (0..1); heaving with the mouth open (0..1). */
static inline float vomitPhase(const Body *b) { return b->vomit > 0.0f ? 1.0f - b->vomit / VOMIT_TIME : 0.0f; }

static inline float bentOver(const Body *b) {
    float p = vomitPhase(b);
    return b->vomit > 0.0f ? smoothstepf(0.0f, 0.3f, p) * (1.0f - smoothstepf(0.8f, 1.0f, p)) : 0.0f;
}

static inline float heaving(const Body *b) {
    float p = vomitPhase(b);
    return b->vomit > 0.0f ? smoothstepf(0.33f, 0.4f, p) * (1.0f - smoothstepf(0.6f, 0.75f, p)) : 0.0f;
}

/* How far into its gesture a hand is (0..1). */
static inline float gestureWeight(const Body *b, int side) {
    if (b->gesture_time <= 0.0f || b->gesture_side != side) return 0.0f;
    return smoothstepf(0.0f, GESTURE_FADE, b->gesture_time) *
           smoothstepf(0.0f, GESTURE_FADE, b->gesture_len - b->gesture_time) * smoothstepf(0.0f, 1.0f, b->gesture_fade);
}

/* How far into this fidget the body is (0..1), and where in it (0..1). */
static inline float fidgetWeight(const Body *b, int kind) {
    if (b->fidget != kind || b->fidget_time <= 0.0f) return 0.0f;
    return smoothstepf(0.0f, FIDGET_FADE, b->fidget_time) * smoothstepf(0.0f, FIDGET_FADE, b->fidget_len - b->fidget_time)
         * smoothstepf(0.0f, 1.0f, b->fidget_fade);
}

/* Out of breath, bent over with the hands on the knees (eased in and out slower than a fidget). */
static inline float catchingBreath(const Body *b) {
    if (b->fidget != FIDGET_KNEES || b->fidget_time <= 0.0f) return 0.0f;
    return smoothstepf(0.0f, 0.9f, b->fidget_time) * smoothstepf(0.0f, 0.9f, b->fidget_len - b->fidget_time) * smoothstepf(0.0f, 1.0f, b->fidget_fade);
}

/* Bent over: being sick, or catching breath. */
static inline float bentDown(const Body *b) { return fmaxf(bentOver(b), 0.85f * catchingBreath(b)); }

static inline float fidgetPhase(const Body *b) {
    return b->fidget_len > 0.0f ? 1.0f - b->fidget_time / b->fidget_len : 0.0f;
}

/* Forward kinematics for a hanging, swinging limb (angle in the y/z plane). */
static inline Vec3 swing(Vec3 from, float length, float angle, float out) {
    return vec3_add(from, vec3_scale(vec3_normalize(vec3(out, -cosf(angle), sinf(angle))), length));
}

/* Critically damped spring (no overshoot): follows the target, smooth in speed too. */
/* A critically damped spring towards goal (moving along at goal_v), solved
   exactly: the same smooth motion at any frame rate, never overshooting or
   flickering however long a frame takes. */
static inline void springStep(float *value, float *velocity, float goal, float goal_v, float omega, float dt) {
    if (dt <= 0.0f) return;
    float d = *value - goal, w = *velocity - goal_v;
    float e = expf(-omega * dt), t = (w + omega * d) * dt;
    *value = goal + goal_v * dt + (d + t) * e; /* (the goal moves on meanwhile: no lag behind a steady goal) */
    *velocity = goal_v + (w - omega * t) * e;
}

static inline void springStep3(Vec3 *value, Vec3 *velocity, Vec3 goal, Vec3 goal_v, float omega, float dt) {
    springStep(&value->x, &velocity->x, goal.x, goal_v.x, omega, dt);
    springStep(&value->y, &velocity->y, goal.y, goal_v.y, omega, dt);
    springStep(&value->z, &velocity->z, goal.z, goal_v.z, omega, dt);
}

static inline void springCritical(float *value, float *velocity, float target, float omega, float dt) {
    springStep(value, velocity, target, 0.0f, omega, dt);
}

#endif /* PEOPLE_BODY_INTERNAL_H */