physics: handle entity collisions

isolation_bkp/dynres
Dominik Madarász 2021-07-26 17:53:18 +02:00
parent 37dc992c2e
commit 99a65431d8
11 changed files with 169 additions and 54 deletions

View File

@ -10,6 +10,7 @@ typedef enum {
PROF_UPDATE_SYSTEMS,
PROF_ENTITY_LERP,
PROF_ENTITY_REMOVAL,
PROF_INTEGRATE_POS,
MAX_PROF,
PROF_FORCE_UINT8 = UINT8_MAX

View File

@ -4,7 +4,7 @@
#define BLOCK_INVALID 0xF
typedef enum {
BLOCK_FLAG_COLLISION = (1 << 0)
BLOCK_FLAG_COLLISION = (1 << 1)
} block_flags;
#include "blocks_info.h"

View File

@ -30,11 +30,13 @@ typedef struct {
uint32_t size;
uint16_t chunk_size;
uint16_t chunk_amount;
uint8_t **block_mapping;
uint16_t dim;
uint64_t tracker_update[3];
uint8_t active_layer_id;
ecs_world_t *ecs;
ecs_query_t *ecs_update;
ecs_entity_t *chunk_mapping;
librg_world *tracker;
world_pkt_reader_proc *reader_proc;
world_pkt_writer_proc *writer_proc;
@ -55,3 +57,13 @@ librg_world * world_tracker(void);
uint16_t world_chunk_size(void);
uint16_t world_chunk_amount(void);
uint16_t world_dim(void);
ecs_entity_t world_chunk_mapping(librg_chunk id);
typedef struct {
uint32_t id;
uint8_t block_id;
ecs_entity_t chunk_id;
float ox, oy;
} world_block_lookup;
world_block_lookup world_block_from_realpos(float x, float y);

View File

@ -46,6 +46,11 @@ typedef struct debug_item {
uint8_t is_collapsed;
} list;
struct {
float val, min, max;
void (*on_change)(float);
} slider;
void (*on_click)(void);
};
@ -79,6 +84,7 @@ static debug_item items[] = {
{ .kind = DITEM_TEXT, .name = "delta time", .proc = DrawDeltaTime },
{ .kind = DITEM_TEXT, .name = "random literal", .text = "hello", .proc = DrawLiteral },
{ .kind = DITEM_TEXT, .name = "zoom", .proc = DrawZoom },
{ .kind = DITEM_SLIDER, .name = "slider", .slider = { .min = 0.0f, .max = 1.0f, .val = 0.5f } },
{ .kind = DITEM_END },
}
}
@ -95,6 +101,7 @@ static debug_item items[] = {
{ .kind = DITEM_RAW, .val = PROF_UPDATE_SYSTEMS, .proc = DrawProfilerDelta },
{ .kind = DITEM_RAW, .val = PROF_ENTITY_LERP, .proc = DrawProfilerDelta },
{ .kind = DITEM_RAW, .val = PROF_ENTITY_REMOVAL, .proc = DrawProfilerDelta },
{ .kind = DITEM_RAW, .val = PROF_INTEGRATE_POS, .proc = DrawProfilerDelta },
{ .kind = DITEM_END },
},
.is_collapsed = 1
@ -163,6 +170,25 @@ debug_draw_result debug_draw_list(debug_item *list, float xpos, float ypos, bool
ypos = res.y;
}break;
case DITEM_SLIDER: {
assert(it->slider.min != it->slider.max);
char const *text = TextFormat("%s: ", it->name);
if (it->name_width == 0) {
it->name_width = UIMeasureText(text, DBG_FONT_SIZE);
}
UIDrawText(text, xpos, ypos, DBG_FONT_SIZE, RAYWHITE);
xpos += it->name_width;
DrawRectangleLines(xpos, ypos, 100.0f, DBG_FONT_SIZE, RAYWHITE);
float stick_x = xpos + ((it->slider.val / it->slider.max) * 100.0f) - 5.0f;
DrawRectangle(stick_x, ypos, 10.0f, DBG_FONT_SIZE, RED);
xpos += 100.0f + 5.0f;
DrawFloat(xpos, ypos, it->slider.val);
ypos += DBG_FONT_SPACING;
}break;
default: {
}break;

View File

@ -22,11 +22,19 @@ uint64_t entity_spawn(char *name) {
ecs_set(world_ecs(), e, EcsName, {.alloc_value = name });
ecs_set(world_ecs(), e, Velocity, {0});
ecs_set(world_ecs(), e, Position, {0});
ecs_add(world_ecs(), e, Walking);
Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL);
#if 1
pos->x=rand() % world_dim();
pos->y=rand() % world_dim();
#else
pos->x=35;
pos->y=33;
#endif
librg_entity_track(world_tracker(), e);
librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), 0, 0, 0));
librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0));
return (uint64_t)e;
}

View File

