physics impl part 2
parent
4d1a90d5cb
commit
719b002989
|
@ -45,6 +45,6 @@ add_library(eco2d-foundation STATIC
|
||||||
|
|
||||||
target_compile_definitions(eco2d-foundation PRIVATE CLIENT)
|
target_compile_definitions(eco2d-foundation PRIVATE CLIENT)
|
||||||
include_directories(src ../modules ../../art/gen)
|
include_directories(src ../modules ../../art/gen)
|
||||||
target_link_libraries(eco2d-foundation raylib raylib_nuklear ferox cwpack flecs-bundle vendors-bundle)
|
target_link_libraries(eco2d-foundation raylib raylib_nuklear cwpack flecs-bundle vendors-bundle)
|
||||||
|
|
||||||
link_system_libs(eco2d-foundation)
|
link_system_libs(eco2d-foundation)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
static debug_draw_queue draw_queue = {0};
|
static debug_draw_queue draw_queue = {0};
|
||||||
|
|
||||||
#ifndef _DEBUG
|
#if !defined(_DEBUG) && 1
|
||||||
static bool draw_is_enabled = false;
|
static bool draw_is_enabled = false;
|
||||||
#else
|
#else
|
||||||
static bool draw_is_enabled = true;
|
static bool draw_is_enabled = true;
|
||||||
|
|
|
@ -256,7 +256,9 @@ static debug_item items[] = {
|
||||||
{ .kind = DITEM_RAW, .val = PROF_RENDER, .proc = DrawProfilerDelta },
|
{ .kind = DITEM_RAW, .val = PROF_RENDER, .proc = DrawProfilerDelta },
|
||||||
{ .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta },
|
{ .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta },
|
||||||
{ .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta },
|
{ .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta },
|
||||||
{ .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta },
|
{ .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta },
|
||||||
|
{ .kind = DITEM_RAW, .val = PROF_PHYS_BLOCK_COLS, .proc = DrawProfilerDelta },
|
||||||
|
{ .kind = DITEM_RAW, .val = PROF_PHYS_BODY_COLS, .proc = DrawProfilerDelta },
|
||||||
{ .kind = DITEM_RAW, .val = PROF_RENDER_PUSH_AND_SORT_ENTRIES, .proc = DrawProfilerDelta },
|
{ .kind = DITEM_RAW, .val = PROF_RENDER_PUSH_AND_SORT_ENTRIES, .proc = DrawProfilerDelta },
|
||||||
{ .kind = DITEM_END },
|
{ .kind = DITEM_END },
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,40 +36,23 @@ typedef struct {
|
||||||
} Drawable;
|
} Drawable;
|
||||||
|
|
||||||
typedef Vector2D Position;
|
typedef Vector2D Position;
|
||||||
typedef struct {
|
typedef Vector2D Velocity;
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float m;
|
|
||||||
} Velocity;
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
PHYS_CIRCLE,
|
PHYS_CIRCLE,
|
||||||
PHYS_RECT
|
PHYS_AABB,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
#define INFINITE_MASS -1.0f
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
uint8_t kind;
|
uint8_t kind;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
float r;
|
float r;
|
||||||
} circle;
|
} circle;
|
||||||
|
};
|
||||||
struct {
|
float mass;
|
||||||
float w;
|
|
||||||
float h;
|
|
||||||
} rect;
|
|
||||||
};
|
|
||||||
|
|
||||||
float density;
|
|
||||||
float static_friction;
|
|
||||||
float dynamic_friction;
|
|
||||||
|
|
||||||
// flags
|
|
||||||
uint8_t inf_inertia:4;
|
|
||||||
uint8_t inf_mass:4;
|
|
||||||
|
|
||||||
// internals
|
|
||||||
uintptr_t body_ptr;
|
|
||||||
} PhysicsBody;
|
} PhysicsBody;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -19,7 +19,7 @@ uint64_t entity_spawn(uint16_t class_id) {
|
||||||
|
|
||||||
if (class_id != EKIND_SERVER) {
|
if (class_id != EKIND_SERVER) {
|
||||||
librg_entity_track(world_tracker(), e);
|
librg_entity_track(world_tracker(), e);
|
||||||
ecs_set(world_ecs(), e, Velocity, {.x = 0, .y = 0, .m = 500.0f});
|
ecs_set(world_ecs(), e, Velocity, { 0 });
|
||||||
entity_set_position(e, (float)(rand() % world_dim()), (float)(rand() % world_dim()));
|
entity_set_position(e, (float)(rand() % world_dim()), (float)(rand() % world_dim()));
|
||||||
|
|
||||||
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
|
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
|
||||||
|
|
|
@ -16,21 +16,13 @@ uint64_t player_spawn(char *name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ecs_set_name(world_ecs(), e, name);
|
ecs_set_name(world_ecs(), e, name);
|
||||||
ecs_set(world_ecs(), e, PhysicsBody, {
|
|
||||||
.kind = PHYS_CIRCLE,
|
|
||||||
.circle.r = 1.5f,
|
|
||||||
.density = 0.5f,
|
|
||||||
.static_friction = 0.35f,
|
|
||||||
.dynamic_friction = 0.15f,
|
|
||||||
.inf_inertia = 1,
|
|
||||||
});
|
|
||||||
ecs_set(world_ecs(), e, ClientInfo, {0});
|
ecs_set(world_ecs(), e, ClientInfo, {0});
|
||||||
ecs_set(world_ecs(), e, Input, {0});
|
ecs_set(world_ecs(), e, Input, {0});
|
||||||
ecs_set(world_ecs(), e, Inventory, {0});
|
ecs_set(world_ecs(), e, Inventory, {0});
|
||||||
ecs_set(world_ecs(), e, Health, {.hp = PLAYER_MAX_HP, .max_hp = PLAYER_MAX_HP});
|
ecs_set(world_ecs(), e, Health, {.hp = PLAYER_MAX_HP, .max_hp = PLAYER_MAX_HP});
|
||||||
ecs_set(world_ecs(), e, HealthRegen, {.amt = 15.0f});
|
ecs_set(world_ecs(), e, HealthRegen, {.amt = 15.0f});
|
||||||
Velocity *v = ecs_get_mut(world_ecs(), e, Velocity);
|
ecs_set(world_ecs(), e, Velocity, { 0 });
|
||||||
*v = (Velocity) { 0, 0, 0.0f };
|
ecs_set(world_ecs(), e, PhysicsBody, { .kind = PHYS_AABB, .mass = INFINITE_MASS });
|
||||||
|
|
||||||
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
|
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
|
||||||
|
|
||||||
|
|
|
@ -125,13 +125,7 @@ uint64_t mob_spawn(void) {
|
||||||
hp->max_hp = hp->hp = 100.0f;
|
hp->max_hp = hp->hp = 100.0f;
|
||||||
|
|
||||||
ecs_add(world_ecs(), e, Mob);
|
ecs_add(world_ecs(), e, Mob);
|
||||||
ecs_set(world_ecs(), e, PhysicsBody, {
|
ecs_set(world_ecs(), e, PhysicsBody, { .kind = PHYS_AABB, .mass = 1.0f });
|
||||||
.kind = PHYS_CIRCLE,
|
|
||||||
.circle.r = 1.5f,
|
|
||||||
.density = 0.25f,
|
|
||||||
.static_friction = 0.35f,
|
|
||||||
.dynamic_friction = 0.15f
|
|
||||||
});
|
|
||||||
|
|
||||||
return (uint64_t)e;
|
return (uint64_t)e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ void platform_create_window(const char *title) {
|
||||||
InitWindow(screenWidth, screenHeight, title);
|
InitWindow(screenWidth, screenHeight, title);
|
||||||
uint32_t flags = /*FLAG_WINDOW_UNDECORATED|*/FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT;
|
uint32_t flags = /*FLAG_WINDOW_UNDECORATED|*/FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT;
|
||||||
#if !defined(PLATFORM_WEB)
|
#if !defined(PLATFORM_WEB)
|
||||||
// flags |= FLAG_VSYNC_HINT;
|
flags |= FLAG_VSYNC_HINT;
|
||||||
#endif
|
#endif
|
||||||
SetWindowState(flags);
|
SetWindowState(flags);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@ static profiler profilers[] = {
|
||||||
{ .id = PROF_RENDER, .name = "render" },
|
{ .id = PROF_RENDER, .name = "render" },
|
||||||
{ .id = PROF_UPDATE_SYSTEMS, .name = "update systems" },
|
{ .id = PROF_UPDATE_SYSTEMS, .name = "update systems" },
|
||||||
{ .id = PROF_ENTITY_LERP, .name = "entity lerp" },
|
{ .id = PROF_ENTITY_LERP, .name = "entity lerp" },
|
||||||
{ .id = PROF_INTEGRATE_POS, .name = "entity movement" },
|
{ .id = PROF_INTEGRATE_POS, .name = "entity movement" },
|
||||||
|
{ .id = PROF_PHYS_BLOCK_COLS, .name = "block collisions" },
|
||||||
|
{ .id = PROF_PHYS_BODY_COLS, .name = "body collisions" },
|
||||||
{ .id = PROF_RENDER_PUSH_AND_SORT_ENTRIES, .name = "push&sort entries" },
|
{ .id = PROF_RENDER_PUSH_AND_SORT_ENTRIES, .name = "push&sort entries" },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,9 @@ typedef enum {
|
||||||
PROF_RENDER,
|
PROF_RENDER,
|
||||||
PROF_UPDATE_SYSTEMS,
|
PROF_UPDATE_SYSTEMS,
|
||||||
PROF_ENTITY_LERP,
|
PROF_ENTITY_LERP,
|
||||||
PROF_INTEGRATE_POS,
|
PROF_INTEGRATE_POS,
|
||||||
|
PROF_PHYS_BLOCK_COLS,
|
||||||
|
PROF_PHYS_BODY_COLS,
|
||||||
PROF_RENDER_PUSH_AND_SORT_ENTRIES,
|
PROF_RENDER_PUSH_AND_SORT_ENTRIES,
|
||||||
|
|
||||||
MAX_PROF,
|
MAX_PROF,
|
||||||
|
|
|
@ -7,14 +7,20 @@
|
||||||
#include "dev/debug_draw.h"
|
#include "dev/debug_draw.h"
|
||||||
#include "core/game.h"
|
#include "core/game.h"
|
||||||
#include "core/rules.h"
|
#include "core/rules.h"
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
extern frWorld *phys_world;
|
ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(0)
|
||||||
|
#define CUTE_C2_IMPLEMENTATION
|
||||||
|
#include "tinyc2.h"
|
||||||
|
ZPL_DIAGNOSTIC_POP
|
||||||
|
|
||||||
#define PHY_BLOCK_COLLISION 1
|
#define PHY_BLOCK_COLLISION 1
|
||||||
#define PHY_WALK_DRAG 4.23f
|
#define PHY_WALK_DRAG 4.23f
|
||||||
#define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f)
|
#define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f)
|
||||||
|
|
||||||
|
ecs_query_t *ecs_rigidbodies;
|
||||||
|
|
||||||
|
#define ECO2D_TICK_RATE (1.0f/20.f)
|
||||||
|
|
||||||
#include "modules/system_onfoot.c"
|
#include "modules/system_onfoot.c"
|
||||||
#include "modules/system_demo.c"
|
#include "modules/system_demo.c"
|
||||||
#include "modules/system_vehicle.c"
|
#include "modules/system_vehicle.c"
|
||||||
|
@ -33,6 +39,177 @@ static inline bool physics_check_aabb(float a1x, float a2x, float a1y, float a2y
|
||||||
return (a1x < b2x && a2x > b1x && a1y < b2y && a2y > b1y);
|
return (a1x < b2x && a2x > b1x && a1y < b2y && a2y > b1y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BlockCollisions(ecs_iter_t *it) {
|
||||||
|
profile(PROF_PHYS_BLOCK_COLS) {
|
||||||
|
Position *p = ecs_field(it, Position, 1);
|
||||||
|
Velocity *v = ecs_field(it, Velocity, 2);
|
||||||
|
|
||||||
|
for (int i = 0; i < it->count; i++) {
|
||||||
|
if (ecs_get(it->world, it->entities[i], IsInVehicle)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (zpl_abs(v[i].x) >= 0.0001f || zpl_abs(v[i].y) >= 0.0001f)
|
||||||
|
{
|
||||||
|
// NOTE(zaklaus): world bounds
|
||||||
|
{
|
||||||
|
float w = (float)world_dim();
|
||||||
|
p[i].x = zpl_clamp(p[i].x, 0, w-1);
|
||||||
|
p[i].y = zpl_clamp(p[i].y, 0, w-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PHY_BLOCK_COLLISION==1
|
||||||
|
// NOTE(zaklaus): X axis
|
||||||
|
{
|
||||||
|
world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y);
|
||||||
|
uint32_t flags = blocks_get_flags(lookup.bid);
|
||||||
|
float bounce = blocks_get_bounce(lookup.bid);
|
||||||
|
if (flags & BLOCK_FLAG_COLLISION && physics_check_aabb(p[i].x-WORLD_BLOCK_SIZE/4, p[i].x+WORLD_BLOCK_SIZE/4, p[i].y-0.5f, p[i].y+0.5f, lookup.aox-WORLD_BLOCK_SIZE/2, lookup.aox+WORLD_BLOCK_SIZE/2, lookup.aoy-WORLD_BLOCK_SIZE/2, lookup.aoy+WORLD_BLOCK_SIZE/2)) {
|
||||||
|
#if 1
|
||||||
|
{
|
||||||
|
debug_v2 a = {p[i].x-WORLD_BLOCK_SIZE/4 + PHY_LOOKAHEAD(v[i].x), p[i].y-0.5f};
|
||||||
|
debug_v2 b = {p[i].x+WORLD_BLOCK_SIZE/4 + PHY_LOOKAHEAD(v[i].x), p[i].y+0.5f};
|
||||||
|
debug_push_rect(a, b, 0xFF0000FF);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
v[i].x = physics_correction(lookup.ox, v[i].x, bounce, WORLD_BLOCK_SIZE/2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE(zaklaus): Y axis
|
||||||
|
{
|
||||||
|
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y+PHY_LOOKAHEAD(v[i].y));
|
||||||
|
uint32_t flags = blocks_get_flags(lookup.bid);
|
||||||
|
float bounce = blocks_get_bounce(lookup.bid);
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
debug_v2 a = {lookup.aox-WORLD_BLOCK_SIZE/2, lookup.aoy-WORLD_BLOCK_SIZE/2};
|
||||||
|
debug_v2 b = {lookup.aox+WORLD_BLOCK_SIZE/2, lookup.aoy+WORLD_BLOCK_SIZE/2};
|
||||||
|
debug_push_rect(a, b, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (flags & BLOCK_FLAG_COLLISION && physics_check_aabb(p[i].x-WORLD_BLOCK_SIZE/4, p[i].x+WORLD_BLOCK_SIZE/4, p[i].y-0.5f, p[i].y+0.5f, lookup.aox-WORLD_BLOCK_SIZE/2, lookup.aox+WORLD_BLOCK_SIZE/2, lookup.aoy-WORLD_BLOCK_SIZE/2, lookup.aoy+WORLD_BLOCK_SIZE/2)) {
|
||||||
|
#if 1
|
||||||
|
{
|
||||||
|
debug_v2 a = {p[i].x-WORLD_BLOCK_SIZE/4, p[i].y-0.5f + PHY_LOOKAHEAD(v[i].y)};
|
||||||
|
debug_v2 b = {p[i].x+WORLD_BLOCK_SIZE/4, p[i].y+0.5f + PHY_LOOKAHEAD(v[i].y)};
|
||||||
|
debug_push_rect(a, b, 0xFF0000FF);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
v[i].y = physics_correction(lookup.oy, v[i].y, bounce, WORLD_BLOCK_SIZE/4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BodyCollisions(ecs_iter_t *it) {
|
||||||
|
Position *p = ecs_field(it, Position, 1);
|
||||||
|
Velocity *v = ecs_field(it, Velocity, 2);
|
||||||
|
PhysicsBody *b = ecs_field(it, PhysicsBody, 3);
|
||||||
|
|
||||||
|
profile(PROF_PHYS_BODY_COLS) {
|
||||||
|
for (int i = 0; i < it->count; i++) {
|
||||||
|
if (ecs_get(it->world, it->entities[i], IsInVehicle)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
debug_v2 a = {p[i].x - WORLD_BLOCK_SIZE/2, p[i].y - WORLD_BLOCK_SIZE/2};
|
||||||
|
debug_v2 b = {p[i].x + WORLD_BLOCK_SIZE/2, p[i].y + WORLD_BLOCK_SIZE/2};
|
||||||
|
debug_push_rect(a, b, 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ecs_iter_t it2 = ecs_query_iter(it->world, ecs_rigidbodies);
|
||||||
|
|
||||||
|
while (ecs_query_next(&it2)) {
|
||||||
|
for (int j = 0; j < it2.count; j++) {
|
||||||
|
if (it->entities[i] == it2.entities[j]) continue;
|
||||||
|
|
||||||
|
Position *p2 = ecs_field(&it2, Position, 1);
|
||||||
|
Velocity *v2 = ecs_field(&it2, Velocity, 2);
|
||||||
|
PhysicsBody *b2 = ecs_field(&it2, PhysicsBody, 3);
|
||||||
|
|
||||||
|
float p_x = p[i].x;
|
||||||
|
float p_y = p[i].y;
|
||||||
|
float p2_x = p2[j].x /*+ v2[j].x*/;
|
||||||
|
float p2_y = p2[j].y /*+ v2[j].y*/;
|
||||||
|
|
||||||
|
c2AABB box_a = {
|
||||||
|
.min = { p_x - WORLD_BLOCK_SIZE / 2, p_y - WORLD_BLOCK_SIZE / 2 },
|
||||||
|
.max = { p_x + WORLD_BLOCK_SIZE / 2, p_y + WORLD_BLOCK_SIZE / 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
c2AABB box_b = {
|
||||||
|
.min = { p2_x - WORLD_BLOCK_SIZE / 2, p2_y - WORLD_BLOCK_SIZE / 2 },
|
||||||
|
.max = { p2_x + WORLD_BLOCK_SIZE / 2, p2_y + WORLD_BLOCK_SIZE / 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
// do a basic sweep first
|
||||||
|
{
|
||||||
|
float r1x = (box_a.max.x-box_a.min.x);
|
||||||
|
float r1y = (box_a.max.y-box_a.min.y);
|
||||||
|
float r1 = (r1x*r1x + r1y*r1y)*.5f;
|
||||||
|
|
||||||
|
float r2x = (box_b.max.x-box_b.min.x);
|
||||||
|
float r2y = (box_b.max.y-box_b.min.y);
|
||||||
|
float r2 = (r2x*r2x + r2y*r2y)*.5f;
|
||||||
|
|
||||||
|
float dx = (p2_x-p_x);
|
||||||
|
float dy = (p2_y-p_y);
|
||||||
|
float d = (dx*dx + dy*dy);
|
||||||
|
|
||||||
|
if (d > r1 && d > r2)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
c2Circle circle_a = {
|
||||||
|
.p = { p_x, p_y },
|
||||||
|
.r = b[i].circle.r,
|
||||||
|
};
|
||||||
|
|
||||||
|
c2Circle circle_b = {
|
||||||
|
.p = { p2_x, p2_y },
|
||||||
|
.r = b2[j].circle.r,
|
||||||
|
};
|
||||||
|
|
||||||
|
const void *shapes_a[] = { &circle_a, &box_a };
|
||||||
|
const void *shapes_b[] = { &circle_b, &box_b };
|
||||||
|
|
||||||
|
c2Manifold m = { 0 };
|
||||||
|
c2Collide(shapes_a[b[i].kind], 0, b[i].kind, shapes_b[b2[j].kind], 0, b2[j].kind, &m);
|
||||||
|
|
||||||
|
c2v n = m.n;
|
||||||
|
|
||||||
|
for (int k = 0; k < m.count; k++) {
|
||||||
|
float d = m.depths[k];
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
c2v pos = m.contact_points[k];
|
||||||
|
debug_v2 a = { pos.x, pos.y };
|
||||||
|
debug_v2 b = { pos.x + n.x*d, pos.y + n.y*d };
|
||||||
|
debug_push_line(a, b, 0xF77FFFFF);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float m1 = b2[j].mass == INFINITE_MASS ? b[i].mass : b2[j].mass;
|
||||||
|
float m2 = b[i].mass == INFINITE_MASS ? (ZPL_F32_MAX-1.0f) : b[i].mass;
|
||||||
|
float mass_ratio = m1 / m2;
|
||||||
|
|
||||||
|
v[i].x -= n.x*d*mass_ratio;
|
||||||
|
v[i].y -= n.y*d*mass_ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IntegratePositions(ecs_iter_t *it) {
|
void IntegratePositions(ecs_iter_t *it) {
|
||||||
profile(PROF_INTEGRATE_POS) {
|
profile(PROF_INTEGRATE_POS) {
|
||||||
Position *p = ecs_field(it, Position, 1);
|
Position *p = ecs_field(it, Position, 1);
|
||||||
|
@ -43,60 +220,7 @@ void IntegratePositions(ecs_iter_t *it) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (zpl_abs(v[i].x) >= 0.0001f || zpl_abs(v[i].y) >= 0.0001f)
|
entity_set_position(it->entities[i], p[i].x+v[i].x*safe_dt(it), p[i].y+v[i].y*safe_dt(it));
|
||||||
{
|
|
||||||
// NOTE(zaklaus): world bounds
|
|
||||||
{
|
|
||||||
float w = (float)world_dim();
|
|
||||||
p[i].x = zpl_clamp(p[i].x, 0, w-1);
|
|
||||||
p[i].y = zpl_clamp(p[i].y, 0, w-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if PHY_BLOCK_COLLISION==1
|
|
||||||
// NOTE(zaklaus): X axis
|
|
||||||
{
|
|
||||||
world_block_lookup lookup = world_block_from_realpos(p[i].x+PHY_LOOKAHEAD(v[i].x), p[i].y);
|
|
||||||
uint32_t flags = blocks_get_flags(lookup.bid);
|
|
||||||
float bounce = blocks_get_bounce(lookup.bid);
|
|
||||||
if (flags & BLOCK_FLAG_COLLISION && physics_check_aabb(p[i].x-WORLD_BLOCK_SIZE/4, p[i].x+WORLD_BLOCK_SIZE/4, p[i].y-0.5f, p[i].y+0.5f, lookup.aox-WORLD_BLOCK_SIZE/2, lookup.aox+WORLD_BLOCK_SIZE/2, lookup.aoy-WORLD_BLOCK_SIZE/2, lookup.aoy+WORLD_BLOCK_SIZE/2)) {
|
|
||||||
#if 1
|
|
||||||
{
|
|
||||||
debug_v2 a = {p[i].x-WORLD_BLOCK_SIZE/4 + PHY_LOOKAHEAD(v[i].x), p[i].y-0.5f};
|
|
||||||
debug_v2 b = {p[i].x+WORLD_BLOCK_SIZE/4 + PHY_LOOKAHEAD(v[i].x), p[i].y+0.5f};
|
|
||||||
debug_push_rect(a, b, 0xFF0000FF);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
v[i].x = physics_correction(lookup.ox, v[i].x, bounce, WORLD_BLOCK_SIZE/2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(zaklaus): Y axis
|
|
||||||
{
|
|
||||||
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y+PHY_LOOKAHEAD(v[i].y));
|
|
||||||
uint32_t flags = blocks_get_flags(lookup.bid);
|
|
||||||
float bounce = blocks_get_bounce(lookup.bid);
|
|
||||||
#if 0
|
|
||||||
{
|
|
||||||
debug_v2 a = {lookup.aox-WORLD_BLOCK_SIZE/2, lookup.aoy-WORLD_BLOCK_SIZE/2};
|
|
||||||
debug_v2 b = {lookup.aox+WORLD_BLOCK_SIZE/2, lookup.aoy+WORLD_BLOCK_SIZE/2};
|
|
||||||
debug_push_rect(a, b, 0xFFFFFFFF);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (flags & BLOCK_FLAG_COLLISION && physics_check_aabb(p[i].x-WORLD_BLOCK_SIZE/4, p[i].x+WORLD_BLOCK_SIZE/4, p[i].y-0.5f, p[i].y+0.5f, lookup.aox-WORLD_BLOCK_SIZE/2, lookup.aox+WORLD_BLOCK_SIZE/2, lookup.aoy-WORLD_BLOCK_SIZE/2, lookup.aoy+WORLD_BLOCK_SIZE/2)) {
|
|
||||||
#if 1
|
|
||||||
{
|
|
||||||
debug_v2 a = {p[i].x-WORLD_BLOCK_SIZE/4, p[i].y-0.5f + PHY_LOOKAHEAD(v[i].y)};
|
|
||||||
debug_v2 b = {p[i].x+WORLD_BLOCK_SIZE/4, p[i].y+0.5f + PHY_LOOKAHEAD(v[i].y)};
|
|
||||||
debug_push_rect(a, b, 0xFF0000FF);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
v[i].y = physics_correction(lookup.oy, v[i].y, bounce, WORLD_BLOCK_SIZE/4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
entity_set_position(it->entities[i], p[i].x+v[i].x*safe_dt(it), p[i].y+v[i].y*safe_dt(it));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
debug_v2 a = {p[i].x, p[i].y};
|
debug_v2 a = {p[i].x, p[i].y};
|
||||||
|
@ -232,86 +356,6 @@ void PlayerClosestInteractable(ecs_iter_t *it){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysOnCreateBody(ecs_iter_t *it) {
|
|
||||||
PhysicsBody *pb = ecs_field(it, PhysicsBody, 1);
|
|
||||||
Position *p = ecs_field(it, Position, 2);
|
|
||||||
|
|
||||||
for (int i = 0; i < it->count; i++) {
|
|
||||||
const frMaterial mat = {
|
|
||||||
.density = pb[i].density,
|
|
||||||
.staticFriction = pb[i].static_friction,
|
|
||||||
.dynamicFriction = pb[i].dynamic_friction,
|
|
||||||
};
|
|
||||||
|
|
||||||
frShape *shape = 0;
|
|
||||||
if (pb[i].kind == PHYS_CIRCLE) {
|
|
||||||
shape = frCreateCircle(mat, pb[i].circle.r);
|
|
||||||
} else {
|
|
||||||
shape = frCreateRectangle(mat, pb[i].rect.w, pb[i].rect.h);
|
|
||||||
}
|
|
||||||
|
|
||||||
frBodyFlags flags = 0x0;
|
|
||||||
if (pb[i].inf_inertia) flags |= FR_FLAG_INFINITE_INERTIA;
|
|
||||||
if (pb[i].inf_mass) flags |= FR_FLAG_INFINITE_MASS;
|
|
||||||
frBody *body = frCreateBodyFromShape(FR_BODY_DYNAMIC, flags, frVec2PixelsToMeters((Vector2){p[i].x, p[i].y}), shape);
|
|
||||||
frAddToWorld(phys_world, body);
|
|
||||||
pb[i].body_ptr = (uintptr_t)body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysOnRemoveBody(ecs_iter_t *it) {
|
|
||||||
PhysicsBody *pb = ecs_field(it, PhysicsBody, 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < it->count; i++) {
|
|
||||||
frBody *body = (frBody*)pb[i].body_ptr;
|
|
||||||
frRemoveFromWorld(phys_world, body);
|
|
||||||
frShape *shape = frGetBodyShape(body);
|
|
||||||
frReleaseBody(body);
|
|
||||||
frReleaseShape(shape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysSetVelocity(ecs_iter_t *it) {
|
|
||||||
PhysicsBody *pb = ecs_field(it, PhysicsBody, 1);
|
|
||||||
Velocity *v = ecs_field(it, Velocity, 2);
|
|
||||||
|
|
||||||
for (int i = 0; i < it->count; i++) {
|
|
||||||
frBody *body = (frBody*)pb[i].body_ptr;
|
|
||||||
frSetBodyVelocity(body, (Vector2) { v[i].x, v[i].y });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysUpdatePosition(ecs_iter_t *it) {
|
|
||||||
PhysicsBody *pb = ecs_field(it, PhysicsBody, 1);
|
|
||||||
Position *p = ecs_field(it, Position, 2);
|
|
||||||
Velocity *v = ecs_field(it, Velocity, 3);
|
|
||||||
|
|
||||||
for (int i = 0; i < it->count; i++) {
|
|
||||||
frBody *body = (frBody*)pb[i].body_ptr;
|
|
||||||
Vector2 pos = frVec2MetersToPixels(frGetBodyPosition(body));
|
|
||||||
p[i].x = pos.x;
|
|
||||||
p[i].y = pos.y;
|
|
||||||
Vector2 vel = frVec2MetersToPixels(frGetBodyVelocity(body));
|
|
||||||
v[i].x = vel.x;
|
|
||||||
v[i].y = vel.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysResetPosition(ecs_iter_t *it) {
|
|
||||||
Position *p = ecs_field(it, Position, 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < it->count; i++) {
|
|
||||||
const PhysicsBody *pb = ecs_get(it->world, it->entities[i], PhysicsBody);
|
|
||||||
if (!pb) continue;
|
|
||||||
frBody *body = (frBody*)pb->body_ptr;
|
|
||||||
frSetBodyPosition(body, (Vector2){p[i].x, p[i].y});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PhysSimulateWorld(ecs_iter_t *it) {
|
|
||||||
frSimulateWorld(phys_world, it->delta_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EnableWorldEdit(ecs_iter_t *it) {
|
void EnableWorldEdit(ecs_iter_t *it) {
|
||||||
world_set_stage(it->world);
|
world_set_stage(it->world);
|
||||||
}
|
}
|
||||||
|
@ -321,8 +365,6 @@ void DisableWorldEdit(ecs_iter_t *it) {
|
||||||
world_set_stage(NULL);
|
world_set_stage(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ECO2D_TICK_RATE (1.0f/20.f)
|
|
||||||
|
|
||||||
#define ECS_SYSTEM_TICKED(world, id, stage, ...)\
|
#define ECS_SYSTEM_TICKED(world, id, stage, ...)\
|
||||||
ECS_SYSTEM(world, id, stage, __VA_ARGS__);\
|
ECS_SYSTEM(world, id, stage, __VA_ARGS__);\
|
||||||
ecs_set_tick_source(world, id, timer);
|
ecs_set_tick_source(world, id, timer);
|
||||||
|
@ -337,6 +379,8 @@ void SystemsImport(ecs_world_t *ecs) {
|
||||||
|
|
||||||
ecs_entity_t timer = ecs_set_interval(ecs, 0, ECO2D_TICK_RATE);
|
ecs_entity_t timer = ecs_set_interval(ecs, 0, ECO2D_TICK_RATE);
|
||||||
|
|
||||||
|
ecs_rigidbodies = ecs_query_new(ecs, "components.Position, components.Velocity, components.PhysicsBody");
|
||||||
|
|
||||||
ECS_SYSTEM(ecs, EnableWorldEdit, EcsOnLoad);
|
ECS_SYSTEM(ecs, EnableWorldEdit, EcsOnLoad);
|
||||||
ECS_SYSTEM(ecs, MovementImpulse, EcsOnLoad, components.Input, components.Velocity, components.Position, !components.IsInVehicle);
|
ECS_SYSTEM(ecs, MovementImpulse, EcsOnLoad, components.Input, components.Velocity, components.Position, !components.IsInVehicle);
|
||||||
ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.DemoNPC);
|
ECS_SYSTEM(ecs, DemoNPCMoveAround, EcsOnLoad, components.Velocity, components.DemoNPC);
|
||||||
|
@ -349,10 +393,9 @@ void SystemsImport(ecs_world_t *ecs) {
|
||||||
|
|
||||||
ECS_OBSERVER(ecs, OnHealthChangePutDelay, EcsOnAdd, components.HealthDecreased);
|
ECS_OBSERVER(ecs, OnHealthChangePutDelay, EcsOnAdd, components.HealthDecreased);
|
||||||
|
|
||||||
ECS_SYSTEM(ecs, PhysSetVelocity, EcsOnValidate, components.PhysicsBody, components.Velocity);
|
ECS_SYSTEM(ecs, BodyCollisions, EcsOnUpdate, components.Position, components.Velocity, components.PhysicsBody);
|
||||||
ECS_SYSTEM(ecs, PhysSimulateWorld, EcsOnValidate);
|
ECS_SYSTEM(ecs, BlockCollisions, EcsOnValidate, components.Position, components.Velocity);
|
||||||
ECS_SYSTEM(ecs, PhysUpdatePosition, EcsOnValidate, components.PhysicsBody, components.Position, components.Velocity);
|
ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, components.Position, components.Velocity);
|
||||||
ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, components.Position, components.Velocity, !components.PhysicsBody);
|
|
||||||
|
|
||||||
ECS_SYSTEM(ecs, EnterVehicle, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle);
|
ECS_SYSTEM(ecs, EnterVehicle, EcsPostUpdate, components.Input, components.Position, !components.IsInVehicle);
|
||||||
ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity);
|
ECS_SYSTEM(ecs, LeaveVehicle, EcsPostUpdate, components.Input, components.IsInVehicle, components.Velocity);
|
||||||
|
@ -384,12 +427,7 @@ void SystemsImport(ecs_world_t *ecs) {
|
||||||
|
|
||||||
ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle);
|
ECS_SYSTEM(ecs, ClearVehicle, EcsUnSet, components.Vehicle);
|
||||||
ECS_SYSTEM(ecs, ThrowItemsOut, EcsUnSet, components.ItemContainer, components.Position);
|
ECS_SYSTEM(ecs, ThrowItemsOut, EcsUnSet, components.ItemContainer, components.Position);
|
||||||
|
|
||||||
// Physics hooks
|
|
||||||
ECS_OBSERVER(ecs, PhysOnCreateBody, EcsOnSet, components.PhysicsBody, components.Position);
|
|
||||||
ECS_OBSERVER(ecs, PhysOnRemoveBody, EcsUnSet, components.PhysicsBody);
|
|
||||||
ECS_OBSERVER(ecs, PhysResetPosition, EcsOnSet, components.Position);
|
|
||||||
|
|
||||||
ECS_SYSTEM(ecs, DisableWorldEdit, EcsPostUpdate);
|
ECS_SYSTEM(ecs, DisableWorldEdit, EcsPostUpdate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "core/game.h"
|
#include "core/game.h"
|
||||||
#include "models/entity.h"
|
#include "models/entity.h"
|
||||||
#include "models/crafting.h"
|
#include "models/crafting.h"
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
#include "packets/pkt_send_librg_update.h"
|
#include "packets/pkt_send_librg_update.h"
|
||||||
|
|
||||||
|
@ -25,7 +24,6 @@ ZPL_TABLE(static, world_component_cache, world_component_cache_, zpl_uintptr); /
|
||||||
static world_data world = {0};
|
static world_data world = {0};
|
||||||
static world_snapshot streamer_snapshot;
|
static world_snapshot streamer_snapshot;
|
||||||
static world_component_cache component_cache;
|
static world_component_cache component_cache;
|
||||||
frWorld *phys_world = 0;
|
|
||||||
|
|
||||||
entity_view *world_build_entity_view(int64_t e) {
|
entity_view *world_build_entity_view(int64_t e) {
|
||||||
entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e);
|
entity_view *cached_ev = world_snapshot_get(&streamer_snapshot, e);
|
||||||
|
@ -296,18 +294,6 @@ void world_free_worldgen_data(void) {
|
||||||
world.outer_data = NULL;
|
world.outer_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline
|
|
||||||
void world_init_physics(void) {
|
|
||||||
const Rectangle bounds = {
|
|
||||||
.x = -0.05f * frNumberPixelsToMeters(world_dim()),
|
|
||||||
.y = -0.05f * frNumberPixelsToMeters(world_dim()),
|
|
||||||
.width = 1.1f * frNumberPixelsToMeters(world_dim()),
|
|
||||||
.height = 1.1f * frNumberPixelsToMeters(world_dim())
|
|
||||||
};
|
|
||||||
|
|
||||||
phys_world = frCreateWorld((Vector2) { 0.0f, 0.0f }, bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
|
int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
|
||||||
world.is_paused = false;
|
world.is_paused = false;
|
||||||
world.seed = seed;
|
world.seed = seed;
|
||||||
|
@ -324,7 +310,6 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
|
||||||
world_init_mapping();
|
world_init_mapping();
|
||||||
world_chunk_setup_grid();
|
world_chunk_setup_grid();
|
||||||
world_free_worldgen_data();
|
world_free_worldgen_data();
|
||||||
world_init_physics();
|
|
||||||
|
|
||||||
zpl_printf("[INFO] Created a new server world\n");
|
zpl_printf("[INFO] Created a new server world\n");
|
||||||
|
|
||||||
|
@ -345,9 +330,7 @@ int32_t world_destroy(void) {
|
||||||
world_component_cache_destroy(&component_cache);
|
world_component_cache_destroy(&component_cache);
|
||||||
zpl_memset(&world, 0, sizeof(world));
|
zpl_memset(&world, 0, sizeof(world));
|
||||||
|
|
||||||
frReleaseWorld(phys_world);
|
zpl_printf("[INFO] World was destroyed.\n");
|
||||||
|
|
||||||
zpl_printf("[INFO] World was destroyed.\n");
|
|
||||||
return WORLD_ERROR_NONE;
|
return WORLD_ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
static Camera2D render_camera;
|
static Camera2D render_camera;
|
||||||
static float zoom_overlay_tran = 0.0f;
|
static float zoom_overlay_tran = 0.0f;
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
#define CAM_OVERLAY_ZOOM_LEVEL 0.15f
|
#define CAM_OVERLAY_ZOOM_LEVEL 0.15f
|
||||||
#define ALPHA(x) ColorAlpha(x, data->tran_time)
|
#define ALPHA(x) ColorAlpha(x, data->tran_time)
|
||||||
|
@ -132,16 +131,7 @@ void renderer_draw(void) {
|
||||||
BeginMode2D(render_camera);
|
BeginMode2D(render_camera);
|
||||||
|
|
||||||
game_world_view_render_world();
|
game_world_view_render_world();
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (game_get_kind() == GAMEKIND_SINGLE) {
|
|
||||||
extern frWorld *phys_world;
|
|
||||||
frDrawSpatialHash(frGetWorldSpatialHash(phys_world), 0.75f, GRAY);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
EndMode2D();
|
EndMode2D();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
add_subdirectory(flecs)
|
add_subdirectory(flecs)
|
||||||
add_subdirectory(cwpack)
|
add_subdirectory(cwpack)
|
||||||
add_subdirectory(raylib-nuklear)
|
add_subdirectory(raylib-nuklear)
|
||||||
add_subdirectory(ferox)
|
|
||||||
|
|
||||||
add_library(vendors-bundle STATIC
|
add_library(vendors-bundle STATIC
|
||||||
sfd.c
|
sfd.c
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
add_library(ferox STATIC
|
|
||||||
src/broadphase.c
|
|
||||||
src/collision.c
|
|
||||||
src/debug.c
|
|
||||||
src/dynamics.c
|
|
||||||
src/geometry.c
|
|
||||||
src/timer.c
|
|
||||||
src/utils.c
|
|
||||||
src/world.c
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(ferox PUBLIC include)
|
|
||||||
target_include_directories(ferox PRIVATE src/external)
|
|
||||||
|
|
||||||
target_link_libraries(ferox PRIVATE raylib)
|
|
|
@ -1,746 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FEROX_H
|
|
||||||
#define FEROX_H
|
|
||||||
|
|
||||||
#include <float.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifndef FEROX_STANDALONE
|
|
||||||
#include "raylib.h"
|
|
||||||
#else
|
|
||||||
#ifndef PI
|
|
||||||
#define PI 3.14159265358979323846f
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define DEG2RAD (PI / 180.0f)
|
|
||||||
#define RAD2DEG (180.0f / PI)
|
|
||||||
|
|
||||||
/* A struct that represents a two-dimensional vector. */
|
|
||||||
typedef struct Vector2 {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
} Vector2;
|
|
||||||
|
|
||||||
/* A struct that represents a rectangle. */
|
|
||||||
typedef struct Rectangle {
|
|
||||||
float x;
|
|
||||||
float y;
|
|
||||||
float width;
|
|
||||||
float height;
|
|
||||||
} Rectangle;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
/* Returns `true` if `rec1` collides with `rec2`. */
|
|
||||||
bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2) {
|
|
||||||
return ((rec1.x + rec1.width) - rec2.x) >= 0 && ((rec2.x + rec2.width) - rec1.x) >= 0
|
|
||||||
&& ((rec1.y + rec1.height) - rec2.y) >= 0 && ((rec2.y + rec2.height) - rec1.y) >= 0;
|
|
||||||
}
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* | Macro Definitions (Global) | */
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#define FR_API_INLINE __forceinline
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#if defined(__STRICT_ANSI__)
|
|
||||||
#define FR_API_INLINE __inline__ __attribute__((always_inline))
|
|
||||||
#else
|
|
||||||
#define FR_API_INLINE inline __attribute__((always_inline))
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define FR_API_INLINE inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FR_STRUCT_ZERO(T) ((T) { 0 })
|
|
||||||
|
|
||||||
/* | Macro Definitions (Configuration) | */
|
|
||||||
|
|
||||||
#define FR_BROADPHASE_CELL_SIZE 3.2f
|
|
||||||
#define FR_BROADPHASE_INVERSE_CELL_SIZE (1.0f / (FR_BROADPHASE_CELL_SIZE))
|
|
||||||
|
|
||||||
#define FR_DEBUG_CIRCLE_SEGMENT_COUNT 32
|
|
||||||
|
|
||||||
#define FR_DYNAMICS_CORRECTION_DEPTH_SCALE 0.24f
|
|
||||||
#define FR_DYNAMICS_CORRECTION_DEPTH_THRESHOLD 0.02f
|
|
||||||
#define FR_DYNAMICS_DYNAMIC_FRICTION_MULTIPLIER 0.85f
|
|
||||||
|
|
||||||
#define FR_GEOMETRY_MAX_VERTEX_COUNT 8
|
|
||||||
|
|
||||||
#define FR_GLOBAL_PIXELS_PER_METER 16.0f
|
|
||||||
|
|
||||||
#define FR_WORLD_ACCUMULATOR_LIMIT 200.0
|
|
||||||
#define FR_WORLD_DEFAULT_GRAVITY ((Vector2) { .y = 9.8f })
|
|
||||||
#define FR_WORLD_MAX_BODY_COUNT 288
|
|
||||||
#define FR_WORLD_MAX_ITERATIONS 12
|
|
||||||
|
|
||||||
/* | Data Type Definitions | */
|
|
||||||
|
|
||||||
/* A struct that represents the type of a rigid body. */
|
|
||||||
typedef enum frBodyType {
|
|
||||||
FR_BODY_UNKNOWN = -1,
|
|
||||||
FR_BODY_STATIC,
|
|
||||||
FR_BODY_KINEMATIC,
|
|
||||||
FR_BODY_DYNAMIC
|
|
||||||
} frBodyType;
|
|
||||||
|
|
||||||
/* An enum that represents the property flag of a rigid body. */
|
|
||||||
typedef enum frBodyFlag {
|
|
||||||
FR_FLAG_NONE = 0x00,
|
|
||||||
FR_FLAG_INFINITE_MASS = 0x01,
|
|
||||||
FR_FLAG_INFINITE_INERTIA = 0x02
|
|
||||||
} frBodyFlag;
|
|
||||||
|
|
||||||
/* A data type that represents the property flags of a rigid body. */
|
|
||||||
typedef uint8_t frBodyFlags;
|
|
||||||
|
|
||||||
/* A struct that represents the type of a collision shape. */
|
|
||||||
typedef enum frShapeType {
|
|
||||||
FR_SHAPE_UNKNOWN,
|
|
||||||
FR_SHAPE_CIRCLE,
|
|
||||||
FR_SHAPE_POLYGON
|
|
||||||
} frShapeType;
|
|
||||||
|
|
||||||
/* A struct that represents the material of a collision shape. */
|
|
||||||
typedef struct frMaterial {
|
|
||||||
float density;
|
|
||||||
float restitution;
|
|
||||||
float staticFriction;
|
|
||||||
float dynamicFriction;
|
|
||||||
} frMaterial;
|
|
||||||
|
|
||||||
/*
|
|
||||||
A struct that represents the position (in meters)
|
|
||||||
and rotation (in radians) of an object.
|
|
||||||
*/
|
|
||||||
typedef struct frTransform {
|
|
||||||
Vector2 position;
|
|
||||||
float rotation;
|
|
||||||
struct {
|
|
||||||
bool valid;
|
|
||||||
float sinA;
|
|
||||||
float cosA;
|
|
||||||
} cache;
|
|
||||||
} frTransform;
|
|
||||||
|
|
||||||
/* A struct that represents the vertices of a polygon. */
|
|
||||||
typedef struct frVertices {
|
|
||||||
Vector2 data[FR_GEOMETRY_MAX_VERTEX_COUNT];
|
|
||||||
int count;
|
|
||||||
} frVertices;
|
|
||||||
|
|
||||||
/* A struct that represents a collision shape. */
|
|
||||||
typedef struct frShape frShape;
|
|
||||||
|
|
||||||
/* A struct that represents a rigid body. */
|
|
||||||
typedef struct frBody frBody;
|
|
||||||
|
|
||||||
/* A struct that represents information for a set of two rigid bodies. */
|
|
||||||
typedef struct frSolverCache {
|
|
||||||
frBody *bodies[2];
|
|
||||||
/* TODO: ... */
|
|
||||||
} frSolverCache;
|
|
||||||
|
|
||||||
/* A struct that represents the details of a collision. */
|
|
||||||
typedef struct frCollision {
|
|
||||||
bool check; // Returns `true` if two collision shapes collide with each other.
|
|
||||||
frSolverCache cache; // The struct that contains rigid bodies that collided with each other.
|
|
||||||
Vector2 direction; // The direction of the collision in a unit vector form.
|
|
||||||
Vector2 points[2]; // The points of the collision between two collision shapes.
|
|
||||||
float depths[2]; // The penetration depths of the collision.
|
|
||||||
int count; // The number of points for this collision.
|
|
||||||
} frCollision;
|
|
||||||
|
|
||||||
/* A callback which will be executed when a collision event occurs. */
|
|
||||||
typedef void (*frCollisionCallback)(frCollision *collision);
|
|
||||||
|
|
||||||
/* A struct that represents the collision event handler of a world. */
|
|
||||||
typedef struct frCollisionHandler {
|
|
||||||
frCollisionCallback preSolve;
|
|
||||||
frCollisionCallback postSolve;
|
|
||||||
} frCollisionHandler;
|
|
||||||
|
|
||||||
/* A struct that represents a ray. */
|
|
||||||
typedef struct frRay {
|
|
||||||
Vector2 origin;
|
|
||||||
Vector2 direction;
|
|
||||||
float maxDistance;
|
|
||||||
bool closest;
|
|
||||||
} frRay;
|
|
||||||
|
|
||||||
/* A struct that represents the details of a raycast hit. */
|
|
||||||
typedef struct frRaycastHit {
|
|
||||||
bool check; // Returns `true` if the ray collides with `shape` or `body`.
|
|
||||||
union {
|
|
||||||
frShape *shape;
|
|
||||||
frBody *body;
|
|
||||||
}; // The collision shape or the body that was hit by the raycast.
|
|
||||||
Vector2 point; // The point at which the raycast hit `shape` or `body`.
|
|
||||||
Vector2 normal; // The normal vector of the raycast hit.
|
|
||||||
float distance; // The distance from the ray's starting point to `shape` or `body`.
|
|
||||||
bool inside; // Returns `true` if the ray's starting point is inside `shape` or `body`.
|
|
||||||
} frRaycastHit;
|
|
||||||
|
|
||||||
/* A struct that represents a spatial hash. */
|
|
||||||
typedef struct frSpatialHash frSpatialHash;
|
|
||||||
|
|
||||||
/* A struct that represents the world that holds rigid bodies. */
|
|
||||||
typedef struct frWorld frWorld;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* | Module Functions (`broadphase`) | */
|
|
||||||
|
|
||||||
/* Creates a new spatial hash from the given bounds and the size of each cell. */
|
|
||||||
frSpatialHash *frCreateSpatialHash(Rectangle bounds, float cellSize);
|
|
||||||
|
|
||||||
/* Releases the memory allocated for `hash`. */
|
|
||||||
void frReleaseSpatialHash(frSpatialHash *hash);
|
|
||||||
|
|
||||||
/* Generates a new key from `rec` and inserts the key-`value` pair to `hash`. */
|
|
||||||
void frAddToSpatialHash(frSpatialHash *hash, Rectangle rec, int value);
|
|
||||||
|
|
||||||
/* Removes all key-value pairs from `hash`. */
|
|
||||||
void frClearSpatialHash(frSpatialHash *hash);
|
|
||||||
|
|
||||||
/* Removes a `key`-value pair from `hash`. */
|
|
||||||
void frRemoveFromSpatialHash(frSpatialHash *hash, int key);
|
|
||||||
|
|
||||||
/* Returns the values of all pairs that collides with `rec` in `hash`. */
|
|
||||||
void frQuerySpatialHash(frSpatialHash *hash, Rectangle rec, int **result);
|
|
||||||
|
|
||||||
/* Returns the bounds of `hash`. */
|
|
||||||
Rectangle frGetSpatialHashBounds(frSpatialHash *hash);
|
|
||||||
|
|
||||||
/* Returns the size of each cell of `hash`. */
|
|
||||||
float frGetSpatialHashCellSize(frSpatialHash *hash);
|
|
||||||
|
|
||||||
/* Sets the bounds of `hash` to `bounds`. */
|
|
||||||
void frSetSpatialHashBounds(frSpatialHash *hash, Rectangle bounds);
|
|
||||||
|
|
||||||
/* Sets the size of each cell of `hash` to `cellSize`. */
|
|
||||||
void frSetSpatialHashCellSize(frSpatialHash *hash, float cellSize);
|
|
||||||
|
|
||||||
/* | Module Functions (`collision`) | */
|
|
||||||
|
|
||||||
/*
|
|
||||||
Computes the collision between `s1` with position and rotation from `tx1` and `s2`
|
|
||||||
with position and rotation from `tx2`.
|
|
||||||
*/
|
|
||||||
frCollision frComputeShapeCollision(frShape *s1, frTransform tx1, frShape *s2, frTransform tx2);
|
|
||||||
|
|
||||||
/* Computes the collision between `b1` and `b2`. */
|
|
||||||
frCollision frComputeBodyCollision(frBody *b1, frBody *b2);
|
|
||||||
|
|
||||||
/* Casts a `ray` to the collision shape `s`. */
|
|
||||||
frRaycastHit frComputeShapeRaycast(frShape *s, frTransform tx, frRay ray);
|
|
||||||
|
|
||||||
/* Casts a `ray` to the rigid body `b`. */
|
|
||||||
frRaycastHit frComputeBodyRaycast(frBody *b, frRay ray);
|
|
||||||
|
|
||||||
/* | Module Functions (`debug`) | */
|
|
||||||
|
|
||||||
#ifndef FEROX_STANDALONE
|
|
||||||
/* Draws an arrow that points from `p1` to `p2`. */
|
|
||||||
void frDrawArrow(Vector2 p1, Vector2 p2, float thick, Color color);
|
|
||||||
|
|
||||||
/* Draws the collision shape of `b`. */
|
|
||||||
void frDrawBody(frBody *b, Color color);
|
|
||||||
|
|
||||||
/* Draws the border of the collision shape of `b`. */
|
|
||||||
void frDrawBodyLines(frBody *b, float thick, Color color);
|
|
||||||
|
|
||||||
/* Draws the AABB and the center of mass of `b`. */
|
|
||||||
void frDrawBodyAABB(frBody *b, float thick, Color color);
|
|
||||||
|
|
||||||
/* Draws the properties of `b`. */
|
|
||||||
void frDrawBodyProperties(frBody *b, Color color);
|
|
||||||
|
|
||||||
/* Draws the border of `hm`. */
|
|
||||||
void frDrawSpatialHash(frSpatialHash *hm, float thick, Color color);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* | Module Functions (`dynamics`) | */
|
|
||||||
|
|
||||||
/* Creates a new rigid body from the given type, flags, and position (in meters). */
|
|
||||||
frBody *frCreateBody(frBodyType type, frBodyFlags flags, Vector2 p);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates a new rigid body from the given type, flags, position (in meters),
|
|
||||||
and a collision shape.
|
|
||||||
*/
|
|
||||||
frBody *frCreateBodyFromShape(frBodyType type, frBodyFlags flags, Vector2 p, frShape *s);
|
|
||||||
|
|
||||||
/* Releases the memory allocated for `b`. */
|
|
||||||
void frReleaseBody(frBody *b);
|
|
||||||
|
|
||||||
/* Attaches the collision shape `s` to `b`. */
|
|
||||||
void frAttachShapeToBody(frBody *b, frShape *s);
|
|
||||||
|
|
||||||
/* Detaches the collision shape `s` from `b`. */
|
|
||||||
void frDetachShapeFromBody(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the size of the struct `frBody`. */
|
|
||||||
size_t frGetBodyStructSize(void);
|
|
||||||
|
|
||||||
/* Returns the type of `b`. */
|
|
||||||
frBodyType frGetBodyType(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the property flags of `b`. */
|
|
||||||
frBodyFlags frGetBodyFlags(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the material of `b`. */
|
|
||||||
frMaterial frGetBodyMaterial(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the mass of `b`. */
|
|
||||||
float frGetBodyMass(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the inverse mass of `b`. */
|
|
||||||
float frGetBodyInverseMass(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the moment of inertia for `b`. */
|
|
||||||
float frGetBodyInertia(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the inverse moment of inertia for `b`. */
|
|
||||||
float frGetBodyInverseInertia(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the velocity of `b`. */
|
|
||||||
Vector2 frGetBodyVelocity(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the angular velocity of `b`. */
|
|
||||||
float frGetBodyAngularVelocity(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the gravity scale of `b`. */
|
|
||||||
float frGetBodyGravityScale(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the position (in meters) and rotation (in radians) of `b`. */
|
|
||||||
frTransform frGetBodyTransform(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the position (in meters) of `b`. */
|
|
||||||
Vector2 frGetBodyPosition(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the rotation (in radians) of `b`. */
|
|
||||||
float frGetBodyRotation(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the collision shape of `b`. */
|
|
||||||
frShape *frGetBodyShape(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the AABB (Axis-Aligned Bounding Box) of `b`. */
|
|
||||||
Rectangle frGetBodyAABB(frBody *b);
|
|
||||||
|
|
||||||
/* Returns the user data of `b`. */
|
|
||||||
void *frGetBodyUserData(frBody *b);
|
|
||||||
|
|
||||||
/* Converts the world coordinates `p` to a position relative to `b`'s transform value. */
|
|
||||||
Vector2 frGetLocalPoint(frBody *b, Vector2 p);
|
|
||||||
|
|
||||||
/* Converts the position relative to `b`'s transform value `p` to world coordinates. */
|
|
||||||
Vector2 frGetWorldPoint(frBody *b, Vector2 p);
|
|
||||||
|
|
||||||
/* Sets the type of `b` to `type`. */
|
|
||||||
void frSetBodyType(frBody *b, frBodyType type);
|
|
||||||
|
|
||||||
/* Sets the property flags of `b` to `flags`. */
|
|
||||||
void frSetBodyFlags(frBody *b, frBodyFlags flags);
|
|
||||||
|
|
||||||
/* Sets the velocity of `b` to `v`. */
|
|
||||||
void frSetBodyVelocity(frBody *b, Vector2 v);
|
|
||||||
|
|
||||||
/* Sets the angular velocity of `b` to `a`. */
|
|
||||||
void frSetBodyAngularVelocity(frBody *b, double a);
|
|
||||||
|
|
||||||
/* Sets the gravity scale value of `b` to `scale`. */
|
|
||||||
void frSetBodyGravityScale(frBody *b, float scale);
|
|
||||||
|
|
||||||
/* Sets the transform value of `b` to `tx`. */
|
|
||||||
void frSetBodyTransform(frBody *b, frTransform tx);
|
|
||||||
|
|
||||||
/* Sets the position of `b` to `p`. */
|
|
||||||
void frSetBodyPosition(frBody *b, Vector2 p);
|
|
||||||
|
|
||||||
/* Sets the rotation of `b` to `rotation`. */
|
|
||||||
void frSetBodyRotation(frBody *b, float rotation);
|
|
||||||
|
|
||||||
/* Sets the user data of `b` to `data`. */
|
|
||||||
void frSetBodyUserData(frBody *b, void *data);
|
|
||||||
|
|
||||||
/* Clears all forces that are applied to `b`. */
|
|
||||||
void frClearBodyForces(frBody *b);
|
|
||||||
|
|
||||||
/* Applies a `gravity` vector to `b`. */
|
|
||||||
void frApplyGravity(frBody *b, Vector2 gravity);
|
|
||||||
|
|
||||||
/* Applies an `impulse` to `b`. */
|
|
||||||
void frApplyImpulse(frBody *b, Vector2 impulse);
|
|
||||||
|
|
||||||
/* Applies a torque `impulse` to `b`. */
|
|
||||||
void frApplyTorqueImpulse(frBody *b, Vector2 p, Vector2 impulse);
|
|
||||||
|
|
||||||
/* Integrates the position of `b` with `dt`. */
|
|
||||||
void frIntegrateForBodyPosition(frBody *b, double dt);
|
|
||||||
|
|
||||||
/* Integrates the velocities of `b` with `dt`. */
|
|
||||||
void frIntegrateForBodyVelocities(frBody *b, double dt);
|
|
||||||
|
|
||||||
/* Resolves the collision between two rigid bodies. */
|
|
||||||
void frResolveCollision(frCollision *collision);
|
|
||||||
|
|
||||||
/* Corrects the positions of two rigid bodies. */
|
|
||||||
void frCorrectBodyPositions(frCollision *collision, float inverseDt);
|
|
||||||
|
|
||||||
/* | Module Functions (`geometry`) | */
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates a new circle-shaped collision shape
|
|
||||||
from the given material and radius (in meters).
|
|
||||||
*/
|
|
||||||
frShape *frCreateCircle(frMaterial material, float radius);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates a new rectangle-shaped collision shape
|
|
||||||
from the given material, width and height (in meters).
|
|
||||||
*/
|
|
||||||
frShape *frCreateRectangle(frMaterial material, float width, float height);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates a new polygon-shaped collision shape
|
|
||||||
from the given material and vertices (in meters).
|
|
||||||
*/
|
|
||||||
frShape *frCreatePolygon(frMaterial material, frVertices vertices);
|
|
||||||
|
|
||||||
/* Creates an empty shape. */
|
|
||||||
frShape *frCreateShape(void);
|
|
||||||
|
|
||||||
/* Returns a clone of `s`. */
|
|
||||||
frShape *frCloneShape(frShape *s);
|
|
||||||
|
|
||||||
/* Releases the memory allocated for `s`. */
|
|
||||||
void frReleaseShape(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the size of the struct `frShape`. */
|
|
||||||
size_t frGetShapeStructSize(void);
|
|
||||||
|
|
||||||
/* Returns the type of `s`. */
|
|
||||||
frShapeType frGetShapeType(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the material of `s`. */
|
|
||||||
frMaterial frGetShapeMaterial(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the area of `s`. */
|
|
||||||
float frGetShapeArea(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the mass of `s`. */
|
|
||||||
float frGetShapeMass(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the moment of inertia for `s`. */
|
|
||||||
float frGetShapeInertia(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the AABB (Axis-Aligned Bounding Box) of `s`. */
|
|
||||||
Rectangle frGetShapeAABB(frShape *s, frTransform tx);
|
|
||||||
|
|
||||||
/* Returns the radius of the `s`. */
|
|
||||||
float frGetCircleRadius(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the width and height of `s`. */
|
|
||||||
Vector2 frGetRectangleDimensions(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the `i + 1`th vertex of `s`. */
|
|
||||||
Vector2 frGetPolygonVertex(frShape *s, int i);
|
|
||||||
|
|
||||||
/* Returns the `i + 1`th normal of `s`. */
|
|
||||||
Vector2 frGetPolygonNormal(frShape *s, int i);
|
|
||||||
|
|
||||||
/* Returns the vertices of `s`. */
|
|
||||||
frVertices frGetPolygonVertices(frShape *s);
|
|
||||||
|
|
||||||
/* Returns the normals of `s`. */
|
|
||||||
frVertices frGetPolygonNormals(frShape *s);
|
|
||||||
|
|
||||||
/* Returns `true` if `s` is a rectangular collision shape. */
|
|
||||||
bool frIsShapeRectangle(frShape *s);
|
|
||||||
|
|
||||||
/* Sets the radius of `s` to `radius`. */
|
|
||||||
void frSetCircleRadius(frShape *s, float radius);
|
|
||||||
|
|
||||||
/* Sets the dimensions (width and height) for `s` to `v`. */
|
|
||||||
void frSetRectangleDimensions(frShape *s, Vector2 v);
|
|
||||||
|
|
||||||
/* Sets the vertices of `s` to `vertices`. */
|
|
||||||
void frSetPolygonVertices(frShape *s, frVertices vertices);
|
|
||||||
|
|
||||||
/* Sets the material of `s` to `material`. */
|
|
||||||
void frSetShapeMaterial(frShape *s, frMaterial material);
|
|
||||||
|
|
||||||
/* Sets the type of `s` to `type`. */
|
|
||||||
void frSetShapeType(frShape *s, frShapeType type);
|
|
||||||
|
|
||||||
/* Returns `true` if `s` contains the point `p`. */
|
|
||||||
bool frShapeContainsPoint(frShape *s, frTransform tx, Vector2 p);
|
|
||||||
|
|
||||||
/* | Module Functions (`timer`) | */
|
|
||||||
|
|
||||||
/* Initializes a monotonic clock. */
|
|
||||||
void frInitClock(void);
|
|
||||||
|
|
||||||
/* Returns the current time (in milliseconds) of the monotonic clock. */
|
|
||||||
double frGetCurrentTime(void);
|
|
||||||
|
|
||||||
/* Returns the difference between `newTime` and `oldTime`. */
|
|
||||||
double frGetTimeDifference(double newTime, double oldTime);
|
|
||||||
|
|
||||||
/* Returns the difference between the current time and `oldTime`. */
|
|
||||||
double frGetTimeSince(double oldTime);
|
|
||||||
|
|
||||||
/* | Module Functions (`utils`) | */
|
|
||||||
|
|
||||||
/* Normalizes `angle` (in radians) to range `[center - ฯ/2, center + ฯ/2]`. */
|
|
||||||
float frNormalizeAngle(float angle, float center);
|
|
||||||
|
|
||||||
/* Returns `true` if `f1` and `f2` are approximately equal. */
|
|
||||||
bool frNumberApproxEquals(float f1, float f2);
|
|
||||||
|
|
||||||
/* | Module Functions (`world`) | */
|
|
||||||
|
|
||||||
/* Creates a new world from the given `gravity` vector and world `bounds` in meters. */
|
|
||||||
frWorld *frCreateWorld(Vector2 gravity, Rectangle bounds);
|
|
||||||
|
|
||||||
/* Removes all rigid bodies from `world`, then releases the memory allocated for `world`. */
|
|
||||||
void frReleaseWorld(frWorld *world);
|
|
||||||
|
|
||||||
/* Adds `b` to `world`. */
|
|
||||||
bool frAddToWorld(frWorld *world, frBody *b);
|
|
||||||
|
|
||||||
/* Removes all rigid bodies from `world`. */
|
|
||||||
void frClearWorld(frWorld *world);
|
|
||||||
|
|
||||||
/* Removes `b` from `world`. */
|
|
||||||
bool frRemoveFromWorld(frWorld *world, frBody *b);
|
|
||||||
|
|
||||||
/* Returns the size of the struct `frWorld`. */
|
|
||||||
size_t frGetWorldStructSize(void);
|
|
||||||
|
|
||||||
/* Returns the rigid body at the `index` in `world`'s array of rigid bodies. */
|
|
||||||
frBody *frGetWorldBody(frWorld *world, int index);
|
|
||||||
|
|
||||||
/* Returns the collision event handler for `world`. */
|
|
||||||
frCollisionHandler frGetWorldCollisionHandler(frWorld *world);
|
|
||||||
|
|
||||||
/* Returns the length of `world`'s array of rigid bodies. */
|
|
||||||
int frGetWorldBodyCount(frWorld *world);
|
|
||||||
|
|
||||||
/* Returns the bounds of `world`. */
|
|
||||||
Rectangle frGetWorldBounds(frWorld *world);
|
|
||||||
|
|
||||||
/* Returns the spatial hash of `world`. */
|
|
||||||
frSpatialHash *frGetWorldSpatialHash(frWorld *world);
|
|
||||||
|
|
||||||
/* Returns the gravity vector of `world`. */
|
|
||||||
Vector2 frGetWorldGravity(frWorld *world);
|
|
||||||
|
|
||||||
/* Returns `true` if `b` collides with the bounds of `world`. */
|
|
||||||
bool frIsInWorldBounds(frWorld *world, frBody *b);
|
|
||||||
|
|
||||||
/* Sets the world bounds of `world` to `bounds`. */
|
|
||||||
void frSetWorldBounds(frWorld *world, Rectangle bounds);
|
|
||||||
|
|
||||||
/* Sets the collision event handler for `world` to `handler`. */
|
|
||||||
void frSetWorldCollisionHandler(frWorld *world, frCollisionHandler handler);
|
|
||||||
|
|
||||||
/* Sets the gravity vector of `world` to `gravity`. */
|
|
||||||
void frSetWorldGravity(frWorld *world, Vector2 gravity);
|
|
||||||
|
|
||||||
/* Simulates the `world` for the time step `dt` (in milliseconds). */
|
|
||||||
void frSimulateWorld(frWorld *world, double dt);
|
|
||||||
|
|
||||||
/* Query the `world` for rigid bodies that collides with `rec`. */
|
|
||||||
int frQueryWorldSpatialHash(frWorld *world, Rectangle rec, frBody **bodies);
|
|
||||||
|
|
||||||
/* Casts a `ray` to all rigid bodies in `world`. */
|
|
||||||
int frComputeWorldRaycast(frWorld *world, frRay ray, frRaycastHit *hits);
|
|
||||||
|
|
||||||
/* | Inline Functions | */
|
|
||||||
|
|
||||||
/* Converts `value` (in pixels) to meters. */
|
|
||||||
FR_API_INLINE float frNumberPixelsToMeters(float value) {
|
|
||||||
return (FR_GLOBAL_PIXELS_PER_METER > 0.0f)
|
|
||||||
? (value / FR_GLOBAL_PIXELS_PER_METER)
|
|
||||||
: 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts `value` (in meters) to pixels. */
|
|
||||||
FR_API_INLINE float frNumberMetersToPixels(float value) {
|
|
||||||
return (FR_GLOBAL_PIXELS_PER_METER > 0.0f)
|
|
||||||
? (value * FR_GLOBAL_PIXELS_PER_METER)
|
|
||||||
: 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts the components of `rec` (in pixels) to meters. */
|
|
||||||
FR_API_INLINE Rectangle frRecPixelsToMeters(Rectangle rec) {
|
|
||||||
return (Rectangle) {
|
|
||||||
.x = frNumberPixelsToMeters(rec.x),
|
|
||||||
.y = frNumberPixelsToMeters(rec.y),
|
|
||||||
.width = frNumberPixelsToMeters(rec.width),
|
|
||||||
.height = frNumberPixelsToMeters(rec.height)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts the components of `rec` (in meters) to pixels. */
|
|
||||||
FR_API_INLINE Rectangle frRecMetersToPixels(Rectangle rec) {
|
|
||||||
return (Rectangle) {
|
|
||||||
.x = frNumberMetersToPixels(rec.x),
|
|
||||||
.y = frNumberMetersToPixels(rec.y),
|
|
||||||
.width = frNumberMetersToPixels(rec.width),
|
|
||||||
.height = frNumberMetersToPixels(rec.height)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adds `v1` and `v2`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2Add(Vector2 v1, Vector2 v2) {
|
|
||||||
return (Vector2) { v1.x + v2.x, v1.y + v2.y };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Subtracts `v2` from `v1`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2Subtract(Vector2 v1, Vector2 v2) {
|
|
||||||
return (Vector2) { v1.x - v2.x, v1.y - v2.y };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Multiplies `v` by `value`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2ScalarMultiply(Vector2 v, float value) {
|
|
||||||
return (Vector2) { v.x * value, v.y * value };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the cross product of `v1` and `v2`. */
|
|
||||||
FR_API_INLINE float frVec2CrossProduct(Vector2 v1, Vector2 v2) {
|
|
||||||
// ํ๋ฉด ๋ฒกํฐ์ ์ธ์ ์ ์ค์นผ๋ผ ๊ฐ์ด๋ค.
|
|
||||||
return (v1.x * v2.y) - (v1.y * v2.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the dot product of `v1` and `v2`. */
|
|
||||||
FR_API_INLINE float frVec2DotProduct(Vector2 v1, Vector2 v2) {
|
|
||||||
return (v1.x * v2.x) + (v1.y * v2.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the squared magnitude of `v`. */
|
|
||||||
FR_API_INLINE float frVec2MagnitudeSqr(Vector2 v) {
|
|
||||||
return frVec2DotProduct(v, v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the magnitude of `v`. */
|
|
||||||
FR_API_INLINE float frVec2Magnitude(Vector2 v) {
|
|
||||||
return sqrtf(frVec2MagnitudeSqr(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the negated vector of `v`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2Negate(Vector2 v) {
|
|
||||||
return (Vector2) { -v.x, -v.y };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts `v` to a unit vector. */
|
|
||||||
FR_API_INLINE Vector2 frVec2Normalize(Vector2 v) {
|
|
||||||
const float magnitude = frVec2Magnitude(v);
|
|
||||||
|
|
||||||
return (magnitude > 0.0f)
|
|
||||||
? frVec2ScalarMultiply(v, 1.0f / magnitude)
|
|
||||||
: FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the angle (in radians) between `v1` and `v2`. */
|
|
||||||
FR_API_INLINE float frVec2Angle(Vector2 v1, Vector2 v2) {
|
|
||||||
return atan2f(v2.y, v2.x) - atan2f(v1.y, v1.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns `true` if `v1` and `v2` are approximately equal. */
|
|
||||||
FR_API_INLINE bool frVec2ApproxEquals(Vector2 v1, Vector2 v2) {
|
|
||||||
return frNumberApproxEquals(v1.x, v2.x) && frNumberApproxEquals(v1.y, v2.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the left normal of `v`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2LeftNormal(Vector2 v) {
|
|
||||||
return frVec2Normalize((Vector2) { -v.y, v.x });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the right normal of `v`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2RightNormal(Vector2 v) {
|
|
||||||
return frVec2Normalize((Vector2) { v.y, -v.x });
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rotates `v` around the origin. */
|
|
||||||
FR_API_INLINE Vector2 frVec2Rotate(Vector2 v, float angle) {
|
|
||||||
const float sinA = sinf(angle);
|
|
||||||
const float cosA = cosf(angle);
|
|
||||||
|
|
||||||
return (Vector2) { (v.x * cosA - v.y * sinA), (v.x * sinA + v.y * cosA) };
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Rotates `v` with the properties of `tx`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2RotateTx(Vector2 v, frTransform tx) {
|
|
||||||
Vector2 result = {
|
|
||||||
(v.x * tx.cache.cosA - v.y * tx.cache.sinA),
|
|
||||||
(v.x * tx.cache.sinA + v.y * tx.cache.cosA)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!tx.cache.valid) result = frVec2Rotate(v, tx.rotation);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Transforms `v` with the properties of `tx`. */
|
|
||||||
FR_API_INLINE Vector2 frVec2Transform(Vector2 v, frTransform tx) {
|
|
||||||
return frVec2Add(tx.position, frVec2RotateTx(v, tx));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns `true` if `v1`, `v2` and `v3` are ordered counter-clockwise. */
|
|
||||||
FR_API_INLINE bool frVec2CCW(Vector2 v1, Vector2 v2, Vector2 v3) {
|
|
||||||
return (v3.y - v1.y) * (v2.x - v1.x) < (v2.y - v1.y) * (v3.x - v1.x);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts the components of `v` (in pixels) to meters. */
|
|
||||||
FR_API_INLINE Vector2 frVec2PixelsToMeters(Vector2 v) {
|
|
||||||
return (FR_GLOBAL_PIXELS_PER_METER > 0.0f)
|
|
||||||
? frVec2ScalarMultiply(v, 1.0f / FR_GLOBAL_PIXELS_PER_METER)
|
|
||||||
: FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Converts the components of `v` (in meters) to pixels. */
|
|
||||||
FR_API_INLINE Vector2 frVec2MetersToPixels(Vector2 v) {
|
|
||||||
return (FR_GLOBAL_PIXELS_PER_METER > 0.0f)
|
|
||||||
? frVec2ScalarMultiply(v, FR_GLOBAL_PIXELS_PER_METER)
|
|
||||||
: FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,201 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
#include "stb_ds.h"
|
|
||||||
|
|
||||||
/* | `broadphase` ๋ชจ๋ ์๋ฃํ ์ ์... | */
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต์ ํค์ ๊ฐ์ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด. */
|
|
||||||
typedef struct frSpatialEntry {
|
|
||||||
int key;
|
|
||||||
int *values;
|
|
||||||
} frSpatialEntry;
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต์ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด. */
|
|
||||||
typedef struct frSpatialHash {
|
|
||||||
Rectangle bounds;
|
|
||||||
float cellSize;
|
|
||||||
float inverseCellSize;
|
|
||||||
frSpatialEntry *entries;
|
|
||||||
int *queryCache;
|
|
||||||
} frSpatialHash;
|
|
||||||
|
|
||||||
/* | `broadphase` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์์ ๋ฒกํฐ `v`์ ๋์ํ๋ ํค๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
static FR_API_INLINE int frComputeSpatialHashKey(frSpatialHash *hash, Vector2 v);
|
|
||||||
|
|
||||||
/* C ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ `qsort()` ํจ์ ํธ์ถ์ ์ฌ์ฉ๋๋ ๋น๊ต ํจ์์ด๋ค. */
|
|
||||||
static int frQuickSortCallback(const void *x, const void *y);
|
|
||||||
|
|
||||||
/* ๊ฒฝ๊ณ ๋ฒ์๊ฐ `bounds`์ด๊ณ ๊ฐ ์
์ ํฌ๊ธฐ๊ฐ `cellSize`์ธ ๊ณต๊ฐ ํด์๋งต์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
frSpatialHash *frCreateSpatialHash(Rectangle bounds, float cellSize) {
|
|
||||||
if (cellSize <= 0.0f) return NULL;
|
|
||||||
|
|
||||||
frSpatialHash *result = calloc(1, sizeof(*result));
|
|
||||||
|
|
||||||
result->bounds = bounds;
|
|
||||||
result->cellSize = cellSize;
|
|
||||||
result->inverseCellSize = 1.0f / cellSize;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํ๋ค. */
|
|
||||||
void frReleaseSpatialHash(frSpatialHash *hash) {
|
|
||||||
if (hash == NULL) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < hmlen(hash->entries); i++)
|
|
||||||
arrfree(hash->entries[i].values);
|
|
||||||
|
|
||||||
hmfree(hash->entries);
|
|
||||||
|
|
||||||
free(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์ ์ง์ฌ๊ฐํ `rec`๋ก ์์ฑํ ํค์ `value`๋ฅผ ์ถ๊ฐํ๋ค. */
|
|
||||||
void frAddToSpatialHash(frSpatialHash *hash, Rectangle rec, int value) {
|
|
||||||
if (hash == NULL || !CheckCollisionRecs(hash->bounds, rec)) return;
|
|
||||||
|
|
||||||
int x0 = frComputeSpatialHashKey(hash, (Vector2) { .x = rec.x });
|
|
||||||
int x1 = frComputeSpatialHashKey(hash, (Vector2) { .x = rec.x + rec.width });
|
|
||||||
|
|
||||||
int y0 = frComputeSpatialHashKey(hash, (Vector2) { .y = rec.y });
|
|
||||||
int y1 = frComputeSpatialHashKey(hash, (Vector2) { .y = rec.y + rec.height });
|
|
||||||
|
|
||||||
for (int y = y0; y <= y1; y += hash->bounds.width) {
|
|
||||||
for (int x = x0; x <= x1; x++) {
|
|
||||||
const int key = x + y;
|
|
||||||
|
|
||||||
frSpatialEntry *entry = hmgetp_null(hash->entries, key);
|
|
||||||
|
|
||||||
if (entry != NULL) {
|
|
||||||
arrput(entry->values, value);
|
|
||||||
} else {
|
|
||||||
frSpatialEntry newEntry = { .key = key };
|
|
||||||
|
|
||||||
arrput(newEntry.values, value);
|
|
||||||
hmputs(hash->entries, newEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์ ๋ชจ๋ ํค์ ๊ฐ์ ์ ๊ฑฐํ๋ค. */
|
|
||||||
void frClearSpatialHash(frSpatialHash *hash) {
|
|
||||||
if (hash == NULL) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < hmlen(hash->entries); i++)
|
|
||||||
arrsetlen(hash->entries[i].values, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์์ ํค๊ฐ `key`์ธ ๊ฐ์ ์ ๊ฑฐํ๋ค. */
|
|
||||||
void frRemoveFromSpatialHash(frSpatialHash *hash, int key) {
|
|
||||||
if (hash == NULL) return;
|
|
||||||
|
|
||||||
frSpatialEntry entry = hmgets(hash->entries, key);
|
|
||||||
arrfree(entry.values);
|
|
||||||
|
|
||||||
hmdel(hash->entries, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์์ ์ง์ฌ๊ฐํ `rec`์ ๊ฒฝ๊ณ ๋ฒ์๊ฐ ๊ฒน์น๋ ๋ชจ๋ ๊ฐ์ฒด์ ์ธ๋ฑ์ค๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
void frQuerySpatialHash(frSpatialHash *hash, Rectangle rec, int **result) {
|
|
||||||
if (hash == NULL || result == NULL || !CheckCollisionRecs(hash->bounds, rec)) return;
|
|
||||||
|
|
||||||
int x0 = frComputeSpatialHashKey(hash, (Vector2) { .x = rec.x });
|
|
||||||
int x1 = frComputeSpatialHashKey(hash, (Vector2) { .x = rec.x + rec.width });
|
|
||||||
|
|
||||||
int y0 = frComputeSpatialHashKey(hash, (Vector2) { .y = rec.y });
|
|
||||||
int y1 = frComputeSpatialHashKey(hash, (Vector2) { .y = rec.y + rec.height });
|
|
||||||
|
|
||||||
const int yOffset = (int) hash->bounds.width;
|
|
||||||
|
|
||||||
for (int y = y0; y <= y1; y += yOffset) {
|
|
||||||
for (int x = x0; x <= x1; x++) {
|
|
||||||
const int key = x + y;
|
|
||||||
|
|
||||||
// ์ฃผ์ด์ง ์
๊ณผ ๋ง๋๋ ๋ชจ๋ ๊ฐ์ฒด์ ์ธ๋ฑ์ค๋ฅผ ๊ตฌํ๋ค.
|
|
||||||
frSpatialEntry *entry = hmgetp_null(hash->entries, key);
|
|
||||||
|
|
||||||
if (entry != NULL) {
|
|
||||||
for (int j = 0; j < arrlen(entry->values); j++)
|
|
||||||
arrput(*result, entry->values[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t oldLength = arrlen(*result);
|
|
||||||
|
|
||||||
if (oldLength <= 1) return;
|
|
||||||
|
|
||||||
// ๊ฒฐ๊ณผ ๋ฐฐ์ด์ ๋จผ์ ํต-์ ๋ ฌํ๋ค.
|
|
||||||
qsort(*result, oldLength, sizeof(**result), frQuickSortCallback);
|
|
||||||
|
|
||||||
size_t newLength = 0;
|
|
||||||
|
|
||||||
// ๊ฒฐ๊ณผ ๋ฐฐ์ด์์ ์ค๋ณต ๊ฐ์ ์ ๊ฑฐํ๋ค.
|
|
||||||
for (int i = 0; i < oldLength; i++)
|
|
||||||
if ((*result)[i] != (*result)[i + 1])
|
|
||||||
(*result)[newLength++] = (*result)[i];
|
|
||||||
|
|
||||||
// ๊ฒฐ๊ณผ ๋ฐฐ์ด์ ๊ธธ์ด๋ฅผ ๋ค์ ์ค์ ํ๋ค.
|
|
||||||
arrsetlen(*result, newLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์ ๊ฒฝ๊ณ ๋ฒ์๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Rectangle frGetSpatialHashBounds(frSpatialHash *hash) {
|
|
||||||
return (hash != NULL) ? hash->bounds : FR_STRUCT_ZERO(Rectangle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์ ๊ฐ ์
์ ํฌ๊ธฐ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetSpatialHashCellSize(frSpatialHash *hash) {
|
|
||||||
return (hash != NULL) ? hash->cellSize : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์ ๊ฒฝ๊ณ ๋ฒ์๋ฅผ `bounds`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetSpatialHashBounds(frSpatialHash *hash, Rectangle bounds) {
|
|
||||||
if (hash != NULL) hash->bounds = bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์ ๊ฐ ์
์ ํฌ๊ธฐ๋ฅผ `cellSize`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetSpatialHashCellSize(frSpatialHash *hash, float cellSize) {
|
|
||||||
if (hash != NULL) hash->cellSize = cellSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ณต๊ฐ ํด์๋งต `hash`์์ ๋ฒกํฐ `v`์ ๋์ํ๋ ํค๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
static FR_API_INLINE int frComputeSpatialHashKey(frSpatialHash *hash, Vector2 v) {
|
|
||||||
if (hash == NULL) return -1;
|
|
||||||
|
|
||||||
const int xIndex = v.x * hash->inverseCellSize;
|
|
||||||
const int yIndex = v.y * hash->inverseCellSize;
|
|
||||||
|
|
||||||
return yIndex * (int) hash->bounds.width + xIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* C ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ `qsort()` ํจ์ ํธ์ถ์ ์ฌ์ฉ๋๋ ๋น๊ต ํจ์์ด๋ค. */
|
|
||||||
static int frQuickSortCallback(const void *x, const void *y) {
|
|
||||||
int nx = *(const int *) x, ny = *(const int *) y;
|
|
||||||
|
|
||||||
return (nx > ny) - (nx < ny);
|
|
||||||
}
|
|
|
@ -1,661 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
/* | `collision` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
/* ๋ํ `s1`์ AABB๊ฐ `s2`์ AABB์ ์ถฉ๋ํ๋์ง ํ์ธํ๋ค. */
|
|
||||||
static bool frCheckCollisionAABB(frShape *s1, frTransform tx1, frShape *s2, frTransform tx2);
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`์์ ๋ฒกํฐ `v`์์ ๋ด์ ์ด ๊ฐ์ฅ ํฐ ๊ผญ์ง์ ์ ์ธ๋ฑ์ค๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
static int frGetPolygonFurthestIndex(frShape *s, frTransform tx, Vector2 v);
|
|
||||||
|
|
||||||
/* ๋ํ `s`์์ ๋ฒกํฐ `v`์ ๊ฐ์ฅ ์์ง์ ๊ฐ๊น์ด ๋ณ์ ๋ฐํํ๋ค. */
|
|
||||||
static frVertices frGetShapeSignificantEdge(frShape *s, frTransform tx, Vector2 v);
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s1`์์ `s2`๋ก์ ์ถฉ๋ ๋ฐฉํฅ๊ณผ ๊น์ด๋ฅผ ๊ณ์ฐํ๋ค. */
|
|
||||||
static int frGetSeparatingAxisIndex(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2,
|
|
||||||
float *depth
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ์ `s1`์์ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionCirclesSAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ์ `s1`์์ ๋ค๊ฐํ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionCirclePolySAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ๋ค๊ฐํ `s1`์์ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionPolysSAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ๋ํ `s1`์์ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionSAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ์ ๋ถ `e`์์ ๋ฒกํฐ `v`์์ ๋ด์ ์ด `minDot`๋ณด๋ค ์์ ๋ถ๋ถ์ ๋ชจ๋ ์๋ผ๋ธ๋ค. */
|
|
||||||
static frVertices frClipEdgeWithAxis(frVertices e, Vector2 v, float minDot);
|
|
||||||
|
|
||||||
/* `o1`์์ `v1` ๋ฐฉํฅ์ผ๋ก ์งํํ๋ ๊ด์ ์ด `o2`์์ `v2` ๋ฐฉํฅ์ผ๋ก ์งํํ๋ ๊ด์ ๊ณผ ๋ง๋๋์ง ๊ณ์ฐํ๋ค. */
|
|
||||||
static bool frComputeIntersectionRays(
|
|
||||||
Vector2 o1, Vector2 v1,
|
|
||||||
Vector2 o2, Vector2 v2,
|
|
||||||
float *distance
|
|
||||||
);
|
|
||||||
|
|
||||||
/* `o`์์ `v` ๋ฐฉํฅ์ผ๋ก ์งํํ๋ ๊ด์ ์ด ์ค์ฌ์ ์ด `c`์ด๊ณ ๋ฐ์ง๋ฆ์ด `r`์ธ ์๊ณผ ๋ง๋๋์ง ๊ณ์ฐํ๋ค. */
|
|
||||||
static bool frComputeIntersectionRayCircle(
|
|
||||||
Vector2 o, Vector2 v,
|
|
||||||
Vector2 c, float r,
|
|
||||||
float *distance
|
|
||||||
);
|
|
||||||
|
|
||||||
/* ๋ํ `s1`์์ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
frCollision frComputeShapeCollision(frShape *s1, frTransform tx1, frShape *s2, frTransform tx2) {
|
|
||||||
return frCheckCollisionAABB(s1, tx1, s2, tx2)
|
|
||||||
? frComputeCollisionSAT(s1, tx1, s2, tx2)
|
|
||||||
: FR_STRUCT_ZERO(frCollision);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b1`์์ `b2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
frCollision frComputeBodyCollision(frBody *b1, frBody *b2) {
|
|
||||||
if (b1 == NULL || b2 == NULL) return FR_STRUCT_ZERO(frCollision);
|
|
||||||
|
|
||||||
return frComputeShapeCollision(
|
|
||||||
frGetBodyShape(b1),
|
|
||||||
frGetBodyTransform(b1),
|
|
||||||
frGetBodyShape(b2),
|
|
||||||
frGetBodyTransform(b2)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ๊ด์ ์ ํฌ์ฌํ๋ค. */
|
|
||||||
frRaycastHit frComputeShapeRaycast(frShape *s, frTransform tx, frRay ray) {
|
|
||||||
if (s == NULL || frGetShapeType(s) == FR_SHAPE_UNKNOWN) return FR_STRUCT_ZERO(frRaycastHit);
|
|
||||||
|
|
||||||
frRaycastHit result = { .shape = s, .distance = FLT_MAX };
|
|
||||||
|
|
||||||
ray.direction = frVec2Normalize(ray.direction);
|
|
||||||
|
|
||||||
float distance = FLT_MAX;
|
|
||||||
|
|
||||||
if (frGetShapeType(s) == FR_SHAPE_CIRCLE) {
|
|
||||||
bool intersects = frComputeIntersectionRayCircle(
|
|
||||||
ray.origin, ray.direction,
|
|
||||||
tx.position, frGetCircleRadius(s),
|
|
||||||
&distance
|
|
||||||
);
|
|
||||||
|
|
||||||
result.check = (distance >= 0.0f) && (distance <= ray.maxDistance);
|
|
||||||
result.inside = (distance < 0.0f);
|
|
||||||
|
|
||||||
if (result.check) {
|
|
||||||
result.distance = distance;
|
|
||||||
|
|
||||||
result.point = frVec2Add(
|
|
||||||
ray.origin,
|
|
||||||
frVec2ScalarMultiply(ray.direction, result.distance)
|
|
||||||
);
|
|
||||||
|
|
||||||
result.normal = frVec2LeftNormal(frVec2Subtract(ray.origin, result.point));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} else if (frGetShapeType(s) == FR_SHAPE_POLYGON) {
|
|
||||||
int intersectionCount = 0;
|
|
||||||
|
|
||||||
frVertices vertices = frGetPolygonVertices(s);
|
|
||||||
|
|
||||||
// ๋ค๊ฐํ์ ๋ณ ์ค์ ๊ด์ ๊ณผ ๊ต์ฐจํ๋ ๋ณ์ด ์กด์ฌํ๋์ง ํ์ธํ๋ค.
|
|
||||||
for (int j = vertices.count - 1, i = 0; i < vertices.count; j = i, i++) {
|
|
||||||
Vector2 v1 = frVec2Transform(vertices.data[i], tx);
|
|
||||||
Vector2 v2 = frVec2Transform(vertices.data[j], tx);
|
|
||||||
|
|
||||||
Vector2 diff = frVec2Subtract(v1, v2);
|
|
||||||
|
|
||||||
bool intersects = frComputeIntersectionRays(
|
|
||||||
ray.origin, ray.direction,
|
|
||||||
v2, diff,
|
|
||||||
&distance
|
|
||||||
);
|
|
||||||
|
|
||||||
if (intersects && distance <= ray.maxDistance) {
|
|
||||||
if (result.distance > distance) {
|
|
||||||
result.distance = distance;
|
|
||||||
|
|
||||||
result.point = frVec2Add(
|
|
||||||
ray.origin,
|
|
||||||
frVec2ScalarMultiply(ray.direction, result.distance)
|
|
||||||
);
|
|
||||||
|
|
||||||
result.normal = frVec2LeftNormal(diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
intersectionCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.inside = (intersectionCount & 1);
|
|
||||||
result.check = (!result.inside) && (intersectionCount > 0);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๊ด์ ์ ํฌ์ฌํ๋ค. */
|
|
||||||
frRaycastHit frComputeBodyRaycast(frBody *b, frRay ray) {
|
|
||||||
if (b == NULL || frGetBodyShape(b) == NULL) return FR_STRUCT_ZERO(frRaycastHit);
|
|
||||||
|
|
||||||
frRaycastHit result = frComputeShapeRaycast(
|
|
||||||
frGetBodyShape(b), frGetBodyTransform(b),
|
|
||||||
ray
|
|
||||||
);
|
|
||||||
|
|
||||||
result.body = b;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s1`์ AABB๊ฐ `s2`์ AABB์ ์ถฉ๋ํ๋์ง ํ์ธํ๋ค. */
|
|
||||||
static bool frCheckCollisionAABB(frShape *s1, frTransform tx1, frShape *s2, frTransform tx2) {
|
|
||||||
return CheckCollisionRecs(frGetShapeAABB(s1, tx1), frGetShapeAABB(s2, tx2));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`์์ ๋ฒกํฐ `v`์์ ๋ด์ ์ด ๊ฐ์ฅ ํฐ ๊ผญ์ง์ ์ ์ธ๋ฑ์ค๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
static int frGetPolygonFurthestIndex(frShape *s, frTransform tx, Vector2 v) {
|
|
||||||
if (frGetShapeType(s) != FR_SHAPE_POLYGON) return -1;
|
|
||||||
|
|
||||||
frVertices vertices = frGetPolygonVertices(s);
|
|
||||||
|
|
||||||
float maxDot = -FLT_MAX;
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
v = frVec2Rotate(v, -tx.rotation);
|
|
||||||
|
|
||||||
for (int i = 0; i < vertices.count; i++) {
|
|
||||||
float dot = frVec2DotProduct(vertices.data[i], v);
|
|
||||||
|
|
||||||
if (maxDot < dot) {
|
|
||||||
maxDot = dot;
|
|
||||||
result = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์์ ๋ฒกํฐ `v`์ ๊ฐ์ฅ ์์ง์ ๊ฐ๊น์ด ๋ณ์ ๋ฐํํ๋ค. */
|
|
||||||
static frVertices frGetShapeSignificantEdge(frShape *s, frTransform tx, Vector2 v) {
|
|
||||||
if (s == NULL || frGetShapeType(s) == FR_SHAPE_UNKNOWN) return FR_STRUCT_ZERO(frVertices);
|
|
||||||
|
|
||||||
frVertices result = FR_STRUCT_ZERO(frVertices);
|
|
||||||
|
|
||||||
if (frGetShapeType(s) == FR_SHAPE_CIRCLE) {
|
|
||||||
result.data[0] = frVec2Transform(frVec2ScalarMultiply(v, frGetCircleRadius(s)), tx);
|
|
||||||
result.count = 1;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} else if (frGetShapeType(s) == FR_SHAPE_POLYGON) {
|
|
||||||
int furthestIndex = frGetPolygonFurthestIndex(s, tx, v);
|
|
||||||
|
|
||||||
frVertices vertices = frGetPolygonVertices(s);
|
|
||||||
|
|
||||||
int prevIndex = (furthestIndex == 0) ? vertices.count - 1 : furthestIndex - 1;
|
|
||||||
int nextIndex = (furthestIndex == vertices.count - 1) ? 0 : furthestIndex + 1;
|
|
||||||
|
|
||||||
Vector2 furthestVertex = frVec2Transform(vertices.data[furthestIndex], tx);
|
|
||||||
|
|
||||||
Vector2 prevVertex = frVec2Transform(vertices.data[prevIndex], tx);
|
|
||||||
Vector2 nextVertex = frVec2Transform(vertices.data[nextIndex], tx);
|
|
||||||
|
|
||||||
Vector2 leftV = frVec2Normalize(frVec2Subtract(furthestVertex, prevVertex));
|
|
||||||
Vector2 rightV = frVec2Normalize(frVec2Subtract(furthestVertex, nextVertex));
|
|
||||||
|
|
||||||
if (frVec2DotProduct(leftV, v) < frVec2DotProduct(rightV, v)) {
|
|
||||||
result.data[0] = prevVertex;
|
|
||||||
result.data[1] = furthestVertex;
|
|
||||||
} else {
|
|
||||||
result.data[0] = furthestVertex;
|
|
||||||
result.data[1] = nextVertex;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.count = 2;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s1`์์ `s2`๋ก์ ์ถฉ๋ ๋ฐฉํฅ๊ณผ ๊น์ด๋ฅผ ๊ณ์ฐํ๋ค. */
|
|
||||||
static int frGetSeparatingAxisIndex(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2,
|
|
||||||
float *depth
|
|
||||||
) {
|
|
||||||
if (frGetShapeType(s1) != FR_SHAPE_POLYGON || frGetShapeType(s2) != FR_SHAPE_POLYGON)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
int result = -1;
|
|
||||||
|
|
||||||
frVertices normals = frGetPolygonNormals(s1);
|
|
||||||
|
|
||||||
float maxDistance = -FLT_MAX;
|
|
||||||
|
|
||||||
for (int i = 0; i < normals.count; i++) {
|
|
||||||
Vector2 vertex = frVec2Transform(frGetPolygonVertex(s1, i), tx1);
|
|
||||||
Vector2 normal = frVec2RotateTx(normals.data[i], tx1);
|
|
||||||
|
|
||||||
int furthestIndex = frGetPolygonFurthestIndex(s2, tx2, frVec2Negate(normal));
|
|
||||||
Vector2 furthestVertex = frVec2Transform(frGetPolygonVertex(s2, furthestIndex), tx2);
|
|
||||||
|
|
||||||
float distance = frVec2DotProduct(normal, frVec2Subtract(furthestVertex, vertex));
|
|
||||||
|
|
||||||
if (maxDistance < distance) {
|
|
||||||
maxDistance = distance;
|
|
||||||
result = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth != NULL) *depth = maxDistance;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ์ `s1`์์ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionCirclesSAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
) {
|
|
||||||
if (frGetShapeType(s1) != FR_SHAPE_CIRCLE || frGetShapeType(s2) != FR_SHAPE_CIRCLE)
|
|
||||||
return FR_STRUCT_ZERO(frCollision);
|
|
||||||
|
|
||||||
frCollision result = FR_STRUCT_ZERO(frCollision);
|
|
||||||
|
|
||||||
Vector2 diff = frVec2Subtract(tx2.position, tx1.position);
|
|
||||||
|
|
||||||
float radiusSum = frGetCircleRadius(s1) + frGetCircleRadius(s2);
|
|
||||||
float magnitudeSqr = frVec2MagnitudeSqr(diff);
|
|
||||||
|
|
||||||
if (radiusSum * radiusSum < magnitudeSqr) return result;
|
|
||||||
|
|
||||||
float diffMagnitude = sqrtf(magnitudeSqr);
|
|
||||||
|
|
||||||
result.check = true;
|
|
||||||
|
|
||||||
if (diffMagnitude > 0.0f) {
|
|
||||||
result.direction = frVec2ScalarMultiply(diff, 1.0f / diffMagnitude);
|
|
||||||
|
|
||||||
frVertices e = frGetShapeSignificantEdge(s1, tx1, result.direction);
|
|
||||||
|
|
||||||
result.points[0] = result.points[1] = e.data[0];
|
|
||||||
result.depths[0] = result.depths[1] = radiusSum - diffMagnitude;
|
|
||||||
} else {
|
|
||||||
result.direction = (Vector2) { .x = 1.0f };
|
|
||||||
|
|
||||||
frVertices e = frGetShapeSignificantEdge(s1, tx1, result.direction);
|
|
||||||
|
|
||||||
result.points[0] = result.points[1] = e.data[0];
|
|
||||||
result.depths[0] = result.depths[1] = frGetCircleRadius(s1);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.count = 1;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ์ `s1`์์ ๋ค๊ฐํ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionCirclePolySAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
) {
|
|
||||||
if (frGetShapeType(s1) == FR_SHAPE_UNKNOWN || frGetShapeType(s2) == FR_SHAPE_UNKNOWN
|
|
||||||
|| frGetShapeType(s1) == frGetShapeType(s2))
|
|
||||||
return FR_STRUCT_ZERO(frCollision);
|
|
||||||
|
|
||||||
frCollision result = FR_STRUCT_ZERO(frCollision);
|
|
||||||
|
|
||||||
frShape *circle = s1, *polygon = s2;
|
|
||||||
frTransform circleTx = tx1, polygonTx = tx2;
|
|
||||||
|
|
||||||
if (frGetShapeType(s1) == FR_SHAPE_POLYGON && frGetShapeType(s2) == FR_SHAPE_CIRCLE) {
|
|
||||||
circle = s2, polygon = s1;
|
|
||||||
circleTx = tx2, polygonTx = tx1;
|
|
||||||
}
|
|
||||||
|
|
||||||
frVertices vertices = frGetPolygonVertices(polygon);
|
|
||||||
frVertices normals = frGetPolygonNormals(polygon);
|
|
||||||
|
|
||||||
// ๋ค๊ฐํ์ ๊ผญ์ง์ ๊ณผ ๋ฒ์ ๋ฒกํฐ๋ฅผ ๋ณํํ๋ ๋์ , ์์ ์ค์ฌ์ ๋ค๊ฐํ์ ๊ธฐ์ค์ผ๋ก ๋ณํํ๋ค.
|
|
||||||
Vector2 center = frVec2Rotate(
|
|
||||||
frVec2Subtract(circleTx.position, polygonTx.position),
|
|
||||||
-polygonTx.rotation
|
|
||||||
);
|
|
||||||
|
|
||||||
float radius = frGetCircleRadius(circle), maxDistance = -FLT_MAX;
|
|
||||||
int normalIndex = -1;
|
|
||||||
|
|
||||||
// ๋ค๊ฐํ์์ ์์ ์ค์ฌ๊ณผ ๊ฑฐ๋ฆฌ๊ฐ ๊ฐ์ฅ ๊ฐ๊น์ด ๋ณ์ ์ฐพ๋๋ค.
|
|
||||||
for (int i = 0; i < vertices.count; i++) {
|
|
||||||
float distance = frVec2DotProduct(
|
|
||||||
normals.data[i],
|
|
||||||
frVec2Subtract(center, vertices.data[i])
|
|
||||||
);
|
|
||||||
|
|
||||||
// ๊ผญ์ง์ ํ๋๋ผ๋ ์์ ๋ฐ๊นฅ์ชฝ์ ์๋ค๋ฉด, ๊ณ์ฐ์ ์ข
๋ฃํ๋ค.
|
|
||||||
if (distance > radius) return result;
|
|
||||||
|
|
||||||
if (maxDistance < distance) {
|
|
||||||
maxDistance = distance;
|
|
||||||
normalIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ๋ค๊ฐํ์ด ์์ ์ค์ฌ์ ํฌํจํ๊ณ ์๋ค๋ฉด, ๊ณ์ฐ์ ์ข
๋ฃํ๋ค.
|
|
||||||
if (maxDistance < 0.0f) {
|
|
||||||
result.check = true;
|
|
||||||
|
|
||||||
result.direction = frVec2Negate(frVec2RotateTx(normals.data[normalIndex], polygonTx));
|
|
||||||
|
|
||||||
if (frVec2DotProduct(frVec2Subtract(tx2.position, tx1.position), result.direction) < 0.0f)
|
|
||||||
result.direction = frVec2Negate(result.direction);
|
|
||||||
|
|
||||||
result.points[0] = result.points[1] = frVec2Add(
|
|
||||||
circleTx.position,
|
|
||||||
frVec2ScalarMultiply(result.direction, -radius)
|
|
||||||
);
|
|
||||||
|
|
||||||
result.depths[0] = result.depths[1] = radius - maxDistance;
|
|
||||||
|
|
||||||
result.count = 1;
|
|
||||||
} else {
|
|
||||||
Vector2 v1 = (normalIndex > 0)
|
|
||||||
? vertices.data[normalIndex - 1]
|
|
||||||
: vertices.data[vertices.count - 1];
|
|
||||||
Vector2 v2 = vertices.data[normalIndex];
|
|
||||||
|
|
||||||
/*
|
|
||||||
1. ์์ด `v1`์ ํฌํจํ๋ ์ ๋ถ๊ณผ ๋ง๋๋๊ฐ?
|
|
||||||
2. ์์ด `v2`๋ฅผ ํฌํจํ๋ ์ ๋ถ๊ณผ ๋ง๋๋๊ฐ?
|
|
||||||
3. ์์ด `v1`๊ณผ `v2` ์ฌ์ด์ ์ ๋ถ๊ณผ ๋ง๋๋๊ฐ?
|
|
||||||
|
|
||||||
=> ๋ฒกํฐ์ ๋ด์ ์ ํตํด ํ์ธํ ์ ์๋ค.
|
|
||||||
*/
|
|
||||||
Vector2 diff1 = frVec2Subtract(center, v1), diff2 = frVec2Subtract(center, v2);
|
|
||||||
|
|
||||||
float v1Dot = frVec2DotProduct(diff1, frVec2Subtract(v2, v1));
|
|
||||||
float v2Dot = frVec2DotProduct(diff2, frVec2Subtract(v1, v2));
|
|
||||||
|
|
||||||
if (v1Dot <= 0.0f) {
|
|
||||||
float magnitudeSqr = frVec2MagnitudeSqr(diff1);
|
|
||||||
|
|
||||||
if (magnitudeSqr > radius * radius) return result;
|
|
||||||
|
|
||||||
result.direction = frVec2Normalize(frVec2RotateTx(frVec2Negate(diff1), polygonTx));
|
|
||||||
|
|
||||||
if (frVec2DotProduct(frVec2Subtract(tx2.position, tx1.position), result.direction) < 0.0f)
|
|
||||||
result.direction = frVec2Negate(result.direction);
|
|
||||||
|
|
||||||
result.points[0] = result.points[1] = frVec2Transform(v1, polygonTx);
|
|
||||||
result.depths[0] = result.depths[1] = radius - sqrtf(magnitudeSqr);
|
|
||||||
} else if (v2Dot <= 0.0f) {
|
|
||||||
float magnitudeSqr = frVec2MagnitudeSqr(diff2);
|
|
||||||
|
|
||||||
if (magnitudeSqr > radius * radius) return result;
|
|
||||||
|
|
||||||
result.direction = frVec2Normalize(frVec2RotateTx(frVec2Negate(diff2), polygonTx));
|
|
||||||
|
|
||||||
if (frVec2DotProduct(frVec2Subtract(tx2.position, tx1.position), result.direction) < 0.0f)
|
|
||||||
result.direction = frVec2Negate(result.direction);
|
|
||||||
|
|
||||||
result.points[0] = result.points[1] = frVec2Transform(v2, polygonTx);
|
|
||||||
result.depths[0] = result.depths[1] = radius - sqrtf(magnitudeSqr);
|
|
||||||
} else {
|
|
||||||
Vector2 normal = normals.data[normalIndex];
|
|
||||||
|
|
||||||
if (frVec2DotProduct(diff1, normal) > radius) return result;
|
|
||||||
|
|
||||||
result.direction = frVec2Negate(frVec2RotateTx(normal, polygonTx));
|
|
||||||
|
|
||||||
if (frVec2DotProduct(frVec2Subtract(tx2.position, tx1.position), result.direction) < 0.0f)
|
|
||||||
result.direction = frVec2Negate(result.direction);
|
|
||||||
|
|
||||||
result.points[0] = result.points[1] = frVec2Add(
|
|
||||||
circleTx.position,
|
|
||||||
frVec2ScalarMultiply(result.direction, -radius)
|
|
||||||
);
|
|
||||||
|
|
||||||
result.depths[0] = result.depths[1] = radius - maxDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.check = true;
|
|
||||||
|
|
||||||
result.count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ๋ค๊ฐํ `s1`์์ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionPolysSAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
) {
|
|
||||||
if (frGetShapeType(s1) != FR_SHAPE_POLYGON || frGetShapeType(s2) != FR_SHAPE_POLYGON)
|
|
||||||
return FR_STRUCT_ZERO(frCollision);
|
|
||||||
|
|
||||||
frCollision result = FR_STRUCT_ZERO(frCollision);
|
|
||||||
|
|
||||||
float depth1 = FLT_MAX, depth2 = FLT_MAX;
|
|
||||||
|
|
||||||
int index1 = frGetSeparatingAxisIndex(s1, tx1, s2, tx2, &depth1);
|
|
||||||
if (depth1 >= 0.0f) return result;
|
|
||||||
|
|
||||||
int index2 = frGetSeparatingAxisIndex(s2, tx2, s1, tx1, &depth2);
|
|
||||||
if (depth2 >= 0.0f) return result;
|
|
||||||
|
|
||||||
Vector2 direction = (depth1 > depth2)
|
|
||||||
? frVec2RotateTx(frGetPolygonNormal(s1, index1), tx1)
|
|
||||||
: frVec2RotateTx(frGetPolygonNormal(s2, index2), tx2);
|
|
||||||
|
|
||||||
if (frVec2DotProduct(frVec2Subtract(tx2.position, tx1.position), direction) < 0.0f)
|
|
||||||
direction = frVec2Negate(direction);
|
|
||||||
|
|
||||||
result.check = true;
|
|
||||||
|
|
||||||
// ํ์ ๋ณํ์ด ์ ์ฉ๋ ๋ฒกํฐ๋ ํ์ ๋ณํ์ ์ ์ฉํ๊ธฐ ์ ์ ๋ฒกํฐ์ ํฌ๊ธฐ๊ฐ ๊ฐ๋ค.
|
|
||||||
result.direction = direction;
|
|
||||||
|
|
||||||
frVertices e1 = frGetShapeSignificantEdge(s1, tx1, direction);
|
|
||||||
frVertices e2 = frGetShapeSignificantEdge(s2, tx2, frVec2Negate(direction));
|
|
||||||
|
|
||||||
frVertices refEdge = e1, incEdge = e2;
|
|
||||||
frTransform refTx = tx1, incTx = tx2;
|
|
||||||
|
|
||||||
float dot1 = frVec2DotProduct(frVec2Subtract(e1.data[1], e1.data[0]), direction);
|
|
||||||
float dot2 = frVec2DotProduct(frVec2Subtract(e2.data[1], e2.data[0]), direction);
|
|
||||||
|
|
||||||
if (fabsf(dot1) > fabsf(dot2)) {
|
|
||||||
refEdge = e2;
|
|
||||||
incEdge = e1;
|
|
||||||
|
|
||||||
refTx = tx2;
|
|
||||||
incTx = tx1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 refV = frVec2Normalize(frVec2Subtract(refEdge.data[1], refEdge.data[0]));
|
|
||||||
|
|
||||||
float refDot1 = frVec2DotProduct(refEdge.data[0], refV);
|
|
||||||
float refDot2 = frVec2DotProduct(refEdge.data[1], refV);
|
|
||||||
|
|
||||||
incEdge = frClipEdgeWithAxis(incEdge, refV, refDot1);
|
|
||||||
incEdge = frClipEdgeWithAxis(incEdge, frVec2Negate(refV), -refDot2);
|
|
||||||
|
|
||||||
Vector2 refNormal = frVec2RightNormal(refV);
|
|
||||||
|
|
||||||
float max_depth = frVec2DotProduct(refEdge.data[0], refNormal);
|
|
||||||
|
|
||||||
result.points[0] = incEdge.data[0], result.points[1] = incEdge.data[1];
|
|
||||||
|
|
||||||
result.depths[0] = frVec2DotProduct(result.points[0], refNormal) - max_depth;
|
|
||||||
result.depths[1] = frVec2DotProduct(result.points[1], refNormal) - max_depth;
|
|
||||||
|
|
||||||
result.count = 2;
|
|
||||||
|
|
||||||
if (result.depths[0] < 0.0f) {
|
|
||||||
result.points[0] = result.points[1];
|
|
||||||
result.depths[0] = result.depths[1];
|
|
||||||
|
|
||||||
result.count = 1;
|
|
||||||
} else if (result.depths[1] < 0.0f) {
|
|
||||||
result.points[1] = result.points[0];
|
|
||||||
result.depths[1] = result.depths[0];
|
|
||||||
|
|
||||||
result.count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ต์ ํ๋ ๋ถ๋ฆฌ ์ถ ์ ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ, ๋ํ `s1`์์ `s2`๋ก์ ์ถฉ๋์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static frCollision frComputeCollisionSAT(
|
|
||||||
frShape *s1, frTransform tx1,
|
|
||||||
frShape *s2, frTransform tx2
|
|
||||||
) {
|
|
||||||
frShapeType type1 = frGetShapeType(s1);
|
|
||||||
frShapeType type2 = frGetShapeType(s2);
|
|
||||||
|
|
||||||
if (type1 == FR_SHAPE_UNKNOWN || type2 == FR_SHAPE_UNKNOWN)
|
|
||||||
return FR_STRUCT_ZERO(frCollision);
|
|
||||||
else if (type1 == FR_SHAPE_CIRCLE && type2 == FR_SHAPE_CIRCLE)
|
|
||||||
return frComputeCollisionCirclesSAT(s1, tx1, s2, tx2);
|
|
||||||
else if (type1 == FR_SHAPE_CIRCLE && type2 == FR_SHAPE_POLYGON
|
|
||||||
|| type1 == FR_SHAPE_POLYGON && type2 == FR_SHAPE_CIRCLE)
|
|
||||||
return frComputeCollisionCirclePolySAT(s1, tx1, s2, tx2);
|
|
||||||
else if (type1 == FR_SHAPE_POLYGON && type2 == FR_SHAPE_POLYGON)
|
|
||||||
return frComputeCollisionPolysSAT(s1, tx1, s2, tx2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ ๋ถ `e`์์ ๋ฒกํฐ `v`์์ ๋ด์ ์ด `minDot`๋ณด๋ค ์์ ๋ถ๋ถ์ ๋ชจ๋ ์๋ผ๋ธ๋ค. */
|
|
||||||
static frVertices frClipEdgeWithAxis(frVertices e, Vector2 v, float minDot) {
|
|
||||||
frVertices result = FR_STRUCT_ZERO(frVertices);
|
|
||||||
|
|
||||||
float p1Dot = frVec2DotProduct(e.data[0], v) - minDot;
|
|
||||||
float p2Dot = frVec2DotProduct(e.data[1], v) - minDot;
|
|
||||||
|
|
||||||
bool insideP1 = (p1Dot >= 0.0f), insideP2 = (p2Dot >= 0.0f);
|
|
||||||
|
|
||||||
if (insideP1 && insideP2) {
|
|
||||||
result.data[0] = e.data[0];
|
|
||||||
result.data[1] = e.data[1];
|
|
||||||
|
|
||||||
result.count = 2;
|
|
||||||
} else {
|
|
||||||
Vector2 edgeV = frVec2Subtract(e.data[1], e.data[0]);
|
|
||||||
|
|
||||||
const float distance = p1Dot / (p1Dot - p2Dot);
|
|
||||||
|
|
||||||
Vector2 midpoint = frVec2Add(
|
|
||||||
e.data[0],
|
|
||||||
frVec2ScalarMultiply(edgeV, distance)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (insideP1 && !insideP2) {
|
|
||||||
result.data[0] = e.data[0];
|
|
||||||
result.data[1] = midpoint;
|
|
||||||
|
|
||||||
result.count = 2;
|
|
||||||
} else if (!insideP1 && insideP2) {
|
|
||||||
result.data[0] = e.data[1];
|
|
||||||
result.data[1] = midpoint;
|
|
||||||
|
|
||||||
result.count = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* `o1`์์ `v1` ๋ฐฉํฅ์ผ๋ก ์งํํ๋ ๊ด์ ์ด `o2`์์ `v2` ๋ฐฉํฅ์ผ๋ก ์งํํ๋ ๊ด์ ๊ณผ ๋ง๋๋์ง ๊ณ์ฐํ๋ค. */
|
|
||||||
static bool frComputeIntersectionRays(
|
|
||||||
Vector2 o1, Vector2 v1,
|
|
||||||
Vector2 o2, Vector2 v2,
|
|
||||||
float *distance
|
|
||||||
) {
|
|
||||||
bool result = true;
|
|
||||||
|
|
||||||
float cross = frVec2CrossProduct(v1, v2);
|
|
||||||
|
|
||||||
// ๋ ๊ด์ ์ ํํํ๊ฑฐ๋ ์ผ์ง์ ์์ ์๋ค.
|
|
||||||
if (frNumberApproxEquals(cross, 0.0f)) {
|
|
||||||
*distance = 0.0f;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2 diff = frVec2Subtract(o2, o1);
|
|
||||||
|
|
||||||
float inverseCross = 1.0f / cross;
|
|
||||||
|
|
||||||
float t = frVec2CrossProduct(diff, v2) * inverseCross;
|
|
||||||
float u = frVec2CrossProduct(diff, v1) * inverseCross;
|
|
||||||
|
|
||||||
// ๋ ๊ด์ ์ ํํํ์ง ์์ผ๋ฉด์ ์๋ก ๋ง๋์ง ์๋๋ค.
|
|
||||||
if (t < 0.0f || u < 0.0f || u > 1.0f) result = false;
|
|
||||||
|
|
||||||
*distance = t;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* `o`์์ `v` ๋ฐฉํฅ์ผ๋ก ์งํํ๋ ๊ด์ ์ด ์ค์ฌ์ ์ด `c`์ด๊ณ ๋ฐ์ง๋ฆ์ด `r`์ธ ์๊ณผ ๋ง๋๋์ง ๊ณ์ฐํ๋ค. */
|
|
||||||
static bool frComputeIntersectionRayCircle(
|
|
||||||
Vector2 o, Vector2 v,
|
|
||||||
Vector2 c, float r,
|
|
||||||
float *distance
|
|
||||||
) {
|
|
||||||
bool result = true;
|
|
||||||
|
|
||||||
Vector2 diff = frVec2Subtract(c, o);
|
|
||||||
|
|
||||||
float dot = frVec2DotProduct(diff, v);
|
|
||||||
|
|
||||||
float heightSqr = frVec2MagnitudeSqr(diff) - (dot * dot);
|
|
||||||
float baseSqr = (r * r) - heightSqr;
|
|
||||||
|
|
||||||
// ๊ด์ ๊ณผ ์์ ์๋ก ๋ง๋์ง ์๋๋ค.
|
|
||||||
if (dot < 0.0f || baseSqr < 0.0f) result = false;
|
|
||||||
|
|
||||||
*distance = dot - sqrtf(baseSqr);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,195 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
/* | `debug` ๋ชจ๋ ๋ณ์... | */
|
|
||||||
|
|
||||||
static const float ARROW_HEAD_LENGTH = 16.0f;
|
|
||||||
|
|
||||||
/* | `debug` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
#ifndef FEROX_STANDALONE
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๋ค๊ฐํ ๊ผญ์ง์ ๋ฐฐ์ด์ ์ธ๊ณ ๊ธฐ์ค์ ํฝ์
์ขํ ๋ฐฐ์ด๋ก ๋ณํํ๋ค. */
|
|
||||||
static frVertices frGetWorldVerticesInPixels(frBody *b);
|
|
||||||
|
|
||||||
/* ๊ฒ์ ํ๋ฉด์ ์ `p1`์์ `p2`๋ก ํฅํ๋ ํ์ดํ๋ฅผ ๊ทธ๋ฆฐ๋ค. */
|
|
||||||
void frDrawArrow(Vector2 p1, Vector2 p2, float thick, Color color) {
|
|
||||||
if (thick <= 0.0f) return;
|
|
||||||
|
|
||||||
p1 = frVec2MetersToPixels(p1);
|
|
||||||
p2 = frVec2MetersToPixels(p2);
|
|
||||||
|
|
||||||
Vector2 diff = frVec2Normalize(frVec2Subtract(p1, p2));
|
|
||||||
|
|
||||||
Vector2 normal1 = frVec2LeftNormal(diff);
|
|
||||||
Vector2 normal2 = frVec2RightNormal(diff);
|
|
||||||
|
|
||||||
Vector2 h1 = frVec2Add(
|
|
||||||
p2,
|
|
||||||
frVec2ScalarMultiply(
|
|
||||||
frVec2Normalize(frVec2Add(diff, normal1)),
|
|
||||||
ARROW_HEAD_LENGTH
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
Vector2 h2 = frVec2Add(
|
|
||||||
p2,
|
|
||||||
frVec2ScalarMultiply(
|
|
||||||
frVec2Normalize(frVec2Add(diff, normal2)),
|
|
||||||
ARROW_HEAD_LENGTH
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
DrawLineEx(p1, p2, thick, color);
|
|
||||||
DrawLineEx(p2, h1, thick, color);
|
|
||||||
DrawLineEx(p2, h2, thick, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฒ์ ํ๋ฉด์ ๊ฐ์ฒด `b`์ ๋ํ์ ๊ทธ๋ฆฐ๋ค. */
|
|
||||||
void frDrawBody(frBody *b, Color color) {
|
|
||||||
if (b == NULL) return;
|
|
||||||
|
|
||||||
frShape *s = frGetBodyShape(b);
|
|
||||||
|
|
||||||
if (frGetShapeType(s) == FR_SHAPE_CIRCLE) {
|
|
||||||
DrawCircleV(
|
|
||||||
frVec2MetersToPixels(frGetBodyPosition(b)),
|
|
||||||
frNumberMetersToPixels(frGetCircleRadius(s)),
|
|
||||||
color
|
|
||||||
);
|
|
||||||
} else if (frGetShapeType(s) == FR_SHAPE_POLYGON) {
|
|
||||||
frVertices worldVertices = frGetWorldVerticesInPixels(b);
|
|
||||||
|
|
||||||
DrawTriangleFan(worldVertices.data, worldVertices.count, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฒ์ ํ๋ฉด์ ๊ฐ์ฒด `b`์ ๋ํ ํ
๋๋ฆฌ๋ฅผ ๊ทธ๋ฆฐ๋ค. */
|
|
||||||
void frDrawBodyLines(frBody *b, float thick, Color color) {
|
|
||||||
if (b == NULL || thick <= 0.0f) return;
|
|
||||||
|
|
||||||
frShape *s = frGetBodyShape(b);
|
|
||||||
|
|
||||||
if (frGetShapeType(s) == FR_SHAPE_CIRCLE) {
|
|
||||||
Vector2 p = frGetBodyPosition(b);
|
|
||||||
|
|
||||||
DrawRing(
|
|
||||||
frVec2MetersToPixels(p),
|
|
||||||
frNumberMetersToPixels(frGetCircleRadius(s)) - thick,
|
|
||||||
frNumberMetersToPixels(frGetCircleRadius(s)),
|
|
||||||
0.0f,
|
|
||||||
360.0f,
|
|
||||||
FR_DEBUG_CIRCLE_SEGMENT_COUNT,
|
|
||||||
color
|
|
||||||
);
|
|
||||||
} else if (frGetShapeType(s) == FR_SHAPE_POLYGON) {
|
|
||||||
frVertices worldVertices = frGetWorldVerticesInPixels(b);
|
|
||||||
|
|
||||||
for (int j = worldVertices.count - 1, i = 0; i < worldVertices.count; j = i, i++)
|
|
||||||
DrawLineEx(worldVertices.data[j], worldVertices.data[i], thick, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฒ์ ํ๋ฉด์ ๊ฐ์ฒด `b`์ AABB์ ์ง๋ ์ค์ฌ์ ๊ทธ๋ฆฐ๋ค. */
|
|
||||||
void frDrawBodyAABB(frBody *b, float thick, Color color) {
|
|
||||||
if (b == NULL || thick <= 0.0f) return;
|
|
||||||
|
|
||||||
Rectangle aabb = frGetBodyAABB(b);
|
|
||||||
|
|
||||||
DrawRectangleLinesEx(
|
|
||||||
(Rectangle) {
|
|
||||||
frNumberMetersToPixels(aabb.x),
|
|
||||||
frNumberMetersToPixels(aabb.y),
|
|
||||||
frNumberMetersToPixels(aabb.width),
|
|
||||||
frNumberMetersToPixels(aabb.height)
|
|
||||||
},
|
|
||||||
thick,
|
|
||||||
color
|
|
||||||
);
|
|
||||||
|
|
||||||
DrawCircleV(frVec2MetersToPixels(frGetBodyPosition(b)), 2.0f, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฒ์ ํ๋ฉด์ ๊ฐ์ฒด `b`์ ๋ฌผ๋ฆฌ๋ ์ ๋ณด๋ฅผ ๊ทธ๋ฆฐ๋ค. */
|
|
||||||
void frDrawBodyProperties(frBody *b, Color color) {
|
|
||||||
Vector2 velocity = frGetBodyVelocity(b);
|
|
||||||
|
|
||||||
frTransform tx = frGetBodyTransform(b);
|
|
||||||
|
|
||||||
DrawTextEx(
|
|
||||||
GetFontDefault(),
|
|
||||||
TextFormat(
|
|
||||||
"m: %fkg, I: %fkg*mยฒ\n"
|
|
||||||
"(x, y) [theta]: (%f, %f) [%f rad.]\n"
|
|
||||||
"v [omega]: (%f, %f) [%f rad.]\n",
|
|
||||||
frGetBodyMass(b), frGetBodyInertia(b),
|
|
||||||
tx.position.x, tx.position.y, tx.rotation,
|
|
||||||
velocity.x, velocity.y, frGetBodyAngularVelocity(b)
|
|
||||||
),
|
|
||||||
frVec2MetersToPixels(frVec2Add(tx.position, (Vector2) { 1.0f, 1.0f })),
|
|
||||||
10.0f,
|
|
||||||
1.0f,
|
|
||||||
color
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฒ์ ํ๋ฉด์ ๊ณต๊ฐ ํด์๋งต `hm`์ ๊ทธ๋ฆฐ๋ค. */
|
|
||||||
void frDrawSpatialHash(frSpatialHash *hm, float thick, Color color) {
|
|
||||||
if (hm == NULL || thick <= 0.0f) return;
|
|
||||||
|
|
||||||
Rectangle bounds = frGetSpatialHashBounds(hm);
|
|
||||||
|
|
||||||
const int vCount = bounds.width * FR_BROADPHASE_INVERSE_CELL_SIZE;
|
|
||||||
const int hCount = bounds.height * FR_BROADPHASE_INVERSE_CELL_SIZE;
|
|
||||||
|
|
||||||
for (int i = 0; i <= vCount; i++)
|
|
||||||
DrawLineEx(
|
|
||||||
frVec2MetersToPixels((Vector2) { FR_BROADPHASE_CELL_SIZE * i, 0.0f }),
|
|
||||||
frVec2MetersToPixels((Vector2) { FR_BROADPHASE_CELL_SIZE * i, bounds.height }),
|
|
||||||
thick,
|
|
||||||
color
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int i = 0; i <= hCount; i++)
|
|
||||||
DrawLineEx(
|
|
||||||
frVec2MetersToPixels((Vector2) { 0.0f, FR_BROADPHASE_CELL_SIZE * i }),
|
|
||||||
frVec2MetersToPixels((Vector2) { bounds.width, FR_BROADPHASE_CELL_SIZE * i }),
|
|
||||||
thick,
|
|
||||||
color
|
|
||||||
);
|
|
||||||
|
|
||||||
DrawRectangleLinesEx(frRecMetersToPixels(bounds), thick, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๋ค๊ฐํ ๊ผญ์ง์ ๋ฐฐ์ด์ ์ธ๊ณ ๊ธฐ์ค์ ํฝ์
์ขํ ๋ฐฐ์ด๋ก ๋ณํํ๋ค. */
|
|
||||||
static frVertices frGetWorldVerticesInPixels(frBody *b) {
|
|
||||||
if (b == NULL) return FR_STRUCT_ZERO(frVertices);
|
|
||||||
|
|
||||||
frVertices vertices = frGetPolygonVertices(frGetBodyShape(b));
|
|
||||||
|
|
||||||
for (int i = 0; i < vertices.count; i++)
|
|
||||||
vertices.data[i] = frVec2MetersToPixels(frGetWorldPoint(b, vertices.data[i]));
|
|
||||||
|
|
||||||
return vertices;
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,529 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
/* | `dynamics` ๋ชจ๋ ์๋ฃํ ์ ์... | */
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด์ ๋ฌผ๋ฆฌ๋์ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด. */
|
|
||||||
typedef struct frMotionData {
|
|
||||||
float mass;
|
|
||||||
float inverseMass;
|
|
||||||
float inertia;
|
|
||||||
float inverseInertia;
|
|
||||||
Vector2 velocity;
|
|
||||||
float angularVelocity;
|
|
||||||
float gravityScale;
|
|
||||||
Vector2 force;
|
|
||||||
float torque;
|
|
||||||
} frMotionData;
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด๋ฅผ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด. */
|
|
||||||
typedef struct frBody {
|
|
||||||
frBodyType type;
|
|
||||||
frBodyFlags flags;
|
|
||||||
frMaterial material;
|
|
||||||
frMotionData motion;
|
|
||||||
frTransform tx;
|
|
||||||
frShape *shape;
|
|
||||||
Rectangle aabb;
|
|
||||||
void *data;
|
|
||||||
} frBody;
|
|
||||||
|
|
||||||
/* | `dynamics` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ง๋์ ๋ค์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static void frResetBodyMass(frBody *b);
|
|
||||||
|
|
||||||
/* ์ข
๋ฅ๊ฐ `type`์ด๊ณ ์์น๊ฐ `p`์ธ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค. */
|
|
||||||
frBody *frCreateBody(frBodyType type, frBodyFlags flags, Vector2 p) {
|
|
||||||
if (type == FR_BODY_UNKNOWN) return NULL;
|
|
||||||
|
|
||||||
frBody *result = calloc(1, sizeof(*result));
|
|
||||||
|
|
||||||
result->material = FR_STRUCT_ZERO(frMaterial);
|
|
||||||
|
|
||||||
frSetBodyType(result, type);
|
|
||||||
frSetBodyFlags(result, flags);
|
|
||||||
frSetBodyGravityScale(result, 1.0f);
|
|
||||||
frSetBodyPosition(result, p);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ข
๋ฅ๊ฐ `type`์ด๊ณ ์์น๊ฐ `p`์ด๋ฉฐ ์ถฉ๋ ์ฒ๋ฆฌ์ฉ ๋ํ์ด `s`์ธ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค. */
|
|
||||||
frBody *frCreateBodyFromShape(frBodyType type, frBodyFlags flags, Vector2 p, frShape *s) {
|
|
||||||
if (type == FR_BODY_UNKNOWN) return NULL;
|
|
||||||
|
|
||||||
frBody *result = frCreateBody(type, flags, p);
|
|
||||||
frAttachShapeToBody(result, s);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํ๋ค. */
|
|
||||||
void frReleaseBody(frBody *b) {
|
|
||||||
if (b != NULL && b->shape != NULL)
|
|
||||||
frDetachShapeFromBody(b);
|
|
||||||
|
|
||||||
free(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b์ ์ถฉ๋ ์ฒ๋ฆฌ์ฉ ๋ํ `s`๋ฅผ ์ถ๊ฐํ๋ค. */
|
|
||||||
void frAttachShapeToBody(frBody *b, frShape *s) {
|
|
||||||
if (b == NULL || s == NULL) return;
|
|
||||||
|
|
||||||
b->shape = s;
|
|
||||||
b->material = frGetShapeMaterial(s);
|
|
||||||
b->aabb = frGetShapeAABB(b->shape, b->tx);
|
|
||||||
|
|
||||||
frResetBodyMass(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์์ ์ถฉ๋ ์ฒ๋ฆฌ์ฉ ๋ํ์ ์ ๊ฑฐํ๋ค. */
|
|
||||||
void frDetachShapeFromBody(frBody *b) {
|
|
||||||
if (b == NULL) return;
|
|
||||||
|
|
||||||
b->shape = NULL;
|
|
||||||
b->material = FR_STRUCT_ZERO(frMaterial);
|
|
||||||
b->aabb = FR_STRUCT_ZERO(Rectangle);
|
|
||||||
|
|
||||||
frResetBodyMass(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ตฌ์กฐ์ฒด `frBody`์ ํฌ๊ธฐ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
size_t frGetBodyStructSize(void) {
|
|
||||||
return sizeof(frBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ข
๋ฅ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
frBodyType frGetBodyType(frBody *b) {
|
|
||||||
return (b != NULL) ? b->type : FR_BODY_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๋นํธ ํ๋๊ทธ ์กฐํฉ์ ๋ฐํํ๋ค. */
|
|
||||||
frBodyFlags frGetBodyFlags(frBody *b) {
|
|
||||||
return (b != NULL) ? b->flags : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ฌ์ง์ ๋ฐํํ๋ค. */
|
|
||||||
frMaterial frGetBodyMaterial(frBody *b) {
|
|
||||||
return (b != NULL) ? b->material : FR_STRUCT_ZERO(frMaterial);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ง๋์ ๋ฐํํ๋ค. */
|
|
||||||
float frGetBodyMass(frBody *b) {
|
|
||||||
return (b != NULL) ? b->motion.mass : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ง๋์ ์ญ์๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetBodyInverseMass(frBody *b) {
|
|
||||||
return (b != NULL) ? b->motion.inverseMass : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๊ด์ฑ ๋ชจ๋ฉํธ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetBodyInertia(frBody *b) {
|
|
||||||
return (b != NULL) ? b->motion.inertia : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๊ด์ฑ ๋ชจ๋ฉํธ์ ์ญ์๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetBodyInverseInertia(frBody *b) {
|
|
||||||
return (b != NULL) ? b->motion.inverseInertia : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์๋๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Vector2 frGetBodyVelocity(frBody *b) {
|
|
||||||
return (b != NULL) ? b->motion.velocity : FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๊ฐ์๋๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetBodyAngularVelocity(frBody *b) {
|
|
||||||
return (b != NULL) ? b->motion.angularVelocity : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ค๋ ฅ ๊ฐ์๋ฅ ์ ๋ฐํํ๋ค. */
|
|
||||||
float frGetBodyGravityScale(frBody *b) {
|
|
||||||
return (b != NULL) ? b->motion.gravityScale : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์์น์ ํ์ ๊ฐ๋ (๋จ์: rad.)๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
frTransform frGetBodyTransform(frBody *b) {
|
|
||||||
return (b != NULL) ? b->tx : FR_STRUCT_ZERO(frTransform);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์์น๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Vector2 frGetBodyPosition(frBody *b) {
|
|
||||||
return (b != NULL) ? b->tx.position : FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ํ์ ๊ฐ๋ (๋จ์: rad.)๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetBodyRotation(frBody *b) {
|
|
||||||
return (b != NULL) ? b->tx.rotation : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ถฉ๋ ์ฒ๋ฆฌ์ฉ ๋ํ์ ๋ฐํํ๋ค. */
|
|
||||||
frShape *frGetBodyShape(frBody *b) {
|
|
||||||
return (b != NULL) ? b->shape : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ AABB๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Rectangle frGetBodyAABB(frBody *b) {
|
|
||||||
return (b != NULL && b->shape != NULL) ? b->aabb : FR_STRUCT_ZERO(Rectangle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
void *frGetBodyUserData(frBody *b) {
|
|
||||||
return (b != NULL) ? b->data : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ ๊ธฐ์ค ์ขํ `p`๋ฅผ ๊ฐ์ฒด `b`๋ฅผ ๊ธฐ์ค์ผ๋ก ํ ์ขํ๋ก ๋ณํํ๋ค. */
|
|
||||||
Vector2 frGetLocalPoint(frBody *b, Vector2 p) {
|
|
||||||
return frVec2Transform(p, (frTransform) { frVec2Negate(b->tx.position), -(b->tx.rotation)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`๋ฅผ ๊ธฐ์ค์ผ๋ก ํ ์ขํ `p`๋ฅผ ์ธ๊ณ ๊ธฐ์ค ์ขํ๋ก ๋ณํํ๋ค. */
|
|
||||||
Vector2 frGetWorldPoint(frBody *b, Vector2 p) {
|
|
||||||
return frVec2Transform(p, b->tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ข
๋ฅ๋ฅผ `type`์ผ๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyType(frBody *b, frBodyType type) {
|
|
||||||
if (b == NULL || b->type == FR_BODY_UNKNOWN || b->type == type)
|
|
||||||
return;
|
|
||||||
|
|
||||||
b->type = type;
|
|
||||||
|
|
||||||
frResetBodyMass(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๋นํธ ํ๋๊ทธ ์กฐํฉ์ `flags`์ผ๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyFlags(frBody *b, frBodyFlags flags) {
|
|
||||||
if (b == NULL || b->flags == flags) return;
|
|
||||||
|
|
||||||
b->flags = flags;
|
|
||||||
|
|
||||||
frResetBodyMass(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์๋๋ฅผ `v`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyVelocity(frBody *b, Vector2 v) {
|
|
||||||
if (b != NULL) b->motion.velocity = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ๊ฐ์๋๋ฅผ `a`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyAngularVelocity(frBody *b, double a) {
|
|
||||||
if (b != NULL) b->motion.angularVelocity = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ค๋ ฅ ๊ฐ์๋ฅ ์ `scale`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyGravityScale(frBody *b, float scale) {
|
|
||||||
if (b == NULL || b->type != FR_BODY_DYNAMIC) return;
|
|
||||||
|
|
||||||
b->motion.gravityScale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์์น์ ํ์ ๊ฐ๋๋ฅผ `tx`์ ๊ฐ์ผ๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyTransform(frBody *b, frTransform tx) {
|
|
||||||
if (b == NULL) return;
|
|
||||||
|
|
||||||
b->tx.position = tx.position;
|
|
||||||
b->tx.rotation = frNormalizeAngle(tx.rotation, PI);
|
|
||||||
|
|
||||||
b->tx.cache.valid = true;
|
|
||||||
b->tx.cache.sinA = sinf(b->tx.rotation);
|
|
||||||
b->tx.cache.cosA = cosf(b->tx.rotation);
|
|
||||||
|
|
||||||
b->aabb = frGetShapeAABB(b->shape, b->tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์์น๋ฅผ `p`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyPosition(frBody *b, Vector2 p) {
|
|
||||||
if (b == NULL) return;
|
|
||||||
|
|
||||||
b->tx.position = p;
|
|
||||||
|
|
||||||
b->aabb = frGetShapeAABB(b->shape, b->tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ํ์ ๊ฐ๋ (๋จ์: rad.)๋ฅผ `rotation`์ผ๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyRotation(frBody *b, float rotation) {
|
|
||||||
if (b == NULL) return;
|
|
||||||
|
|
||||||
b->tx.rotation = frNormalizeAngle(rotation, PI);
|
|
||||||
|
|
||||||
b->tx.cache.valid = true;
|
|
||||||
b->tx.cache.sinA = sinf(b->tx.rotation);
|
|
||||||
b->tx.cache.cosA = cosf(b->tx.rotation);
|
|
||||||
|
|
||||||
b->aabb = frGetShapeAABB(b->shape, b->tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ `data`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetBodyUserData(frBody *b, void *data) {
|
|
||||||
if (b != NULL) b->data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ค๋ ฅ ๊ฐ์๋ `gravity`๋ฅผ ์ ์ฉํ๋ค. */
|
|
||||||
void frApplyGravity(frBody *b, Vector2 gravity) {
|
|
||||||
if (b == NULL || b->motion.inverseMass <= 0.0f) return;
|
|
||||||
|
|
||||||
b->motion.force = frVec2Add(
|
|
||||||
b->motion.force,
|
|
||||||
frVec2ScalarMultiply(
|
|
||||||
gravity,
|
|
||||||
b->motion.gravityScale * b->motion.mass
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ถฉ๊ฒฉ๋ `impulse`๋ฅผ ์ ์ฉํ๋ค. */
|
|
||||||
void frApplyImpulse(frBody *b, Vector2 impulse) {
|
|
||||||
if (b == NULL || b->motion.inverseMass <= 0.0f) return;
|
|
||||||
|
|
||||||
b->motion.velocity = frVec2Add(
|
|
||||||
b->motion.velocity,
|
|
||||||
frVec2ScalarMultiply(
|
|
||||||
impulse,
|
|
||||||
b->motion.inverseMass
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b` ์์ ์ `p`์ ๊ฐ์ด๋๋ `impulse`๋ฅผ ์ ์ฉํ๋ค. */
|
|
||||||
void frApplyTorqueImpulse(frBody *b, Vector2 p, Vector2 impulse) {
|
|
||||||
if (b == NULL || b->motion.inverseInertia <= 0.0f) return;
|
|
||||||
|
|
||||||
b->motion.angularVelocity += b->motion.inverseInertia
|
|
||||||
* frVec2CrossProduct(p, impulse);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์์ฉํ๋ ๋ชจ๋ ํ์ ์ ๊ฑฐํ๋ค. */
|
|
||||||
void frClearBodyForces(frBody *b) {
|
|
||||||
if (b == NULL) return;
|
|
||||||
|
|
||||||
b->motion.force = FR_STRUCT_ZERO(Vector2);
|
|
||||||
b->motion.torque = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋จ์ ์๊ฐ `dt` ์ดํ์ ๊ฐ์ฒด `b`์ ์์น๋ฅผ ๊ณ์ฐํ๋ค. */
|
|
||||||
void frIntegrateForBodyPosition(frBody *b, double dt) {
|
|
||||||
if (b == NULL || b->type == FR_BODY_STATIC) return;
|
|
||||||
|
|
||||||
frSetBodyPosition(b, frVec2Add(b->tx.position, frVec2ScalarMultiply(b->motion.velocity, dt)));
|
|
||||||
frSetBodyRotation(b, b->tx.rotation + (b->motion.angularVelocity * dt));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋จ์ ์๊ฐ `dt` ์ดํ์ ๊ฐ์ฒด `b`์ ์๋์ ๊ฐ์๋๋ฅผ ๊ณ์ฐํ๋ค. */
|
|
||||||
void frIntegrateForBodyVelocities(frBody *b, double dt) {
|
|
||||||
if (b == NULL || b->motion.inverseMass <= 0.0f) return;
|
|
||||||
|
|
||||||
b->motion.velocity = frVec2Add(
|
|
||||||
b->motion.velocity,
|
|
||||||
frVec2ScalarMultiply(
|
|
||||||
b->motion.force,
|
|
||||||
b->motion.inverseMass * dt
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
b->motion.angularVelocity += (b->motion.torque * b->motion.inverseInertia) * dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b1`๊ณผ `b2` ์ฌ์ด์ ์ถฉ๋์ ํด๊ฒฐํ๋ค. */
|
|
||||||
void frResolveCollision(frCollision *collision) {
|
|
||||||
if (collision == NULL || !collision->check) return;
|
|
||||||
|
|
||||||
frBody *b1 = collision->cache.bodies[0];
|
|
||||||
frBody *b2 = collision->cache.bodies[1];
|
|
||||||
|
|
||||||
if (b1 == NULL || b2 == NULL) return;
|
|
||||||
|
|
||||||
if (b1->motion.inverseMass + b2->motion.inverseMass <= 0.0f) {
|
|
||||||
if (frGetBodyType(b1) == FR_BODY_STATIC) b1->motion.velocity = FR_STRUCT_ZERO(Vector2);
|
|
||||||
if (frGetBodyType(b2) == FR_BODY_STATIC) b2->motion.velocity = FR_STRUCT_ZERO(Vector2);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float epsilon = fmaxf(0.0f, fminf(b1->material.restitution, b2->material.restitution));
|
|
||||||
|
|
||||||
float staticMu = b1->material.staticFriction * b2->material.staticFriction;
|
|
||||||
float dynamicMu = b1->material.dynamicFriction * b2->material.dynamicFriction;
|
|
||||||
|
|
||||||
// ๋์ฒด๋ก ์ด๋ ๋ง์ฐฐ ๊ณ์๋ ์ ์ง ๋ง์ฐฐ ๊ณ์๋ณด๋ค ์์ ๊ฐ์ ๊ฐ์ง๋ค.
|
|
||||||
if (staticMu < dynamicMu) dynamicMu = FR_DYNAMICS_DYNAMIC_FRICTION_MULTIPLIER * staticMu;
|
|
||||||
|
|
||||||
for (int i = 0; i < collision->count; i++) {
|
|
||||||
Vector2 r1 = frVec2Subtract(collision->points[i], frGetBodyPosition(b1));
|
|
||||||
Vector2 r2 = frVec2Subtract(collision->points[i], frGetBodyPosition(b2));
|
|
||||||
|
|
||||||
Vector2 r1Normal = frVec2LeftNormal(r1);
|
|
||||||
Vector2 r2Normal = frVec2LeftNormal(r2);
|
|
||||||
|
|
||||||
// `b1`์ด ์ธก์ ํ `b2`์ ์๋ (`b1`์ ๋ํ `b2`์ ์๋ ์๋)๋ฅผ ๊ณ์ฐํ๋ค.
|
|
||||||
Vector2 relativeVelocity = frVec2Subtract(
|
|
||||||
frVec2Add(
|
|
||||||
b2->motion.velocity,
|
|
||||||
frVec2ScalarMultiply(r2Normal, b2->motion.angularVelocity)
|
|
||||||
),
|
|
||||||
frVec2Add(
|
|
||||||
b1->motion.velocity,
|
|
||||||
frVec2ScalarMultiply(r1Normal, b1->motion.angularVelocity)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
float relativeVelocityDot = frVec2DotProduct(relativeVelocity, collision->direction);
|
|
||||||
|
|
||||||
// ๋ ๊ฐ์ฒด๊ฐ ์๋ก ์ถฉ๋ํ๋ ๋ฐฉํฅ์ผ๋ก ์งํํ๊ณ ์์ง ์์ผ๋ฉด ๊ณ์ฐ์ ์ข
๋ฃํ๋ค.
|
|
||||||
if (relativeVelocityDot > 0.0f) return;
|
|
||||||
|
|
||||||
float r1NormalDot = frVec2DotProduct(r1Normal, collision->direction);
|
|
||||||
float r2NormalDot = frVec2DotProduct(r2Normal, collision->direction);
|
|
||||||
|
|
||||||
float inverseMassSum = (b1->motion.inverseMass + b2->motion.inverseMass)
|
|
||||||
+ b1->motion.inverseInertia * (r1NormalDot * r1NormalDot)
|
|
||||||
+ b2->motion.inverseInertia * (r2NormalDot * r2NormalDot);
|
|
||||||
|
|
||||||
float impulseParam = (-(1.0f + epsilon) * relativeVelocityDot)
|
|
||||||
/ (collision->count * inverseMassSum);
|
|
||||||
|
|
||||||
Vector2 impulse = frVec2ScalarMultiply(collision->direction, impulseParam);
|
|
||||||
|
|
||||||
frApplyImpulse(b1, frVec2Negate(impulse));
|
|
||||||
frApplyTorqueImpulse(b1, r1, frVec2Negate(impulse));
|
|
||||||
|
|
||||||
frApplyImpulse(b2, impulse);
|
|
||||||
frApplyTorqueImpulse(b2, r2, impulse);
|
|
||||||
|
|
||||||
// ๋ง์ฐฐ๋ ฅ ์ ์ฉ์ ์ํด ์๋ ์๋๋ฅผ ๋ค์ ๊ณ์ฐํ๋ค.
|
|
||||||
relativeVelocity = frVec2Subtract(
|
|
||||||
frVec2Add(
|
|
||||||
b2->motion.velocity,
|
|
||||||
frVec2ScalarMultiply(r2Normal, b2->motion.angularVelocity)
|
|
||||||
),
|
|
||||||
frVec2Add(
|
|
||||||
b1->motion.velocity,
|
|
||||||
frVec2ScalarMultiply(r1Normal, b1->motion.angularVelocity)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
relativeVelocityDot = frVec2DotProduct(relativeVelocity, collision->direction);
|
|
||||||
|
|
||||||
Vector2 tangent = frVec2Normalize(
|
|
||||||
frVec2Subtract(
|
|
||||||
relativeVelocity,
|
|
||||||
frVec2ScalarMultiply(collision->direction, relativeVelocityDot)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
r1NormalDot = frVec2DotProduct(r1Normal, tangent);
|
|
||||||
r2NormalDot = frVec2DotProduct(r2Normal, tangent);
|
|
||||||
|
|
||||||
inverseMassSum = (b1->motion.inverseMass + b2->motion.inverseMass)
|
|
||||||
+ b1->motion.inverseInertia * (r1NormalDot * r1NormalDot)
|
|
||||||
+ b2->motion.inverseInertia * (r2NormalDot * r2NormalDot);
|
|
||||||
|
|
||||||
float frictionParam = -frVec2DotProduct(relativeVelocity, tangent)
|
|
||||||
/ (collision->count * inverseMassSum);
|
|
||||||
|
|
||||||
Vector2 friction = (fabsf(frictionParam) < impulseParam * staticMu)
|
|
||||||
? frVec2ScalarMultiply(tangent, frictionParam)
|
|
||||||
: frVec2ScalarMultiply(tangent, -impulseParam * dynamicMu);
|
|
||||||
|
|
||||||
frApplyImpulse(b1, frVec2Negate(friction));
|
|
||||||
frApplyTorqueImpulse(b1, r1, frVec2Negate(friction));
|
|
||||||
|
|
||||||
frApplyImpulse(b2, friction);
|
|
||||||
frApplyTorqueImpulse(b2, r2, friction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b1`๊ณผ `b2`์ ์์น๋ฅผ ์ ์ ํ๊ฒ ๋ณด์ ํ๋ค. */
|
|
||||||
void frCorrectBodyPositions(frCollision *collision, float inverseDt) {
|
|
||||||
if (collision == NULL || !collision->check) return;
|
|
||||||
|
|
||||||
frBody *b1 = collision->cache.bodies[0];
|
|
||||||
frBody *b2 = collision->cache.bodies[1];
|
|
||||||
|
|
||||||
if (b1 == NULL || b2 == NULL) return;
|
|
||||||
|
|
||||||
if (b1->motion.inverseMass + b2->motion.inverseMass <= 0.0f) {
|
|
||||||
if (frGetBodyType(b1) == FR_BODY_STATIC) b1->motion.velocity = FR_STRUCT_ZERO(Vector2);
|
|
||||||
if (frGetBodyType(b2) == FR_BODY_STATIC) b2->motion.velocity = FR_STRUCT_ZERO(Vector2);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float maxDepth = fmaxf(collision->depths[0], collision->depths[1]);
|
|
||||||
|
|
||||||
if (maxDepth <= FR_DYNAMICS_CORRECTION_DEPTH_THRESHOLD) return;
|
|
||||||
|
|
||||||
// ์ถฉ๋ ๋ฐฉํฅ์ ๋ฌด์กฐ๊ฑด `b1`์์ `b2`๋ก ํฅํ๋ค.
|
|
||||||
Vector2 correction = frVec2ScalarMultiply(
|
|
||||||
collision->direction,
|
|
||||||
FR_DYNAMICS_CORRECTION_DEPTH_SCALE * (
|
|
||||||
(inverseDt * (maxDepth - FR_DYNAMICS_CORRECTION_DEPTH_THRESHOLD))
|
|
||||||
/ (b1->motion.inverseMass + b2->motion.inverseMass)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
frSetBodyPosition(
|
|
||||||
b1,
|
|
||||||
frVec2Subtract(
|
|
||||||
b1->tx.position,
|
|
||||||
frVec2ScalarMultiply(correction, b1->motion.inverseMass)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
frSetBodyPosition(
|
|
||||||
b2,
|
|
||||||
frVec2Add(
|
|
||||||
b2->tx.position,
|
|
||||||
frVec2ScalarMultiply(correction, b2->motion.inverseMass)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`์ ์ง๋์ ๋ค์ ๊ณ์ฐํ๋ค. */
|
|
||||||
static void frResetBodyMass(frBody *b) {
|
|
||||||
if (b == NULL) return;
|
|
||||||
|
|
||||||
b->motion.mass = 0.0f;
|
|
||||||
b->motion.inertia = 0.0f;
|
|
||||||
|
|
||||||
if (b->type == FR_BODY_STATIC) {
|
|
||||||
b->motion.velocity = FR_STRUCT_ZERO(Vector2);
|
|
||||||
b->motion.angularVelocity = 0.0f;
|
|
||||||
} else if (b->type == FR_BODY_DYNAMIC) {
|
|
||||||
if (b->shape != NULL) {
|
|
||||||
if (!(b->flags & FR_FLAG_INFINITE_MASS))
|
|
||||||
b->motion.mass = frGetShapeMass(b->shape);
|
|
||||||
|
|
||||||
if (!(b->flags & FR_FLAG_INFINITE_INERTIA))
|
|
||||||
b->motion.inertia = frGetShapeInertia(b->shape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b->motion.mass == 0.0f) b->motion.inverseMass = 0.0f;
|
|
||||||
else b->motion.inverseMass = 1.0f / b->motion.mass;
|
|
||||||
|
|
||||||
if (b->motion.inertia == 0.0f) b->motion.inverseInertia = 0.0f;
|
|
||||||
else b->motion.inverseInertia = 1.0f / b->motion.inertia;
|
|
||||||
}
|
|
|
@ -1,319 +0,0 @@
|
||||||
#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL)
|
|
||||||
#define SOKOL_TIME_IMPL
|
|
||||||
#endif
|
|
||||||
#ifndef SOKOL_TIME_INCLUDED
|
|
||||||
/*
|
|
||||||
sokol_time.h -- simple cross-platform time measurement
|
|
||||||
|
|
||||||
Project URL: https://github.com/floooh/sokol
|
|
||||||
|
|
||||||
Do this:
|
|
||||||
#define SOKOL_IMPL or
|
|
||||||
#define SOKOL_TIME_IMPL
|
|
||||||
before you include this file in *one* C or C++ file to create the
|
|
||||||
implementation.
|
|
||||||
|
|
||||||
Optionally provide the following defines with your own implementations:
|
|
||||||
SOKOL_ASSERT(c) - your own assert macro (default: assert(c))
|
|
||||||
SOKOL_TIME_API_DECL - public function declaration prefix (default: extern)
|
|
||||||
SOKOL_API_DECL - same as SOKOL_TIME_API_DECL
|
|
||||||
SOKOL_API_IMPL - public function implementation prefix (default: -)
|
|
||||||
|
|
||||||
If sokol_time.h is compiled as a DLL, define the following before
|
|
||||||
including the declaration or implementation:
|
|
||||||
|
|
||||||
SOKOL_DLL
|
|
||||||
|
|
||||||
On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport)
|
|
||||||
or __declspec(dllimport) as needed.
|
|
||||||
|
|
||||||
void stm_setup();
|
|
||||||
Call once before any other functions to initialize sokol_time
|
|
||||||
(this calls for instance QueryPerformanceFrequency on Windows)
|
|
||||||
|
|
||||||
uint64_t stm_now();
|
|
||||||
Get current point in time in unspecified 'ticks'. The value that
|
|
||||||
is returned has no relation to the 'wall-clock' time and is
|
|
||||||
not in a specific time unit, it is only useful to compute
|
|
||||||
time differences.
|
|
||||||
|
|
||||||
uint64_t stm_diff(uint64_t new, uint64_t old);
|
|
||||||
Computes the time difference between new and old. This will always
|
|
||||||
return a positive, non-zero value.
|
|
||||||
|
|
||||||
uint64_t stm_since(uint64_t start);
|
|
||||||
Takes the current time, and returns the elapsed time since start
|
|
||||||
(this is a shortcut for "stm_diff(stm_now(), start)")
|
|
||||||
|
|
||||||
uint64_t stm_laptime(uint64_t* last_time);
|
|
||||||
This is useful for measuring frame time and other recurring
|
|
||||||
events. It takes the current time, returns the time difference
|
|
||||||
to the value in last_time, and stores the current time in
|
|
||||||
last_time for the next call. If the value in last_time is 0,
|
|
||||||
the return value will be zero (this usually happens on the
|
|
||||||
very first call).
|
|
||||||
|
|
||||||
uint64_t stm_round_to_common_refresh_rate(uint64_t duration)
|
|
||||||
This oddly named function takes a measured frame time and
|
|
||||||
returns the closest "nearby" common display refresh rate frame duration
|
|
||||||
in ticks. If the input duration isn't close to any common display
|
|
||||||
refresh rate, the input duration will be returned unchanged as a fallback.
|
|
||||||
The main purpose of this function is to remove jitter/inaccuracies from
|
|
||||||
measured frame times, and instead use the display refresh rate as
|
|
||||||
frame duration.
|
|
||||||
NOTE: for more robust frame timing, consider using the
|
|
||||||
sokol_app.h function sapp_frame_duration()
|
|
||||||
|
|
||||||
Use the following functions to convert a duration in ticks into
|
|
||||||
useful time units:
|
|
||||||
|
|
||||||
double stm_sec(uint64_t ticks);
|
|
||||||
double stm_ms(uint64_t ticks);
|
|
||||||
double stm_us(uint64_t ticks);
|
|
||||||
double stm_ns(uint64_t ticks);
|
|
||||||
Converts a tick value into seconds, milliseconds, microseconds
|
|
||||||
or nanoseconds. Note that not all platforms will have nanosecond
|
|
||||||
or even microsecond precision.
|
|
||||||
|
|
||||||
Uses the following time measurement functions under the hood:
|
|
||||||
|
|
||||||
Windows: QueryPerformanceFrequency() / QueryPerformanceCounter()
|
|
||||||
MacOS/iOS: mach_absolute_time()
|
|
||||||
emscripten: emscripten_get_now()
|
|
||||||
Linux+others: clock_gettime(CLOCK_MONOTONIC)
|
|
||||||
|
|
||||||
zlib/libpng license
|
|
||||||
|
|
||||||
Copyright (c) 2018 Andre Weissflog
|
|
||||||
|
|
||||||
This software is provided 'as-is', without any express or implied warranty.
|
|
||||||
In no event will the authors be held liable for any damages arising from the
|
|
||||||
use of this software.
|
|
||||||
|
|
||||||
Permission is granted to anyone to use this software for any purpose,
|
|
||||||
including commercial applications, and to alter it and redistribute it
|
|
||||||
freely, subject to the following restrictions:
|
|
||||||
|
|
||||||
1. The origin of this software must not be misrepresented; you must not
|
|
||||||
claim that you wrote the original software. If you use this software in a
|
|
||||||
product, an acknowledgment in the product documentation would be
|
|
||||||
appreciated but is not required.
|
|
||||||
|
|
||||||
2. Altered source versions must be plainly marked as such, and must not
|
|
||||||
be misrepresented as being the original software.
|
|
||||||
|
|
||||||
3. This notice may not be removed or altered from any source
|
|
||||||
distribution.
|
|
||||||
*/
|
|
||||||
#define SOKOL_TIME_INCLUDED (1)
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL)
|
|
||||||
#define SOKOL_TIME_API_DECL SOKOL_API_DECL
|
|
||||||
#endif
|
|
||||||
#ifndef SOKOL_TIME_API_DECL
|
|
||||||
#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL)
|
|
||||||
#define SOKOL_TIME_API_DECL __declspec(dllexport)
|
|
||||||
#elif defined(_WIN32) && defined(SOKOL_DLL)
|
|
||||||
#define SOKOL_TIME_API_DECL __declspec(dllimport)
|
|
||||||
#else
|
|
||||||
#define SOKOL_TIME_API_DECL extern
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SOKOL_TIME_API_DECL void stm_setup(void);
|
|
||||||
SOKOL_TIME_API_DECL uint64_t stm_now(void);
|
|
||||||
SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);
|
|
||||||
SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks);
|
|
||||||
SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time);
|
|
||||||
SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks);
|
|
||||||
SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks);
|
|
||||||
SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks);
|
|
||||||
SOKOL_TIME_API_DECL double stm_us(uint64_t ticks);
|
|
||||||
SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
#endif // SOKOL_TIME_INCLUDED
|
|
||||||
|
|
||||||
/*-- IMPLEMENTATION ----------------------------------------------------------*/
|
|
||||||
#ifdef SOKOL_TIME_IMPL
|
|
||||||
#define SOKOL_TIME_IMPL_INCLUDED (1)
|
|
||||||
#include <string.h> /* memset */
|
|
||||||
|
|
||||||
#ifndef SOKOL_API_IMPL
|
|
||||||
#define SOKOL_API_IMPL
|
|
||||||
#endif
|
|
||||||
#ifndef SOKOL_ASSERT
|
|
||||||
#include <assert.h>
|
|
||||||
#define SOKOL_ASSERT(c) assert(c)
|
|
||||||
#endif
|
|
||||||
#ifndef _SOKOL_PRIVATE
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
|
||||||
#define _SOKOL_PRIVATE __attribute__((unused)) static
|
|
||||||
#else
|
|
||||||
#define _SOKOL_PRIVATE static
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <windows.h>
|
|
||||||
typedef struct {
|
|
||||||
uint32_t initialized;
|
|
||||||
LARGE_INTEGER freq;
|
|
||||||
LARGE_INTEGER start;
|
|
||||||
} _stm_state_t;
|
|
||||||
#elif defined(__APPLE__) && defined(__MACH__)
|
|
||||||
#include <mach/mach_time.h>
|
|
||||||
typedef struct {
|
|
||||||
uint32_t initialized;
|
|
||||||
mach_timebase_info_data_t timebase;
|
|
||||||
uint64_t start;
|
|
||||||
} _stm_state_t;
|
|
||||||
#elif defined(__EMSCRIPTEN__)
|
|
||||||
#include <emscripten/emscripten.h>
|
|
||||||
typedef struct {
|
|
||||||
uint32_t initialized;
|
|
||||||
double start;
|
|
||||||
} _stm_state_t;
|
|
||||||
#else /* anything else, this will need more care for non-Linux platforms */
|
|
||||||
#ifdef ESP8266
|
|
||||||
// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined
|
|
||||||
#define CLOCK_MONOTONIC 0
|
|
||||||
#endif
|
|
||||||
#include <time.h>
|
|
||||||
typedef struct {
|
|
||||||
uint32_t initialized;
|
|
||||||
uint64_t start;
|
|
||||||
} _stm_state_t;
|
|
||||||
#endif
|
|
||||||
static _stm_state_t _stm;
|
|
||||||
|
|
||||||
/* prevent 64-bit overflow when computing relative timestamp
|
|
||||||
see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3
|
|
||||||
*/
|
|
||||||
#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))
|
|
||||||
_SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) {
|
|
||||||
int64_t q = value / denom;
|
|
||||||
int64_t r = value % denom;
|
|
||||||
return q * numer + r * numer / denom;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SOKOL_API_IMPL void stm_setup(void) {
|
|
||||||
memset(&_stm, 0, sizeof(_stm));
|
|
||||||
_stm.initialized = 0xABCDABCD;
|
|
||||||
#if defined(_WIN32)
|
|
||||||
QueryPerformanceFrequency(&_stm.freq);
|
|
||||||
QueryPerformanceCounter(&_stm.start);
|
|
||||||
#elif defined(__APPLE__) && defined(__MACH__)
|
|
||||||
mach_timebase_info(&_stm.timebase);
|
|
||||||
_stm.start = mach_absolute_time();
|
|
||||||
#elif defined(__EMSCRIPTEN__)
|
|
||||||
_stm.start = emscripten_get_now();
|
|
||||||
#else
|
|
||||||
struct timespec ts;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
_stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL uint64_t stm_now(void) {
|
|
||||||
SOKOL_ASSERT(_stm.initialized == 0xABCDABCD);
|
|
||||||
uint64_t now;
|
|
||||||
#if defined(_WIN32)
|
|
||||||
LARGE_INTEGER qpc_t;
|
|
||||||
QueryPerformanceCounter(&qpc_t);
|
|
||||||
now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart);
|
|
||||||
#elif defined(__APPLE__) && defined(__MACH__)
|
|
||||||
const uint64_t mach_now = mach_absolute_time() - _stm.start;
|
|
||||||
now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom);
|
|
||||||
#elif defined(__EMSCRIPTEN__)
|
|
||||||
double js_now = emscripten_get_now() - _stm.start;
|
|
||||||
now = (uint64_t) (js_now * 1000000.0);
|
|
||||||
#else
|
|
||||||
struct timespec ts;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start;
|
|
||||||
#endif
|
|
||||||
return now;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) {
|
|
||||||
if (new_ticks > old_ticks) {
|
|
||||||
return new_ticks - old_ticks;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) {
|
|
||||||
return stm_diff(stm_now(), start_ticks);
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) {
|
|
||||||
SOKOL_ASSERT(last_time);
|
|
||||||
uint64_t dt = 0;
|
|
||||||
uint64_t now = stm_now();
|
|
||||||
if (0 != *last_time) {
|
|
||||||
dt = stm_diff(now, *last_time);
|
|
||||||
}
|
|
||||||
*last_time = now;
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
// first number is frame duration in ns, second number is tolerance in ns,
|
|
||||||
// the resulting min/max values must not overlap!
|
|
||||||
static const uint64_t _stm_refresh_rates[][2] = {
|
|
||||||
{ 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms
|
|
||||||
{ 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms
|
|
||||||
{ 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms
|
|
||||||
{ 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25
|
|
||||||
{ 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms
|
|
||||||
{ 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms
|
|
||||||
{ 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms
|
|
||||||
{ 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms
|
|
||||||
{ 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms
|
|
||||||
{ 0, 0 }, // keep the last element always at zero
|
|
||||||
};
|
|
||||||
|
|
||||||
SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) {
|
|
||||||
uint64_t ns;
|
|
||||||
int i = 0;
|
|
||||||
while (0 != (ns = _stm_refresh_rates[i][0])) {
|
|
||||||
uint64_t tol = _stm_refresh_rates[i][1];
|
|
||||||
if ((ticks > (ns - tol)) && (ticks < (ns + tol))) {
|
|
||||||
return ns;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
// fallthough: didn't fit into any buckets
|
|
||||||
return ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL double stm_sec(uint64_t ticks) {
|
|
||||||
return (double)ticks / 1000000000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL double stm_ms(uint64_t ticks) {
|
|
||||||
return (double)ticks / 1000000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL double stm_us(uint64_t ticks) {
|
|
||||||
return (double)ticks / 1000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOKOL_API_IMPL double stm_ns(uint64_t ticks) {
|
|
||||||
return (double)ticks;
|
|
||||||
}
|
|
||||||
#endif /* SOKOL_TIME_IMPL */
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,474 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
/* | `geometry` ๋ชจ๋ ์๋ฃํ ์ ์... | */
|
|
||||||
|
|
||||||
/* ์ถฉ๋ ๊ฐ์ง์ฉ ๋ํ์ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด. */
|
|
||||||
typedef struct frShape {
|
|
||||||
frShapeType type;
|
|
||||||
frMaterial material;
|
|
||||||
bool isRect;
|
|
||||||
float area;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
float radius;
|
|
||||||
} circle;
|
|
||||||
struct {
|
|
||||||
frVertices vertices;
|
|
||||||
frVertices normals;
|
|
||||||
} polygon;
|
|
||||||
};
|
|
||||||
} frShape;
|
|
||||||
|
|
||||||
/* | `geometry` ๋ชจ๋ ์์... | */
|
|
||||||
|
|
||||||
static const float INVERSE_TWELVE = (1.0f / 12.0f);
|
|
||||||
|
|
||||||
/* | `geometry` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ๋์ด๋ฅผ ๊ณ์ฐํ๋ค. */
|
|
||||||
static void frComputeArea(frShape *s);
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`๋ฅผ ๋ณผ๋ก ๋ค๊ฐํ์ผ๋ก ๋ณํํ๋ค. */
|
|
||||||
static void frComputeConvex(frShape *s);
|
|
||||||
|
|
||||||
/* ๊ผญ์ง์ ๋ฐฐ์ด `vertices`๋ก ๋ง๋ค ์ ์๋ ๊ฐ์ฅ ํฐ ๋ณผ๋ก ๋ค๊ฐํ์ ๊ผญ์ง์ ๋ฐฐ์ด์ ๋ฐํํ๋ค. */
|
|
||||||
static frVertices frJarvisMarch(frVertices *vertices);
|
|
||||||
|
|
||||||
/* ๋ฐ์ง๋ฆ์ด `radius`์ธ ์์ ๋ํ๋ด๋ ๋ํ์ ์์ฑํ๋ค. */
|
|
||||||
frShape *frCreateCircle(frMaterial material, float radius) {
|
|
||||||
frShape *result = frCreateShape();
|
|
||||||
|
|
||||||
result->type = FR_SHAPE_CIRCLE;
|
|
||||||
result->material = material;
|
|
||||||
|
|
||||||
frSetCircleRadius(result, radius);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ๋ก ๊ธธ์ด๊ฐ `width`์ด๊ณ ์ธ๋ก ๊ธธ์ด๊ฐ `height`์ธ ์ง์ฌ๊ฐํ์ ๋ํ๋ด๋ ๋ํ์ ์์ฑํ๋ค. */
|
|
||||||
frShape *frCreateRectangle(frMaterial material, float width, float height) {
|
|
||||||
frShape *result = frCreateShape();
|
|
||||||
|
|
||||||
const float halfWidth = 0.5f * width, halfHeight = 0.5f * height;
|
|
||||||
|
|
||||||
frVertices vertices = {
|
|
||||||
.data = {
|
|
||||||
(Vector2) { -halfWidth, -halfHeight },
|
|
||||||
(Vector2) { -halfWidth, halfHeight },
|
|
||||||
(Vector2) { halfWidth, halfHeight },
|
|
||||||
(Vector2) { halfWidth, -halfHeight }
|
|
||||||
},
|
|
||||||
.count = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
result->type = FR_SHAPE_POLYGON;
|
|
||||||
result->material = material;
|
|
||||||
result->isRect = true;
|
|
||||||
|
|
||||||
frSetPolygonVertices(result, vertices);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ผญ์ง์ ๋ฐฐ์ด์ด `vertices`์ธ ๋ค๊ฐํ์ ๋ํ๋ด๋ ๋ํ์ ์์ฑํ๋ค. */
|
|
||||||
frShape *frCreatePolygon(frMaterial material, frVertices vertices) {
|
|
||||||
frShape *result = frCreateShape();
|
|
||||||
|
|
||||||
if (vertices.count > FR_GEOMETRY_MAX_VERTEX_COUNT)
|
|
||||||
vertices.count = FR_GEOMETRY_MAX_VERTEX_COUNT;
|
|
||||||
|
|
||||||
result->type = FR_SHAPE_POLYGON;
|
|
||||||
result->material = material;
|
|
||||||
|
|
||||||
frSetPolygonVertices(result, vertices);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ํํ๊ฐ ์ ํด์ง์ง ์์ ๋ํ์ ์์ฑํ๋ค. */
|
|
||||||
frShape *frCreateShape(void) {
|
|
||||||
frShape *result = calloc(1, sizeof(*result));
|
|
||||||
|
|
||||||
result->type = FR_SHAPE_UNKNOWN;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ํํ๊ฐ ๊ฐ์ ์๋ก์ด ๋ํ์ ๋ฐํํ๋ค. */
|
|
||||||
frShape *frCloneShape(frShape *s) {
|
|
||||||
if (s == NULL || s->type == FR_SHAPE_UNKNOWN) return NULL;
|
|
||||||
|
|
||||||
frShape *result = frCreateShape();
|
|
||||||
|
|
||||||
result->type = s->type;
|
|
||||||
result->material = s->material;
|
|
||||||
result->isRect = s->isRect;
|
|
||||||
result->area = s->area;
|
|
||||||
|
|
||||||
if (result->type == FR_SHAPE_CIRCLE)
|
|
||||||
result->circle.radius = s->circle.radius;
|
|
||||||
else if (result->type == FR_SHAPE_POLYGON) {
|
|
||||||
result->polygon.vertices.count = s->polygon.vertices.count;
|
|
||||||
result->polygon.normals.count = s->polygon.normals.count;
|
|
||||||
|
|
||||||
for (int i = 0; i < s->polygon.vertices.count; i++)
|
|
||||||
result->polygon.vertices.data[i] = s->polygon.vertices.data[i];
|
|
||||||
|
|
||||||
for (int i = 0; i < s->polygon.normals.count; i++)
|
|
||||||
result->polygon.normals.data[i] = s->polygon.normals.data[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํ๋ค. */
|
|
||||||
void frReleaseShape(frShape *s) {
|
|
||||||
free(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ตฌ์กฐ์ฒด `frShape`์ ํฌ๊ธฐ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
size_t frGetShapeStructSize(void) {
|
|
||||||
return sizeof(frShape);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ์ข
๋ฅ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
frShapeType frGetShapeType(frShape *s) {
|
|
||||||
return (s != NULL) ? s->type : FR_SHAPE_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ์ฌ์ง์ ๋ฐํํ๋ค. */
|
|
||||||
frMaterial frGetShapeMaterial(frShape *s) {
|
|
||||||
return (s != NULL) ? s->material : FR_STRUCT_ZERO(frMaterial);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ๋์ด๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetShapeArea(frShape *s) {
|
|
||||||
return (s != NULL && s->area >= 0.0f) ? s->area : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ์ง๋์ ๋ฐํํ๋ค. */
|
|
||||||
float frGetShapeMass(frShape *s) {
|
|
||||||
return (s != NULL) ? s->material.density * frGetShapeArea(s) : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ๊ด์ฑ ๋ชจ๋ฉํธ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
float frGetShapeInertia(frShape *s) {
|
|
||||||
if (s == NULL || s->type == FR_SHAPE_UNKNOWN) return 0.0f;
|
|
||||||
|
|
||||||
float result = 0.0f;
|
|
||||||
|
|
||||||
if (s->type == FR_SHAPE_CIRCLE) {
|
|
||||||
result = 0.5f * frGetShapeMass(s) * (s->circle.radius * s->circle.radius);
|
|
||||||
} else if (s->type == FR_SHAPE_POLYGON) {
|
|
||||||
float xInertia = 0.0f, yInertia = 0.0f;
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Second_moment_of_area#Any_polygon
|
|
||||||
for (int j = s->polygon.vertices.count - 1, i = 0; i < s->polygon.vertices.count; j = i, i++) {
|
|
||||||
Vector2 v1 = s->polygon.vertices.data[j];
|
|
||||||
Vector2 v2 = s->polygon.vertices.data[i];
|
|
||||||
|
|
||||||
const float cross = frVec2CrossProduct(v1, v2);
|
|
||||||
|
|
||||||
xInertia += cross * ((v1.y * v1.y) + (v1.y * v2.y) + (v2.y * v2.y));
|
|
||||||
yInertia += cross * ((v1.x * v1.x) + (v1.x * v2.x) + (v2.x * v2.x));
|
|
||||||
}
|
|
||||||
|
|
||||||
result = fabsf((xInertia + yInertia) * INVERSE_TWELVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s->material.density * result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ AABB๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Rectangle frGetShapeAABB(frShape *s, frTransform tx) {
|
|
||||||
Rectangle result = FR_STRUCT_ZERO(Rectangle);
|
|
||||||
|
|
||||||
if (s == NULL || s->type == FR_SHAPE_UNKNOWN) return result;
|
|
||||||
|
|
||||||
if (s->type == FR_SHAPE_CIRCLE) {
|
|
||||||
result.x = tx.position.x - s->circle.radius;
|
|
||||||
result.y = tx.position.y - s->circle.radius;
|
|
||||||
|
|
||||||
result.width = result.height = 2.0f * s->circle.radius;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} else if (s->type == FR_SHAPE_POLYGON) {
|
|
||||||
Vector2 minVertex = { FLT_MAX, FLT_MAX };
|
|
||||||
Vector2 maxVertex = { -FLT_MAX, -FLT_MAX };
|
|
||||||
|
|
||||||
// AABB์ X์ขํ์ Y์ขํ์ ์ต์๊ฐ๊ณผ ์ต๋๊ฐ์ ๊ตฌํ๋ค.
|
|
||||||
for (int i = 0; i < s->polygon.vertices.count; i++) {
|
|
||||||
Vector2 vertex = frVec2Transform(s->polygon.vertices.data[i], tx);
|
|
||||||
|
|
||||||
if (minVertex.x > vertex.x) minVertex.x = vertex.x;
|
|
||||||
if (minVertex.y > vertex.y) minVertex.y = vertex.y;
|
|
||||||
|
|
||||||
if (maxVertex.x < vertex.x) maxVertex.x = vertex.x;
|
|
||||||
if (maxVertex.y < vertex.y) maxVertex.y = vertex.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
float deltaX = maxVertex.x - minVertex.x;
|
|
||||||
float deltaY = maxVertex.y - minVertex.y;
|
|
||||||
|
|
||||||
result.x = minVertex.x;
|
|
||||||
result.y = minVertex.y;
|
|
||||||
|
|
||||||
result.width = deltaX;
|
|
||||||
result.height = deltaY;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ `s`์ ๋ฐ์ง๋ฆ์ ๋ฐํํ๋ค. */
|
|
||||||
float frGetCircleRadius(frShape *s) {
|
|
||||||
return (s != NULL && s->type == FR_SHAPE_CIRCLE) ? s->circle.radius : 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ง์ฌ๊ฐํ `s`์ ๊ฐ๋ก ๋ฐ ์ธ๋ก ๊ธธ์ด๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Vector2 frGetRectangleDimensions(frShape *s) {
|
|
||||||
if (!frIsShapeRectangle(s)) return FR_STRUCT_ZERO(Vector2);
|
|
||||||
|
|
||||||
return (Vector2) {
|
|
||||||
s->polygon.vertices.data[2].x - s->polygon.vertices.data[1].x,
|
|
||||||
s->polygon.vertices.data[1].y - s->polygon.vertices.data[0].y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`์ `i + 1`๋ฒ์งธ ๊ผญ์ง์ ์ ๋ฐํํ๋ค. */
|
|
||||||
Vector2 frGetPolygonVertex(frShape *s, int i) {
|
|
||||||
return (s != NULL && i >= 0 && i < s->polygon.vertices.count)
|
|
||||||
? s->polygon.vertices.data[i]
|
|
||||||
: FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`์ `i + 1`๋ฒ์งธ ๋ฒ์ ๋ฒกํฐ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Vector2 frGetPolygonNormal(frShape *s, int i) {
|
|
||||||
return (s != NULL && i >= 0 && i < s->polygon.normals.count)
|
|
||||||
? s->polygon.normals.data[i]
|
|
||||||
: FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`์ ๊ผญ์ง์ ๋ฐฐ์ด์ ๋ฐํํ๋ค. */
|
|
||||||
frVertices frGetPolygonVertices(frShape *s) {
|
|
||||||
return (s != NULL) ? s->polygon.vertices : FR_STRUCT_ZERO(frVertices);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`์ ๋ฒ์ ๋ฒกํฐ ๋ฐฐ์ด์ ๋ฐํํ๋ค. */
|
|
||||||
frVertices frGetPolygonNormals(frShape *s) {
|
|
||||||
return (s != NULL) ? s->polygon.normals : FR_STRUCT_ZERO(frVertices);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`๊ฐ ์ง์ฌ๊ฐํ์ธ์ง ํ์ธํ๋ค. */
|
|
||||||
bool frIsShapeRectangle(frShape *s) {
|
|
||||||
return (s != NULL) && (s->type == FR_SHAPE_POLYGON) && s->isRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ `s`์ ๋ฐ์ง๋ฆ์ `radius`๋ก ๋ณ๊ฒฝํ๋ค. */
|
|
||||||
void frSetCircleRadius(frShape *s, float radius) {
|
|
||||||
if (s == NULL || s->type != FR_SHAPE_CIRCLE) return;
|
|
||||||
|
|
||||||
s->circle.radius = radius;
|
|
||||||
|
|
||||||
frComputeArea(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ง์ฌ๊ฐํ `s`์ ๊ฐ๋ก ๋ฐ ์ธ๋ก ๊ธธ์ด๋ฅผ `v`์ X๊ฐ๊ณผ Y๊ฐ์ผ๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetRectangleDimensions(frShape *s, Vector2 v) {
|
|
||||||
if (!frIsShapeRectangle(s) || v.x <= 0.0f || v.y <= 0.0f) return;
|
|
||||||
|
|
||||||
v = frVec2ScalarMultiply(v, 0.5f);
|
|
||||||
|
|
||||||
frVertices vertices = {
|
|
||||||
.data = {
|
|
||||||
(Vector2) { -v.x, -v.y },
|
|
||||||
(Vector2) { -v.x, v.y },
|
|
||||||
(Vector2) { v.x, v.y },
|
|
||||||
(Vector2) { v.x, -v.y }
|
|
||||||
},
|
|
||||||
.count = 4
|
|
||||||
};
|
|
||||||
|
|
||||||
frSetPolygonVertices(s, vertices);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`์ ๊ผญ์ง์ ๋ฐฐ์ด์ `vertices`๋ก ๋ณ๊ฒฝํ๋ค. */
|
|
||||||
void frSetPolygonVertices(frShape *s, frVertices vertices) {
|
|
||||||
if (s == NULL || s->type != FR_SHAPE_POLYGON) return;
|
|
||||||
|
|
||||||
s->polygon.vertices.count = vertices.count;
|
|
||||||
s->polygon.normals.count = vertices.count;
|
|
||||||
|
|
||||||
for (int i = 0; i < vertices.count; i++)
|
|
||||||
s->polygon.vertices.data[i] = vertices.data[i];
|
|
||||||
|
|
||||||
// ์ง์ฌ๊ฐํ์ ์ด๋ฏธ ๋ณผ๋ก ๋ค๊ฐํ์ด๋ฏ๋ก ๋ณํํ์ง ์๋๋ค.
|
|
||||||
if (!s->isRect) frComputeConvex(s);
|
|
||||||
|
|
||||||
frComputeArea(s);
|
|
||||||
|
|
||||||
for (int j = vertices.count - 1, i = 0; i < vertices.count; j = i, i++)
|
|
||||||
s->polygon.normals.data[i] = frVec2LeftNormal(
|
|
||||||
frVec2Subtract(
|
|
||||||
s->polygon.vertices.data[i],
|
|
||||||
s->polygon.vertices.data[j]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ์ฌ์ง์ `material`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetShapeMaterial(frShape *s, frMaterial material) {
|
|
||||||
if (s != NULL) s->material = material;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ์ข
๋ฅ๋ฅผ `type`์ผ๋ก ๋ณ๊ฒฝํ๋ค. */
|
|
||||||
void frSetShapeType(frShape *s, frShapeType type) {
|
|
||||||
if (s != NULL && s->type != type) s->type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ `p`๊ฐ ๋ํ `s`์ ๋ด๋ถ์ ์๋์ง ํ์ธํ๋ค. */
|
|
||||||
bool frShapeContainsPoint(frShape *s, frTransform tx, Vector2 p) {
|
|
||||||
if (s == NULL || s->type == FR_SHAPE_UNKNOWN) return false;
|
|
||||||
|
|
||||||
Rectangle aabb = frGetShapeAABB(s, tx);
|
|
||||||
|
|
||||||
// `p`๊ฐ `s`์ AABB ๋ด๋ถ์ ์๋์ง ๋จผ์ ํ์ธํ๋ค.
|
|
||||||
if (p.x < aabb.x || p.x > aabb.x + aabb.width
|
|
||||||
|| p.y < aabb.y || p.y > aabb.y + aabb.height)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (s->type == FR_SHAPE_CIRCLE) {
|
|
||||||
float deltaX = p.x - tx.position.x;
|
|
||||||
float deltaY = p.y - tx.position.y;
|
|
||||||
|
|
||||||
float radius = frGetCircleRadius(s);
|
|
||||||
|
|
||||||
return (deltaX * deltaX) + (deltaY * deltaY) <= radius * radius;
|
|
||||||
} else if (s->type == FR_SHAPE_POLYGON) {
|
|
||||||
frRay ray = { p, (Vector2) { .x = 1.0f }, FLT_MAX };
|
|
||||||
|
|
||||||
frRaycastHit raycast = frComputeShapeRaycast(s, tx, ray);
|
|
||||||
|
|
||||||
return raycast.inside;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ํ `s`์ ๋์ด๋ฅผ ๊ณ์ฐํ๋ค. */
|
|
||||||
static void frComputeArea(frShape *s) {
|
|
||||||
if (s == NULL || s->type == FR_SHAPE_UNKNOWN)
|
|
||||||
s->area = 0.0f;
|
|
||||||
else if (s->type == FR_SHAPE_CIRCLE)
|
|
||||||
s->area = PI * (s->circle.radius * s->circle.radius);
|
|
||||||
else if (s->type == FR_SHAPE_POLYGON) {
|
|
||||||
if (s->isRect) {
|
|
||||||
frVertices *vertices = &(s->polygon.vertices);
|
|
||||||
|
|
||||||
float width = vertices->data[2].x - vertices->data[1].x;
|
|
||||||
float height = vertices->data[1].y - vertices->data[0].y;
|
|
||||||
|
|
||||||
s->area = width * height;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float twiceAreaSum = 0.0f;
|
|
||||||
|
|
||||||
for (int i = 0; i < s->polygon.vertices.count - 1; i++) {
|
|
||||||
float twiceArea = frVec2CrossProduct(
|
|
||||||
frVec2Subtract(s->polygon.vertices.data[i], s->polygon.vertices.data[0]),
|
|
||||||
frVec2Subtract(s->polygon.vertices.data[i + 1], s->polygon.vertices.data[0])
|
|
||||||
);
|
|
||||||
|
|
||||||
twiceAreaSum += twiceArea;
|
|
||||||
}
|
|
||||||
|
|
||||||
s->area = fabsf(0.5f * twiceAreaSum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ค๊ฐํ `s`๋ฅผ ๋ณผ๋ก ๋ค๊ฐํ์ผ๋ก ๋ณํํ๋ค. */
|
|
||||||
static void frComputeConvex(frShape *s) {
|
|
||||||
if (s == NULL || s->type != FR_SHAPE_POLYGON) return;
|
|
||||||
|
|
||||||
frVertices result = frJarvisMarch(&(s->polygon.vertices));
|
|
||||||
|
|
||||||
for (int i = 0; i < result.count; i++)
|
|
||||||
s->polygon.vertices.data[i] = result.data[i];
|
|
||||||
|
|
||||||
s->polygon.vertices.count = result.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ผญ์ง์ ๋ฐฐ์ด `vertices`๋ก ๋ง๋ค ์ ์๋ ๊ฐ์ฅ ํฐ ๋ณผ๋ก ๋ค๊ฐํ์ ๊ผญ์ง์ ๋ฐฐ์ด์ ๋ฐํํ๋ค. */
|
|
||||||
static frVertices frJarvisMarch(frVertices *vertices) {
|
|
||||||
if (vertices == NULL || vertices->count == 0) return FR_STRUCT_ZERO(frVertices);
|
|
||||||
|
|
||||||
frVertices result = FR_STRUCT_ZERO(frVertices);
|
|
||||||
|
|
||||||
int leftmostIndex = 0, pivotIndex = 0, nextIndex = 0, vertexCount = 0;
|
|
||||||
|
|
||||||
// ์ฃผ์ด์ง ๊ผญ์ง์ ๋ฐฐ์ด์์ X์ขํ ๊ฐ์ด ๊ฐ์ฅ ์์ ๊ผญ์ง์ L์ ์ฐพ๋๋ค.
|
|
||||||
for (int i = 1; i < vertices->count; i++)
|
|
||||||
if (vertices->data[leftmostIndex].x > vertices->data[i].x)
|
|
||||||
leftmostIndex = i;
|
|
||||||
|
|
||||||
result.data[vertexCount++] = vertices->data[leftmostIndex];
|
|
||||||
|
|
||||||
// ๊ธฐ์ค์ P๋ฅผ ๋ฐฉ๊ธ ์ฐพ์ ๊ผญ์ง์ L๋ก ์ค์ ํ๋ค.
|
|
||||||
pivotIndex = leftmostIndex;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
// ๊ธฐ์ค์ P์ ๋ฐ๋ก ๋ค์์ ์ค๋ ๊ผญ์ง์ Q๋ฅผ ์ฐพ๋๋ค.
|
|
||||||
for (int i = 0; i < vertices->count; i++) {
|
|
||||||
if (i == pivotIndex) continue;
|
|
||||||
|
|
||||||
nextIndex = i;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ๊ธฐ์ค์ P์ ๊ผญ์ง์ Q ์ฌ์ด์ ์ค๋ ๊ผญ์ง์ R์ ์ฐพ๋๋ค.
|
|
||||||
for (int i = 0; i < vertices->count; i++) {
|
|
||||||
if (i == pivotIndex || i == nextIndex)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// ๊ธฐ์ค์ P, ๊ผญ์ง์ R๊ณผ ๊ผญ์ง์ Q๊ฐ ๋ฐ์๊ณ ๋ฐฉํฅ์ผ๋ก ์ ๋ ฌ๋์ด ์๋์ง ํ์ธํ๋ค.
|
|
||||||
if (frVec2CCW(vertices->data[pivotIndex], vertices->data[i], vertices->data[nextIndex]))
|
|
||||||
nextIndex = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ๊ผญ์ง์ Q๊ฐ ์์์ ์ธ ๊ผญ์ง์ L์ด ๋๋ฉด ํ์์ ์ข
๋ฃํ๋ค.
|
|
||||||
if (nextIndex == leftmostIndex) break;
|
|
||||||
|
|
||||||
pivotIndex = nextIndex;
|
|
||||||
|
|
||||||
// ์๋ก ์ฐพ์ ๊ผญ์ง์ ์ ๋ฐฐ์ด์ ์ ์ฅํ๋ค.
|
|
||||||
result.data[vertexCount++] = vertices->data[nextIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
result.count = vertexCount;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
/* | `timer` ๋ชจ๋ ๋งคํฌ๋ก ์ ์... | */
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#define NOGDI
|
|
||||||
#define NOUSER
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SOKOL_TIME_IMPL
|
|
||||||
#include "sokol_time.h"
|
|
||||||
|
|
||||||
/* | `timer` ๋ชจ๋ ๋ณ์... | */
|
|
||||||
|
|
||||||
/* ๋จ์กฐ ์๊ณ์ ์ด๊ธฐํ ์ฌ๋ถ๋ฅผ ๋ํ๋ด๋ ๋ณ์. */
|
|
||||||
static bool initialized = false;
|
|
||||||
|
|
||||||
/* | `timer` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
/* ๋จ์กฐ ์๊ณ๋ฅผ ์ด๊ธฐํํ๋ค. */
|
|
||||||
void frInitClock(void) {
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
stm_setup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋จ์กฐ ์๊ณ์ ํ์ฌ ์๊ฐ (๋จ์: ms)์ ๋ฐํํ๋ค. */
|
|
||||||
double frGetCurrentTime(void) {
|
|
||||||
if (!initialized) frInitClock();
|
|
||||||
|
|
||||||
return stm_ms(stm_now());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋จ์กฐ ์๊ณ์ ์๊ฐ `newTime`๊ณผ `oldTime`์ ์ฐจ์ด๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
double frGetTimeDifference(double newTime, double oldTime) {
|
|
||||||
return newTime - oldTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋จ์กฐ ์๊ณ์ ํ์ฌ ์๊ฐ๊ณผ `old_time`๊ณผ์ ์ฐจ์ด๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
double frGetTimeSince(double oldTime) {
|
|
||||||
return frGetTimeDifference(frGetCurrentTime(), oldTime);
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
|
|
||||||
#define STB_DS_IMPLEMENTATION
|
|
||||||
#include "stb_ds.h"
|
|
||||||
|
|
||||||
/* | `utils` ๋ชจ๋ ๋งคํฌ๋ก ์ ์... | */
|
|
||||||
|
|
||||||
#define TWO_PI (2.0f * PI)
|
|
||||||
|
|
||||||
/* | `utils` ๋ชจ๋ ์์... | */
|
|
||||||
|
|
||||||
static const float INVERSE_TWO_PI = (1.0f / TWO_PI);
|
|
||||||
|
|
||||||
/* | `utils` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
/* ๊ฐ๋ `angle` (๋จ์: rad.)์ ์ ๊ทํํ์ฌ, ๊ตฌ๊ฐ `[center - ฯ, center + ฯ]`์ ํฌํจ๋๋๋ก ํ๋ค. */
|
|
||||||
float frNormalizeAngle(float angle, float center) {
|
|
||||||
if (angle > center - PI && angle < center + PI) return angle;
|
|
||||||
|
|
||||||
return angle - (TWO_PI * floorf((angle + PI - center) * INVERSE_TWO_PI));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๋ถ๋ ์์์ ๊ฐ `f1`์ด `f2`์ ๊ทผ์ ํ ๊ฐ์ธ์ง ํ์ธํ๋ค. */
|
|
||||||
bool frNumberApproxEquals(float f1, float f2) {
|
|
||||||
return fabsf(f1 - f2) <= fmaxf(1.0f, fmaxf(f1, f2)) * FLT_EPSILON;
|
|
||||||
}
|
|
|
@ -1,377 +0,0 @@
|
||||||
๏ปฟ/*
|
|
||||||
Copyright (c) 2021-2022 jdeokkim
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ferox.h"
|
|
||||||
#include "stb_ds.h"
|
|
||||||
|
|
||||||
/* | `world` ๋ชจ๋ ์๋ฃํ ์ ์... | */
|
|
||||||
|
|
||||||
/* ๋ฌผ๋ฆฌ ๋ฒ์น์ด ์กด์ฌํ๋ ์ธ๊ณ๋ฅผ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด. */
|
|
||||||
typedef struct frWorld {
|
|
||||||
Vector2 gravity;
|
|
||||||
frBody **bodies;
|
|
||||||
frSpatialHash *hash;
|
|
||||||
frCollision *collisions;
|
|
||||||
frCollisionHandler handler;
|
|
||||||
double accumulator;
|
|
||||||
double timestamp;
|
|
||||||
int *queries;
|
|
||||||
} frWorld;
|
|
||||||
|
|
||||||
/* | `world` ๋ชจ๋ ํจ์... | */
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์
๋ฐ์ดํธ ์ด์ ์์
์ ์คํํ๋ค. */
|
|
||||||
static void frPreUpdateWorld(frWorld *world);
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์
๋ฐ์ดํธ ์ดํ ์์
์ ์คํํ๋ค. */
|
|
||||||
static void frPostUpdateWorld(frWorld *world);
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`๋ฅผ ์๊ฐ `dt` (๋จ์: ms)๋งํผ ์
๋ฐ์ดํธํ๋ค. */
|
|
||||||
static void frUpdateWorld(frWorld *world, double dt);
|
|
||||||
|
|
||||||
/* ์ค๋ ฅ ๊ฐ์๋๊ฐ `gravity`์ด๊ณ ๊ฒฝ๊ณ ๋ฒ์๊ฐ `bounds`์ธ ์ธ๊ณ๋ฅผ ์์ฑํ๋ค. */
|
|
||||||
frWorld *frCreateWorld(Vector2 gravity, Rectangle bounds) {
|
|
||||||
frWorld *result = calloc(1, sizeof(*result));
|
|
||||||
|
|
||||||
result->gravity = gravity;
|
|
||||||
result->hash = frCreateSpatialHash(bounds, FR_BROADPHASE_CELL_SIZE);
|
|
||||||
result->timestamp = frGetCurrentTime();
|
|
||||||
|
|
||||||
arrsetcap(result->bodies, FR_WORLD_MAX_BODY_COUNT);
|
|
||||||
arrsetcap(result->collisions, FR_WORLD_MAX_BODY_COUNT * FR_WORLD_MAX_BODY_COUNT);
|
|
||||||
arrsetcap(result->queries, FR_WORLD_MAX_BODY_COUNT);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํด์ ํ๋ค. */
|
|
||||||
void frReleaseWorld(frWorld *world) {
|
|
||||||
if (world == NULL) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++) {
|
|
||||||
frBody *b = world->bodies[i];
|
|
||||||
frShape *s = frGetBodyShape(b);
|
|
||||||
|
|
||||||
frReleaseShape(s);
|
|
||||||
frReleaseBody(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
frReleaseSpatialHash(world->hash);
|
|
||||||
|
|
||||||
arrfree(world->bodies);
|
|
||||||
arrfree(world->collisions);
|
|
||||||
arrfree(world->queries);
|
|
||||||
|
|
||||||
free(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ๊ฐ์ฒด `b`๋ฅผ ์ถ๊ฐํ๋ค. */
|
|
||||||
bool frAddToWorld(frWorld *world, frBody *b) {
|
|
||||||
if (world == NULL || b == NULL || arrlen(world->bodies) >= FR_WORLD_MAX_BODY_COUNT)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
arrput(world->bodies, b);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ์ ๊ฑฐํ๋ค. */
|
|
||||||
void frClearWorld(frWorld *world) {
|
|
||||||
if (world == NULL) return;
|
|
||||||
|
|
||||||
frClearSpatialHash(world->hash);
|
|
||||||
|
|
||||||
arrsetlen(world->bodies, 0);
|
|
||||||
arrsetlen(world->collisions, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์์ ๊ฐ์ฒด `b`๋ฅผ ์ ๊ฑฐํ๋ค. */
|
|
||||||
bool frRemoveFromWorld(frWorld *world, frBody *b) {
|
|
||||||
if (world == NULL || b == NULL) return false;
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++) {
|
|
||||||
if (world->bodies[i] == b) {
|
|
||||||
arrdeln(world->bodies, i, 1);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ตฌ์กฐ์ฒด `frWorld`์ ํฌ๊ธฐ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
size_t frGetWorldStructSize(void) {
|
|
||||||
return sizeof(frWorld);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์์ ์ธ๋ฑ์ค๊ฐ `index`์ธ ๊ฐ์ฒด์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
frBody *frGetWorldBody(frWorld *world, int index) {
|
|
||||||
return (world != NULL && index >= 0 && index < arrlen(world->bodies))
|
|
||||||
? world->bodies[index]
|
|
||||||
: NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์ถฉ๋ ํธ๋ค๋ฌ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
frCollisionHandler frGetWorldCollisionHandler(frWorld *world) {
|
|
||||||
return (world != NULL) ? world->handler : FR_STRUCT_ZERO(frCollisionHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ๊ฐ์ฒด ๋ฐฐ์ด์ ํฌ๊ธฐ๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
int frGetWorldBodyCount(frWorld *world) {
|
|
||||||
return (world != NULL) ? arrlen(world->bodies) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ๊ฒฝ๊ณ ๋ฒ์๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Rectangle frGetWorldBounds(frWorld *world) {
|
|
||||||
return (world != NULL)
|
|
||||||
? frGetSpatialHashBounds(world->hash)
|
|
||||||
: FR_STRUCT_ZERO(Rectangle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ๊ณต๊ฐ ํด์๋งต์ ๋ฐํํ๋ค. */
|
|
||||||
frSpatialHash *frGetWorldSpatialHash(frWorld *world){
|
|
||||||
return (world != NULL) ? world->hash : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์ค๋ ฅ ๊ฐ์๋๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
Vector2 frGetWorldGravity(frWorld *world) {
|
|
||||||
return (world != NULL) ? world->gravity : FR_STRUCT_ZERO(Vector2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ๊ฐ์ฒด `b`๊ฐ ์ธ๊ณ `world`์ ๊ฒฝ๊ณ ๋ฒ์ ์์ ์๋์ง ํ์ธํ๋ค. */
|
|
||||||
bool frIsInWorldBounds(frWorld *world, frBody *b) {
|
|
||||||
if (world == NULL || b == NULL) return false;
|
|
||||||
|
|
||||||
return CheckCollisionRecs(frGetBodyAABB(b), frGetWorldBounds(world));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ๊ฒฝ๊ณ ๋ฒ์๋ฅผ `bounds`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetWorldBounds(frWorld *world, Rectangle bounds) {
|
|
||||||
if (world != NULL) frSetSpatialHashBounds(world->hash, bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์ถฉ๋ ํธ๋ค๋ฌ๋ฅผ `handler`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetWorldCollisionHandler(frWorld *world, frCollisionHandler handler) {
|
|
||||||
if (world != NULL) world->handler = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์ค๋ ฅ ๊ฐ์๋๋ฅผ `gravity`๋ก ์ค์ ํ๋ค. */
|
|
||||||
void frSetWorldGravity(frWorld *world, Vector2 gravity) {
|
|
||||||
if (world != NULL) world->gravity = gravity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์๊ฐ์ `dt` (๋จ์: ms)๋งํผ ํ๋ฅด๊ฒ ํ๋ค. */
|
|
||||||
void frSimulateWorld(frWorld *world, double dt) {
|
|
||||||
if (world == NULL || dt == 0.0f) return;
|
|
||||||
|
|
||||||
double currentTime = frGetCurrentTime();
|
|
||||||
double elapsedTime = frGetTimeDifference(currentTime, world->timestamp);
|
|
||||||
|
|
||||||
world->accumulator += elapsedTime;
|
|
||||||
world->timestamp = currentTime;
|
|
||||||
|
|
||||||
if (world->accumulator > FR_WORLD_ACCUMULATOR_LIMIT)
|
|
||||||
world->accumulator = FR_WORLD_ACCUMULATOR_LIMIT;
|
|
||||||
|
|
||||||
for (; world->accumulator >= dt; world->accumulator -= dt)
|
|
||||||
frUpdateWorld(world, dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์์ ์ง์ฌ๊ฐํ `rec`์ ๊ฒฝ๊ณ ๋ฒ์๊ฐ ๊ฒน์น๋ ๋ชจ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. */
|
|
||||||
int frQueryWorldSpatialHash(frWorld *world, Rectangle rec, frBody **bodies) {
|
|
||||||
if (world == NULL || bodies == NULL) return 0;
|
|
||||||
|
|
||||||
arrsetlen(world->queries, 0);
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++)
|
|
||||||
frAddToSpatialHash(world->hash, frGetBodyAABB(world->bodies[i]), i);
|
|
||||||
|
|
||||||
frQuerySpatialHash(world->hash, rec, &world->queries);
|
|
||||||
|
|
||||||
int result = arrlen(world->queries);
|
|
||||||
|
|
||||||
for (int i = 0; i < result; i++)
|
|
||||||
bodies[i] = world->bodies[world->queries[i]];
|
|
||||||
|
|
||||||
frClearSpatialHash(world->hash);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ๋ชจ๋ ๊ฐ์ฒด์ ๊ด์ ์ ํฌ์ฌํ๋ค. */
|
|
||||||
int frComputeWorldRaycast(frWorld *world, frRay ray, frRaycastHit *hits) {
|
|
||||||
if (world == NULL || hits == NULL) return 0;
|
|
||||||
|
|
||||||
arrsetlen(world->queries, 0);
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++)
|
|
||||||
frAddToSpatialHash(world->hash, frGetBodyAABB(world->bodies[i]), i);
|
|
||||||
|
|
||||||
Vector2 p1 = ray.origin, p2 = frVec2Add(
|
|
||||||
ray.origin,
|
|
||||||
frVec2ScalarMultiply(
|
|
||||||
frVec2Normalize(ray.direction),
|
|
||||||
ray.maxDistance
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
Rectangle rec = (Rectangle) {
|
|
||||||
.x = fminf(p1.x, p2.x),
|
|
||||||
.y = fminf(p1.y, p2.y),
|
|
||||||
.width = fabsf(p2.x - p1.x),
|
|
||||||
.height = fabsf(p2.y - p1.y)
|
|
||||||
};
|
|
||||||
|
|
||||||
frQuerySpatialHash(world->hash, rec, &world->queries);
|
|
||||||
|
|
||||||
if (arrlen(world->queries) <= 0) return 0;
|
|
||||||
|
|
||||||
if (ray.closest) {
|
|
||||||
float minDistance = FLT_MAX;
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->queries); i++) {
|
|
||||||
frBody *body = world->bodies[world->queries[i]];
|
|
||||||
|
|
||||||
frRaycastHit raycast = frComputeBodyRaycast(body, ray);
|
|
||||||
|
|
||||||
if (!raycast.check) continue;
|
|
||||||
|
|
||||||
if (minDistance > raycast.distance) {
|
|
||||||
minDistance = raycast.distance;
|
|
||||||
hits[0] = raycast;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frClearSpatialHash(world->hash);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->queries); i++) {
|
|
||||||
frBody *body = world->bodies[world->queries[i]];
|
|
||||||
|
|
||||||
frRaycastHit raycast = frComputeBodyRaycast(body, ray);
|
|
||||||
|
|
||||||
if (!raycast.check) continue;
|
|
||||||
|
|
||||||
hits[count++] = raycast;
|
|
||||||
}
|
|
||||||
|
|
||||||
frClearSpatialHash(world->hash);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์
๋ฐ์ดํธ ์ด์ ์์
์ ์คํํ๋ค. */
|
|
||||||
static void frPreUpdateWorld(frWorld *world) {
|
|
||||||
if (world == NULL || world->hash == NULL) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++)
|
|
||||||
frAddToSpatialHash(world->hash, frGetBodyAABB(world->bodies[i]), i);
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++) {
|
|
||||||
frQuerySpatialHash(
|
|
||||||
world->hash,
|
|
||||||
frGetBodyAABB(world->bodies[i]),
|
|
||||||
&world->queries
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int k = 0; k < arrlen(world->queries); k++) {
|
|
||||||
int j = world->queries[k];
|
|
||||||
|
|
||||||
if (j <= i) continue;
|
|
||||||
|
|
||||||
frBody *b1 = world->bodies[i];
|
|
||||||
frBody *b2 = world->bodies[j];
|
|
||||||
|
|
||||||
frCollision collision = frComputeBodyCollision(b1, b2);
|
|
||||||
|
|
||||||
if (collision.check) {
|
|
||||||
collision.cache.bodies[0] = b1;
|
|
||||||
collision.cache.bodies[1] = b2;
|
|
||||||
|
|
||||||
arrput(world->collisions, collision);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arrsetlen(world->queries, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`์ ์
๋ฐ์ดํธ ์ดํ ์์
์ ์คํํ๋ค. */
|
|
||||||
static void frPostUpdateWorld(frWorld *world) {
|
|
||||||
if (world == NULL || world->hash == NULL) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++)
|
|
||||||
frClearBodyForces(world->bodies[i]);
|
|
||||||
|
|
||||||
arrsetlen(world->collisions, 0);
|
|
||||||
|
|
||||||
frClearSpatialHash(world->hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ์ธ๊ณ `world`๋ฅผ ์๊ฐ `dt` (๋จ์: ms)๋งํผ ์
๋ฐ์ดํธํ๋ค. */
|
|
||||||
static void frUpdateWorld(frWorld *world, double dt) {
|
|
||||||
if (world == NULL || world->bodies == NULL || world->collisions == NULL) return;
|
|
||||||
|
|
||||||
frPreUpdateWorld(world);
|
|
||||||
|
|
||||||
// ๊ฐ์ฒด ์ฌ์ด์ ์ถฉ๋ ํด๊ฒฐ ์ง์ ์ ์ฌ์ ์ ์๋ ํจ์๋ฅผ ํธ์ถํ๋ค.
|
|
||||||
for (int i = 0; i < arrlen(world->collisions); i++) {
|
|
||||||
frCollisionCallback preSolveCallback = world->handler.preSolve;
|
|
||||||
|
|
||||||
if (preSolveCallback != NULL) preSolveCallback(&world->collisions[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ๊ฐ์ฒด์ ์ค๋ ฅ์ ์ ์ฉํ๊ณ , ๊ฐ์ฒด์ ์๋์ ๊ฐ์๋๋ฅผ ๊ณ์ฐํ๋ค.
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++) {
|
|
||||||
frApplyGravity(world->bodies[i], world->gravity);
|
|
||||||
frIntegrateForBodyVelocities(world->bodies[i], dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ์์ฐจ์ ์ผ๋ก ์ถฉ๊ฒฉ๋์ ๋ฐ๋ณต ์ ์ฉํ์ฌ, ๋ ๊ฐ์ฒด ์ฌ์ด์ ์ถฉ๋์ ํด๊ฒฐํ๋ค.
|
|
||||||
for (int i = 0; i < FR_WORLD_MAX_ITERATIONS; i++)
|
|
||||||
for (int j = 0; j < arrlen(world->collisions); j++)
|
|
||||||
frResolveCollision(&world->collisions[j]);
|
|
||||||
|
|
||||||
// ๊ฐ์ฒด์ ํ์ฌ ์์น๋ฅผ ๊ณ์ฐํ๋ค.
|
|
||||||
for (int i = 0; i < arrlen(world->bodies); i++)
|
|
||||||
frIntegrateForBodyPosition(world->bodies[i], dt);
|
|
||||||
|
|
||||||
float inverseDt = (dt != 0.0f) ? (1.0f / dt) : 0.0f;
|
|
||||||
|
|
||||||
// ๊ฐ์ฒด์ ์์น๋ฅผ ์ ์ ํ๊ฒ ๋ณด์ ํ๋ค.
|
|
||||||
for (int i = 0; i < arrlen(world->collisions); i++)
|
|
||||||
frCorrectBodyPositions(&world->collisions[i], inverseDt);
|
|
||||||
|
|
||||||
// ๊ฐ์ฒด ์ฌ์ด์ ์ถฉ๋ ํด๊ฒฐ ์งํ์ ์ฌ์ ์ ์๋ ํจ์๋ฅผ ํธ์ถํ๋ค.
|
|
||||||
for (int i = 0; i < arrlen(world->collisions); i++) {
|
|
||||||
frCollisionCallback postSolveCallback = world->handler.postSolve;
|
|
||||||
|
|
||||||
if (postSolveCallback != NULL) postSolveCallback(&world->collisions[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
frPostUpdateWorld(world);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -40,6 +40,8 @@
|
||||||
<Define>_WIN64</Define>
|
<Define>_WIN64</Define>
|
||||||
<Define>RAYLIB_NUKLEAR_IMPLEMENTATION</Define>
|
<Define>RAYLIB_NUKLEAR_IMPLEMENTATION</Define>
|
||||||
<Define>NK_IMPLEMENTATION</Define>
|
<Define>NK_IMPLEMENTATION</Define>
|
||||||
|
<Define>TINYC2_IMPL</Define>
|
||||||
|
<Define>CUTE_C2_IMPLEMENTATION</Define>
|
||||||
</Defines>
|
</Defines>
|
||||||
<ConfigProperties>
|
<ConfigProperties>
|
||||||
<ConfigAndPlatform>
|
<ConfigAndPlatform>
|
||||||
|
|
Loadingโฆ
Reference in New Issue