1 #include <asprintf/asprintf.h>
2 #include <dragonstd/map.h>
5 #include "client/cube.h"
6 #include "client/client_config.h"
7 #include "client/client_entity.h"
8 #include "client/client_player.h"
9 #include "client/frustum.h"
10 #include "client/gl_debug.h"
11 #include "client/light.h"
12 #include "client/shader.h"
13 #include "client/window.h"
15 ClientEntityType client_entity_types[COUNT_ENTITY];
17 ModelShader client_entity_shader;
21 } __attribute__((packed)) EntityVertex;
22 Mesh client_entity_cube = {
23 .layout = &(VertexLayout) {
24 .attributes = (VertexAttribute[]) {
25 {GL_FLOAT, 3, sizeof(v3f32)}, // position
26 {GL_FLOAT, 3, sizeof(v3f32)}, // normal
29 .size = sizeof(EntityVertex),
38 static GLuint shader_prog;
40 static GLint loc_depthOffset;
41 static LightShader light_shader;
43 static List nametagged;
44 static pthread_mutex_t mtx_nametagged;
47 // called when adding, getting or removing an entity from the map
48 static int cmp_entity(const Refcount *entity, const u64 *id)
50 return u64_cmp(&((ClientEntity *) entity->obj)->data.id, id);
54 // called when server sent removal of entity
55 static void entity_drop(ClientEntity *entity)
57 if (entity->type->remove)
58 entity->type->remove(entity);
60 if (entity->nametag) {
61 pthread_mutex_lock(&mtx_nametagged);
62 list_del(&nametagged, &entity->rc, &cmp_ref, &refcount_drp, NULL, NULL);
63 pthread_mutex_unlock(&mtx_nametagged);
65 entity->nametag->visible = false;
68 refcount_drp(&entity->rc);
72 // called when all refs have been dropped
73 static void entity_delete(ClientEntity *entity)
75 if (entity->type->free)
76 entity->type->free(entity);
78 refcount_dst(&entity->rc);
80 if (entity->data.nametag)
81 free(entity->data.nametag);
83 pthread_rwlock_init(&entity->lock_pos_rot, NULL);
84 pthread_rwlock_init(&entity->lock_nametag, NULL);
85 pthread_rwlock_init(&entity->lock_box_off, NULL);
90 static void update_nametag(ClientEntity *entity)
92 if (entity->nametag) {
93 gui_text(entity->nametag, entity->data.nametag);
95 if (!entity->data.nametag)
96 entity->nametag->visible = false;
97 } else if (entity->data.nametag) {
98 entity->nametag = gui_add(NULL, (GUIElementDef) {
99 .pos = {-1.0f, -1.0f},
103 .align = {0.5f, 0.5f},
104 .scale = {1.0f, 1.0f},
105 .scale_type = SCALE_TEXT,
106 .affect_parent_scale = false,
107 .text = entity->data.nametag,
109 .text_color = (v4f32) {1.0f, 1.0f, 1.0f, 1.0f},
110 .bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.5f},
113 pthread_mutex_lock(&mtx_nametagged);
114 list_apd(&nametagged, refcount_inc(&entity->rc));
115 pthread_mutex_unlock(&mtx_nametagged);
119 static void update_nametag_pos(ClientEntity *entity)
121 if (!entity->data.nametag)
124 pthread_rwlock_rdlock(&entity->lock_pos_rot);
125 pthread_rwlock_rdlock(&entity->lock_box_off);
128 if (entity->nametag_offset)
129 mat4x4_mul(mvp, frustum, *entity->nametag_offset);
131 mat4x4_dup(mvp, frustum);
133 vec4 dst, src = {0.0f, 0.0f, 0.0f, 1.0f};
134 mat4x4_mul_vec4(dst, mvp, src);
140 if ((entity->nametag->visible = dst[2] >= -1.0f && dst[2] <= 1.0f)) {
141 entity->nametag->def.pos = (v2f32) {dst[0] * 0.5f + 0.5f, 1.0f - (dst[1] * 0.5f + 0.5f)};
142 gui_transform(entity->nametag);
145 pthread_rwlock_unlock(&entity->lock_box_off);
146 pthread_rwlock_unlock(&entity->lock_pos_rot);
151 void client_entity_init()
154 list_ini(&nametagged);
155 pthread_mutex_init(&mtx_nametagged, NULL);
159 // called on shutdown
160 void client_entity_deinit()
162 // forget all entities
163 map_cnl(&entities, &refcount_drp, NULL, NULL, 0);
164 list_clr(&nametagged, &refcount_drp, NULL, NULL);
165 pthread_mutex_destroy(&mtx_nametagged);
168 bool client_entity_gfx_init()
171 asprintf(&shader_def, "#define VIEW_DISTANCE %lf\n", client_config.view_distance);
173 if (!shader_program_create(RESSOURCE_PATH "shaders/3d/entity", &shader_prog, shader_def)) {
174 fprintf(stderr, "[error] failed to create entity shader program\n");
180 loc_VP = glGetUniformLocation(shader_prog, "VP"); GL_DEBUG
181 loc_depthOffset = glGetUniformLocation(shader_prog, "depthOffset"); GL_DEBUG
183 EntityVertex vertices[6][6];
184 for (int f = 0; f < 6; f++) {
185 for (int v = 0; v < 6; v++) {
186 vertices[f][v].position = cube_vertices[f][v].position;
187 vertices[f][v].normal = cube_vertices[f][v].normal;
191 client_entity_cube.data = vertices;
192 mesh_upload(&client_entity_cube);
194 client_entity_shader.prog = shader_prog;
195 client_entity_shader.loc_transform = glGetUniformLocation(shader_prog, "model"); GL_DEBUG
197 light_shader.prog = shader_prog;
198 light_shader_locate(&light_shader);
200 client_entity_depth_offset(0.0f);
205 void client_entity_gfx_deinit()
207 glDeleteProgram(shader_prog); GL_DEBUG
208 mesh_destroy(&client_entity_cube);
211 void client_entity_gfx_update()
213 glProgramUniformMatrix4fv(shader_prog, loc_VP, 1, GL_FALSE, frustum[0]); GL_DEBUG
214 light_shader_update(&light_shader);
216 pthread_mutex_lock(&mtx_nametagged);
217 list_itr(&nametagged, &update_nametag_pos, NULL, &refcount_obj);
218 pthread_mutex_unlock(&mtx_nametagged);
221 void client_entity_depth_offset(f32 offset)
223 glProgramUniform1f(shader_prog, loc_depthOffset, offset);
226 ClientEntity *client_entity_grab(u64 id)
228 return id ? map_get(&entities, &id, &cmp_entity, &refcount_grb) : NULL;
231 void client_entity_transform(ClientEntity *entity)
236 entity->model->root->pos = v3f64_to_f32(entity->data.pos); // ToDo: the render pipeline needs to be updated to handle 64-bit positions
237 entity->model->root->rot = entity->data.rot;
239 if (entity->type->transform)
240 entity->type->transform(entity);
242 model_node_transform(entity->model->root);
245 void client_entity_add(__attribute__((unused)) void *peer, ToClientEntityAdd *pkt)
247 if (pkt->type >= COUNT_ENTITY)
250 ClientEntity *entity = malloc(sizeof *entity);
252 entity->data = pkt->data;
253 entity->type = &client_entity_types[pkt->type];
254 refcount_ini(&entity->rc, entity, &entity_delete);
256 pkt->data.nametag = NULL;
258 entity->model = NULL;
259 entity->nametag = NULL;
261 if (entity->type->add)
262 entity->type->add(entity);
264 update_nametag(entity);
266 pthread_rwlock_init(&entity->lock_pos_rot, NULL);
267 pthread_rwlock_init(&entity->lock_nametag, NULL);
268 pthread_rwlock_init(&entity->lock_box_off, NULL);
270 if (!map_add(&entities, &entity->data.id, &entity->rc, &cmp_entity, &refcount_inc))
271 fprintf(stderr, "[warning] failed to add entity %lu\n", entity->data.id);
273 refcount_drp(&entity->rc);
276 void client_entity_remove(__attribute__((unused)) void *peer, ToClientEntityRemove *pkt)
278 map_del(&entities, &pkt->id, &cmp_entity, &entity_drop, NULL, &refcount_obj);
281 void client_entity_update_pos_rot(__attribute__((unused)) void *peer, ToClientEntityUpdatePosRot *pkt)
283 ClientEntity *entity = client_entity_grab(pkt->id);
288 pthread_rwlock_wrlock(&entity->lock_pos_rot);
290 entity->data.pos = pkt->pos;
291 entity->data.rot = pkt->rot;
293 if (entity->type->update_pos_rot)
294 entity->type->update_pos_rot(entity);
296 client_entity_transform(entity);
298 pthread_rwlock_unlock(&entity->lock_pos_rot);
300 refcount_drp(&entity->rc);
303 void client_entity_update_nametag(__attribute__((unused)) void *peer, ToClientEntityUpdateNametag *pkt)
305 ClientEntity *entity = client_entity_grab(pkt->id);
310 pthread_rwlock_wrlock(&entity->lock_nametag);
312 if (entity->data.nametag)
313 free(entity->data.nametag);
315 entity->data.nametag = pkt->nametag;
318 if (entity->type->update_nametag)
319 entity->type->update_nametag(entity);
321 update_nametag(entity);
322 pthread_rwlock_unlock(&entity->lock_nametag);
324 refcount_drp(&entity->rc);