核心热路径(数学/内存/字符串/性能分析)用MASM汇编(SSE/AVX2) 外围系统(窗口/渲染/ECS/音频/UI/脚本)用C语言 含自研编辑器、VisualScript积木编程、中文渲染(glyph_atlas)
203 lines
7.0 KiB
C
203 lines
7.0 KiB
C
#include "physics.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
PhysicsWorld* physics_create(void) {
|
|
PhysicsWorld* w = (PhysicsWorld*)calloc(1, sizeof(PhysicsWorld));
|
|
w->gravity_enabled = 1;
|
|
w->gravity = (Vec3){0, -9.81f, 0};
|
|
return w;
|
|
}
|
|
|
|
void physics_destroy(PhysicsWorld* world) {
|
|
free(world);
|
|
}
|
|
|
|
uint32_t physics_add_aabb(PhysicsWorld* world, uint32_t entity, Vec3 min, Vec3 max) {
|
|
uint32_t idx = world->collider_count++;
|
|
Collider* c = &world->colliders[idx];
|
|
c->entity_id = entity;
|
|
c->type = COLLIDER_AABB;
|
|
c->aabb.min = min;
|
|
c->aabb.max = max;
|
|
c->is_trigger = 0;
|
|
c->is_static = 0;
|
|
return idx;
|
|
}
|
|
|
|
uint32_t physics_add_sphere(PhysicsWorld* world, uint32_t entity, Vec3 center, float radius) {
|
|
uint32_t idx = world->collider_count++;
|
|
Collider* c = &world->colliders[idx];
|
|
c->entity_id = entity;
|
|
c->type = COLLIDER_SPHERE;
|
|
c->sphere.center = center;
|
|
c->sphere.radius = radius;
|
|
return idx;
|
|
}
|
|
|
|
uint32_t physics_add_plane(PhysicsWorld* world, uint32_t entity, Vec3 normal, float distance) {
|
|
uint32_t idx = world->collider_count++;
|
|
Collider* c = &world->colliders[idx];
|
|
c->entity_id = entity;
|
|
c->type = COLLIDER_PLANE;
|
|
vec3_normalize(&normal, &c->plane.normal);
|
|
c->plane.distance = distance;
|
|
c->is_static = 1;
|
|
return idx;
|
|
}
|
|
|
|
void physics_remove_collider(PhysicsWorld* world, uint32_t index) {
|
|
if (index < world->collider_count) {
|
|
world->colliders[index] = world->colliders[--world->collider_count];
|
|
}
|
|
}
|
|
|
|
void physics_update_collider(PhysicsWorld* world, uint32_t index, Vec3 position) {
|
|
Collider* c = &world->colliders[index];
|
|
if (c->type == COLLIDER_SPHERE) {
|
|
c->sphere.center = position;
|
|
}
|
|
}
|
|
|
|
static int aabb_vs_aabb(const Collider* a, const Collider* b, Vec3* normal, float* depth) {
|
|
Vec3 diff;
|
|
Vec3 centerA = {(a->aabb.min.x + a->aabb.max.x) * 0.5f, (a->aabb.min.y + a->aabb.max.y) * 0.5f, (a->aabb.min.z + a->aabb.max.z) * 0.5f};
|
|
Vec3 centerB = {(b->aabb.min.x + b->aabb.max.x) * 0.5f, (b->aabb.min.y + b->aabb.max.y) * 0.5f, (b->aabb.min.z + b->aabb.max.z) * 0.5f};
|
|
vec3_sub(¢erA, ¢erB, &diff);
|
|
Vec3 half_a, half_b, size;
|
|
vec3_sub(&a->aabb.max, &a->aabb.min, &half_a);
|
|
vec3_scale(&half_a, 0.5f, &half_a);
|
|
vec3_sub(&b->aabb.max, &b->aabb.min, &half_b);
|
|
vec3_scale(&half_b, 0.5f, &half_b);
|
|
|
|
float dx = fabsf(diff.x) - (half_a.x + half_b.x);
|
|
float dy = fabsf(diff.y) - (half_a.y + half_b.y);
|
|
float dz = fabsf(diff.z) - (half_a.z + half_b.z);
|
|
|
|
if (dx < 0 && dy < 0 && dz < 0) {
|
|
*depth = -fmaxf(fmaxf(dx, dy), dz);
|
|
if (dx > dy && dx > dz) normal = &(Vec3){copysignf(1, diff.x), 0, 0};
|
|
else if (dy > dz) { normal->x = 0; normal->y = copysignf(1, diff.y); normal->z = 0; }
|
|
else { normal->x = 0; normal->y = 0; normal->z = copysignf(1, diff.z); }
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sphere_vs_sphere(const Collider* a, const Collider* b, Vec3* normal, float* depth) {
|
|
Vec3 diff;
|
|
vec3_sub(&a->sphere.center, &b->sphere.center, &diff);
|
|
float dist = vec3_length(&diff);
|
|
float radii = a->sphere.radius + b->sphere.radius;
|
|
|
|
if (dist < radii) {
|
|
*depth = radii - dist;
|
|
if (dist > 0.0001f) {
|
|
vec3_normalize(&diff, normal);
|
|
} else {
|
|
*normal = (Vec3){0, 1, 0};
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sphere_vs_aabb(const Collider* sphere, const Collider* aabb, Vec3* normal, float* depth) {
|
|
Vec3 closest;
|
|
closest.x = fmaxf(aabb->aabb.min.x, fminf(sphere->sphere.center.x, aabb->aabb.max.x));
|
|
closest.y = fmaxf(aabb->aabb.min.y, fminf(sphere->sphere.center.y, aabb->aabb.max.y));
|
|
closest.z = fmaxf(aabb->aabb.min.z, fminf(sphere->sphere.center.z, aabb->aabb.max.z));
|
|
|
|
Vec3 diff;
|
|
vec3_sub(&sphere->sphere.center, &closest, &diff);
|
|
float dist_sq = vec3_dot(&diff, &diff);
|
|
|
|
if (dist_sq < sphere->sphere.radius * sphere->sphere.radius) {
|
|
float dist = sqrtf(dist_sq);
|
|
*depth = sphere->sphere.radius - dist;
|
|
if (dist > 0.0001f) {
|
|
vec3_normalize(&diff, normal);
|
|
} else {
|
|
*normal = (Vec3){0, 1, 0};
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sphere_vs_plane(const Collider* sphere, const Collider* plane, Vec3* normal, float* depth) {
|
|
float dist = vec3_dot(&sphere->sphere.center, &plane->plane.normal) - plane->plane.distance;
|
|
if (dist < sphere->sphere.radius) {
|
|
*depth = sphere->sphere.radius - dist;
|
|
*normal = plane->plane.normal;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int physics_check_collision(const Collider* a, const Collider* b, Vec3* normal, float* depth) {
|
|
if (a->type == COLLIDER_AABB && b->type == COLLIDER_AABB)
|
|
return aabb_vs_aabb(a, b, normal, depth);
|
|
if (a->type == COLLIDER_SPHERE && b->type == COLLIDER_SPHERE)
|
|
return sphere_vs_sphere(a, b, normal, depth);
|
|
if (a->type == COLLIDER_SPHERE && b->type == COLLIDER_AABB)
|
|
return sphere_vs_aabb(a, b, normal, depth);
|
|
if (a->type == COLLIDER_AABB && b->type == COLLIDER_SPHERE) {
|
|
int hit = sphere_vs_aabb(b, a, normal, depth);
|
|
if (hit) { normal->x = -normal->x; normal->y = -normal->y; normal->z = -normal->z; }
|
|
return hit;
|
|
}
|
|
if (a->type == COLLIDER_SPHERE && b->type == COLLIDER_PLANE)
|
|
return sphere_vs_plane(a, b, normal, depth);
|
|
if (a->type == COLLIDER_PLANE && b->type == COLLIDER_SPHERE) {
|
|
int hit = sphere_vs_plane(b, a, normal, depth);
|
|
if (hit) { normal->x = -normal->x; normal->y = -normal->y; normal->z = -normal->z; }
|
|
return hit;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int physics_raycast(PhysicsWorld* world, Vec3 origin, Vec3 direction, float max_dist,
|
|
uint32_t* hit_entity, Vec3* hit_point, Vec3* hit_normal) {
|
|
vec3_normalize(&direction, &direction);
|
|
float closest_t = max_dist;
|
|
int hit = 0;
|
|
|
|
for (uint32_t i = 0; i < world->collider_count; i++) {
|
|
Collider* c = &world->colliders[i];
|
|
if (c->type == COLLIDER_SPHERE) {
|
|
Vec3 oc;
|
|
vec3_sub(&origin, &c->sphere.center, &oc);
|
|
float a = vec3_dot(&direction, &direction);
|
|
float b = 2.0f * vec3_dot(&oc, &direction);
|
|
float cc = vec3_dot(&oc, &oc) - c->sphere.radius * c->sphere.radius;
|
|
float disc = b * b - 4 * a * cc;
|
|
if (disc >= 0) {
|
|
float t = (-b - sqrtf(disc)) / (2 * a);
|
|
if (t > 0 && t < closest_t) {
|
|
closest_t = t;
|
|
*hit_entity = c->entity_id;
|
|
Vec3 scaled;
|
|
vec3_scale(&direction, t, &scaled);
|
|
vec3_add(&origin, &scaled, hit_point);
|
|
vec3_sub(hit_point, &c->sphere.center, hit_normal);
|
|
vec3_normalize(hit_normal, hit_normal);
|
|
hit = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return hit;
|
|
}
|
|
|
|
void physics_step(PhysicsWorld* world, float dt) {
|
|
(void)world;
|
|
(void)dt;
|
|
}
|
|
|
|
void physics_set_gravity(PhysicsWorld* world, Vec3 gravity) {
|
|
world->gravity = gravity;
|
|
}
|