@ -21,28 +21,18 @@ uint64_t player_spawn(char *name) {
ECS_IMPORT(world_ecs(), General);
ECS_IMPORT(world_ecs(), Controllers);
ECS_IMPORT(world_ecs(), Net);
ecs_set(world_ecs(), e, EcsName, {.alloc_value = name });
ecs_add(world_ecs(), e, EcsClient);
ecs_set(world_ecs(), e, ClientInfo, {0});
ecs_set(world_ecs(), e, Input, {0});
ecs_add(world_ecs(), e, Player);
Position *pos = ecs_get_mut(world_ecs(), e, Position, NULL);
#if 1
pos->x=rand() % world_dim();
pos->y=rand() % world_dim();
#else
pos->x=10;
pos->y=10;
#endif
librg_entity_owner_set(world_tracker(), e, (int64_t)e);
librg_entity_radius_set(world_tracker(), e, 3);
librg_entity_chunk_set(world_tracker(), e, librg_chunk_from_realpos(world_tracker(), pos->x, pos->y, 0));
return (uint64_t)e;
}
void player_despawn(uint64_t ent_id) {
librg_entity_untrack(world_tracker(), ent_id);
ecs_delete(world_ecs(), ent_id);
entity_despawn(ent_id);
}

View File

@ -13,6 +13,7 @@ static profiler profilers[] = {
{ .id = PROF_UPDATE_SYSTEMS, .name = "update systems" },
{ .id = PROF_ENTITY_LERP, .name = "entity lerp" },
{ .id = PROF_ENTITY_REMOVAL, .name = "entity removal" },
{ .id = PROF_INTEGRATE_POS, .name = "entity movement" },
};
static_assert((sizeof(profilers)/sizeof(profilers[0])) == MAX_PROF, "mismatched profilers");

View File

