dragonblocks_alpha/src/client/client_entity.c

320 lines
7.8 KiB
C

#include <asprintf.h>
#include <dragonstd/map.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "client/cube.h"
#include "client/client_config.h"
#include "client/client_entity.h"
#include "client/client_player.h"
#include "client/frustum.h"
#include "client/opengl.h"
#include "client/light.h"
#include "client/shader.h"
#include "client/window.h"
ClientEntityType client_entity_types[COUNT_ENTITY];
ModelShader client_entity_shader;
typedef struct {
v3f32 position;
v3f32 normal;
} __attribute__((packed)) EntityVertex;
Mesh client_entity_cube = {
.layout = &(VertexLayout) {
.attributes = (VertexAttribute[]) {
{GL_FLOAT, 3, sizeof(v3f32)}, // position
{GL_FLOAT, 3, sizeof(v3f32)}, // normal
},
.count = 2,
.size = sizeof(EntityVertex),
},
.vao = 0,
.vbo = 0,
.data = NULL,
.count = 36,
.free_data = false,
};
static GLuint shader_prog;
static GLint loc_VP;
static GLint loc_depthOffset;
static LightShader light_shader;
static Map entities;
static List nametagged;
static pthread_mutex_t mtx_nametagged;
// any thread
// called when adding, getting or removing an entity from the map
static int cmp_entity(const Refcount *entity, const u64 *id)
{
return u64_cmp(&((ClientEntity *) entity->obj)->data.id, id);
}
// recv thread
// called when server sent removal of entity
static void entity_drop(ClientEntity *entity)
{
if (entity->type->remove)
entity->type->remove(entity);
if (entity->nametag) {
pthread_mutex_lock(&mtx_nametagged);
list_del(&nametagged, &entity->rc, &cmp_ref, &refcount_drp, NULL, NULL);
pthread_mutex_unlock(&mtx_nametagged);
entity->nametag->visible = false;
}
refcount_drp(&entity->rc);
}
// any thread
// called when all refs have been dropped
static void entity_delete(ClientEntity *entity)
{
if (entity->type->free)
entity->type->free(entity);
refcount_dst(&entity->rc);
if (entity->data.nametag)
free(entity->data.nametag);
pthread_rwlock_init(&entity->lock_pos_rot, NULL);
pthread_rwlock_init(&entity->lock_nametag, NULL);
pthread_rwlock_init(&entity->lock_box_off, NULL);
free(entity);
}
static void update_nametag(ClientEntity *entity)
{
if (entity->nametag) {
gui_text(entity->nametag, entity->data.nametag);
if (!entity->data.nametag)
entity->nametag->visible = false;
} else if (entity->data.nametag) {
entity->nametag = gui_add(NULL, (GUIElementDef) {
.pos = {-1.0f, -1.0f},
.z_index = 0.1f,
.offset = {0, 0},
.margin = {4, 4},
.align = {0.5f, 0.5f},
.scale = {1.0f, 1.0f},
.scale_type = SCALE_TEXT,
.affect_parent_scale = false,
.text = entity->data.nametag,
.image = NULL,
.text_color = (v4f32) {1.0f, 1.0f, 1.0f, 1.0f},
.bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.5f},
});
pthread_mutex_lock(&mtx_nametagged);
list_apd(&nametagged, refcount_inc(&entity->rc));
pthread_mutex_unlock(&mtx_nametagged);
}
}
static void update_nametag_pos(ClientEntity *entity)
{
if (!entity->data.nametag)
return;
pthread_rwlock_rdlock(&entity->lock_pos_rot);
pthread_rwlock_rdlock(&entity->lock_box_off);
mat4x4 mvp;
if (entity->nametag_offset)
mat4x4_mul(mvp, frustum, *entity->nametag_offset);
else
mat4x4_dup(mvp, frustum);
vec4 dst, src = {0.0f, 0.0f, 0.0f, 1.0f};
mat4x4_mul_vec4(dst, mvp, src);
dst[0] /= dst[3];
dst[1] /= dst[3];
dst[2] /= dst[3];
if ((entity->nametag->visible = dst[2] >= -1.0f && dst[2] <= 1.0f)) {
entity->nametag->def.pos = (v2f32) {dst[0] * 0.5f + 0.5f, 1.0f - (dst[1] * 0.5f + 0.5f)};
gui_transform(entity->nametag);
}
pthread_rwlock_unlock(&entity->lock_box_off);
pthread_rwlock_unlock(&entity->lock_pos_rot);
}
// main thread
// called on startup
void client_entity_init()
{
map_ini(&entities);
list_ini(&nametagged);
pthread_mutex_init(&mtx_nametagged, NULL);
}
// main thead
// called on shutdown
void client_entity_deinit()
{
// forget all entities
map_cnl(&entities, &refcount_drp, NULL, NULL, 0);
list_clr(&nametagged, &refcount_drp, NULL, NULL);
pthread_mutex_destroy(&mtx_nametagged);
}
void client_entity_gfx_init()
{
char *shader_def;
asprintf(&shader_def, "#define VIEW_DISTANCE %lf\n", client_config.view_distance);
shader_prog = shader_program_create(ASSET_PATH "shaders/3d/entity", shader_def);
free(shader_def);
loc_VP = glGetUniformLocation(shader_prog, "VP"); GL_DEBUG
loc_depthOffset = glGetUniformLocation(shader_prog, "depthOffset"); GL_DEBUG
EntityVertex vertices[6][6];
for (int f = 0; f < 6; f++) {
for (int v = 0; v < 6; v++) {
vertices[f][v].position = cube_vertices[f][v].position;
vertices[f][v].normal = cube_vertices[f][v].normal;
}
}
client_entity_cube.data = vertices;
mesh_upload(&client_entity_cube);
client_entity_shader.prog = shader_prog;
client_entity_shader.loc_transform = glGetUniformLocation(shader_prog, "model"); GL_DEBUG
light_shader.prog = shader_prog;
light_shader_locate(&light_shader);
client_entity_depth_offset(0.0f);
}
void client_entity_gfx_deinit()
{
glDeleteProgram(shader_prog); GL_DEBUG
mesh_destroy(&client_entity_cube);
}
void client_entity_gfx_update()
{
glProgramUniformMatrix4fv(shader_prog, loc_VP, 1, GL_FALSE, frustum[0]); GL_DEBUG
light_shader_update(&light_shader);
pthread_mutex_lock(&mtx_nametagged);
list_itr(&nametagged, &update_nametag_pos, NULL, &refcount_obj);
pthread_mutex_unlock(&mtx_nametagged);
}
void client_entity_depth_offset(f32 offset)
{
glProgramUniform1f(shader_prog, loc_depthOffset, offset);
}
ClientEntity *client_entity_grab(u64 id)
{
return id ? map_get(&entities, &id, &cmp_entity, &refcount_grb) : NULL;
}
void client_entity_transform(ClientEntity *entity)
{
if (!entity->model)
return;
entity->model->root->pos = v3f64_to_f32(entity->data.pos); // ToDo: the render pipeline needs to be updated to handle 64-bit positions
entity->model->root->rot = entity->data.rot;
if (entity->type->transform)
entity->type->transform(entity);
model_node_transform(entity->model->root);
}
void client_entity_add(__attribute__((unused)) void *peer, ToClientEntityAdd *pkt)
{
if (pkt->type >= COUNT_ENTITY)
return;
ClientEntity *entity = malloc(sizeof *entity);
entity->data = pkt->data;
entity->type = &client_entity_types[pkt->type];
refcount_ini(&entity->rc, entity, &entity_delete);
pkt->data.nametag = NULL;
entity->model = NULL;
entity->nametag = NULL;
if (entity->type->add)
entity->type->add(entity);
update_nametag(entity);
pthread_rwlock_init(&entity->lock_pos_rot, NULL);
pthread_rwlock_init(&entity->lock_nametag, NULL);
pthread_rwlock_init(&entity->lock_box_off, NULL);
if (!map_add(&entities, &entity->data.id, &entity->rc, &cmp_entity, &refcount_inc))
fprintf(stderr, "[warning] failed to add entity %" PRIu64 "\n", entity->data.id);
refcount_drp(&entity->rc);
}
void client_entity_remove(__attribute__((unused)) void *peer, ToClientEntityRemove *pkt)
{
map_del(&entities, &pkt->id, &cmp_entity, &entity_drop, NULL, &refcount_obj);
}
void client_entity_update_pos_rot(__attribute__((unused)) void *peer, ToClientEntityUpdatePosRot *pkt)
{
ClientEntity *entity = client_entity_grab(pkt->id);
if (!entity)
return;
pthread_rwlock_wrlock(&entity->lock_pos_rot);
entity->data.pos = pkt->pos;
entity->data.rot = pkt->rot;
if (entity->type->update_pos_rot)
entity->type->update_pos_rot(entity);
client_entity_transform(entity);
pthread_rwlock_unlock(&entity->lock_pos_rot);
refcount_drp(&entity->rc);
}
void client_entity_update_nametag(__attribute__((unused)) void *peer, ToClientEntityUpdateNametag *pkt)
{
ClientEntity *entity = client_entity_grab(pkt->id);
if (!entity)
return;
pthread_rwlock_wrlock(&entity->lock_nametag);
if (entity->data.nametag)
free(entity->data.nametag);
entity->data.nametag = pkt->nametag;
pkt->nametag = NULL;
if (entity->type->update_nametag)
entity->type->update_nametag(entity);
update_nametag(entity);
pthread_rwlock_unlock(&entity->lock_nametag);
refcount_drp(&entity->rc);
}