strichmaennchen git · main
git clone https://git.christianimmanuel.de/games/strichmaennchen.gitwget https://git.christianimmanuel.de/games/strichmaennchen/archive/strichmaennchen.tar.gzobject.c raw
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <float.h>
double fabs(double x);
#include "object.h"
#include "linalg.h"
void Collider_update(Collider *c);
Object *Object_create(ObjectType type, VertexPreset vertex, Vec3 position, Vec3 size, float weight) {
Object *obj = malloc(sizeof(Object));
if (!obj) return NULL;
obj->type = type;
obj->position = position;
obj->weight = weight;
obj->strength = 100;
obj->vertex = vertex;
obj->rotation = (Vec3){0,0,0};
obj->mesh = mesh_default;
obj->velocity = (Vec3){0,0,0};
obj->angular_velocity = (Vec3){0,0,0};
obj->collider.half_extents = vec3_scale(size, 0.5);
switch (type) {
case OBJ_TRIANGLE:
obj->mesh = Mesh_createTriangle();
obj->collider.isStatic = true;
break;
case OBJ_AXES:
obj->mesh = Mesh_createAxes(200);
obj->collider.isStatic = true;
break;
case OBJ_PYRAMID:
obj->mesh = Mesh_createPyramid(4.0f, 4.0f);
obj->collider.isStatic = true;
break;
case OBJ_CUBE:
obj->mesh = Mesh_createCube(size, vertex);
obj->collider.isStatic = false;
break;
case OBJ_CYLINDER:
obj->mesh = Mesh_createCylinder(size.x, size.y, 30, vertex);
obj->collider.isStatic = false;
break;
case OBJ_CYLINDER_STATIC:
obj->mesh = Mesh_createCylinder(size.x, size.y, 30, vertex);
obj->collider.isStatic = true;
break;
case OBJ_WALL:
obj->mesh = Mesh_createCube(size, vertex);
obj->collider.isStatic = true;
break;
case OBJ_HUMAN:
obj->mesh = Mesh_createHuman(vertex, false);
obj->collider.isStatic = false;
break;
case OBJ_HUMAN_PLAYER:
obj->mesh = Mesh_createHuman(vertex, true);
obj->collider.isStatic = false;
break;
case OBJ_GROUND:
obj->mesh = Mesh_createCube(size, vertex);
obj->collider.isStatic = true;
break;
case OBJ_TREE:
obj->mesh = Mesh_createTree(3.0f, 0.3f, 8, 1.5f);
obj->collider.isStatic = true;
obj->position.y = -size.y/2.0f;
break;
case OBJ_HOUSE_MAIN:
obj->mesh = Mesh_createHouse();
obj->collider.isStatic = true;
obj->position.y = -size.y/2.0f;
break;
case OBJ_BARN:
obj->mesh = Mesh_createBarn();
obj->collider.isStatic = true;
obj->position.y = -size.y/2.0f;
break;
default:
obj->mesh = mesh_default;
obj->collider.half_extents = (Vec3){0,0,0};
obj->collider.isStatic = true;
fprintf(stderr, "Warning: unsupported ObjectType (%d)\n", type);
break;
}
obj->position_center = (Vec3){0, obj->collider.half_extents.y, 0};
obj->collider.position = obj->position_center;
Collider_update(&obj->collider);
return obj;
}
void Object_destroy(Object* obj) {
free(obj);
}
void Object_render(Object *obj, Renderer *renderer, Camera *camera, Time* time) {
if (!obj) return;
TransformMatrices m;
m.projection = camera->projection;
m.view = camera->view;
// Translate by bottom position + half height to center the mesh
Vec3 renderPos = vec3_add(obj->position, (Vec3){0, obj->collider.half_extents.y, 0});
m.model = mat4_identity();
m.model = mat4_mul(m.model, mat4_translate(renderPos));
// yaw around global Y (physics)
m.model = mat4_mul(m.model, mat4_rotate_y(obj->rotation.y));
// then apply local tilt (visual placement)
m.model = mat4_mul(m.model, mat4_rotate_x(obj->rotation.x));
m.model = mat4_mul(m.model, mat4_rotate_z(obj->rotation.z));
//Mat4 mvp = mat4_mul(m.projection, mat4_mul(m.view, m.model));
Mesh_draw(obj->mesh, renderer, &m, time, camera->position);
}
Vec3 colliderCenterWorld(Object *o) {
return vec3_add(o->position, o->collider.position);
}
float OBB_project(Collider *c, Vec3 axis) {
// Projection of half-extents along axis
return fabsf(c->half_extents.x * vec3_dot(axis, c->axes[0])) +
fabsf(c->half_extents.y * vec3_dot(axis, c->axes[1])) +
fabsf(c->half_extents.z * vec3_dot(axis, c->axes[2]));
}
bool OBB_overlap_MTV(Object *o_a, Object *o_b, Vec3 *mtv_axis, float *penetration) {
Vec3 t = vec3_sub(colliderCenterWorld(o_b), colliderCenterWorld(o_a));
Collider* a = &o_a->collider;
Collider* b = &o_b->collider;
Vec3 axesA[3] = {a->axes[0], a->axes[1], a->axes[2]};
Vec3 axesB[3] = {b->axes[0], b->axes[1], b->axes[2]};
Vec3 testAxes[15];
int idx = 0;
for (int i = 0; i < 3; i++) testAxes[idx++] = axesA[i];
for (int i = 0; i < 3; i++) testAxes[idx++] = axesB[i];
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
testAxes[idx++] = vec3_cross(axesA[i], axesB[j]);
float minPen = FLT_MAX;
Vec3 minAxis = {0,0,0};
for (int i = 0; i < 15; i++) {
Vec3 axis = testAxes[i];
if (vec3_length(axis) < 1e-6f) continue;
axis = vec3_normalize(axis);
float projA = OBB_project(a, axis);
float projB = OBB_project(b, axis);
float dist = fabsf(vec3_dot(t, axis));
float pen = projA + projB - dist;
if (pen < 0)
return false; // separating axis found
if (pen < minPen) {
minPen = pen;
minAxis = axis;
}
}
*mtv_axis = minAxis;
*penetration = minPen;
return true;
}
void Object_applyGravity(Object *obj, float dt) {
if (obj->collider.isStatic || obj->is_grounded) return;
obj->velocity.y -= 9.81f * dt;
}
void Collider_positionGround(Object* obj, Object* ground) {
if (!ground->collider.isStatic) return;
Collider* g = &ground->collider;
Collider* o = &obj->collider;
// Ground's Y-axis in world space
Vec3 up = g->axes[1]; // assuming Y is "up"
// Vector from obj center to ground center
Vec3 delta = vec3_sub(colliderCenterWorld(obj), colliderCenterWorld(ground));
// Project delta onto ground up-axis
float distAlongUp = vec3_dot(delta, up);
// Compute half extents along up-axis
float objHalf = OBB_project(o, up);
float groundHalf = OBB_project(g, up);
// penetration = distance we need to move obj down to sit on top
float penetration = (distAlongUp - (groundHalf + objHalf));
if (penetration > 1.0f) penetration = 1.0f;
// Only snap if penetrating (obj is above ground)
if (penetration < 0) {
// maximum step height: 1/3 of object height
float max_step = obj->collider.half_extents.y*2/3.0f;
if (obj->type != OBJ_HUMAN && obj->type != OBJ_HUMAN_PLAYER)
max_step = 1e6f; // effectively unlimited for other objects
if (-penetration <= max_step) {
// move object along negative up-axis to sit on top
obj->position = vec3_add(obj->position, vec3_scale(up, -penetration));
obj->velocity.y = 0;
obj->is_grounded = true;
} else {
obj->is_grounded = false;
}
}
}
static float calc_PushFactor(int obj_weight, int obj_strength, int other_weight) {
if (obj_strength <= 0 || obj_weight <= 0)
return 0.0f;
float max_weight = obj_weight * (obj_strength / 50.0f);
if (other_weight <= 0)
return 1.0f;
if (other_weight >= max_weight)
return 0.0f;
return 1.0f - (other_weight / max_weight);
}
void Object_resolveDynamicCollision(Object *obj, Object *other) {
if (obj->collider.isStatic && other->collider.isStatic) return;
Vec3 mtv_axis;
float penetration;
if (!OBB_overlap_MTV(obj, other, &mtv_axis, &penetration)) return;
// Ensure MTV points from obj -> other
Vec3 dir = vec3_sub(colliderCenterWorld(other), colliderCenterWorld(obj));
if (vec3_dot(dir, mtv_axis) < 0) mtv_axis = vec3_scale(mtv_axis, -1.0f);
mtv_axis = vec3_normalize(mtv_axis);
const Vec3 world_up = {0,1,0};
float verticalAlignment = fabsf(vec3_dot(mtv_axis, world_up));
// --- Handle vertical collisions ---
if (verticalAlignment > 0.70f) {
if (other->collider.isStatic) {
float dotUp = vec3_dot(mtv_axis, world_up);
if (dotUp < 0.0f) {
Collider_positionGround(obj, other);
obj->velocity.y = 0.0f;
obj->is_grounded = true;
return;
} else {
if (obj->velocity.y > 0.0f) obj->velocity.y = 0.0f;
return;
}
} else {
obj->velocity.y = 0.0f;
obj->is_grounded = true;
}
}
// --- 1. Separate objects along MTV ---
const float MAX_CORRECTION = 0.5f;
if (penetration > MAX_CORRECTION) penetration = MAX_CORRECTION;
if (!other->collider.isStatic) {
float pushRatioObjToOther = calc_PushFactor(obj->weight, obj->strength, other->weight);
if (pushRatioObjToOther > 0.0f) {
Vec3 pushVec = vec3_scale(mtv_axis, penetration * pushRatioObjToOther);
other->position = vec3_add(other->position, pushVec);
} else {
obj->position = vec3_sub(obj->position, vec3_scale(mtv_axis, penetration));
obj->velocity = vec3_scale(obj->velocity, 0.2f);
}
} else if (!obj->collider.isStatic) {
obj->position = vec3_sub(obj->position, vec3_scale(mtv_axis, penetration));
}
// --- 2. Apply horizontal linear velocity ---
Vec3 horizontalAxis = (Vec3){ mtv_axis.x, 0.0f, mtv_axis.z };
if (vec3_length(horizontalAxis) > 1e-6f)
horizontalAxis = vec3_normalize(horizontalAxis);
else
horizontalAxis = (Vec3){0,0,0};
float pushStrength = 2.0f;
float pushRatioObjToOther = calc_PushFactor(obj->weight, obj->strength, other->weight);
if (!other->collider.isStatic && pushRatioObjToOther > 0.0f) {
Vec3 dv = vec3_scale(horizontalAxis, pushStrength * pushRatioObjToOther);
other->velocity = vec3_add(other->velocity, dv);
}
if (!obj->collider.isStatic && pushRatioObjToOther > 0.0f) {
obj->velocity = vec3_sub(obj->velocity, vec3_scale(horizontalAxis, pushStrength * 0.2f * pushRatioObjToOther));
}
// --- 3. Apply angular velocity with stronger torque ---
float angularPushFactor = 10.5f; // increase this for stronger spin
if (!other->collider.isStatic && vec3_length(horizontalAxis) > 1e-6f && pushRatioObjToOther > 0.0f) {
Vec3 offset = vec3_sub(colliderCenterWorld(obj), colliderCenterWorld(other));
offset.y = 0.0f;
Vec3 impulse = vec3_scale(horizontalAxis, pushStrength * pushRatioObjToOther);
float torque = (offset.x * impulse.z - offset.z * impulse.x) * angularPushFactor;
other->angular_velocity.y += torque / (other->weight > 0 ? other->weight : 1.0f);
}
}
void Object_applyFriction(Object *obj, float dt) {
if (obj->collider.isStatic) return;
// Base friction coefficient (tweak)
float frictionCoeff = 0.5f;
// Friction proportional to weight (mass)
float friction = frictionCoeff * fminf(obj->weight, 10.0f);
// Apply only to horizontal velocity (XZ)
obj->velocity.x -= obj->velocity.x * friction * dt;
obj->velocity.z -= obj->velocity.z * friction * dt;
// Stop very small velocities to prevent jitter
if (fabs(obj->velocity.x) < 0.01f) obj->velocity.x = 0;
if (fabs(obj->velocity.z) < 0.01f) obj->velocity.z = 0;
}
void Object_resolveCollisions(Object *obj, Object **worldObjects, int count) {
for (int j = 0; j < count; j++) {
Object *other = worldObjects[j];
if (other == obj) continue;
// Only snap to static ground objects
if (other->collider.isStatic) {
Vec3 mtv_axis;
float penetration;
if (OBB_overlap_MTV(obj, other, &mtv_axis, &penetration)) {
// Check if collision is mainly vertical (i.e., obj landed on top)
float verticalAlignment = fabsf(vec3_dot(mtv_axis, other->collider.axes[1]));
if (verticalAlignment > 0.7f) { // mostly vertical collision
Collider_positionGround(obj, other);
}
}
}
// Resolve dynamic collisions for everything
Object_resolveDynamicCollision(obj, other);
}
}
void Collider_update(Collider *c) {
// Build rotation matrix from Euler angles
Mat4 rot = mat4_identity();
rot = mat4_mul(rot, mat4_rotate_y(c->rotation.y));
rot = mat4_mul(rot, mat4_rotate_x(c->rotation.x));
rot = mat4_mul(rot, mat4_rotate_z(c->rotation.z));
// Local basis
Vec3 x = {1,0,0};
Vec3 y = {0,1,0};
Vec3 z = {0,0,1};
// Transform into world space
c->axes[0] = mat4_mul_vec3_dir(rot, x);
c->axes[1] = mat4_mul_vec3_dir(rot, y);
c->axes[2] = mat4_mul_vec3_dir(rot, z);
// Normalize (in case scaling sneaks in later)
c->axes[0] = vec3_normalize(c->axes[0]);
c->axes[1] = vec3_normalize(c->axes[1]);
c->axes[2] = vec3_normalize(c->axes[2]);
}
void Object_setRotation(Object *obj, Vec3 rot) {
obj->rotation = rot;
obj->collider.rotation = rot;
Collider_update(&obj->collider);
}
void Object_rotate(Object *obj, Vec3 delta) {
obj->rotation = vec3_add(obj->rotation, delta);
obj->collider.rotation = obj->rotation; // keep collider in sync
Collider_update(&obj->collider); // update axes
}
void Object_update(Object *obj, Object **worldObjects, int count, float dt) {
if (obj->collider.isStatic) return;
Object_applyGravity(obj, dt);
// --- integrate physics yaw only (prevent physics from changing visual tilt) ---
float yaw_delta = obj->angular_velocity.y * dt;
obj->rotation.y += yaw_delta; // only affect yaw from physics
obj->angular_velocity.y *= 0.9f; // damping for yaw
// keep collider orientation in sync with current rotation
obj->collider.rotation = obj->rotation;
Collider_update(&obj->collider);
Vec3 total = vec3_add(obj->movement, obj->velocity);
if (vec3_length(total) > 0.0f) {
obj->is_grounded = false;
obj->position = vec3_add(obj->position, vec3_scale(total, dt));
Object_resolveCollisions(obj, worldObjects, count);
}
Object_applyFriction(obj, dt);
obj->movement = (Vec3){0,0,0};
}