@ -42,7 +42,7 @@ entity_view world_build_entity_view(int64_t e) {
view.blocks_used = 1;
for (int i = 0; i < world.chunk_size*world.chunk_size; i += 1) {
view.blocks[i] = *ecs_vector_get(chpos->blocks, uint8_t, i);
view.blocks[i] = world.block_mapping[chpos->id][i];
}
}
@ -139,6 +139,8 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
ECS_IMPORT(world.ecs, General);
ECS_IMPORT(world.ecs, Net);
world.ecs_update = ecs_query_new(world.ecs, "net.ClientInfo, general.Position");
world.chunk_mapping = zpl_malloc(sizeof(ecs_entity_t)*zpl_square(chunk_amount));
world.block_mapping = zpl_malloc(sizeof(uint8_t*)*zpl_square(chunk_amount));
int32_t world_build_status = worldgen_test(&world);
ZPL_ASSERT(world_build_status >= 0);
@ -149,14 +151,16 @@ int32_t world_init(int32_t seed, uint16_t chunk_size, uint16_t chunk_amount) {
librg_entity_track(world.tracker, e);
librg_entity_chunk_set(world.tracker, e, i);
librg_chunk_to_chunkpos(world.tracker, i, &chunk->x, &chunk->y, NULL);
chunk->blocks = NULL;
world.chunk_mapping[i] = e;
world.block_mapping[i] = zpl_malloc(sizeof(uint8_t)*zpl_square(chunk_size));
chunk->id = i;
for (int y = 0; y < world.chunk_size; y += 1) {
for (int x = 0; x < world.chunk_size; x += 1) {
int chk = world.chunk_size * i;
int chk_x = chk % world.dim;
int chk_y = chk / world.dim;
uint8_t *c = ecs_vector_add(&chunk->blocks, uint8_t);
uint8_t *c = &world.block_mapping[i][(y*chunk_size)+x];
*c = world.data[(chk_y+y)*world.dim + (chk_x+x)];
}
}
@ -171,6 +175,11 @@ int32_t world_destroy(void) {
librg_world_destroy(world.tracker);
ecs_fini(world.ecs);
zpl_mfree(world.data);
zpl_mfree(world.chunk_mapping);
for (int i = 0; i < zpl_square(world.chunk_amount); i+=1) {
zpl_mfree(world.block_mapping[i]);
}
zpl_mfree(world.block_mapping);
zpl_memset(&world, 0, sizeof(world));
zpl_printf("[INFO] World was destroyed.\n");
return WORLD_ERROR_NONE;
@ -269,3 +278,41 @@ uint16_t world_chunk_amount(void) {
uint16_t world_dim(void) {
return WORLD_BLOCK_SIZE * world.chunk_size * world.chunk_amount;
}
ecs_entity_t world_chunk_mapping(librg_chunk id) {
assert(id >= 0 && id < zpl_square(world.chunk_amount));
return world.chunk_mapping[id];
}
world_block_lookup world_block_from_realpos(float x, float y) {
x = zpl_clamp(x, 0, world_dim()-1);
y = zpl_clamp(y, 0, world_dim()-1);
librg_chunk chunk_id = librg_chunk_from_realpos(world.tracker, x, y, 0);
ecs_entity_t e = world.chunk_mapping[chunk_id];
int32_t size = world.chunk_size * WORLD_BLOCK_SIZE;
int16_t chunk_x, chunk_y;
librg_chunk_to_chunkpos(world.tracker, chunk_id, &chunk_x, &chunk_y, NULL);
// NOTE(zaklaus): pos relative to chunk
float chx = x - chunk_x * size;
float chy = y - chunk_y * size;
uint32_t bx = (uint32_t)chx / WORLD_BLOCK_SIZE;
uint32_t by = (uint32_t)chy / WORLD_BLOCK_SIZE;
uint32_t block_idx = (by*world.chunk_size)+bx;
uint8_t block_id = world.block_mapping[chunk_id][block_idx];
// NOTE(zaklaus): pos relative to block's center
float box = chx - bx * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
float boy = chy - by * WORLD_BLOCK_SIZE - WORLD_BLOCK_SIZE/2.0f;
world_block_lookup lookup = {
.id = block_idx,
.block_id = block_id,
.chunk_id = e,
.ox = box,
.oy = boy,
};
return lookup;
}

View File

@ -9,9 +9,10 @@ ECS_STRUCT(Vector2D, {
});
ECS_STRUCT(Chunk, {
uint32_t id;
int16_t x;
int16_t y;
ecs_vector(uint8_t) blocks;
//ecs_vector(uint8_t) blocks;
});
ECS_STRUCT(Drawable, {

View File

@ -13,7 +13,7 @@ typedef struct {
ECS_DECLARE_COMPONENT(Velocity);
ECS_DECLARE_ENTITY(MoveWalk);
ECS_DECLARE_ENTITY(UpdateTrackerPos);
ECS_DECLARE_ENTITY(HandleCollisions);
ECS_DECLARE_ENTITY(IntegratePositions);
} Physics;
#define PhysicsImportHandles(handles)\
@ -23,6 +23,6 @@ typedef struct {
ECS_IMPORT_COMPONENT(handles, Velocity);\
ECS_IMPORT_ENTITY(handles, MoveWalk);\
ECS_IMPORT_ENTITY(handles, UpdateTrackerPos);\
ECS_IMPORT_ENTITY(handles, HandleCollisions);\
ECS_IMPORT_ENTITY(handles, IntegratePositions);\
void PhysicsImport(ecs_world_t *ecs);

View File

@ -1,23 +1,28 @@
#include "zpl.h"
#include "modules/physics.h"
#include "world/world.h"
#include "world/blocks.h"
#include "profiler.h"
#define PHY_BLOCK_COLLISION 1
#define PHY_WALK_DRAG 0.12
#define PHY_LOOKAHEAD(x) (zpl_sign(x)*16.0f)
#define PHY_CORRECTION(x) ((zpl_max(0.0f, (WORLD_BLOCK_SIZE/2.0f) - zpl_abs(x))*zpl_sign(x)))*(WORLD_BLOCK_SIZE/2.0f)
void MoveWalk(ecs_iter_t *it) {
Position *p = ecs_column(it, Position, 1);
Velocity *v = ecs_column(it, Velocity, 2);
Velocity *v = ecs_column(it, Velocity, 1);
for (int i = 0; i < it->count; i++) {
p[i].x += v[i].x * it->delta_time;
p[i].y += v[i].y * it->delta_time;
v[i].x = zpl_lerp(v[i].x, 0.0f, PHY_WALK_DRAG);
v[i].y = zpl_lerp(v[i].y, 0.0f, PHY_WALK_DRAG);
}
}
void HandleCollisions(ecs_iter_t *it) {
void IntegratePositions(ecs_iter_t *it) {
profile(PROF_INTEGRATE_POS) {
Position *p = ecs_column(it, Position, 1);
Velocity *v = ecs_column(it, Velocity, 2);
for (int i = 0; i < it->count; i++) {
// NOTE(zaklaus): world bounds
@ -26,6 +31,30 @@ void HandleCollisions(ecs_iter_t *it) {
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.block_id);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].x = PHY_CORRECTION(lookup.ox);
}
}
// 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.block_id);
if (flags & BLOCK_FLAG_COLLISION) {
v[i].y = PHY_CORRECTION(lookup.oy);
}
}
#endif
p[i].x += v[i].x * it->delta_time;
p[i].y += v[i].y * it->delta_time;
}
}
}
@ -47,8 +76,8 @@ void PhysicsImport(ecs_world_t *ecs) {
ECS_META(ecs, Velocity);
ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, general.Position, Velocity);
ECS_SYSTEM(ecs, HandleCollisions, EcsOnValidate, general.Position, Velocity);
ECS_SYSTEM(ecs, MoveWalk, EcsOnUpdate, Velocity);
ECS_SYSTEM(ecs, IntegratePositions, EcsOnValidate, general.Position, Velocity);
ECS_SYSTEM(ecs, UpdateTrackerPos, EcsPostUpdate, general.Position);
ECS_SET_TYPE(Movement);
@ -57,5 +86,5 @@ void PhysicsImport(ecs_world_t *ecs) {
ECS_SET_COMPONENT(Velocity);
ECS_SET_ENTITY(MoveWalk);
ECS_SET_ENTITY(UpdateTrackerPos);
ECS_SET_ENTITY(HandleCollisions);
ECS_SET_ENTITY(IntegratePositions);
}