]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/client_player.c
326ab53948e2033920a06ca1713acc3b34949e3d
[dragonblocks_alpha.git] / src / client / client_player.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "client/camera.h"
4 #include "client/client.h"
5 #include "client/client_player.h"
6 #include "client/client_terrain.h"
7 #include "client/cube.h"
8 #include "client/debug_menu.h"
9 #include "client/texture.h"
10 #include "environment.h"
11 #include "physics.h"
12
13 typedef struct {
14         ModelNode *nametag;
15         ModelNode *neck;
16         ModelNode *eyes;
17         ModelNode *shoulder_left;
18         ModelNode *shoulder_right;
19         ModelNode *hip_left;
20         ModelNode *hip_right;
21 } PlayerModelBones;
22
23 struct ClientPlayer client_player;
24
25 static ClientEntity *player_entity;
26 static pthread_rwlock_t lock_player_entity;
27
28 static Model *player_model;
29
30 // updat epos/rot box/eye functions
31
32 static void update_camera()
33 {
34         vec4 dst, src = {0.0f, 0.0f, 0.0f, 1.0f};
35
36         PlayerModelBones *bones = player_entity->extra;
37
38         if (bones->eyes)
39                 mat4x4_mul_vec4(dst, bones->eyes->abs, src);
40         else
41                 vec4_dup(dst, src);
42
43         camera_set_position((v3f32) {dst[0], dst[1], dst[2]});
44 }
45
46 static void update_pos()
47 {
48         debug_menu_changed(ENTRY_POS);
49         debug_menu_changed(ENTRY_HUMIDITY);
50         debug_menu_changed(ENTRY_TEMPERATURE);
51 }
52
53 static void update_rot()
54 {
55         camera_set_angle(M_PI / 2 - player_entity->data.rot.y, -player_entity->data.rot.x);
56         debug_menu_changed(ENTRY_YAW);
57         debug_menu_changed(ENTRY_PITCH);
58 }
59
60 static void update_transform()
61 {
62         client_entity_transform(player_entity);
63         update_camera();
64 }
65
66 static void send_pos_rot()
67 {
68         update_transform();
69
70         dragonnet_peer_send_ToServerPosRot(client, &(ToServerPosRot) {
71                 .pos = player_entity->data.pos,
72                 .rot = player_entity->data.rot,
73         });
74 }
75
76 static void recv_pos_rot()
77 {
78         update_transform();
79
80         update_pos();
81         update_rot();
82 }
83
84 // entity callbacks
85
86 static void on_add(ClientEntity *entity)
87 {
88         entity->model = model_clone(player_model);
89         entity->model->extra = refcount_grb(&entity->rc);
90
91         PlayerModelBones *bones = entity->extra = malloc(sizeof *bones);
92         *bones = (PlayerModelBones) {NULL};
93         model_get_bones(entity->model, (ModelBoneMapping[]) {
94                 {"player.nametag",                    &bones->nametag       },
95                 {"player.neck",                       &bones->neck          },
96                 {"player.neck.head.eyes",             &bones->eyes          },
97                 {"player.body.upper.shoulders.left",  &bones->shoulder_left },
98                 {"player.body.upper.shoulders.right", &bones->shoulder_right},
99                 {"player.body.lower.hips.left",       &bones->hip_left      },
100                 {"player.body.lower.hips.right",      &bones->hip_right     },
101         }, 7);
102
103         entity->nametag_offset = bones->nametag ? &bones->nametag->abs : NULL;
104         entity->box_collision = (aabb3f32) {{-0.45f, 0.0f, -0.45f}, {0.45f, 1.8f, 0.45f}};
105
106         model_scene_add(entity->model);
107         client_entity_transform(entity);
108 }
109
110 static void on_remove(ClientEntity *entity)
111 {
112         entity->model->flags.delete = 1;
113         entity->model = NULL;
114 }
115
116 static void on_free(ClientEntity *entity)
117 {
118         free(entity->extra);
119 }
120
121 static void on_transform(ClientEntity *entity)
122 {
123         PlayerModelBones *bones = entity->extra;
124
125         entity->model->root->rot.x = entity->model->root->rot.z = 0.0f;
126
127         if (bones->neck) {
128                 bones->neck->rot.x = entity->data.rot.x;
129                 model_node_transform(bones->neck);
130         }
131 }
132
133 static void local_on_add(ClientEntity *entity)
134 {
135         pthread_rwlock_wrlock(&lock_player_entity);
136
137         if (player_entity) {
138                 fprintf(stderr, "[error] attempt to re-add localplayer entity\n");
139                 exit(EXIT_FAILURE);
140         }
141
142         on_add(entity);
143
144         player_entity = refcount_grb(&entity->rc);
145         recv_pos_rot();
146
147         entity->type->update_nametag(entity);
148
149         pthread_rwlock_unlock(&lock_player_entity);
150 }
151
152 static void local_on_remove(ClientEntity *entity)
153 {
154         pthread_rwlock_wrlock(&lock_player_entity);
155         refcount_drp(&entity->rc);
156         player_entity = NULL;
157         pthread_rwlock_unlock(&lock_player_entity);
158
159         on_remove(entity);
160 }
161
162 static void local_on_update_pos_rot(__attribute__((unused)) ClientEntity *entity)
163 {
164         recv_pos_rot();
165 }
166
167 static void local_on_update_nametag(ClientEntity *entity)
168 {
169         if (entity->data.nametag) {
170                 free(entity->data.nametag);
171                 entity->data.nametag = NULL;
172         }
173 }
174
175 static void __attribute__((unused)) on_model_step(Model *model, __attribute__((unused)) f64 dtime)
176 {
177         PlayerModelBones *bones = ((ClientEntity *) model->extra)->extra;
178         (void) bones;
179 }
180
181 static void on_model_delete(Model *model)
182 {
183         if (model->extra)
184                 refcount_drp(&((ClientEntity *) model->extra)->rc);
185 }
186
187 // called on startup
188 void client_player_init()
189 {
190         client_player.movement = (ToClientMovement) {
191                 .flight = false,
192                 .collision = true,
193                 .speed = 0.0f,
194                 .jump = 0.0f,
195                 .gravity = 0.0f,
196         };
197
198         client_entity_types[ENTITY_PLAYER] = (ClientEntityType) {
199                 .add = &on_add,
200                 .remove = &on_remove,
201                 .free = &on_free,
202                 .update_pos_rot = NULL,
203                 .update_nametag = NULL,
204                 .transform = &on_transform,
205         };
206
207         client_entity_types[ENTITY_LOCALPLAYER] = (ClientEntityType) {
208                 .add = &local_on_add,
209                 .remove = &local_on_remove,
210                 .free = &on_free,
211                 .update_pos_rot = &local_on_update_pos_rot,
212                 .update_nametag = &local_on_update_nametag,
213                 .transform = &on_transform,
214         };
215
216         pthread_rwlock_init(&client_player.lock_movement, NULL);
217
218         player_entity = NULL;
219         pthread_rwlock_init(&lock_player_entity, NULL);
220 }
221
222 // called on shutdown
223 void client_player_deinit()
224 {
225         pthread_rwlock_destroy(&client_player.lock_movement);
226         pthread_rwlock_destroy(&lock_player_entity);
227 }
228
229 void client_player_gfx_init()
230 {
231         player_model = model_load(
232                 RESSOURCE_PATH "models/player.txt", RESSOURCE_PATH "textures/models/player",
233                 &client_entity_cube, &client_entity_shader);
234
235         player_model->callbacks.step = &on_model_step;
236         player_model->callbacks.delete = &on_model_delete;
237 }
238
239 void client_player_gfx_deinit()
240 {
241         model_delete(player_model);
242 }
243
244 ClientEntity *client_player_entity()
245 {
246         ClientEntity *entity = NULL;
247
248         pthread_rwlock_rdlock(&lock_player_entity);
249         if (player_entity)
250                 entity = refcount_grb(&player_entity->rc);
251         pthread_rwlock_unlock(&lock_player_entity);
252
253         return entity;
254 }
255
256 void client_player_update_pos(ClientEntity *entity)
257 {
258         pthread_rwlock_rdlock(&lock_player_entity);
259
260         if (entity == player_entity) {
261                 update_pos();
262                 send_pos_rot();
263         }
264
265         pthread_rwlock_unlock(&lock_player_entity);
266 }
267
268 void client_player_update_rot(ClientEntity *entity)
269 {
270         pthread_rwlock_rdlock(&lock_player_entity);
271
272         if (entity == player_entity) {
273                 update_rot();
274                 send_pos_rot();
275         }
276
277         pthread_rwlock_unlock(&lock_player_entity);
278 }
279
280 // jump if possible
281 void client_player_jump()
282 {
283         ClientEntity *entity = client_player_entity();
284         if (!entity)
285                 return;
286
287         pthread_rwlock_rdlock(&entity->lock_pos_rot);
288         pthread_rwlock_rdlock(&entity->lock_box_off);
289
290         if (physics_ground(
291                 client_terrain,
292                 client_player.movement.collision,
293                 entity->box_collision,
294                 &entity->data.pos,
295                 &client_player.velocity
296         ))
297                 client_player.velocity.y += client_player.movement.jump;
298
299         pthread_rwlock_unlock(&entity->lock_box_off);
300         pthread_rwlock_unlock(&entity->lock_pos_rot);
301
302         refcount_drp(&entity->rc);
303 }
304
305 // to be called every frame
306 void client_player_tick(f64 dtime)
307 {
308         ClientEntity *entity = client_player_entity();
309         if (!entity)
310                 return;
311
312         pthread_rwlock_rdlock(&client_player.lock_movement);
313         pthread_rwlock_wrlock(&entity->lock_pos_rot);
314         pthread_rwlock_rdlock(&entity->lock_box_off);
315
316         if (physics_step(
317                 client_terrain,
318                 client_player.movement.collision,
319                 entity->box_collision,
320                 &entity->data.pos,
321                 &client_player.velocity,
322                 &(v3f64) {
323                         0.0,
324                         client_player.movement.flight ? 0.0 : -client_player.movement.gravity,
325                         0.0,
326                 },
327                 dtime
328         ))
329                 client_player_update_pos(entity);
330
331         pthread_rwlock_unlock(&entity->lock_box_off);
332         pthread_rwlock_unlock(&entity->lock_pos_rot);
333         pthread_rwlock_unlock(&client_player.lock_movement);
334
335         refcount_drp(&entity->rc);
336 }