]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/client_entity.c
806d519c3681678f1f1c6dda2eca0c9b13c81a8d
[dragonblocks_alpha.git] / src / client / client_entity.c
1 #include <asprintf/asprintf.h>
2 #include <dragonstd/map.h>
3 #include <stdio.h>
4 #include <stdlib.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"
14
15 ClientEntityType client_entity_types[COUNT_ENTITY];
16
17 ModelShader client_entity_shader;
18 typedef struct {
19         v3f32 position;
20         v3f32 normal;
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
27                 },
28                 .count = 2,
29                 .size = sizeof(EntityVertex),
30         },
31         .vao = 0,
32         .vbo = 0,
33         .data = NULL,
34         .count = 36,
35         .free_data = false,
36 };
37
38 static GLuint shader_prog;
39 static GLint loc_VP;
40 static GLint loc_depthOffset;
41 static LightShader light_shader;
42 static Map entities;
43 static List nametagged;
44 static pthread_mutex_t mtx_nametagged;
45
46 // any thread
47 // called when adding, getting or removing an entity from the map
48 static int cmp_entity(const Refcount *entity, const u64 *id)
49 {
50         return u64_cmp(&((ClientEntity *) entity->obj)->data.id, id);
51 }
52
53 // recv thread
54 // called when server sent removal of entity
55 static void entity_drop(ClientEntity *entity)
56 {
57         if (entity->type->remove)
58                 entity->type->remove(entity);
59
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);
64
65                 entity->nametag->visible = false;
66         }
67
68         refcount_drp(&entity->rc);
69 }
70
71 // any thread
72 // called when all refs have been dropped
73 static void entity_delete(ClientEntity *entity)
74 {
75         if (entity->type->free)
76                 entity->type->free(entity);
77
78         refcount_dst(&entity->rc);
79
80         if (entity->data.nametag)
81                 free(entity->data.nametag);
82
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);
86
87         free(entity);
88 }
89
90 static void update_nametag(ClientEntity *entity)
91 {
92         if (entity->nametag) {
93                 gui_text(entity->nametag, entity->data.nametag);
94
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},
100                         .z_index = 0.1f,
101                         .offset = {0, 0},
102                         .margin = {4, 4},
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,
108                         .image = NULL,
109                         .text_color = (v4f32) {1.0f, 1.0f, 1.0f, 1.0f},
110                         .bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.5f},
111                 });
112
113                 pthread_mutex_lock(&mtx_nametagged);
114                 list_apd(&nametagged, refcount_inc(&entity->rc));
115                 pthread_mutex_unlock(&mtx_nametagged);
116         }
117 }
118
119 static void update_nametag_pos(ClientEntity *entity)
120 {
121         if (!entity->data.nametag)
122                 return;
123
124         pthread_rwlock_rdlock(&entity->lock_pos_rot);
125         pthread_rwlock_rdlock(&entity->lock_box_off);
126
127         mat4x4 mvp;
128         if (entity->nametag_offset)
129                 mat4x4_mul(mvp, frustum, *entity->nametag_offset);
130         else
131                 mat4x4_dup(mvp, frustum);
132
133         vec4 dst, src = {0.0f, 0.0f, 0.0f, 1.0f};
134         mat4x4_mul_vec4(dst, mvp, src);
135
136         dst[0] /= dst[3];
137         dst[1] /= dst[3];
138         dst[2] /= dst[3];
139
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);
143         }
144
145         pthread_rwlock_unlock(&entity->lock_box_off);
146         pthread_rwlock_unlock(&entity->lock_pos_rot);
147 }
148
149 // main thread
150 // called on startup
151 void client_entity_init()
152 {
153         map_ini(&entities);
154         list_ini(&nametagged);
155         pthread_mutex_init(&mtx_nametagged, NULL);
156 }
157
158 // main thead
159 // called on shutdown
160 void client_entity_deinit()
161 {
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);
166 }
167
168 bool client_entity_gfx_init()
169 {
170         char *shader_def;
171         asprintf(&shader_def, "#define VIEW_DISTANCE %lf\n", client_config.view_distance);
172
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");
175                 return false;
176         }
177
178         free(shader_def);
179
180         loc_VP = glGetUniformLocation(shader_prog, "VP"); GL_DEBUG
181         loc_depthOffset = glGetUniformLocation(shader_prog, "depthOffset"); GL_DEBUG
182
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;
188                 }
189         }
190
191         client_entity_cube.data = vertices;
192         mesh_upload(&client_entity_cube);
193
194         client_entity_shader.prog = shader_prog;
195         client_entity_shader.loc_transform = glGetUniformLocation(shader_prog, "model"); GL_DEBUG
196
197         light_shader.prog = shader_prog;
198         light_shader_locate(&light_shader);
199
200         client_entity_depth_offset(0.0f);
201
202         return true;
203 }
204
205 void client_entity_gfx_deinit()
206 {
207         glDeleteProgram(shader_prog); GL_DEBUG
208         mesh_destroy(&client_entity_cube);
209 }
210
211 void client_entity_gfx_update()
212 {
213         glProgramUniformMatrix4fv(shader_prog, loc_VP, 1, GL_FALSE, frustum[0]); GL_DEBUG
214         light_shader_update(&light_shader);
215
216         pthread_mutex_lock(&mtx_nametagged);
217         list_itr(&nametagged, &update_nametag_pos, NULL, &refcount_obj);
218         pthread_mutex_unlock(&mtx_nametagged);
219 }
220
221 void client_entity_depth_offset(f32 offset)
222 {
223         glProgramUniform1f(shader_prog, loc_depthOffset, offset);
224 }
225
226 ClientEntity *client_entity_grab(u64 id)
227 {
228         return id ? map_get(&entities, &id, &cmp_entity, &refcount_grb) : NULL;
229 }
230
231 void client_entity_transform(ClientEntity *entity)
232 {
233         if (!entity->model)
234                 return;
235
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;
238
239         if (entity->type->transform)
240                 entity->type->transform(entity);
241
242         model_node_transform(entity->model->root);
243 }
244
245 void client_entity_add(__attribute__((unused)) void *peer, ToClientEntityAdd *pkt)
246 {
247         if (pkt->type >= COUNT_ENTITY)
248                 return;
249
250         ClientEntity *entity = malloc(sizeof *entity);
251
252         entity->data = pkt->data;
253         entity->type = &client_entity_types[pkt->type];
254         refcount_ini(&entity->rc, entity, &entity_delete);
255
256         pkt->data.nametag = NULL;
257
258         entity->model = NULL;
259         entity->nametag = NULL;
260
261         if (entity->type->add)
262                 entity->type->add(entity);
263
264         update_nametag(entity);
265
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);
269
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);
272
273         refcount_drp(&entity->rc);
274 }
275
276 void client_entity_remove(__attribute__((unused)) void *peer, ToClientEntityRemove *pkt)
277 {
278         map_del(&entities, &pkt->id, &cmp_entity, &entity_drop, NULL, &refcount_obj);
279 }
280
281 void client_entity_update_pos_rot(__attribute__((unused)) void *peer, ToClientEntityUpdatePosRot *pkt)
282 {
283         ClientEntity *entity = client_entity_grab(pkt->id);
284
285         if (!entity)
286                 return;
287
288         pthread_rwlock_wrlock(&entity->lock_pos_rot);
289
290         entity->data.pos = pkt->pos;
291         entity->data.rot = pkt->rot;
292
293         if (entity->type->update_pos_rot)
294                 entity->type->update_pos_rot(entity);
295
296         client_entity_transform(entity);
297
298         pthread_rwlock_unlock(&entity->lock_pos_rot);
299
300         refcount_drp(&entity->rc);
301 }
302
303 void client_entity_update_nametag(__attribute__((unused)) void *peer, ToClientEntityUpdateNametag *pkt)
304 {
305         ClientEntity *entity = client_entity_grab(pkt->id);
306
307         if (!entity)
308                 return;
309
310         pthread_rwlock_wrlock(&entity->lock_nametag);
311
312         if (entity->data.nametag)
313                 free(entity->data.nametag);
314
315         entity->data.nametag = pkt->nametag;
316         pkt->nametag = NULL;
317
318         if (entity->type->update_nametag)
319                 entity->type->update_nametag(entity);
320
321         update_nametag(entity);
322         pthread_rwlock_unlock(&entity->lock_nametag);
323
324         refcount_drp(&entity->rc);
325 }