]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/client_entity.c
Rework structure
[dragonblocks_alpha.git] / src / client / client_entity.c
1 #include <asprintf.h>
2 #include <dragonstd/map.h>
3 #include <inttypes.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include "client/cube.h"
7 #include "client/client_config.h"
8 #include "client/client_entity.h"
9 #include "client/client_player.h"
10 #include "client/frustum.h"
11 #include "client/gl_debug.h"
12 #include "client/light.h"
13 #include "client/shader.h"
14 #include "client/window.h"
15
16 ClientEntityType client_entity_types[COUNT_ENTITY];
17
18 ModelShader client_entity_shader;
19 typedef struct {
20         v3f32 position;
21         v3f32 normal;
22 } __attribute__((packed)) EntityVertex;
23 Mesh client_entity_cube = {
24         .layout = &(VertexLayout) {
25                 .attributes = (VertexAttribute[]) {
26                         {GL_FLOAT, 3, sizeof(v3f32)}, // position
27                         {GL_FLOAT, 3, sizeof(v3f32)}, // normal
28                 },
29                 .count = 2,
30                 .size = sizeof(EntityVertex),
31         },
32         .vao = 0,
33         .vbo = 0,
34         .data = NULL,
35         .count = 36,
36         .free_data = false,
37 };
38
39 static GLuint shader_prog;
40 static GLint loc_VP;
41 static GLint loc_depthOffset;
42 static LightShader light_shader;
43 static Map entities;
44 static List nametagged;
45 static pthread_mutex_t mtx_nametagged;
46
47 // any thread
48 // called when adding, getting or removing an entity from the map
49 static int cmp_entity(const Refcount *entity, const u64 *id)
50 {
51         return u64_cmp(&((ClientEntity *) entity->obj)->data.id, id);
52 }
53
54 // recv thread
55 // called when server sent removal of entity
56 static void entity_drop(ClientEntity *entity)
57 {
58         if (entity->type->remove)
59                 entity->type->remove(entity);
60
61         if (entity->nametag) {
62                 pthread_mutex_lock(&mtx_nametagged);
63                 list_del(&nametagged, &entity->rc, &cmp_ref, &refcount_drp, NULL, NULL);
64                 pthread_mutex_unlock(&mtx_nametagged);
65
66                 entity->nametag->visible = false;
67         }
68
69         refcount_drp(&entity->rc);
70 }
71
72 // any thread
73 // called when all refs have been dropped
74 static void entity_delete(ClientEntity *entity)
75 {
76         if (entity->type->free)
77                 entity->type->free(entity);
78
79         refcount_dst(&entity->rc);
80
81         if (entity->data.nametag)
82                 free(entity->data.nametag);
83
84         pthread_rwlock_init(&entity->lock_pos_rot, NULL);
85         pthread_rwlock_init(&entity->lock_nametag, NULL);
86         pthread_rwlock_init(&entity->lock_box_off, NULL);
87
88         free(entity);
89 }
90
91 static void update_nametag(ClientEntity *entity)
92 {
93         if (entity->nametag) {
94                 gui_text(entity->nametag, entity->data.nametag);
95
96                 if (!entity->data.nametag)
97                         entity->nametag->visible = false;
98         } else if (entity->data.nametag) {
99                 entity->nametag = gui_add(NULL, (GUIElementDef) {
100                         .pos = {-1.0f, -1.0f},
101                         .z_index = 0.1f,
102                         .offset = {0, 0},
103                         .margin = {4, 4},
104                         .align = {0.5f, 0.5f},
105                         .scale = {1.0f, 1.0f},
106                         .scale_type = SCALE_TEXT,
107                         .affect_parent_scale = false,
108                         .text = entity->data.nametag,
109                         .image = NULL,
110                         .text_color = (v4f32) {1.0f, 1.0f, 1.0f, 1.0f},
111                         .bg_color = (v4f32) {0.0f, 0.0f, 0.0f, 0.5f},
112                 });
113
114                 pthread_mutex_lock(&mtx_nametagged);
115                 list_apd(&nametagged, refcount_inc(&entity->rc));
116                 pthread_mutex_unlock(&mtx_nametagged);
117         }
118 }
119
120 static void update_nametag_pos(ClientEntity *entity)
121 {
122         if (!entity->data.nametag)
123                 return;
124
125         pthread_rwlock_rdlock(&entity->lock_pos_rot);
126         pthread_rwlock_rdlock(&entity->lock_box_off);
127
128         mat4x4 mvp;
129         if (entity->nametag_offset)
130                 mat4x4_mul(mvp, frustum, *entity->nametag_offset);
131         else
132                 mat4x4_dup(mvp, frustum);
133
134         vec4 dst, src = {0.0f, 0.0f, 0.0f, 1.0f};
135         mat4x4_mul_vec4(dst, mvp, src);
136
137         dst[0] /= dst[3];
138         dst[1] /= dst[3];
139         dst[2] /= dst[3];
140
141         if ((entity->nametag->visible = dst[2] >= -1.0f && dst[2] <= 1.0f)) {
142                 entity->nametag->def.pos = (v2f32) {dst[0] * 0.5f + 0.5f, 1.0f - (dst[1] * 0.5f + 0.5f)};
143                 gui_transform(entity->nametag);
144         }
145
146         pthread_rwlock_unlock(&entity->lock_box_off);
147         pthread_rwlock_unlock(&entity->lock_pos_rot);
148 }
149
150 // main thread
151 // called on startup
152 void client_entity_init()
153 {
154         map_ini(&entities);
155         list_ini(&nametagged);
156         pthread_mutex_init(&mtx_nametagged, NULL);
157 }
158
159 // main thead
160 // called on shutdown
161 void client_entity_deinit()
162 {
163         // forget all entities
164         map_cnl(&entities, &refcount_drp, NULL, NULL, 0);
165         list_clr(&nametagged, &refcount_drp, NULL, NULL);
166         pthread_mutex_destroy(&mtx_nametagged);
167 }
168
169 void client_entity_gfx_init()
170 {
171         char *shader_def;
172         asprintf(&shader_def, "#define VIEW_DISTANCE %lf\n", client_config.view_distance);
173         shader_prog = shader_program_create(ASSET_PATH "shaders/3d/entity", shader_def);
174         free(shader_def);
175
176         loc_VP = glGetUniformLocation(shader_prog, "VP"); GL_DEBUG
177         loc_depthOffset = glGetUniformLocation(shader_prog, "depthOffset"); GL_DEBUG
178
179         EntityVertex vertices[6][6];
180         for (int f = 0; f < 6; f++) {
181                 for (int v = 0; v < 6; v++) {
182                         vertices[f][v].position = cube_vertices[f][v].position;
183                         vertices[f][v].normal = cube_vertices[f][v].normal;
184                 }
185         }
186
187         client_entity_cube.data = vertices;
188         mesh_upload(&client_entity_cube);
189
190         client_entity_shader.prog = shader_prog;
191         client_entity_shader.loc_transform = glGetUniformLocation(shader_prog, "model"); GL_DEBUG
192
193         light_shader.prog = shader_prog;
194         light_shader_locate(&light_shader);
195
196         client_entity_depth_offset(0.0f);
197 }
198
199 void client_entity_gfx_deinit()
200 {
201         glDeleteProgram(shader_prog); GL_DEBUG
202         mesh_destroy(&client_entity_cube);
203 }
204
205 void client_entity_gfx_update()
206 {
207         glProgramUniformMatrix4fv(shader_prog, loc_VP, 1, GL_FALSE, frustum[0]); GL_DEBUG
208         light_shader_update(&light_shader);
209
210         pthread_mutex_lock(&mtx_nametagged);
211         list_itr(&nametagged, &update_nametag_pos, NULL, &refcount_obj);
212         pthread_mutex_unlock(&mtx_nametagged);
213 }
214
215 void client_entity_depth_offset(f32 offset)
216 {
217         glProgramUniform1f(shader_prog, loc_depthOffset, offset);
218 }
219
220 ClientEntity *client_entity_grab(u64 id)
221 {
222         return id ? map_get(&entities, &id, &cmp_entity, &refcount_grb) : NULL;
223 }
224
225 void client_entity_transform(ClientEntity *entity)
226 {
227         if (!entity->model)
228                 return;
229
230         entity->model->root->pos = v3f64_to_f32(entity->data.pos); // ToDo: the render pipeline needs to be updated to handle 64-bit positions
231         entity->model->root->rot = entity->data.rot;
232
233         if (entity->type->transform)
234                 entity->type->transform(entity);
235
236         model_node_transform(entity->model->root);
237 }
238
239 void client_entity_add(__attribute__((unused)) void *peer, ToClientEntityAdd *pkt)
240 {
241         if (pkt->type >= COUNT_ENTITY)
242                 return;
243
244         ClientEntity *entity = malloc(sizeof *entity);
245
246         entity->data = pkt->data;
247         entity->type = &client_entity_types[pkt->type];
248         refcount_ini(&entity->rc, entity, &entity_delete);
249
250         pkt->data.nametag = NULL;
251
252         entity->model = NULL;
253         entity->nametag = NULL;
254
255         if (entity->type->add)
256                 entity->type->add(entity);
257
258         update_nametag(entity);
259
260         pthread_rwlock_init(&entity->lock_pos_rot, NULL);
261         pthread_rwlock_init(&entity->lock_nametag, NULL);
262         pthread_rwlock_init(&entity->lock_box_off, NULL);
263
264         if (!map_add(&entities, &entity->data.id, &entity->rc, &cmp_entity, &refcount_inc))
265                 fprintf(stderr, "[warning] failed to add entity %" PRIu64 "\n", entity->data.id);
266
267         refcount_drp(&entity->rc);
268 }
269
270 void client_entity_remove(__attribute__((unused)) void *peer, ToClientEntityRemove *pkt)
271 {
272         map_del(&entities, &pkt->id, &cmp_entity, &entity_drop, NULL, &refcount_obj);
273 }
274
275 void client_entity_update_pos_rot(__attribute__((unused)) void *peer, ToClientEntityUpdatePosRot *pkt)
276 {
277         ClientEntity *entity = client_entity_grab(pkt->id);
278
279         if (!entity)
280                 return;
281
282         pthread_rwlock_wrlock(&entity->lock_pos_rot);
283
284         entity->data.pos = pkt->pos;
285         entity->data.rot = pkt->rot;
286
287         if (entity->type->update_pos_rot)
288                 entity->type->update_pos_rot(entity);
289
290         client_entity_transform(entity);
291
292         pthread_rwlock_unlock(&entity->lock_pos_rot);
293
294         refcount_drp(&entity->rc);
295 }
296
297 void client_entity_update_nametag(__attribute__((unused)) void *peer, ToClientEntityUpdateNametag *pkt)
298 {
299         ClientEntity *entity = client_entity_grab(pkt->id);
300
301         if (!entity)
302                 return;
303
304         pthread_rwlock_wrlock(&entity->lock_nametag);
305
306         if (entity->data.nametag)
307                 free(entity->data.nametag);
308
309         entity->data.nametag = pkt->nametag;
310         pkt->nametag = NULL;
311
312         if (entity->type->update_nametag)
313                 entity->type->update_nametag(entity);
314
315         update_nametag(entity);
316         pthread_rwlock_unlock(&entity->lock_nametag);
317
318         refcount_drp(&entity->rc);
319 }