refactor separate framework from game code, add demo3d
This commit is contained in:
parent
19ae869769
commit
40f5cdcaa5
92 changed files with 2665 additions and 6547 deletions
17
demo3d/client/world/demo3d_chunk.c
Normal file
17
demo3d/client/world/demo3d_chunk.c
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#include "demo3d_chunk.h"
|
||||
|
||||
#include "demo3d_bsp.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
demo3d_chunk* demo3d_chunk_create_bsp(u32 id) {
|
||||
demo3d_chunk* chunk = pxl8_calloc(1, sizeof(demo3d_chunk));
|
||||
if (!chunk) return NULL;
|
||||
chunk->id = id;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
void demo3d_chunk_destroy(demo3d_chunk* chunk) {
|
||||
if (!chunk) return;
|
||||
if (chunk->bsp) demo3d_bsp_destroy(chunk->bsp);
|
||||
pxl8_free(chunk);
|
||||
}
|
||||
21
demo3d/client/world/demo3d_chunk.h
Normal file
21
demo3d/client/world/demo3d_chunk.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "demo3d_bsp.h"
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct demo3d_chunk {
|
||||
u32 id;
|
||||
u32 version;
|
||||
demo3d_bsp* bsp;
|
||||
} demo3d_chunk;
|
||||
|
||||
demo3d_chunk* demo3d_chunk_create_bsp(u32 id);
|
||||
void demo3d_chunk_destroy(demo3d_chunk* chunk);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
401
demo3d/client/world/demo3d_chunk_cache.c
Normal file
401
demo3d/client/world/demo3d_chunk_cache.c
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
#include "demo3d_chunk_cache.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_bytes.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
static demo3d_chunk_cache_entry* find_entry_bsp(demo3d_chunk_cache* cache, u32 id) {
|
||||
for (u32 i = 0; i < cache->entry_count; i++) {
|
||||
demo3d_chunk_cache_entry* e = &cache->entries[i];
|
||||
if (e->valid && e->chunk && e->chunk->id == id) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static demo3d_chunk_cache_entry* alloc_entry(demo3d_chunk_cache* cache) {
|
||||
if (cache->entry_count < DEMO3D_CHUNK_CACHE_SIZE) {
|
||||
demo3d_chunk_cache_entry* e = &cache->entries[cache->entry_count++];
|
||||
memset(e, 0, sizeof(*e));
|
||||
return e;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < DEMO3D_CHUNK_CACHE_SIZE; i++) {
|
||||
if (!cache->entries[i].valid) {
|
||||
demo3d_chunk_cache_entry* e = &cache->entries[i];
|
||||
memset(e, 0, sizeof(*e));
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
u64 oldest = cache->entries[0].last_used;
|
||||
u32 slot = 0;
|
||||
for (u32 i = 1; i < DEMO3D_CHUNK_CACHE_SIZE; i++) {
|
||||
if (cache->entries[i].last_used < oldest) {
|
||||
oldest = cache->entries[i].last_used;
|
||||
slot = i;
|
||||
}
|
||||
}
|
||||
|
||||
demo3d_chunk_cache_entry* e = &cache->entries[slot];
|
||||
if (e->chunk) {
|
||||
demo3d_chunk_destroy(e->chunk);
|
||||
}
|
||||
memset(e, 0, sizeof(*e));
|
||||
return e;
|
||||
}
|
||||
|
||||
static void assembly_reset(demo3d_chunk_assembly* a) {
|
||||
a->id = 0;
|
||||
a->cx = 0;
|
||||
a->cy = 0;
|
||||
a->cz = 0;
|
||||
a->version = 0;
|
||||
a->fragment_count = 0;
|
||||
a->fragments_received = 0;
|
||||
a->data_size = 0;
|
||||
a->active = false;
|
||||
a->complete = false;
|
||||
}
|
||||
|
||||
static void assembly_init(demo3d_chunk_assembly* a, const demo3d_chunk_msg_header* hdr) {
|
||||
assembly_reset(a);
|
||||
a->id = hdr->id;
|
||||
a->cx = hdr->cx;
|
||||
a->cy = hdr->cy;
|
||||
a->cz = hdr->cz;
|
||||
a->version = hdr->version;
|
||||
a->fragment_count = hdr->fragment_count;
|
||||
a->active = true;
|
||||
|
||||
u32 needed = DEMO3D_CHUNK_MAX_PAYLOAD * hdr->fragment_count;
|
||||
if (a->data_capacity < needed) {
|
||||
a->data_capacity = needed;
|
||||
a->data = pxl8_realloc(a->data, a->data_capacity);
|
||||
}
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_vertex(pxl8_stream* s, demo3d_bsp_vertex* v) {
|
||||
v->position.x = pxl8_read_f32_be(s);
|
||||
v->position.y = pxl8_read_f32_be(s);
|
||||
v->position.z = pxl8_read_f32_be(s);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_edge(pxl8_stream* s, demo3d_bsp_edge* e) {
|
||||
e->vertex[0] = pxl8_read_u16_be(s);
|
||||
e->vertex[1] = pxl8_read_u16_be(s);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_plane(pxl8_stream* s, demo3d_bsp_plane* p) {
|
||||
p->normal.x = pxl8_read_f32_be(s);
|
||||
p->normal.y = pxl8_read_f32_be(s);
|
||||
p->normal.z = pxl8_read_f32_be(s);
|
||||
p->dist = pxl8_read_f32_be(s);
|
||||
p->type = (i32)pxl8_read_u32_be(s);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_face(pxl8_stream* s, demo3d_bsp_face* f) {
|
||||
f->first_edge = pxl8_read_u32_be(s);
|
||||
f->lightmap_offset = pxl8_read_u32_be(s);
|
||||
f->num_edges = pxl8_read_u16_be(s);
|
||||
f->plane_id = pxl8_read_u16_be(s);
|
||||
f->side = pxl8_read_u16_be(s);
|
||||
pxl8_read_bytes(s, f->styles, 4);
|
||||
f->material_id = pxl8_read_u16_be(s);
|
||||
f->aabb_min.x = pxl8_read_f32_be(s);
|
||||
f->aabb_min.y = pxl8_read_f32_be(s);
|
||||
f->aabb_min.z = pxl8_read_f32_be(s);
|
||||
f->aabb_max.x = pxl8_read_f32_be(s);
|
||||
f->aabb_max.y = pxl8_read_f32_be(s);
|
||||
f->aabb_max.z = pxl8_read_f32_be(s);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_node(pxl8_stream* s, demo3d_bsp_node* n) {
|
||||
n->children[0] = (i32)pxl8_read_u32_be(s);
|
||||
n->children[1] = (i32)pxl8_read_u32_be(s);
|
||||
n->first_face = pxl8_read_u16_be(s);
|
||||
n->maxs[0] = (i16)pxl8_read_u16_be(s);
|
||||
n->maxs[1] = (i16)pxl8_read_u16_be(s);
|
||||
n->maxs[2] = (i16)pxl8_read_u16_be(s);
|
||||
n->mins[0] = (i16)pxl8_read_u16_be(s);
|
||||
n->mins[1] = (i16)pxl8_read_u16_be(s);
|
||||
n->mins[2] = (i16)pxl8_read_u16_be(s);
|
||||
n->num_faces = pxl8_read_u16_be(s);
|
||||
n->plane_id = pxl8_read_u32_be(s);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_leaf(pxl8_stream* s, demo3d_bsp_leaf* l) {
|
||||
pxl8_read_bytes(s, l->ambient_level, 4);
|
||||
l->contents = (i32)pxl8_read_u32_be(s);
|
||||
l->first_marksurface = pxl8_read_u16_be(s);
|
||||
l->maxs[0] = (i16)pxl8_read_u16_be(s);
|
||||
l->maxs[1] = (i16)pxl8_read_u16_be(s);
|
||||
l->maxs[2] = (i16)pxl8_read_u16_be(s);
|
||||
l->mins[0] = (i16)pxl8_read_u16_be(s);
|
||||
l->mins[1] = (i16)pxl8_read_u16_be(s);
|
||||
l->mins[2] = (i16)pxl8_read_u16_be(s);
|
||||
l->num_marksurfaces = pxl8_read_u16_be(s);
|
||||
l->visofs = (i32)pxl8_read_u32_be(s);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_portal(pxl8_stream* s, demo3d_bsp_portal* p) {
|
||||
p->x0 = pxl8_read_f32_be(s);
|
||||
p->z0 = pxl8_read_f32_be(s);
|
||||
p->x1 = pxl8_read_f32_be(s);
|
||||
p->z1 = pxl8_read_f32_be(s);
|
||||
p->target_leaf = pxl8_read_u32_be(s);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static pxl8_result deserialize_cell_portals(pxl8_stream* s, demo3d_bsp_cell_portals* cp) {
|
||||
cp->num_portals = pxl8_read_u8(s);
|
||||
pxl8_read_u8(s);
|
||||
pxl8_read_u8(s);
|
||||
pxl8_read_u8(s);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
deserialize_portal(s, &cp->portals[i]);
|
||||
}
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
static demo3d_bsp* assembly_to_bsp(demo3d_chunk_assembly* a) {
|
||||
if (!a->complete || a->data_size < 48) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
demo3d_bsp* bsp = pxl8_calloc(1, sizeof(demo3d_bsp));
|
||||
if (!bsp) return NULL;
|
||||
|
||||
pxl8_stream s = pxl8_stream_create(a->data, (u32)a->data_size);
|
||||
|
||||
demo3d_bsp_wire_header wire_hdr;
|
||||
demo3d_protocol_deserialize_bsp_wire_header(a->data, 48, &wire_hdr);
|
||||
s.offset = 48;
|
||||
|
||||
if (wire_hdr.num_vertices > 0) {
|
||||
bsp->vertices = pxl8_calloc(wire_hdr.num_vertices, sizeof(demo3d_bsp_vertex));
|
||||
bsp->num_vertices = wire_hdr.num_vertices;
|
||||
for (u32 i = 0; i < wire_hdr.num_vertices; i++) {
|
||||
deserialize_vertex(&s, &bsp->vertices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_edges > 0) {
|
||||
bsp->edges = pxl8_calloc(wire_hdr.num_edges, sizeof(demo3d_bsp_edge));
|
||||
bsp->num_edges = wire_hdr.num_edges;
|
||||
for (u32 i = 0; i < wire_hdr.num_edges; i++) {
|
||||
deserialize_edge(&s, &bsp->edges[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_surfedges > 0) {
|
||||
bsp->surfedges = pxl8_calloc(wire_hdr.num_surfedges, sizeof(i32));
|
||||
bsp->num_surfedges = wire_hdr.num_surfedges;
|
||||
for (u32 i = 0; i < wire_hdr.num_surfedges; i++) {
|
||||
bsp->surfedges[i] = (i32)pxl8_read_u32_be(&s);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_planes > 0) {
|
||||
bsp->planes = pxl8_calloc(wire_hdr.num_planes, sizeof(demo3d_bsp_plane));
|
||||
bsp->num_planes = wire_hdr.num_planes;
|
||||
for (u32 i = 0; i < wire_hdr.num_planes; i++) {
|
||||
deserialize_plane(&s, &bsp->planes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_faces > 0) {
|
||||
bsp->faces = pxl8_calloc(wire_hdr.num_faces, sizeof(demo3d_bsp_face));
|
||||
bsp->num_faces = wire_hdr.num_faces;
|
||||
for (u32 i = 0; i < wire_hdr.num_faces; i++) {
|
||||
deserialize_face(&s, &bsp->faces[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_nodes > 0) {
|
||||
bsp->nodes = pxl8_calloc(wire_hdr.num_nodes, sizeof(demo3d_bsp_node));
|
||||
bsp->num_nodes = wire_hdr.num_nodes;
|
||||
for (u32 i = 0; i < wire_hdr.num_nodes; i++) {
|
||||
deserialize_node(&s, &bsp->nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_leafs > 0) {
|
||||
bsp->leafs = pxl8_calloc(wire_hdr.num_leafs, sizeof(demo3d_bsp_leaf));
|
||||
bsp->num_leafs = wire_hdr.num_leafs;
|
||||
for (u32 i = 0; i < wire_hdr.num_leafs; i++) {
|
||||
deserialize_leaf(&s, &bsp->leafs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_marksurfaces > 0) {
|
||||
bsp->marksurfaces = pxl8_calloc(wire_hdr.num_marksurfaces, sizeof(u16));
|
||||
bsp->num_marksurfaces = wire_hdr.num_marksurfaces;
|
||||
for (u32 i = 0; i < wire_hdr.num_marksurfaces; i++) {
|
||||
bsp->marksurfaces[i] = pxl8_read_u16_be(&s);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_cell_portals > 0) {
|
||||
bsp->cell_portals = pxl8_calloc(wire_hdr.num_cell_portals, sizeof(demo3d_bsp_cell_portals));
|
||||
bsp->num_cell_portals = wire_hdr.num_cell_portals;
|
||||
for (u32 i = 0; i < wire_hdr.num_cell_portals; i++) {
|
||||
deserialize_cell_portals(&s, &bsp->cell_portals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.visdata_size > 0) {
|
||||
bsp->visdata = pxl8_malloc(wire_hdr.visdata_size);
|
||||
bsp->visdata_size = wire_hdr.visdata_size;
|
||||
pxl8_read_bytes(&s, bsp->visdata, wire_hdr.visdata_size);
|
||||
}
|
||||
|
||||
if (wire_hdr.num_vertex_lights > 0) {
|
||||
bsp->vertex_lights = pxl8_calloc(wire_hdr.num_vertex_lights, sizeof(u32));
|
||||
bsp->num_vertex_lights = wire_hdr.num_vertex_lights;
|
||||
for (u32 i = 0; i < wire_hdr.num_vertex_lights; i++) {
|
||||
bsp->vertex_lights[i] = pxl8_read_u32_be(&s);
|
||||
}
|
||||
}
|
||||
|
||||
if (wire_hdr.num_heightfield > 0) {
|
||||
bsp->heightfield = pxl8_calloc(wire_hdr.num_heightfield, sizeof(f32));
|
||||
bsp->num_heightfield = wire_hdr.num_heightfield;
|
||||
for (u32 i = 0; i < wire_hdr.num_heightfield; i++) {
|
||||
u32 raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->heightfield[i], &raw, sizeof(f32));
|
||||
}
|
||||
bsp->heightfield_w = pxl8_read_u16_be(&s);
|
||||
bsp->heightfield_h = pxl8_read_u16_be(&s);
|
||||
u32 ox_raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->heightfield_ox, &ox_raw, sizeof(f32));
|
||||
u32 oz_raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->heightfield_oz, &oz_raw, sizeof(f32));
|
||||
u32 cs_raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->heightfield_cell_size, &cs_raw, sizeof(f32));
|
||||
}
|
||||
|
||||
u32 raw;
|
||||
raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->bounds_min_x, &raw, sizeof(f32));
|
||||
raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->bounds_min_z, &raw, sizeof(f32));
|
||||
raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->bounds_max_x, &raw, sizeof(f32));
|
||||
raw = pxl8_read_u32_be(&s);
|
||||
memcpy(&bsp->bounds_max_z, &raw, sizeof(f32));
|
||||
|
||||
return bsp;
|
||||
}
|
||||
|
||||
static pxl8_result assemble_bsp(demo3d_chunk_cache* cache, demo3d_chunk_assembly* a) {
|
||||
demo3d_bsp* bsp = assembly_to_bsp(a);
|
||||
if (!bsp) {
|
||||
assembly_reset(a);
|
||||
return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
demo3d_chunk_cache_entry* entry = find_entry_bsp(cache, a->id);
|
||||
if (entry) {
|
||||
if (entry->chunk && entry->chunk->bsp) {
|
||||
demo3d_bsp_destroy(entry->chunk->bsp);
|
||||
}
|
||||
} else {
|
||||
entry = alloc_entry(cache);
|
||||
entry->chunk = demo3d_chunk_create_bsp(a->id);
|
||||
entry->valid = true;
|
||||
}
|
||||
|
||||
entry->chunk->bsp = bsp;
|
||||
entry->chunk->version = a->version;
|
||||
entry->last_used = cache->frame_counter;
|
||||
|
||||
assembly_reset(a);
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
demo3d_chunk_cache* demo3d_chunk_cache_create(void) {
|
||||
demo3d_chunk_cache* cache = pxl8_calloc(1, sizeof(demo3d_chunk_cache));
|
||||
if (!cache) return NULL;
|
||||
assembly_reset(&cache->assembly);
|
||||
return cache;
|
||||
}
|
||||
|
||||
void demo3d_chunk_cache_destroy(demo3d_chunk_cache* cache) {
|
||||
if (!cache) return;
|
||||
|
||||
for (u32 i = 0; i < cache->entry_count; i++) {
|
||||
demo3d_chunk_cache_entry* e = &cache->entries[i];
|
||||
if (e->chunk) demo3d_chunk_destroy(e->chunk);
|
||||
}
|
||||
|
||||
pxl8_free(cache->assembly.data);
|
||||
pxl8_free(cache);
|
||||
}
|
||||
|
||||
pxl8_result demo3d_chunk_cache_receive(demo3d_chunk_cache* cache,
|
||||
const demo3d_chunk_msg_header* hdr,
|
||||
const u8* payload, usize len) {
|
||||
if (!cache || !hdr || !payload) return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
|
||||
demo3d_chunk_assembly* a = &cache->assembly;
|
||||
|
||||
bool new_assembly = !a->active ||
|
||||
a->id != hdr->id ||
|
||||
a->version != hdr->version;
|
||||
|
||||
if (new_assembly) {
|
||||
assembly_init(a, hdr);
|
||||
}
|
||||
|
||||
if (hdr->fragment_idx >= DEMO3D_CHUNK_MAX_FRAGMENTS) {
|
||||
return PXL8_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
u32 offset = (u32)hdr->fragment_idx * DEMO3D_CHUNK_MAX_PAYLOAD;
|
||||
u32 required = offset + (u32)len;
|
||||
|
||||
if (required > a->data_capacity) {
|
||||
return PXL8_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(a->data + offset, payload, len);
|
||||
|
||||
if (required > a->data_size) {
|
||||
a->data_size = required;
|
||||
}
|
||||
|
||||
a->fragments_received++;
|
||||
|
||||
if (hdr->flags & DEMO3D_CHUNK_FLAG_FINAL) {
|
||||
a->complete = true;
|
||||
return assemble_bsp(cache, a);
|
||||
}
|
||||
|
||||
return PXL8_OK;
|
||||
}
|
||||
|
||||
demo3d_chunk* demo3d_chunk_cache_get_bsp(demo3d_chunk_cache* cache, u32 id) {
|
||||
if (!cache) return NULL;
|
||||
demo3d_chunk_cache_entry* e = find_entry_bsp(cache, id);
|
||||
if (e) {
|
||||
e->last_used = cache->frame_counter;
|
||||
return e->chunk;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void demo3d_chunk_cache_tick(demo3d_chunk_cache* cache) {
|
||||
if (!cache) return;
|
||||
cache->frame_counter++;
|
||||
}
|
||||
55
demo3d/client/world/demo3d_chunk_cache.h
Normal file
55
demo3d/client/world/demo3d_chunk_cache.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_mesh.h"
|
||||
#include "demo3d_protocol.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "demo3d_chunk.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEMO3D_CHUNK_CACHE_SIZE 512
|
||||
#define DEMO3D_CHUNK_MAX_FRAGMENTS 255
|
||||
#define DEMO3D_CHUNK_MAX_DATA_SIZE 131072
|
||||
|
||||
typedef struct demo3d_chunk_cache_entry {
|
||||
demo3d_chunk* chunk;
|
||||
u64 last_used;
|
||||
bool valid;
|
||||
} demo3d_chunk_cache_entry;
|
||||
|
||||
typedef struct demo3d_chunk_assembly {
|
||||
u32 id;
|
||||
i32 cx, cy, cz;
|
||||
u32 version;
|
||||
u8 fragment_count;
|
||||
u8 fragments_received;
|
||||
u8* data;
|
||||
u32 data_size;
|
||||
u32 data_capacity;
|
||||
bool active;
|
||||
bool complete;
|
||||
} demo3d_chunk_assembly;
|
||||
|
||||
typedef struct demo3d_chunk_cache {
|
||||
demo3d_chunk_cache_entry entries[DEMO3D_CHUNK_CACHE_SIZE];
|
||||
demo3d_chunk_assembly assembly;
|
||||
u32 entry_count;
|
||||
u64 frame_counter;
|
||||
} demo3d_chunk_cache;
|
||||
|
||||
demo3d_chunk_cache* demo3d_chunk_cache_create(void);
|
||||
void demo3d_chunk_cache_destroy(demo3d_chunk_cache* cache);
|
||||
|
||||
pxl8_result demo3d_chunk_cache_receive(demo3d_chunk_cache* cache,
|
||||
const demo3d_chunk_msg_header* hdr,
|
||||
const u8* payload, usize len);
|
||||
|
||||
demo3d_chunk* demo3d_chunk_cache_get_bsp(demo3d_chunk_cache* cache, u32 id);
|
||||
|
||||
void demo3d_chunk_cache_tick(demo3d_chunk_cache* cache);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
496
demo3d/client/world/demo3d_entity.c
Normal file
496
demo3d/client/world/demo3d_entity.c
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
#include "demo3d_entity.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_mem.h"
|
||||
|
||||
#define DEMO3D_ENTITY_COMPONENT_NAME_MAX 32
|
||||
#define DEMO3D_ENTITY_RELATIONSHIP_NAME_MAX 32
|
||||
|
||||
typedef struct demo3d_component_type {
|
||||
char name[DEMO3D_ENTITY_COMPONENT_NAME_MAX];
|
||||
u32 size;
|
||||
} demo3d_component_type;
|
||||
|
||||
typedef struct demo3d_component_storage {
|
||||
u32* sparse;
|
||||
void* dense_data;
|
||||
demo3d_entity* dense_entities;
|
||||
u32 count;
|
||||
} demo3d_component_storage;
|
||||
|
||||
typedef struct demo3d_relationship_type {
|
||||
char name[DEMO3D_ENTITY_RELATIONSHIP_NAME_MAX];
|
||||
} demo3d_relationship_type;
|
||||
|
||||
typedef struct demo3d_relationship_entry {
|
||||
demo3d_entity subject;
|
||||
demo3d_entity object;
|
||||
demo3d_entity_relationship rel;
|
||||
u32 next_by_subject;
|
||||
u32 next_by_object;
|
||||
} demo3d_relationship_entry;
|
||||
|
||||
struct demo3d_entity_pool {
|
||||
u32* generations;
|
||||
u32* free_list;
|
||||
u32 free_count;
|
||||
u32 capacity;
|
||||
u32 alive_count;
|
||||
|
||||
demo3d_component_type* component_types;
|
||||
demo3d_component_storage* component_storage;
|
||||
u32 component_type_count;
|
||||
u32 component_type_capacity;
|
||||
|
||||
demo3d_relationship_type* relationship_types;
|
||||
u32 relationship_type_count;
|
||||
u32 relationship_type_capacity;
|
||||
|
||||
demo3d_relationship_entry* relationships;
|
||||
u32* rel_by_subject;
|
||||
u32* rel_by_object;
|
||||
u32 relationship_count;
|
||||
u32 relationship_capacity;
|
||||
};
|
||||
|
||||
demo3d_entity_pool* demo3d_entity_pool_create(u32 capacity) {
|
||||
demo3d_entity_pool* pool = pxl8_calloc(1, sizeof(demo3d_entity_pool));
|
||||
if (!pool) return NULL;
|
||||
|
||||
pool->capacity = capacity;
|
||||
pool->generations = pxl8_calloc(capacity, sizeof(u32));
|
||||
pool->free_list = pxl8_malloc(capacity * sizeof(u32));
|
||||
|
||||
if (!pool->generations || !pool->free_list) {
|
||||
demo3d_entity_pool_destroy(pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < capacity; i++) {
|
||||
pool->free_list[i] = capacity - 1 - i;
|
||||
}
|
||||
pool->free_count = capacity;
|
||||
|
||||
pool->component_type_capacity = 16;
|
||||
pool->component_types = pxl8_calloc(pool->component_type_capacity, sizeof(demo3d_component_type));
|
||||
pool->component_storage = pxl8_calloc(pool->component_type_capacity, sizeof(demo3d_component_storage));
|
||||
|
||||
pool->relationship_type_capacity = 16;
|
||||
pool->relationship_types = pxl8_calloc(pool->relationship_type_capacity, sizeof(demo3d_relationship_type));
|
||||
|
||||
pool->relationship_capacity = 256;
|
||||
pool->relationships = pxl8_malloc(pool->relationship_capacity * sizeof(demo3d_relationship_entry));
|
||||
pool->rel_by_subject = pxl8_malloc(capacity * sizeof(u32));
|
||||
pool->rel_by_object = pxl8_malloc(capacity * sizeof(u32));
|
||||
|
||||
for (u32 i = 0; i < capacity; i++) {
|
||||
pool->rel_by_subject[i] = UINT32_MAX;
|
||||
pool->rel_by_object[i] = UINT32_MAX;
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
void demo3d_entity_pool_clear(demo3d_entity_pool* pool) {
|
||||
if (!pool) return;
|
||||
|
||||
for (u32 i = 0; i < pool->capacity; i++) {
|
||||
pool->generations[i] = 0;
|
||||
pool->free_list[i] = pool->capacity - 1 - i;
|
||||
pool->rel_by_subject[i] = UINT32_MAX;
|
||||
pool->rel_by_object[i] = UINT32_MAX;
|
||||
}
|
||||
pool->free_count = pool->capacity;
|
||||
pool->alive_count = 0;
|
||||
|
||||
for (u32 i = 0; i < pool->component_type_count; i++) {
|
||||
pool->component_storage[i].count = 0;
|
||||
}
|
||||
|
||||
pool->relationship_count = 0;
|
||||
}
|
||||
|
||||
void demo3d_entity_pool_destroy(demo3d_entity_pool* pool) {
|
||||
if (!pool) return;
|
||||
|
||||
for (u32 i = 0; i < pool->component_type_count; i++) {
|
||||
pxl8_free(pool->component_storage[i].sparse);
|
||||
pxl8_free(pool->component_storage[i].dense_data);
|
||||
pxl8_free(pool->component_storage[i].dense_entities);
|
||||
}
|
||||
|
||||
pxl8_free(pool->component_types);
|
||||
pxl8_free(pool->component_storage);
|
||||
pxl8_free(pool->relationship_types);
|
||||
pxl8_free(pool->relationships);
|
||||
pxl8_free(pool->rel_by_subject);
|
||||
pxl8_free(pool->rel_by_object);
|
||||
pxl8_free(pool->generations);
|
||||
pxl8_free(pool->free_list);
|
||||
pxl8_free(pool);
|
||||
}
|
||||
|
||||
demo3d_entity demo3d_entity_spawn(demo3d_entity_pool* pool) {
|
||||
if (!pool || pool->free_count == 0) return DEMO3D_ENTITY_INVALID;
|
||||
|
||||
u32 idx = pool->free_list[--pool->free_count];
|
||||
pool->generations[idx]++;
|
||||
pool->alive_count++;
|
||||
|
||||
return (demo3d_entity){ .idx = idx, .gen = pool->generations[idx] };
|
||||
}
|
||||
|
||||
void demo3d_entity_despawn(demo3d_entity_pool* pool, demo3d_entity e) {
|
||||
if (!pool || !demo3d_entity_alive(pool, e)) return;
|
||||
|
||||
for (u32 i = 0; i < pool->component_type_count; i++) {
|
||||
demo3d_entity_component_remove(pool, e, i + 1);
|
||||
}
|
||||
|
||||
pool->free_list[pool->free_count++] = e.idx;
|
||||
pool->generations[e.idx]++;
|
||||
pool->alive_count--;
|
||||
}
|
||||
|
||||
bool demo3d_entity_alive(const demo3d_entity_pool* pool, demo3d_entity e) {
|
||||
if (!pool || e.idx >= pool->capacity) return false;
|
||||
return pool->generations[e.idx] == e.gen && e.gen != 0;
|
||||
}
|
||||
|
||||
u32 demo3d_entity_count(const demo3d_entity_pool* pool) {
|
||||
return pool ? pool->alive_count : 0;
|
||||
}
|
||||
|
||||
demo3d_entity_component demo3d_entity_component_register(demo3d_entity_pool* pool, const char* name, u32 size) {
|
||||
if (!pool || !name || size == 0) return DEMO3D_ENTITY_COMPONENT_INVALID;
|
||||
|
||||
demo3d_entity_component existing = demo3d_entity_component_find(pool, name);
|
||||
if (existing != DEMO3D_ENTITY_COMPONENT_INVALID) return existing;
|
||||
|
||||
if (pool->component_type_count >= pool->component_type_capacity) {
|
||||
u32 new_capacity = pool->component_type_capacity * 2;
|
||||
demo3d_component_type* new_types = pxl8_realloc(pool->component_types, new_capacity * sizeof(demo3d_component_type));
|
||||
demo3d_component_storage* new_storage = pxl8_realloc(pool->component_storage, new_capacity * sizeof(demo3d_component_storage));
|
||||
if (!new_types || !new_storage) return DEMO3D_ENTITY_COMPONENT_INVALID;
|
||||
|
||||
pool->component_types = new_types;
|
||||
pool->component_storage = new_storage;
|
||||
memset(&pool->component_types[pool->component_type_capacity], 0, (new_capacity - pool->component_type_capacity) * sizeof(demo3d_component_type));
|
||||
memset(&pool->component_storage[pool->component_type_capacity], 0, (new_capacity - pool->component_type_capacity) * sizeof(demo3d_component_storage));
|
||||
pool->component_type_capacity = new_capacity;
|
||||
}
|
||||
|
||||
u32 type_idx = pool->component_type_count++;
|
||||
strncpy(pool->component_types[type_idx].name, name, DEMO3D_ENTITY_COMPONENT_NAME_MAX - 1);
|
||||
pool->component_types[type_idx].size = size;
|
||||
|
||||
demo3d_component_storage* storage = &pool->component_storage[type_idx];
|
||||
storage->sparse = pxl8_malloc(pool->capacity * sizeof(u32));
|
||||
storage->dense_data = pxl8_malloc(pool->capacity * size);
|
||||
storage->dense_entities = pxl8_malloc(pool->capacity * sizeof(demo3d_entity));
|
||||
storage->count = 0;
|
||||
|
||||
for (u32 i = 0; i < pool->capacity; i++) {
|
||||
storage->sparse[i] = UINT32_MAX;
|
||||
}
|
||||
|
||||
return type_idx + 1;
|
||||
}
|
||||
|
||||
demo3d_entity_component demo3d_entity_component_find(const demo3d_entity_pool* pool, const char* name) {
|
||||
if (!pool || !name) return DEMO3D_ENTITY_COMPONENT_INVALID;
|
||||
|
||||
for (u32 i = 0; i < pool->component_type_count; i++) {
|
||||
if (strcmp(pool->component_types[i].name, name) == 0) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return DEMO3D_ENTITY_COMPONENT_INVALID;
|
||||
}
|
||||
|
||||
const char* demo3d_entity_component_name(const demo3d_entity_pool* pool, demo3d_entity_component comp) {
|
||||
if (!pool || comp == 0 || comp > pool->component_type_count) return NULL;
|
||||
return pool->component_types[comp - 1].name;
|
||||
}
|
||||
|
||||
void* demo3d_entity_component_add(demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
|
||||
if (!pool || !demo3d_entity_alive(pool, e)) return NULL;
|
||||
if (comp == 0 || comp > pool->component_type_count) return NULL;
|
||||
|
||||
u32 type_idx = comp - 1;
|
||||
demo3d_component_storage* storage = &pool->component_storage[type_idx];
|
||||
u32 size = pool->component_types[type_idx].size;
|
||||
|
||||
if (storage->sparse[e.idx] != UINT32_MAX) {
|
||||
return (u8*)storage->dense_data + storage->sparse[e.idx] * size;
|
||||
}
|
||||
|
||||
u32 dense_idx = storage->count++;
|
||||
storage->sparse[e.idx] = dense_idx;
|
||||
storage->dense_entities[dense_idx] = e;
|
||||
|
||||
void* data = (u8*)storage->dense_data + dense_idx * size;
|
||||
memset(data, 0, size);
|
||||
return data;
|
||||
}
|
||||
|
||||
void* demo3d_entity_component_get(const demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
|
||||
if (!pool || !demo3d_entity_alive(pool, e)) return NULL;
|
||||
if (comp == 0 || comp > pool->component_type_count) return NULL;
|
||||
|
||||
u32 type_idx = comp - 1;
|
||||
const demo3d_component_storage* storage = &pool->component_storage[type_idx];
|
||||
|
||||
if (storage->sparse[e.idx] == UINT32_MAX) return NULL;
|
||||
|
||||
u32 size = pool->component_types[type_idx].size;
|
||||
return (u8*)storage->dense_data + storage->sparse[e.idx] * size;
|
||||
}
|
||||
|
||||
void demo3d_entity_component_remove(demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
|
||||
if (!pool || !demo3d_entity_alive(pool, e)) return;
|
||||
if (comp == 0 || comp > pool->component_type_count) return;
|
||||
|
||||
u32 type_idx = comp - 1;
|
||||
demo3d_component_storage* storage = &pool->component_storage[type_idx];
|
||||
u32 size = pool->component_types[type_idx].size;
|
||||
|
||||
u32 dense_idx = storage->sparse[e.idx];
|
||||
if (dense_idx == UINT32_MAX) return;
|
||||
|
||||
u32 last_idx = storage->count - 1;
|
||||
if (dense_idx != last_idx) {
|
||||
demo3d_entity last_entity = storage->dense_entities[last_idx];
|
||||
memcpy((u8*)storage->dense_data + dense_idx * size,
|
||||
(u8*)storage->dense_data + last_idx * size, size);
|
||||
storage->dense_entities[dense_idx] = last_entity;
|
||||
storage->sparse[last_entity.idx] = dense_idx;
|
||||
}
|
||||
|
||||
storage->sparse[e.idx] = UINT32_MAX;
|
||||
storage->count--;
|
||||
}
|
||||
|
||||
bool demo3d_entity_component_has(const demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp) {
|
||||
if (!pool || !demo3d_entity_alive(pool, e)) return false;
|
||||
if (comp == 0 || comp > pool->component_type_count) return false;
|
||||
|
||||
return pool->component_storage[comp - 1].sparse[e.idx] != UINT32_MAX;
|
||||
}
|
||||
|
||||
demo3d_entity_relationship demo3d_entity_relationship_register(demo3d_entity_pool* pool, const char* name) {
|
||||
if (!pool || !name) return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
|
||||
|
||||
demo3d_entity_relationship existing = demo3d_entity_relationship_find(pool, name);
|
||||
if (existing != DEMO3D_ENTITY_RELATIONSHIP_INVALID) return existing;
|
||||
|
||||
if (pool->relationship_type_count >= pool->relationship_type_capacity) {
|
||||
u32 new_capacity = pool->relationship_type_capacity * 2;
|
||||
demo3d_relationship_type* new_types = pxl8_realloc(pool->relationship_types, new_capacity * sizeof(demo3d_relationship_type));
|
||||
if (!new_types) return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
|
||||
|
||||
pool->relationship_types = new_types;
|
||||
memset(&pool->relationship_types[pool->relationship_type_capacity], 0, (new_capacity - pool->relationship_type_capacity) * sizeof(demo3d_relationship_type));
|
||||
pool->relationship_type_capacity = new_capacity;
|
||||
}
|
||||
|
||||
u32 type_idx = pool->relationship_type_count++;
|
||||
strncpy(pool->relationship_types[type_idx].name, name, DEMO3D_ENTITY_RELATIONSHIP_NAME_MAX - 1);
|
||||
|
||||
return type_idx + 1;
|
||||
}
|
||||
|
||||
demo3d_entity_relationship demo3d_entity_relationship_find(const demo3d_entity_pool* pool, const char* name) {
|
||||
if (!pool || !name) return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
|
||||
|
||||
for (u32 i = 0; i < pool->relationship_type_count; i++) {
|
||||
if (strcmp(pool->relationship_types[i].name, name) == 0) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return DEMO3D_ENTITY_RELATIONSHIP_INVALID;
|
||||
}
|
||||
|
||||
const char* demo3d_entity_relationship_name(const demo3d_entity_pool* pool, demo3d_entity_relationship rel) {
|
||||
if (!pool || rel == 0 || rel > pool->relationship_type_count) return NULL;
|
||||
return pool->relationship_types[rel - 1].name;
|
||||
}
|
||||
|
||||
void demo3d_entity_relationship_add(demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object) {
|
||||
if (!pool) return;
|
||||
if (!demo3d_entity_alive(pool, subject) || !demo3d_entity_alive(pool, object)) return;
|
||||
if (rel == 0 || rel > pool->relationship_type_count) return;
|
||||
if (demo3d_entity_relationship_has(pool, subject, rel, object)) return;
|
||||
|
||||
if (pool->relationship_count >= pool->relationship_capacity) {
|
||||
u32 new_capacity = pool->relationship_capacity * 2;
|
||||
demo3d_relationship_entry* new_rels = pxl8_realloc(pool->relationships, new_capacity * sizeof(demo3d_relationship_entry));
|
||||
if (!new_rels) return;
|
||||
pool->relationships = new_rels;
|
||||
pool->relationship_capacity = new_capacity;
|
||||
}
|
||||
|
||||
u32 entry_idx = pool->relationship_count++;
|
||||
demo3d_relationship_entry* entry = &pool->relationships[entry_idx];
|
||||
entry->subject = subject;
|
||||
entry->object = object;
|
||||
entry->rel = rel;
|
||||
|
||||
entry->next_by_subject = pool->rel_by_subject[subject.idx];
|
||||
pool->rel_by_subject[subject.idx] = entry_idx;
|
||||
|
||||
entry->next_by_object = pool->rel_by_object[object.idx];
|
||||
pool->rel_by_object[object.idx] = entry_idx;
|
||||
}
|
||||
|
||||
void demo3d_entity_relationship_remove(demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object) {
|
||||
if (!pool) return;
|
||||
if (rel == 0 || rel > pool->relationship_type_count) return;
|
||||
|
||||
u32* prev_ptr = &pool->rel_by_subject[subject.idx];
|
||||
u32 idx = *prev_ptr;
|
||||
|
||||
while (idx != UINT32_MAX) {
|
||||
demo3d_relationship_entry* entry = &pool->relationships[idx];
|
||||
if (demo3d_entity_eq(entry->subject, subject) &&
|
||||
demo3d_entity_eq(entry->object, object) &&
|
||||
entry->rel == rel) {
|
||||
|
||||
*prev_ptr = entry->next_by_subject;
|
||||
|
||||
u32* obj_prev = &pool->rel_by_object[object.idx];
|
||||
while (*obj_prev != UINT32_MAX) {
|
||||
if (*obj_prev == idx) {
|
||||
*obj_prev = entry->next_by_object;
|
||||
break;
|
||||
}
|
||||
obj_prev = &pool->relationships[*obj_prev].next_by_object;
|
||||
}
|
||||
|
||||
if (idx != pool->relationship_count - 1) {
|
||||
u32 last_idx = pool->relationship_count - 1;
|
||||
demo3d_relationship_entry* last = &pool->relationships[last_idx];
|
||||
|
||||
u32* last_subj_prev = &pool->rel_by_subject[last->subject.idx];
|
||||
while (*last_subj_prev != UINT32_MAX) {
|
||||
if (*last_subj_prev == last_idx) {
|
||||
*last_subj_prev = idx;
|
||||
break;
|
||||
}
|
||||
last_subj_prev = &pool->relationships[*last_subj_prev].next_by_subject;
|
||||
}
|
||||
|
||||
u32* last_obj_prev = &pool->rel_by_object[last->object.idx];
|
||||
while (*last_obj_prev != UINT32_MAX) {
|
||||
if (*last_obj_prev == last_idx) {
|
||||
*last_obj_prev = idx;
|
||||
break;
|
||||
}
|
||||
last_obj_prev = &pool->relationships[*last_obj_prev].next_by_object;
|
||||
}
|
||||
|
||||
*entry = *last;
|
||||
}
|
||||
pool->relationship_count--;
|
||||
return;
|
||||
}
|
||||
prev_ptr = &entry->next_by_subject;
|
||||
idx = *prev_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool demo3d_entity_relationship_has(const demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object) {
|
||||
if (!pool) return false;
|
||||
if (rel == 0 || rel > pool->relationship_type_count) return false;
|
||||
|
||||
u32 idx = pool->rel_by_subject[subject.idx];
|
||||
while (idx != UINT32_MAX) {
|
||||
const demo3d_relationship_entry* entry = &pool->relationships[idx];
|
||||
if (demo3d_entity_eq(entry->subject, subject) &&
|
||||
demo3d_entity_eq(entry->object, object) &&
|
||||
entry->rel == rel) {
|
||||
return true;
|
||||
}
|
||||
idx = entry->next_by_subject;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 demo3d_entity_relationship_subjects(const demo3d_entity_pool* pool, demo3d_entity object, demo3d_entity_relationship rel, demo3d_entity* out, u32 max) {
|
||||
if (!pool || !out || max == 0) return 0;
|
||||
if (rel == 0 || rel > pool->relationship_type_count) return 0;
|
||||
|
||||
u32 count = 0;
|
||||
u32 idx = pool->rel_by_object[object.idx];
|
||||
while (idx != UINT32_MAX && count < max) {
|
||||
const demo3d_relationship_entry* entry = &pool->relationships[idx];
|
||||
if (demo3d_entity_eq(entry->object, object) && entry->rel == rel) {
|
||||
out[count++] = entry->subject;
|
||||
}
|
||||
idx = entry->next_by_object;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
u32 demo3d_entity_relationship_objects(const demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity* out, u32 max) {
|
||||
if (!pool || !out || max == 0) return 0;
|
||||
if (rel == 0 || rel > pool->relationship_type_count) return 0;
|
||||
|
||||
u32 count = 0;
|
||||
u32 idx = pool->rel_by_subject[subject.idx];
|
||||
while (idx != UINT32_MAX && count < max) {
|
||||
const demo3d_relationship_entry* entry = &pool->relationships[idx];
|
||||
if (demo3d_entity_eq(entry->subject, subject) && entry->rel == rel) {
|
||||
out[count++] = entry->object;
|
||||
}
|
||||
idx = entry->next_by_subject;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void demo3d_entity_each(demo3d_entity_pool* pool, demo3d_entity_component comp, demo3d_entity_each_fn fn, void* ctx) {
|
||||
if (!pool || !fn) return;
|
||||
if (comp == 0 || comp > pool->component_type_count) return;
|
||||
|
||||
demo3d_component_storage* storage = &pool->component_storage[comp - 1];
|
||||
for (u32 i = 0; i < storage->count; i++) {
|
||||
demo3d_entity e = storage->dense_entities[i];
|
||||
if (demo3d_entity_alive(pool, e)) {
|
||||
fn(pool, e, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void demo3d_entity_each_with(demo3d_entity_pool* pool, const demo3d_entity_component* comps, u32 count, demo3d_entity_each_fn fn, void* ctx) {
|
||||
if (!pool || !comps || !fn || count == 0) return;
|
||||
|
||||
u32 smallest_idx = 0;
|
||||
u32 smallest_count = UINT32_MAX;
|
||||
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
if (comps[i] == 0 || comps[i] > pool->component_type_count) return;
|
||||
u32 storage_count = pool->component_storage[comps[i] - 1].count;
|
||||
if (storage_count < smallest_count) {
|
||||
smallest_count = storage_count;
|
||||
smallest_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
demo3d_component_storage* storage = &pool->component_storage[comps[smallest_idx] - 1];
|
||||
for (u32 i = 0; i < storage->count; i++) {
|
||||
demo3d_entity e = storage->dense_entities[i];
|
||||
if (!demo3d_entity_alive(pool, e)) continue;
|
||||
|
||||
bool has_all = true;
|
||||
for (u32 j = 0; j < count && has_all; j++) {
|
||||
if (j != smallest_idx) {
|
||||
has_all = demo3d_entity_component_has(pool, e, comps[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_all) {
|
||||
fn(pool, e, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
demo3d/client/world/demo3d_entity.h
Normal file
67
demo3d/client/world/demo3d_entity.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#pragma once
|
||||
|
||||
#include "pxl8_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct demo3d_entity_pool demo3d_entity_pool;
|
||||
|
||||
typedef struct demo3d_entity {
|
||||
u32 idx;
|
||||
u32 gen;
|
||||
} demo3d_entity;
|
||||
|
||||
#define DEMO3D_ENTITY_INVALID ((demo3d_entity){0, 0})
|
||||
|
||||
typedef u32 demo3d_entity_component;
|
||||
typedef u32 demo3d_entity_relationship;
|
||||
|
||||
#define DEMO3D_ENTITY_COMPONENT_INVALID 0
|
||||
#define DEMO3D_ENTITY_RELATIONSHIP_INVALID 0
|
||||
|
||||
demo3d_entity_pool* demo3d_entity_pool_create(u32 capacity);
|
||||
void demo3d_entity_pool_clear(demo3d_entity_pool* pool);
|
||||
void demo3d_entity_pool_destroy(demo3d_entity_pool* pool);
|
||||
|
||||
demo3d_entity demo3d_entity_spawn(demo3d_entity_pool* pool);
|
||||
void demo3d_entity_despawn(demo3d_entity_pool* pool, demo3d_entity e);
|
||||
bool demo3d_entity_alive(const demo3d_entity_pool* pool, demo3d_entity e);
|
||||
u32 demo3d_entity_count(const demo3d_entity_pool* pool);
|
||||
|
||||
demo3d_entity_component demo3d_entity_component_register(demo3d_entity_pool* pool, const char* name, u32 size);
|
||||
demo3d_entity_component demo3d_entity_component_find(const demo3d_entity_pool* pool, const char* name);
|
||||
const char* demo3d_entity_component_name(const demo3d_entity_pool* pool, demo3d_entity_component comp);
|
||||
|
||||
void* demo3d_entity_component_add(demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp);
|
||||
void* demo3d_entity_component_get(const demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp);
|
||||
void demo3d_entity_component_remove(demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp);
|
||||
bool demo3d_entity_component_has(const demo3d_entity_pool* pool, demo3d_entity e, demo3d_entity_component comp);
|
||||
|
||||
demo3d_entity_relationship demo3d_entity_relationship_register(demo3d_entity_pool* pool, const char* name);
|
||||
demo3d_entity_relationship demo3d_entity_relationship_find(const demo3d_entity_pool* pool, const char* name);
|
||||
const char* demo3d_entity_relationship_name(const demo3d_entity_pool* pool, demo3d_entity_relationship rel);
|
||||
|
||||
void demo3d_entity_relationship_add(demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object);
|
||||
void demo3d_entity_relationship_remove(demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object);
|
||||
bool demo3d_entity_relationship_has(const demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity object);
|
||||
|
||||
u32 demo3d_entity_relationship_subjects(const demo3d_entity_pool* pool, demo3d_entity object, demo3d_entity_relationship rel, demo3d_entity* out, u32 max);
|
||||
u32 demo3d_entity_relationship_objects(const demo3d_entity_pool* pool, demo3d_entity subject, demo3d_entity_relationship rel, demo3d_entity* out, u32 max);
|
||||
|
||||
typedef void (*demo3d_entity_each_fn)(demo3d_entity_pool* pool, demo3d_entity e, void* ctx);
|
||||
void demo3d_entity_each(demo3d_entity_pool* pool, demo3d_entity_component comp, demo3d_entity_each_fn fn, void* ctx);
|
||||
void demo3d_entity_each_with(demo3d_entity_pool* pool, const demo3d_entity_component* comps, u32 count, demo3d_entity_each_fn fn, void* ctx);
|
||||
|
||||
static inline bool demo3d_entity_valid(demo3d_entity e) {
|
||||
return e.idx != 0 || e.gen != 0;
|
||||
}
|
||||
|
||||
static inline bool demo3d_entity_eq(demo3d_entity a, demo3d_entity b) {
|
||||
return a.idx == b.idx && a.gen == b.gen;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
981
demo3d/client/world/demo3d_world.c
Normal file
981
demo3d/client/world/demo3d_world.c
Normal file
|
|
@ -0,0 +1,981 @@
|
|||
#include "demo3d_world.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pxl8_platform.h"
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
#include <stdatomic.h>
|
||||
#include "pxl8_queue.h"
|
||||
#endif
|
||||
|
||||
#include "demo3d_bsp_render.h"
|
||||
#include "pxl8_io.h"
|
||||
#include "pxl8_gfx3d.h"
|
||||
#include "pxl8_log.h"
|
||||
#include "pxl8_mem.h"
|
||||
#include "demo3d_protocol.h"
|
||||
#include "demo3d_sim.h"
|
||||
|
||||
#define DEMO3D_VIS_MAX_NODES (DEMO3D_WORLD_MAX_LOADED_CHUNKS * 512)
|
||||
#define DEMO3D_VIS_MAX_QUEUE (DEMO3D_VIS_MAX_NODES * 4)
|
||||
#define DEMO3D_VIS_BYTES ((DEMO3D_VIS_MAX_NODES + 7) / 8)
|
||||
#define DEMO3D_WORLD_ENTITY_CAPACITY 256
|
||||
|
||||
typedef struct {
|
||||
u16 chunk_idx;
|
||||
u16 leaf_idx;
|
||||
pxl8_rect window;
|
||||
} world_vis_node;
|
||||
|
||||
struct demo3d_world {
|
||||
demo3d_loaded_chunk loaded[DEMO3D_WORLD_MAX_LOADED_CHUNKS];
|
||||
u32 loaded_count;
|
||||
demo3d_chunk* active_chunk;
|
||||
demo3d_bsp_render_state* active_render_state;
|
||||
|
||||
pxl8_gfx_material shared_materials[16];
|
||||
bool shared_material_set[16];
|
||||
|
||||
demo3d_chunk_cache* chunk_cache;
|
||||
demo3d_entity_pool* entities;
|
||||
|
||||
demo3d_sim_entity local_player;
|
||||
u64 client_tick;
|
||||
|
||||
pxl8_vec2 pointer_motion;
|
||||
demo3d_sim_config sim_config;
|
||||
|
||||
u8 vis_bits[DEMO3D_VIS_BYTES];
|
||||
u8* vis_ptrs[DEMO3D_WORLD_MAX_LOADED_CHUNKS];
|
||||
pxl8_rect vis_windows[DEMO3D_VIS_MAX_NODES];
|
||||
pxl8_rect* vis_win_ptrs[DEMO3D_WORLD_MAX_LOADED_CHUNKS];
|
||||
world_vis_node vis_queue[DEMO3D_VIS_MAX_QUEUE];
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
demo3d_sim_entity render_state[2];
|
||||
atomic_uint active_buffer;
|
||||
pxl8_thread* sim_thread;
|
||||
atomic_bool sim_running;
|
||||
atomic_bool sim_paused;
|
||||
demo3d_net* net;
|
||||
pxl8_queue input_queue;
|
||||
f32 sim_accumulator;
|
||||
#endif
|
||||
};
|
||||
|
||||
demo3d_world* demo3d_world_create(void) {
|
||||
demo3d_world* world = pxl8_calloc(1, sizeof(demo3d_world));
|
||||
if (!world) return NULL;
|
||||
|
||||
world->chunk_cache = demo3d_chunk_cache_create();
|
||||
world->entities = demo3d_entity_pool_create(DEMO3D_WORLD_ENTITY_CAPACITY);
|
||||
world->sim_config = (demo3d_sim_config){
|
||||
.move_speed = 180.0f,
|
||||
.ground_accel = 10.0f,
|
||||
.air_accel = 1.0f,
|
||||
.stop_speed = 100.0f,
|
||||
.friction = 6.0f,
|
||||
.gravity = 800.0f,
|
||||
.jump_velocity = 200.0f,
|
||||
.player_radius = 16.0f,
|
||||
.player_height = 72.0f,
|
||||
.max_pitch = 1.5f,
|
||||
};
|
||||
|
||||
if (!world->chunk_cache || !world->entities) {
|
||||
demo3d_world_destroy(world);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
void demo3d_world_destroy(demo3d_world* world) {
|
||||
if (!world) return;
|
||||
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
demo3d_bsp_render_state_destroy(world->loaded[i].render_state);
|
||||
}
|
||||
demo3d_chunk_cache_destroy(world->chunk_cache);
|
||||
demo3d_entity_pool_destroy(world->entities);
|
||||
pxl8_free(world);
|
||||
}
|
||||
|
||||
demo3d_chunk_cache* demo3d_world_get_chunk_cache(demo3d_world* world) {
|
||||
if (!world) return NULL;
|
||||
return world->chunk_cache;
|
||||
}
|
||||
|
||||
demo3d_chunk* demo3d_world_active_chunk(demo3d_world* world) {
|
||||
if (!world) return NULL;
|
||||
return world->active_chunk;
|
||||
}
|
||||
|
||||
demo3d_sim_world demo3d_world_sim_world(const demo3d_world* world, pxl8_vec3 pos) {
|
||||
demo3d_sim_world sim = {0};
|
||||
const f32 chunk_size = 16.0f * 64.0f;
|
||||
sim.chunk_size = chunk_size;
|
||||
|
||||
i32 pcx = (i32)floorf(pos.x / chunk_size);
|
||||
i32 pcz = (i32)floorf(pos.z / chunk_size);
|
||||
sim.center_cx = pcx;
|
||||
sim.center_cz = pcz;
|
||||
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
const demo3d_loaded_chunk* lc = &world->loaded[i];
|
||||
if (!lc->chunk || !lc->chunk->bsp) continue;
|
||||
i32 dx = lc->cx - pcx + 1;
|
||||
i32 dz = lc->cz - pcz + 1;
|
||||
if (dx >= 0 && dx <= 2 && dz >= 0 && dz <= 2) {
|
||||
sim.chunks[dz * 3 + dx] = lc->chunk->bsp;
|
||||
}
|
||||
}
|
||||
|
||||
return sim;
|
||||
}
|
||||
|
||||
static void entity_to_userdata(const demo3d_sim_entity* ent, u8* userdata) {
|
||||
u8* p = userdata;
|
||||
memcpy(p, &ent->pos.x, 4); p += 4;
|
||||
memcpy(p, &ent->pos.y, 4); p += 4;
|
||||
memcpy(p, &ent->pos.z, 4); p += 4;
|
||||
memcpy(p, &ent->yaw, 4); p += 4;
|
||||
memcpy(p, &ent->pitch, 4); p += 4;
|
||||
memcpy(p, &ent->vel.x, 4); p += 4;
|
||||
memcpy(p, &ent->vel.y, 4); p += 4;
|
||||
memcpy(p, &ent->vel.z, 4); p += 4;
|
||||
memcpy(p, &ent->flags, 4); p += 4;
|
||||
memcpy(p, &ent->kind, 2);
|
||||
}
|
||||
|
||||
static void userdata_to_entity(const u8* userdata, demo3d_sim_entity* ent) {
|
||||
const u8* p = userdata;
|
||||
memcpy(&ent->pos.x, p, 4); p += 4;
|
||||
memcpy(&ent->pos.y, p, 4); p += 4;
|
||||
memcpy(&ent->pos.z, p, 4); p += 4;
|
||||
memcpy(&ent->yaw, p, 4); p += 4;
|
||||
memcpy(&ent->pitch, p, 4); p += 4;
|
||||
memcpy(&ent->vel.x, p, 4); p += 4;
|
||||
memcpy(&ent->vel.y, p, 4); p += 4;
|
||||
memcpy(&ent->vel.z, p, 4); p += 4;
|
||||
memcpy(&ent->flags, p, 4); p += 4;
|
||||
memcpy(&ent->kind, p, 2);
|
||||
}
|
||||
|
||||
bool demo3d_world_point_solid(const demo3d_world* world, f32 x, f32 y, f32 z) {
|
||||
if (!world) return false;
|
||||
|
||||
if (world->active_chunk && world->active_chunk->bsp) {
|
||||
return demo3d_bsp_point_solid(world->active_chunk->bsp, (pxl8_vec3){x, y, z});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
pxl8_ray demo3d_world_ray(const demo3d_world* world, pxl8_vec3 from, pxl8_vec3 to) {
|
||||
pxl8_ray result = {0};
|
||||
|
||||
if (!world) return result;
|
||||
|
||||
pxl8_vec3 dir = pxl8_vec3_sub(to, from);
|
||||
f32 length = pxl8_vec3_length(dir);
|
||||
if (length < 0.001f) return result;
|
||||
|
||||
f32 step_size = 1.0f;
|
||||
f32 traveled = 0.0f;
|
||||
|
||||
while (traveled < length) {
|
||||
f32 t = traveled / length;
|
||||
pxl8_vec3 pos = {
|
||||
from.x + dir.x * t,
|
||||
from.y + dir.y * t,
|
||||
from.z + dir.z * t
|
||||
};
|
||||
|
||||
if (demo3d_world_point_solid(world, pos.x, pos.y, pos.z)) {
|
||||
result.hit = true;
|
||||
result.fraction = t;
|
||||
result.point = pos;
|
||||
|
||||
f32 eps = 0.1f;
|
||||
bool sx_neg = demo3d_world_point_solid(world, pos.x - eps, pos.y, pos.z);
|
||||
bool sx_pos = demo3d_world_point_solid(world, pos.x + eps, pos.y, pos.z);
|
||||
bool sy_neg = demo3d_world_point_solid(world, pos.x, pos.y - eps, pos.z);
|
||||
bool sy_pos = demo3d_world_point_solid(world, pos.x, pos.y + eps, pos.z);
|
||||
bool sz_neg = demo3d_world_point_solid(world, pos.x, pos.y, pos.z - eps);
|
||||
bool sz_pos = demo3d_world_point_solid(world, pos.x, pos.y, pos.z + eps);
|
||||
|
||||
result.normal = (pxl8_vec3){
|
||||
(sx_neg && !sx_pos) ? 1.0f : (!sx_neg && sx_pos) ? -1.0f : 0.0f,
|
||||
(sy_neg && !sy_pos) ? 1.0f : (!sy_neg && sy_pos) ? -1.0f : 0.0f,
|
||||
(sz_neg && !sz_pos) ? 1.0f : (!sz_neg && sz_pos) ? -1.0f : 0.0f
|
||||
};
|
||||
|
||||
f32 nl = pxl8_vec3_length(result.normal);
|
||||
if (nl > 0.001f) {
|
||||
result.normal = pxl8_vec3_scale(result.normal, 1.0f / nl);
|
||||
} else {
|
||||
result.normal = (pxl8_vec3){0, 1, 0};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
traveled += step_size;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pxl8_ray demo3d_world_sweep(const demo3d_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius) {
|
||||
pxl8_ray result = {0};
|
||||
|
||||
if (!world) return result;
|
||||
|
||||
f32 diag = radius * 0.707f;
|
||||
|
||||
bool dest_blocked = demo3d_world_point_solid(world, to.x, to.y, to.z) ||
|
||||
demo3d_world_point_solid(world, to.x + radius, to.y, to.z) ||
|
||||
demo3d_world_point_solid(world, to.x - radius, to.y, to.z) ||
|
||||
demo3d_world_point_solid(world, to.x, to.y, to.z + radius) ||
|
||||
demo3d_world_point_solid(world, to.x, to.y, to.z - radius) ||
|
||||
demo3d_world_point_solid(world, to.x + diag, to.y, to.z + diag) ||
|
||||
demo3d_world_point_solid(world, to.x + diag, to.y, to.z - diag) ||
|
||||
demo3d_world_point_solid(world, to.x - diag, to.y, to.z + diag) ||
|
||||
demo3d_world_point_solid(world, to.x - diag, to.y, to.z - diag);
|
||||
|
||||
if (dest_blocked) {
|
||||
result.hit = true;
|
||||
result.fraction = 0;
|
||||
result.point = from;
|
||||
pxl8_vec3 dir = pxl8_vec3_sub(to, from);
|
||||
f32 length = pxl8_vec3_length(dir);
|
||||
if (length > 0.001f) {
|
||||
result.normal = pxl8_vec3_scale(dir, -1.0f / length);
|
||||
} else {
|
||||
result.normal = (pxl8_vec3){0, 1, 0};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void demo3d_world_update(demo3d_world* world, f32 dt) {
|
||||
(void)dt;
|
||||
if (!world) return;
|
||||
demo3d_chunk_cache_tick(world->chunk_cache);
|
||||
}
|
||||
|
||||
static inline bool vr_valid(pxl8_rect r) {
|
||||
return r.x0 < r.x1 && r.y0 < r.y1;
|
||||
}
|
||||
|
||||
static inline pxl8_rect vr_intersect(pxl8_rect a, pxl8_rect b) {
|
||||
return (pxl8_rect){
|
||||
.x0 = a.x0 > b.x0 ? a.x0 : b.x0,
|
||||
.y0 = a.y0 > b.y0 ? a.y0 : b.y0,
|
||||
.x1 = a.x1 < b.x1 ? a.x1 : b.x1,
|
||||
.y1 = a.y1 < b.y1 ? a.y1 : b.y1,
|
||||
};
|
||||
}
|
||||
|
||||
static pxl8_rect project_portal(f32 px0, f32 pz0, f32 px1, f32 pz1,
|
||||
f32 y_lo, f32 y_hi, const pxl8_mat4* vp) {
|
||||
pxl8_vec3 corners[4] = {
|
||||
{px0, y_lo, pz0}, {px1, y_lo, pz1},
|
||||
{px1, y_hi, pz1}, {px0, y_hi, pz0},
|
||||
};
|
||||
|
||||
const f32 NEAR_W = 0.001f;
|
||||
pxl8_vec4 clip[4];
|
||||
bool front[4];
|
||||
i32 fc = 0;
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
clip[i] = pxl8_mat4_multiply_vec4(*vp, (pxl8_vec4){
|
||||
corners[i].x, corners[i].y, corners[i].z, 1.0f});
|
||||
front[i] = clip[i].w > NEAR_W;
|
||||
if (front[i]) fc++;
|
||||
}
|
||||
|
||||
if (fc == 0) return (pxl8_rect){0, 0, 0, 0};
|
||||
if (fc < 4) return (pxl8_rect){-1.0f, -1.0f, 1.0f, 1.0f};
|
||||
|
||||
pxl8_rect r = {1e30f, 1e30f, -1e30f, -1e30f};
|
||||
|
||||
for (i32 i = 0; i < 4; i++) {
|
||||
f32 iw = 1.0f / clip[i].w;
|
||||
f32 nx = clip[i].x * iw, ny = clip[i].y * iw;
|
||||
if (nx < r.x0) r.x0 = nx; if (nx > r.x1) r.x1 = nx;
|
||||
if (ny < r.y0) r.y0 = ny; if (ny > r.y1) r.y1 = ny;
|
||||
}
|
||||
|
||||
if (r.x0 < -1.0f) r.x0 = -1.0f;
|
||||
if (r.y0 < -1.0f) r.y0 = -1.0f;
|
||||
if (r.x1 > 1.0f) r.x1 = 1.0f;
|
||||
if (r.y1 > 1.0f) r.y1 = 1.0f;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void compute_edge_leafs(demo3d_loaded_chunk* lc) {
|
||||
const f32 CHUNK_SIZE = 16.0f * 64.0f;
|
||||
const demo3d_bsp* bsp = lc->chunk->bsp;
|
||||
f32 cx0 = lc->cx * CHUNK_SIZE;
|
||||
f32 cz0 = lc->cz * CHUNK_SIZE;
|
||||
f32 cx1 = cx0 + CHUNK_SIZE;
|
||||
f32 cz1 = cz0 + CHUNK_SIZE;
|
||||
|
||||
memset(lc->edges, 0, sizeof(lc->edges));
|
||||
|
||||
for (u32 i = 0; i < bsp->num_leafs; i++) {
|
||||
const demo3d_bsp_leaf* leaf = &bsp->leafs[i];
|
||||
if (bsp->leafs[i].contents == -1) continue;
|
||||
|
||||
if ((f32)leaf->mins[2] <= (f32)((i16)cz0) + 1.0f && lc->edges[0].count < 16)
|
||||
lc->edges[0].leafs[lc->edges[0].count++] = (u16)i;
|
||||
if ((f32)leaf->maxs[2] >= (f32)((i16)cz1) - 1.0f && lc->edges[1].count < 16)
|
||||
lc->edges[1].leafs[lc->edges[1].count++] = (u16)i;
|
||||
if ((f32)leaf->mins[0] <= (f32)((i16)cx0) + 1.0f && lc->edges[2].count < 16)
|
||||
lc->edges[2].leafs[lc->edges[2].count++] = (u16)i;
|
||||
if ((f32)leaf->maxs[0] >= (f32)((i16)cx1) - 1.0f && lc->edges[3].count < 16)
|
||||
lc->edges[3].leafs[lc->edges[3].count++] = (u16)i;
|
||||
}
|
||||
}
|
||||
|
||||
static i32 world_find_chunk(const demo3d_world* world, i32 cx, i32 cz) {
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
if (world->loaded[i].cx == cx && world->loaded[i].cz == cz &&
|
||||
world->loaded[i].chunk && world->loaded[i].chunk->bsp)
|
||||
return (i32)i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void world_mark_leaf_faces(const demo3d_bsp* bsp, demo3d_bsp_render_state* rs, u32 leaf_idx) {
|
||||
const demo3d_bsp_leaf* leaf = &bsp->leafs[leaf_idx];
|
||||
for (u32 i = 0; i < leaf->num_marksurfaces; i++) {
|
||||
u32 si = leaf->first_marksurface + i;
|
||||
if (si < bsp->num_marksurfaces) {
|
||||
u32 fi = bsp->marksurfaces[si];
|
||||
if (fi < bsp->num_faces && rs)
|
||||
rs->render_face_flags[fi] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool vis_try_enqueue(u8** vis, pxl8_rect** windows, world_vis_node* queue,
|
||||
u32* tail, u32 max_queue,
|
||||
u16 ci, u16 li, pxl8_rect nw) {
|
||||
if (!vis[ci] || !windows[ci]) return false;
|
||||
|
||||
u32 byte = li >> 3;
|
||||
u32 bit = 1 << (li & 7);
|
||||
|
||||
if (vis[ci][byte] & bit) {
|
||||
pxl8_rect* ex = &windows[ci][li];
|
||||
bool expanded = false;
|
||||
if (nw.x0 < ex->x0) { ex->x0 = nw.x0; expanded = true; }
|
||||
if (nw.y0 < ex->y0) { ex->y0 = nw.y0; expanded = true; }
|
||||
if (nw.x1 > ex->x1) { ex->x1 = nw.x1; expanded = true; }
|
||||
if (nw.y1 > ex->y1) { ex->y1 = nw.y1; expanded = true; }
|
||||
if (expanded && *tail < max_queue)
|
||||
queue[(*tail)++] = (world_vis_node){ci, li, *ex};
|
||||
return expanded;
|
||||
}
|
||||
|
||||
vis[ci][byte] |= bit;
|
||||
windows[ci][li] = nw;
|
||||
if (*tail < max_queue)
|
||||
queue[(*tail)++] = (world_vis_node){ci, li, nw};
|
||||
return true;
|
||||
}
|
||||
|
||||
static void world_compute_visibility(demo3d_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||
const f32 CHUNK_SIZE = 16.0f * 64.0f;
|
||||
const f32 PORTAL_Y_HI = 192.0f;
|
||||
|
||||
const pxl8_mat4* vp = pxl8_3d_get_view_proj(gfx);
|
||||
|
||||
i32 cam_ci = -1, cam_li = -1;
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
demo3d_loaded_chunk* lc = &world->loaded[i];
|
||||
if (!lc->chunk || !lc->chunk->bsp) continue;
|
||||
const demo3d_bsp* bsp = lc->chunk->bsp;
|
||||
f32 cx0 = lc->cx * CHUNK_SIZE;
|
||||
f32 cz0 = lc->cz * CHUNK_SIZE;
|
||||
if (camera_pos.x < cx0 || camera_pos.x >= cx0 + CHUNK_SIZE ||
|
||||
camera_pos.z < cz0 || camera_pos.z >= cz0 + CHUNK_SIZE) continue;
|
||||
if (bsp->bounds_max_x > bsp->bounds_min_x &&
|
||||
(camera_pos.x < bsp->bounds_min_x || camera_pos.x >= bsp->bounds_max_x ||
|
||||
camera_pos.z < bsp->bounds_min_z || camera_pos.z >= bsp->bounds_max_z))
|
||||
continue;
|
||||
i32 leaf = demo3d_bsp_find_leaf(bsp, camera_pos);
|
||||
if (leaf >= 0 && (u32)leaf < bsp->num_leafs &&
|
||||
bsp->leafs[leaf].contents == -2) {
|
||||
cam_ci = (i32)i;
|
||||
cam_li = leaf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cam_ci < 0 || !vp) {
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
demo3d_bsp_render_state* rs = world->loaded[i].render_state;
|
||||
if (!rs) continue;
|
||||
if (rs->render_face_flags)
|
||||
memset(rs->render_face_flags, 1, rs->num_faces);
|
||||
rs->exterior = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
memset(world->vis_bits, 0, sizeof(world->vis_bits));
|
||||
memset(world->vis_ptrs, 0, sizeof(world->vis_ptrs));
|
||||
memset(world->vis_win_ptrs, 0, sizeof(world->vis_win_ptrs));
|
||||
|
||||
u32 voff = 0, woff = 0;
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
if (world->loaded[i].chunk && world->loaded[i].chunk->bsp) {
|
||||
u32 nl = world->loaded[i].chunk->bsp->num_leafs;
|
||||
u32 vbytes = (nl + 7) / 8;
|
||||
if (voff + vbytes > DEMO3D_VIS_BYTES || woff + nl > DEMO3D_VIS_MAX_NODES)
|
||||
continue;
|
||||
world->vis_ptrs[i] = world->vis_bits + voff;
|
||||
voff += vbytes;
|
||||
world->vis_win_ptrs[i] = world->vis_windows + woff;
|
||||
woff += nl;
|
||||
}
|
||||
}
|
||||
|
||||
if (!world->vis_ptrs[cam_ci] || !world->vis_win_ptrs[cam_ci]) return;
|
||||
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
demo3d_bsp_render_state* rs = world->loaded[i].render_state;
|
||||
if (!rs) continue;
|
||||
if (rs->render_face_flags)
|
||||
memset(rs->render_face_flags, 0, rs->num_faces);
|
||||
rs->exterior = false;
|
||||
}
|
||||
|
||||
u32 head = 0, tail = 0;
|
||||
|
||||
pxl8_rect full_screen = {-1.0f, -1.0f, 1.0f, 1.0f};
|
||||
const demo3d_bsp* cam_bsp = world->loaded[cam_ci].chunk->bsp;
|
||||
bool cam_exterior = cam_bsp->leafs[cam_li].contents == 0 &&
|
||||
!(cam_bsp->bounds_max_x > cam_bsp->bounds_min_x);
|
||||
|
||||
world->vis_ptrs[cam_ci][cam_li >> 3] |= (1 << (cam_li & 7));
|
||||
world->vis_win_ptrs[cam_ci][cam_li] = full_screen;
|
||||
world->vis_queue[tail++] = (world_vis_node){(u16)cam_ci, (u16)cam_li, full_screen};
|
||||
|
||||
while (head < tail) {
|
||||
world_vis_node cur = world->vis_queue[head++];
|
||||
demo3d_loaded_chunk* lc = &world->loaded[cur.chunk_idx];
|
||||
const demo3d_bsp* bsp = lc->chunk->bsp;
|
||||
|
||||
world_mark_leaf_faces(bsp, lc->render_state, cur.leaf_idx);
|
||||
|
||||
if (bsp->cell_portals && cur.leaf_idx < bsp->num_cell_portals) {
|
||||
bool is_cam_leaf = (cur.chunk_idx == (u16)cam_ci && cur.leaf_idx == (u16)cam_li);
|
||||
bool is_cam_chunk = (cur.chunk_idx == (u16)cam_ci);
|
||||
const demo3d_bsp_cell_portals* cp = &bsp->cell_portals[cur.leaf_idx];
|
||||
for (u8 pi = 0; pi < cp->num_portals; pi++) {
|
||||
const demo3d_bsp_portal* p = &cp->portals[pi];
|
||||
u32 target = p->target_leaf;
|
||||
if (target >= bsp->num_leafs) continue;
|
||||
if (bsp->leafs[target].contents == -1) continue;
|
||||
if (is_cam_chunk && !demo3d_bsp_is_leaf_visible(bsp, cam_li, (i32)target)) continue;
|
||||
|
||||
if (is_cam_leaf || cam_exterior) {
|
||||
vis_try_enqueue(world->vis_ptrs, world->vis_win_ptrs,
|
||||
world->vis_queue, &tail, DEMO3D_VIS_MAX_QUEUE,
|
||||
cur.chunk_idx, (u16)target, full_screen);
|
||||
continue;
|
||||
}
|
||||
|
||||
pxl8_rect psr = project_portal(p->x0, p->z0, p->x1, p->z1,
|
||||
0.0f, PORTAL_Y_HI, vp);
|
||||
if (!vr_valid(psr)) continue;
|
||||
pxl8_rect nw = vr_intersect(cur.window, psr);
|
||||
if (!vr_valid(nw)) continue;
|
||||
|
||||
vis_try_enqueue(world->vis_ptrs, world->vis_win_ptrs,
|
||||
world->vis_queue, &tail, DEMO3D_VIS_MAX_QUEUE,
|
||||
cur.chunk_idx, (u16)target, nw);
|
||||
}
|
||||
}
|
||||
|
||||
const demo3d_bsp_leaf* leaf = &bsp->leafs[cur.leaf_idx];
|
||||
f32 chunk_x0 = lc->cx * CHUNK_SIZE;
|
||||
f32 chunk_z0 = lc->cz * CHUNK_SIZE;
|
||||
f32 chunk_x1 = chunk_x0 + CHUNK_SIZE;
|
||||
f32 chunk_z1 = chunk_z0 + CHUNK_SIZE;
|
||||
|
||||
struct { i32 dx, dz; bool at_edge; f32 bnd; } dirs[4] = {
|
||||
{ 0, -1, (f32)leaf->mins[2] <= (f32)((i16)chunk_z0) + 1.0f, chunk_z0 },
|
||||
{ 0, 1, (f32)leaf->maxs[2] >= (f32)((i16)chunk_z1) - 1.0f, chunk_z1 },
|
||||
{-1, 0, (f32)leaf->mins[0] <= (f32)((i16)chunk_x0) + 1.0f, chunk_x0 },
|
||||
{ 1, 0, (f32)leaf->maxs[0] >= (f32)((i16)chunk_x1) - 1.0f, chunk_x1 },
|
||||
};
|
||||
|
||||
static const u8 opposite_edge[4] = {1, 0, 3, 2};
|
||||
|
||||
for (u32 d = 0; d < 4; d++) {
|
||||
if (!dirs[d].at_edge) continue;
|
||||
i32 nci = world_find_chunk(world, lc->cx + dirs[d].dx, lc->cz + dirs[d].dz);
|
||||
if (nci < 0) continue;
|
||||
|
||||
const demo3d_bsp* nbsp = world->loaded[nci].chunk->bsp;
|
||||
const demo3d_edge_leafs* nedge = &world->loaded[nci].edges[opposite_edge[d]];
|
||||
|
||||
for (u8 k = 0; k < nedge->count; k++) {
|
||||
u16 nl = nedge->leafs[k];
|
||||
const demo3d_bsp_leaf* nleaf =  ->leafs[nl];
|
||||
|
||||
bool overlaps = false;
|
||||
if (dirs[d].dx != 0) {
|
||||
overlaps = leaf->maxs[2] > nleaf->mins[2] && leaf->mins[2] < nleaf->maxs[2];
|
||||
} else {
|
||||
overlaps = leaf->maxs[0] > nleaf->mins[0] && leaf->mins[0] < nleaf->maxs[0];
|
||||
}
|
||||
if (!overlaps) continue;
|
||||
|
||||
f32 bpx0, bpz0, bpx1, bpz1;
|
||||
if (dirs[d].dx != 0) {
|
||||
bpx0 = dirs[d].bnd; bpx1 = dirs[d].bnd;
|
||||
i16 zlo = leaf->mins[2] > nleaf->mins[2] ? leaf->mins[2] : nleaf->mins[2];
|
||||
i16 zhi = leaf->maxs[2] < nleaf->maxs[2] ? leaf->maxs[2] : nleaf->maxs[2];
|
||||
bpz0 = (f32)zlo; bpz1 = (f32)zhi;
|
||||
} else {
|
||||
bpz0 = dirs[d].bnd; bpz1 = dirs[d].bnd;
|
||||
i16 xlo = leaf->mins[0] > nleaf->mins[0] ? leaf->mins[0] : nleaf->mins[0];
|
||||
i16 xhi = leaf->maxs[0] < nleaf->maxs[0] ? leaf->maxs[0] : nleaf->maxs[0];
|
||||
bpx0 = (f32)xlo; bpx1 = (f32)xhi;
|
||||
}
|
||||
|
||||
if (cam_exterior) {
|
||||
vis_try_enqueue(world->vis_ptrs, world->vis_win_ptrs,
|
||||
world->vis_queue, &tail, DEMO3D_VIS_MAX_QUEUE,
|
||||
(u16)nci, (u16)nl, full_screen);
|
||||
continue;
|
||||
}
|
||||
|
||||
pxl8_rect psr = project_portal(bpx0, bpz0, bpx1, bpz1,
|
||||
0.0f, PORTAL_Y_HI, vp);
|
||||
if (!vr_valid(psr)) continue;
|
||||
pxl8_rect nw = vr_intersect(cur.window, psr);
|
||||
if (!vr_valid(nw)) continue;
|
||||
|
||||
vis_try_enqueue(world->vis_ptrs, world->vis_win_ptrs,
|
||||
world->vis_queue, &tail, DEMO3D_VIS_MAX_QUEUE,
|
||||
(u16)nci, (u16)nl, nw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void demo3d_world_render(demo3d_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos) {
|
||||
if (!world || !gfx) return;
|
||||
|
||||
world_compute_visibility(world, gfx, camera_pos);
|
||||
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
demo3d_loaded_chunk* lc = &world->loaded[i];
|
||||
if (!lc->chunk || !lc->chunk->bsp || !lc->render_state) continue;
|
||||
demo3d_bsp_render(gfx, lc->chunk->bsp, lc->render_state, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_shared_materials(demo3d_world* world, demo3d_bsp_render_state* rs) {
|
||||
for (u16 i = 0; i < 16; i++) {
|
||||
if (world->shared_material_set[i]) {
|
||||
demo3d_bsp_set_material(rs, i, &world->shared_materials[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void demo3d_world_sync(demo3d_world* world, demo3d_net* net) {
|
||||
if (!world || !net) return;
|
||||
|
||||
if (!demo3d_net_has_chunk(net)) {
|
||||
u32 old_count = world->loaded_count;
|
||||
world->loaded_count = 0;
|
||||
world->active_chunk = NULL;
|
||||
world->active_render_state = NULL;
|
||||
for (u32 i = 0; i < old_count; i++) {
|
||||
demo3d_bsp_render_state_destroy(world->loaded[i].render_state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
i32 center_cx = demo3d_net_chunk_cx(net);
|
||||
i32 center_cz = demo3d_net_chunk_cz(net);
|
||||
|
||||
demo3d_loaded_chunk old_loaded[DEMO3D_WORLD_MAX_LOADED_CHUNKS];
|
||||
u32 old_count = world->loaded_count;
|
||||
memcpy(old_loaded, world->loaded, sizeof(old_loaded));
|
||||
|
||||
demo3d_loaded_chunk new_loaded[DEMO3D_WORLD_MAX_LOADED_CHUNKS];
|
||||
u32 new_count = 0;
|
||||
demo3d_chunk* new_active_chunk = NULL;
|
||||
demo3d_bsp_render_state* new_active_rs = NULL;
|
||||
|
||||
for (i32 dz = -2; dz <= 2; dz++) {
|
||||
for (i32 dx = -2; dx <= 2; dx++) {
|
||||
i32 cx = center_cx + dx;
|
||||
i32 cz = center_cz + dz;
|
||||
u32 id = demo3d_chunk_hash(cx, cz);
|
||||
|
||||
demo3d_chunk* chunk = demo3d_chunk_cache_get_bsp(world->chunk_cache, id);
|
||||
if (!chunk || !chunk->bsp) {
|
||||
for (u32 j = 0; j < old_count; j++) {
|
||||
if (old_loaded[j].active && old_loaded[j].cx == cx && old_loaded[j].cz == cz) {
|
||||
u32 idx = new_count++;
|
||||
new_loaded[idx] = old_loaded[j];
|
||||
old_loaded[j].active = false;
|
||||
if (dx == 0 && dz == 0) {
|
||||
new_active_chunk = new_loaded[idx].chunk;
|
||||
new_active_rs = new_loaded[idx].render_state;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
demo3d_bsp_render_state* rs = NULL;
|
||||
for (u32 j = 0; j < old_count; j++) {
|
||||
if (old_loaded[j].active && old_loaded[j].cx == cx && old_loaded[j].cz == cz &&
|
||||
old_loaded[j].chunk == chunk) {
|
||||
rs = old_loaded[j].render_state;
|
||||
old_loaded[j].active = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rs) {
|
||||
rs = demo3d_bsp_render_state_create(chunk->bsp->num_faces);
|
||||
if (rs && rs->render_face_flags)
|
||||
memset(rs->render_face_flags, 1, rs->num_faces);
|
||||
apply_shared_materials(world, rs);
|
||||
}
|
||||
|
||||
u32 idx = new_count++;
|
||||
new_loaded[idx] = (demo3d_loaded_chunk){
|
||||
.chunk = chunk,
|
||||
.render_state = rs,
|
||||
.cx = cx,
|
||||
.cz = cz,
|
||||
.active = true,
|
||||
};
|
||||
compute_edge_leafs(&new_loaded[idx]);
|
||||
|
||||
if (dx == 0 && dz == 0) {
|
||||
new_active_chunk = chunk;
|
||||
new_active_rs = rs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (u32 j = 0; j < old_count; j++) {
|
||||
if (!old_loaded[j].active) continue;
|
||||
if (new_count < DEMO3D_WORLD_MAX_LOADED_CHUNKS &&
|
||||
old_loaded[j].chunk && old_loaded[j].chunk->bsp &&
|
||||
demo3d_chunk_cache_get_bsp(world->chunk_cache, old_loaded[j].chunk->id)) {
|
||||
new_loaded[new_count++] = old_loaded[j];
|
||||
} else {
|
||||
demo3d_bsp_render_state_destroy(old_loaded[j].render_state);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(world->loaded, new_loaded, sizeof(new_loaded));
|
||||
world->active_chunk = new_active_chunk;
|
||||
world->active_render_state = new_active_rs;
|
||||
world->loaded_count = new_count;
|
||||
}
|
||||
|
||||
void demo3d_world_set_bsp_material(demo3d_world* world, u16 material_id, const pxl8_gfx_material* material) {
|
||||
if (!world || !material || material_id >= 16) return;
|
||||
|
||||
world->shared_materials[material_id] = *material;
|
||||
world->shared_material_set[material_id] = true;
|
||||
|
||||
for (u32 i = 0; i < world->loaded_count; i++) {
|
||||
if (world->loaded[i].render_state) {
|
||||
demo3d_bsp_set_material(world->loaded[i].render_state, material_id, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void demo3d_world_set_sim_config(demo3d_world* world, const demo3d_sim_config* config) {
|
||||
if (!world || !config) return;
|
||||
world->sim_config = *config;
|
||||
}
|
||||
|
||||
void demo3d_world_init_local_player(demo3d_world* world, f32 x, f32 y, f32 z) {
|
||||
if (!world) return;
|
||||
world->local_player.pos = (pxl8_vec3){x, y, z};
|
||||
world->local_player.vel = (pxl8_vec3){0, 0, 0};
|
||||
world->local_player.yaw = 0;
|
||||
world->local_player.pitch = 0;
|
||||
world->local_player.flags = DEMO3D_SIM_FLAG_ALIVE | DEMO3D_SIM_FLAG_PLAYER | DEMO3D_SIM_FLAG_GROUNDED;
|
||||
world->local_player.kind = 0;
|
||||
world->client_tick = 0;
|
||||
world->pointer_motion = (pxl8_vec2){0};
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
world->render_state[0] = world->local_player;
|
||||
world->render_state[1] = world->local_player;
|
||||
#endif
|
||||
}
|
||||
|
||||
void demo3d_world_set_look(demo3d_world* world, f32 yaw, f32 pitch) {
|
||||
if (!world) return;
|
||||
world->local_player.yaw = yaw;
|
||||
world->local_player.pitch = pitch;
|
||||
world->pointer_motion = (pxl8_vec2){.yaw = yaw, .pitch = pitch};
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
world->render_state[0].yaw = yaw;
|
||||
world->render_state[0].pitch = pitch;
|
||||
world->render_state[1].yaw = yaw;
|
||||
world->render_state[1].pitch = pitch;
|
||||
#endif
|
||||
}
|
||||
|
||||
demo3d_sim_entity* demo3d_world_local_player(demo3d_world* world) {
|
||||
if (!world) return NULL;
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
const demo3d_sim_entity* state = demo3d_world_get_render_state(world);
|
||||
if (!state) return NULL;
|
||||
if (!(state->flags & DEMO3D_SIM_FLAG_ALIVE)) return NULL;
|
||||
return (demo3d_sim_entity*)state;
|
||||
#else
|
||||
if (!(world->local_player.flags & DEMO3D_SIM_FLAG_ALIVE)) return NULL;
|
||||
return &world->local_player;
|
||||
#endif
|
||||
}
|
||||
|
||||
void demo3d_world_predict(demo3d_world* world, demo3d_net* net, const demo3d_input_msg* input, f32 dt) {
|
||||
if (!world || !net || !input) return;
|
||||
if (!(world->local_player.flags & DEMO3D_SIM_FLAG_ALIVE)) return;
|
||||
|
||||
demo3d_sim_world sim = demo3d_world_sim_world(world, world->local_player.pos);
|
||||
demo3d_sim_move_player(&world->local_player, input, &sim, &world->sim_config, dt);
|
||||
|
||||
world->client_tick++;
|
||||
|
||||
entity_to_userdata(&world->local_player, demo3d_net_predicted_state(net));
|
||||
demo3d_net_predicted_tick_set(net, world->client_tick);
|
||||
}
|
||||
|
||||
void demo3d_world_reconcile(demo3d_world* world, demo3d_net* net, f32 dt) {
|
||||
if (!world || !net) return;
|
||||
if (!(world->local_player.flags & DEMO3D_SIM_FLAG_ALIVE)) return;
|
||||
if (!demo3d_net_needs_correction(net)) return;
|
||||
|
||||
u64 player_id = demo3d_net_player_id(net);
|
||||
const u8* server_state = demo3d_net_entity_userdata(net, player_id);
|
||||
if (!server_state) return;
|
||||
|
||||
demo3d_sim_entity server_player = {0};
|
||||
userdata_to_entity(server_state, &server_player);
|
||||
|
||||
if (!(server_player.flags & DEMO3D_SIM_FLAG_ALIVE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
world->local_player = server_player;
|
||||
|
||||
const demo3d_snapshot_header* snap = demo3d_net_snapshot(net);
|
||||
u64 server_tick = snap ? snap->tick : 0;
|
||||
|
||||
for (u64 tick = server_tick + 1; tick <= world->client_tick; tick++) {
|
||||
const demo3d_input_msg* input = demo3d_net_input_at(net, tick);
|
||||
if (!input) continue;
|
||||
|
||||
demo3d_sim_world sim = demo3d_world_sim_world(world, world->local_player.pos);
|
||||
demo3d_sim_move_player(&world->local_player, input, &sim, &world->sim_config, dt);
|
||||
}
|
||||
|
||||
entity_to_userdata(&world->local_player, demo3d_net_predicted_state(net));
|
||||
}
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
|
||||
#define SIM_TIMESTEP (1.0f / 60.0f)
|
||||
|
||||
static void demo3d_world_sim_tick(demo3d_world* world, f32 dt) {
|
||||
bool alive = (world->local_player.flags & DEMO3D_SIM_FLAG_ALIVE) != 0;
|
||||
demo3d_input_msg merged = {0};
|
||||
demo3d_input_msg* input = NULL;
|
||||
|
||||
while ((input = pxl8_queue_pop(&world->input_queue))) {
|
||||
merged.look_dx += input->look_dx;
|
||||
merged.look_dy += input->look_dy;
|
||||
merged.move_x = input->move_x;
|
||||
merged.move_y = input->move_y;
|
||||
merged.buttons |= input->buttons;
|
||||
pxl8_free(input);
|
||||
}
|
||||
|
||||
if (!alive) {
|
||||
return;
|
||||
}
|
||||
|
||||
const f32 MAX_LOOK_DELTA = 100.0f;
|
||||
if (merged.look_dx > MAX_LOOK_DELTA) merged.look_dx = MAX_LOOK_DELTA;
|
||||
if (merged.look_dx < -MAX_LOOK_DELTA) merged.look_dx = -MAX_LOOK_DELTA;
|
||||
if (merged.look_dy > MAX_LOOK_DELTA) merged.look_dy = MAX_LOOK_DELTA;
|
||||
if (merged.look_dy < -MAX_LOOK_DELTA) merged.look_dy = -MAX_LOOK_DELTA;
|
||||
|
||||
merged.tick = world->client_tick;
|
||||
merged.timestamp = pxl8_get_ticks_ns();
|
||||
merged.yaw = world->pointer_motion.yaw;
|
||||
if (world->net) {
|
||||
demo3d_net_send_input(world->net, &merged);
|
||||
}
|
||||
|
||||
world->local_player.yaw = world->pointer_motion.yaw;
|
||||
world->local_player.pitch = world->pointer_motion.pitch;
|
||||
merged.look_dx = 0;
|
||||
merged.look_dy = 0;
|
||||
|
||||
demo3d_sim_world sim = demo3d_world_sim_world(world, world->local_player.pos);
|
||||
demo3d_sim_move_player(&world->local_player, &merged, &sim, &world->sim_config, dt);
|
||||
|
||||
if (world->net) {
|
||||
entity_to_userdata(&world->local_player, demo3d_net_predicted_state(world->net));
|
||||
demo3d_net_predicted_tick_set(world->net, world->client_tick);
|
||||
}
|
||||
|
||||
world->client_tick++;
|
||||
}
|
||||
|
||||
static void demo3d_world_swap_buffers(demo3d_world* world) {
|
||||
u32 back = atomic_load(&world->active_buffer) ^ 1;
|
||||
world->render_state[back] = world->local_player;
|
||||
atomic_store(&world->active_buffer, back);
|
||||
}
|
||||
|
||||
static int demo3d_world_sim_thread(void* data) {
|
||||
demo3d_world* world = (demo3d_world*)data;
|
||||
u64 last_time = pxl8_get_ticks_ns();
|
||||
|
||||
while (atomic_load(&world->sim_running)) {
|
||||
if (atomic_load(&world->sim_paused)) {
|
||||
last_time = pxl8_get_ticks_ns();
|
||||
pxl8_sleep_ms(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
u64 now = pxl8_get_ticks_ns();
|
||||
f32 dt = (f32)(now - last_time) / 1e9f;
|
||||
last_time = now;
|
||||
|
||||
if (dt > 0.1f) dt = 0.1f;
|
||||
if (dt < 0.0001f) dt = 0.0001f;
|
||||
|
||||
world->sim_accumulator += dt;
|
||||
|
||||
while (world->sim_accumulator >= SIM_TIMESTEP) {
|
||||
demo3d_chunk_cache_tick(world->chunk_cache);
|
||||
|
||||
if (world->net) {
|
||||
pxl8_packet* pkt;
|
||||
while ((pkt = pxl8_net_pop_packet(world->net->transport))) {
|
||||
demo3d_net_dispatch_packet(world->net, pkt);
|
||||
pxl8_net_packet_free(pkt);
|
||||
}
|
||||
|
||||
demo3d_world_sync(world, world->net);
|
||||
demo3d_world_reconcile(world, world->net, SIM_TIMESTEP);
|
||||
}
|
||||
|
||||
demo3d_world_sim_tick(world, SIM_TIMESTEP);
|
||||
world->sim_accumulator -= SIM_TIMESTEP;
|
||||
}
|
||||
|
||||
demo3d_world_swap_buffers(world);
|
||||
pxl8_sleep_ms(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void demo3d_world_start_sim_thread(demo3d_world* world, demo3d_net* net) {
|
||||
if (!world || world->sim_thread) return;
|
||||
|
||||
world->net = net;
|
||||
pxl8_queue_init(&world->input_queue);
|
||||
atomic_store(&world->active_buffer, 0);
|
||||
atomic_store(&world->sim_running, true);
|
||||
world->sim_accumulator = 0.0f;
|
||||
|
||||
world->render_state[0] = world->local_player;
|
||||
world->render_state[1] = world->local_player;
|
||||
|
||||
world->sim_thread = pxl8_thread_create(demo3d_world_sim_thread, "pxl8_sim", world);
|
||||
}
|
||||
|
||||
void demo3d_world_stop_sim_thread(demo3d_world* world) {
|
||||
if (!world || !world->sim_thread) return;
|
||||
|
||||
atomic_store(&world->sim_running, false);
|
||||
pxl8_thread_wait(world->sim_thread, NULL);
|
||||
world->sim_thread = NULL;
|
||||
|
||||
demo3d_input_msg* input;
|
||||
while ((input = pxl8_queue_pop(&world->input_queue))) {
|
||||
pxl8_free(input);
|
||||
}
|
||||
}
|
||||
|
||||
void demo3d_world_pause_sim(demo3d_world* world, bool paused) {
|
||||
if (!world) return;
|
||||
|
||||
if (paused) {
|
||||
atomic_store(&world->sim_paused, true);
|
||||
} else {
|
||||
demo3d_input_msg* input;
|
||||
while ((input = pxl8_queue_pop(&world->input_queue))) {
|
||||
pxl8_free(input);
|
||||
}
|
||||
world->sim_accumulator = 0.0f;
|
||||
atomic_store(&world->sim_paused, false);
|
||||
}
|
||||
}
|
||||
|
||||
void demo3d_world_push_input(demo3d_world* world, const demo3d_input_msg* input) {
|
||||
if (!world || !input) return;
|
||||
|
||||
world->pointer_motion.yaw -= input->look_dx * 0.008f;
|
||||
f32 pitch = world->pointer_motion.pitch - input->look_dy * 0.008f;
|
||||
if (pitch > world->sim_config.max_pitch) pitch = world->sim_config.max_pitch;
|
||||
if (pitch < -world->sim_config.max_pitch) pitch = -world->sim_config.max_pitch;
|
||||
world->pointer_motion.pitch = pitch;
|
||||
|
||||
demo3d_input_msg* copy = pxl8_malloc(sizeof(demo3d_input_msg));
|
||||
if (copy) {
|
||||
*copy = *input;
|
||||
if (!pxl8_queue_push(&world->input_queue, copy)) {
|
||||
pxl8_free(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const demo3d_sim_entity* demo3d_world_get_render_state(const demo3d_world* world) {
|
||||
if (!world) return NULL;
|
||||
u32 front = atomic_load(&((demo3d_world*)world)->active_buffer);
|
||||
return &world->render_state[front];
|
||||
}
|
||||
|
||||
f32 demo3d_world_get_interp_alpha(const demo3d_world* world) {
|
||||
if (!world) return 1.0f;
|
||||
return world->sim_accumulator / SIM_TIMESTEP;
|
||||
}
|
||||
|
||||
#endif
|
||||
71
demo3d/client/world/demo3d_world.h
Normal file
71
demo3d/client/world/demo3d_world.h
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#pragma once
|
||||
|
||||
#include "demo3d_entity.h"
|
||||
#include "pxl8_gfx.h"
|
||||
#include "pxl8_gfx3d.h"
|
||||
#include "pxl8_math.h"
|
||||
#include "demo3d_net.h"
|
||||
#include "demo3d_sim.h"
|
||||
#include "pxl8_types.h"
|
||||
#include "demo3d_chunk.h"
|
||||
#include "demo3d_chunk_cache.h"
|
||||
#include "demo3d_bsp_render.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define DEMO3D_WORLD_MAX_LOADED_CHUNKS 64
|
||||
|
||||
typedef struct {
|
||||
u16 leafs[16];
|
||||
u8 count;
|
||||
} demo3d_edge_leafs;
|
||||
|
||||
typedef struct demo3d_loaded_chunk {
|
||||
demo3d_chunk* chunk;
|
||||
demo3d_bsp_render_state* render_state;
|
||||
demo3d_edge_leafs edges[4];
|
||||
i32 cx;
|
||||
i32 cz;
|
||||
bool active;
|
||||
} demo3d_loaded_chunk;
|
||||
|
||||
typedef struct demo3d_world demo3d_world;
|
||||
|
||||
demo3d_world* demo3d_world_create(void);
|
||||
void demo3d_world_destroy(demo3d_world* world);
|
||||
|
||||
demo3d_chunk_cache* demo3d_world_get_chunk_cache(demo3d_world* world);
|
||||
demo3d_chunk* demo3d_world_active_chunk(demo3d_world* world);
|
||||
|
||||
bool demo3d_world_point_solid(const demo3d_world* world, f32 x, f32 y, f32 z);
|
||||
pxl8_ray demo3d_world_ray(const demo3d_world* world, pxl8_vec3 from, pxl8_vec3 to);
|
||||
pxl8_ray demo3d_world_sweep(const demo3d_world* world, pxl8_vec3 from, pxl8_vec3 to, f32 radius);
|
||||
|
||||
void demo3d_world_update(demo3d_world* world, f32 dt);
|
||||
void demo3d_world_render(demo3d_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);
|
||||
void demo3d_world_sync(demo3d_world* world, demo3d_net* net);
|
||||
|
||||
void demo3d_world_set_bsp_material(demo3d_world* world, u16 material_id, const pxl8_gfx_material* material);
|
||||
|
||||
void demo3d_world_set_sim_config(demo3d_world* world, const demo3d_sim_config* config);
|
||||
void demo3d_world_init_local_player(demo3d_world* world, f32 x, f32 y, f32 z);
|
||||
void demo3d_world_set_look(demo3d_world* world, f32 yaw, f32 pitch);
|
||||
demo3d_sim_entity* demo3d_world_local_player(demo3d_world* world);
|
||||
demo3d_sim_world demo3d_world_sim_world(const demo3d_world* world, pxl8_vec3 pos);
|
||||
void demo3d_world_predict(demo3d_world* world, demo3d_net* net, const demo3d_input_msg* input, f32 dt);
|
||||
void demo3d_world_reconcile(demo3d_world* world, demo3d_net* net, f32 dt);
|
||||
|
||||
#ifdef PXL8_ASYNC_THREADS
|
||||
void demo3d_world_start_sim_thread(demo3d_world* world, demo3d_net* net);
|
||||
void demo3d_world_stop_sim_thread(demo3d_world* world);
|
||||
void demo3d_world_pause_sim(demo3d_world* world, bool paused);
|
||||
void demo3d_world_push_input(demo3d_world* world, const demo3d_input_msg* input);
|
||||
const demo3d_sim_entity* demo3d_world_get_render_state(const demo3d_world* world);
|
||||
f32 demo3d_world_get_interp_alpha(const demo3d_world* world);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue