/apps/demos/scenery: some friction

This commit is contained in:
veclavtalica 2025-03-02 00:02:32 +03:00
parent 5abd1ced1c
commit 119bd52c51

View File

@ -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);
}