diff --git a/apps/demos/scenery/scenes/ingame.c b/apps/demos/scenery/scenes/ingame.c index a795c76..3ef1069 100644 --- a/apps/demos/scenery/scenes/ingame.c +++ b/apps/demos/scenery/scenes/ingame.c @@ -53,14 +53,16 @@ static float heightmap[TERRAIN_DISTANCE][TERRAIN_DISTANCE]; /* else if length(v(o) + (F(o)/m)*t) <= (f(k)*x(n)*t), v(o) = 0 */ /* else, F = -unit(v(o)*f(k)*x(n)) */ -#define VEHICLE_MASS 10.0f +#define VEHICLE_MASS 200.0f #define VEHICLE_LENGTH 3.0f #define VEHICLE_WIDTH 1.7f #define VEHICLE_HEIGHT 1.3f /* spring constant */ -#define VEHICLE_SPRING_K 20.0f +#define VEHICLE_SPRING_K 400.0f /* damping constant */ -#define VEHICLE_SPRING_C 50.0f +#define VEHICLE_SPRING_C 20.0f +#define VEHICLE_FRICTION_S 1000000.0f +#define VEHICLE_FRICTION_K 1000000.0f /* initial, ideal corner positions */ static const Vec3 vbpi[8] = { @@ -110,11 +112,11 @@ static void draw_vehicle(SceneIngame *scn) { static void process_vehicle(SceneIngame *scn) { /* apply gravity */ + Vec3 Facc[8] = {0}; + Vec3 const Fg = { .y = -VEHICLE_MASS * G_CONST }; - for (size_t i = 0; i < 8; ++i) { - Vec3 const vd = vec3_scale(Fg, (1.0f / VEHICLE_MASS) * ctx.frame_duration); - vbv[i] = vec3_add(vbv[i], vd); - } + for (size_t i = 0; i < 8; ++i) + Facc[i] = vec3_add(Facc[i], Fg); /* apply springs */ for (size_t i = 0; i < 24; ++i) { @@ -129,30 +131,54 @@ static void process_vehicle(SceneIngame *scn) { float const x = vec3_length(pd) - lar; float const v = vec3_dot(vec3_sub(v1, v0), pn); Vec3 const Fs = vec3_scale(pn, -VEHICLE_SPRING_K * x - VEHICLE_SPRING_C * v); - Vec3 const vd = vec3_scale(Fs, (1.0f / VEHICLE_MASS) * ctx.frame_duration); - - vbv[vbs[i][0]] = vec3_sub(vbv[vbs[i][0]], vd); - vbv[vbs[i][1]] = vec3_add(vbv[vbs[i][1]], vd); + Facc[vbs[i][0]] = vec3_sub(Facc[vbs[i][0]], Fs); + Facc[vbs[i][1]] = vec3_add(Facc[vbs[i][1]], Fs); } - /* spring out points that are underground */ + /* spring and friction against the ground */ for (size_t i = 0; i < 8; ++i) { Vec3 const p = vbp[i]; Vec3 const v = vbv[i]; float const h = height_at(scn, (Vec2){ p.x, p.z }); if (h > p.y) { + /* displacement force */ Vec3 const n = normal_at(scn, (Vec2){ p.x, p.z }); float const xn = (h - p.y) * n.y; float const vn = vec3_dot(v, n); Vec3 const Fd = vec3_scale(n, -VEHICLE_SPRING_K * xn - VEHICLE_SPRING_C * vn); - vbv[i] = vec3_add(vbv[i], vec3_scale(Fd, -(1.0f / VEHICLE_MASS) * ctx.frame_duration)); + Facc[i] = vec3_sub(Facc[i], Fd); + + /* friction force, perpendicular to displacement */ + /* portions aligned to surface normal */ + Vec3 const vov = vec3_sub(v, vec3_scale(n, vn)); + float const vo = vec3_length(vov); + Vec3 const Fov = vec3_sub(Facc[i], vec3_scale(n, vec3_dot(Facc[i], n))); + float const Fo = vec3_length(Fov); + float const fkxn = VEHICLE_FRICTION_K * xn; + if (fabsf(0.0f - vo) <= 0.0001f) { + /* cannot overcome static friction, zero force along the surface */ + if (Fo <= VEHICLE_FRICTION_S * xn) { + Facc[i] = vec3_sub(Facc[i], Fov); + } + /* apply kinematic friction */ + else { + Facc[i] = vec3_sub(Facc[i], vec3_scale(vec3_norm(Fov), fkxn)); + } + /* velocity with gain along the surface will not overcome */ + } else if (vo + (Fo / VEHICLE_MASS) * ctx.frame_duration <= fkxn * ctx.frame_duration) { + vbv[i] = vec3_sub(vbv[i], vov); + } else { + Facc[i] = vec3_sub(Facc[i], vec3_scale(vec3_norm(vov), fkxn)); + } } } - /* apply velocity */ + /* integrate forces and velocity */ for (size_t i = 0; i < 8; ++i) { + Vec3 const vd = vec3_scale(Facc[i], (1.0f / VEHICLE_MASS) * ctx.frame_duration); + vbv[i] = vec3_add(vbv[i], vd); vbp[i] = vec3_add(vbp[i], vbv[i]); - vbv[i] = vec3_scale(vbv[i], 0.99f); /* friction... */ + vbv[i] = vec3_scale(vbv[i], 0.99f); } } @@ -188,8 +214,8 @@ static void process_fly_mode(State *state) { /* TODO: could be baked in map format */ static Vec3 normal_at(SceneIngame *scn, Vec2 position) { - int const x = (int)(HALF_TERRAIN_DISTANCE + (position.x - scn->pos.x)); - int const y = (int)(HALF_TERRAIN_DISTANCE + (position.y - scn->pos.z)); + int const x = (int)(roundf(HALF_TERRAIN_DISTANCE + (position.x - scn->pos.x))); + int const y = (int)(roundf(HALF_TERRAIN_DISTANCE + (position.y - scn->pos.z))); float const height0 = heightmap[x][y]; float const height1 = heightmap[x + 1][y]; @@ -203,31 +229,22 @@ static Vec3 normal_at(SceneIngame *scn, Vec2 position) { /* TODO: don't operate on triangles, instead interpolate on quads */ static float height_at(SceneIngame *scn, Vec2 position) { - float height0, height1, height2, weight0, weight1, weight2; + int const x = (int)(roundf(HALF_TERRAIN_DISTANCE + (position.x - scn->pos.x))); + int const y = (int)(roundf(HALF_TERRAIN_DISTANCE + (position.y - scn->pos.z))); - int const x = (int)(HALF_TERRAIN_DISTANCE + (position.x - scn->pos.x)); - int const y = (int)(HALF_TERRAIN_DISTANCE + (position.y - scn->pos.z)); - - height0 = heightmap[x][y]; - height1 = heightmap[x + 1][y + 1]; + float const height0 = heightmap[x][y]; + float const height1 = heightmap[x + 1][y]; + float const height2 = heightmap[x][y + 1]; + float const height3 = heightmap[x + 1][y + 1]; Vec2 incell = { position.x - floorf(position.x), position.y - floorf(position.y) }; - /* who needs barycentric coordinates, am i right? */ - weight0 = 1 / sqrtf(powf(incell.x, 2) + powf(incell.y, 2)); - weight1 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(1 - incell.y, 2)); + float const weight0 = (1 - incell.x) * (1 - incell.y); + float const weight1 = ( incell.x) * (1 - incell.y); + float const weight2 = (1 - incell.x) * ( incell.y); + float const weight3 = ( incell.x) * ( incell.y); - /* find which triangle we're directly under */ - /* for this manhattan distance is sufficient */ - if (incell.x + (1 - incell.y) < (1 - incell.x) + incell.y) { - height2 = heightmap[x][y + 1]; - weight2 = 1 / sqrtf(powf(incell.x, 2) + powf(1 - incell.y, 2)); - } else { - height2 = heightmap[x + 1][y]; - weight2 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(incell.y, 2)); - } - - return (height0 * weight0 + height1 * weight1 + height2 * weight2) / (weight0 + weight1 + weight2); + return (height0 * weight0 + height1 * weight1 + height2 * weight2 + height3 * weight3) / (weight0 + weight1 + weight2 + weight3); }