]> git.lizzy.rs Git - dragonblocks_alpha.git/blob - src/client/terrain_gfx.c
32380e423f892f280dbbb3ef7ce27dd77074f61a
[dragonblocks_alpha.git] / src / client / terrain_gfx.c
1 #include <asprintf/asprintf.h>
2 #include <linmath.h/linmath.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include "client/client_config.h"
6 #include "client/client_node.h"
7 #include "client/client_terrain.h"
8 #include "client/cube.h"
9 #include "client/facedir.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/terrain_gfx.h"
15
16 typedef struct {
17         bool visible;
18         bool transparent;
19         ModelBatch *batch;
20         ModelBatch *batch_transparent;
21         TerrainChunk *chunk;
22         v3s32 chunkp;
23         TerrainChunk *nbrs[6];
24         bool *tried_nbrs;
25         bool show_edges;
26         bool shown_edges;
27 } ChunkRenderData;
28
29 static VertexLayout terrain_vertex_layout = {
30         .attributes = (VertexAttribute []) {
31                 {GL_FLOAT, 3, sizeof(v3f32)}, // position
32                 {GL_FLOAT, 3, sizeof(v3f32)}, // normal
33                 {GL_FLOAT, 2, sizeof(v2f32)}, // textureCoordinates
34                 {GL_FLOAT, 1, sizeof(f32  )}, // textureIndex
35                 {GL_FLOAT, 3, sizeof(v3f32)}, // color
36         },
37         .count = 5,
38         .size = sizeof(TerrainVertex),
39 };
40
41 static v3f32 center_offset = {
42         CHUNK_SIZE * 0.5f + 0.5f,
43         CHUNK_SIZE * 0.5f + 0.5f,
44         CHUNK_SIZE * 0.5f + 0.5f,
45 };
46
47 static GLuint shader_prog;
48 static GLint loc_VP;
49
50 static LightShader light_shader;
51 static ModelShader model_shader;
52
53 static inline bool show_face(NodeType self, NodeType nbr)
54 {
55         switch (client_node_defs[self].visibility) {
56                 case VISIBILITY_CLIP:
57                         return true;
58
59                 case VISIBILITY_BLEND:
60                         return nbr != self;
61
62                 case VISIBILITY_SOLID:
63                         return nbr != NODE_UNLOADED && client_node_defs[nbr].visibility != VISIBILITY_SOLID;
64
65                 default: // impossible
66                         break;
67         }
68
69         // impossible
70         return false;
71 }
72
73 static inline void render_node(ChunkRenderData *data, v3s32 offset)
74 {
75         NodeArgsRender args;
76
77         args.node = &data->chunk->data[offset.x][offset.y][offset.z];
78
79         ClientNodeDef *def = &client_node_defs[args.node->type];
80         if (def->visibility == VISIBILITY_NONE)
81                 return;
82
83         v3f32 vertex_offset = v3f32_sub(v3s32_to_f32(offset), center_offset);
84         if (def->render)
85                 args.pos = v3s32_add(offset, data->chunkp);
86
87         for (args.f = 0; args.f < 6; args.f++) {
88                 v3s32 nbr_offset = v3s32_add(offset, facedir[args.f]);
89
90                 TerrainChunk *nbr_chunk;
91
92                 if (nbr_offset.z >= 0 && nbr_offset.z < CHUNK_SIZE &&
93                         nbr_offset.x >= 0 && nbr_offset.x < CHUNK_SIZE &&
94                         nbr_offset.y >= 0 && nbr_offset.y < CHUNK_SIZE) {
95                         nbr_chunk = data->chunk;
96                 } else if (!(nbr_chunk = data->nbrs[args.f]) && !data->tried_nbrs[args.f]) {
97                         nbr_chunk = data->nbrs[args.f] = terrain_get_chunk(client_terrain,
98                                 v3s32_add(data->chunk->pos, facedir[args.f]), false);
99                         data->tried_nbrs[args.f] = true;
100                 }
101
102                 NodeType nbr_node = NODE_UNLOADED;
103                 if (nbr_chunk)
104                         nbr_node = nbr_chunk->data
105                                 [(nbr_offset.x + CHUNK_SIZE) % CHUNK_SIZE]
106                                 [(nbr_offset.y + CHUNK_SIZE) % CHUNK_SIZE]
107                                 [(nbr_offset.z + CHUNK_SIZE) % CHUNK_SIZE].type;
108
109                 bool show = show_face(args.node->type, nbr_node);
110
111                 if (show)
112                         data->visible = true;
113                 else if (data->show_edges && nbr_chunk != data->chunk && def->visibility == VISIBILITY_SOLID)
114                         data->shown_edges = show = true;
115
116                 if (!show)
117                         continue;
118
119                 ModelBatch *batch = data->batch;
120
121                 if (def->visibility == VISIBILITY_BLEND) {
122                         data->transparent = true;
123                         batch = data->batch_transparent;
124                 }
125
126                 for (args.v = 0; args.v < 6; args.v++) {
127                         args.vertex.cube = cube_vertices[args.f][args.v];
128                         args.vertex.cube.position = v3f32_add(args.vertex.cube.position, vertex_offset);
129                         args.vertex.color = (v3f32) {1.0f, 1.0f, 1.0f};
130
131                         if (def->render)
132                                 def->render(&args);
133
134                         model_batch_add_vertex(batch, def->tiles.textures[args.f]->txo, &args.vertex);
135                 }
136         }
137 }
138
139 static void animate_chunk_model(Model *model, f64 dtime)
140 {
141         bool finished = (model->root->scale.x += dtime * 2.0f) > 1.0f;
142         if (finished)
143                 model->root->scale.x = 1.0f;
144
145         model->root->scale.z
146                 = model->root->scale.y
147                 = model->root->scale.x;
148
149         model_node_transform(model->root);
150
151         if (finished) {
152                 model->callbacks.step = NULL;
153
154                 if (model->extra) {
155                         TerrainChunk *chunk = model->extra;
156
157                         pthread_mutex_lock(&chunk->mtx);
158                         client_terrain_meshgen_task(chunk, false);
159                         pthread_mutex_unlock(&chunk->mtx);
160                 }
161         }
162 }
163
164 static Model *create_chunk_model(TerrainChunk *chunk, bool animate, bool *depends)
165 {
166         ChunkRenderData data = {
167                 .visible = false,
168                 .transparent = false,
169                 .batch = model_batch_create(&model_shader, &terrain_vertex_layout, offsetof(TerrainVertex, textureIndex)),
170                 .batch_transparent = model_batch_create(&model_shader, &terrain_vertex_layout, offsetof(TerrainVertex, textureIndex)),
171                 .chunk = chunk,
172                 .chunkp = v3s32_scale(chunk->pos, CHUNK_SIZE),
173                 .nbrs = {NULL},
174                 .tried_nbrs = depends,
175                 .show_edges = animate,
176                 .shown_edges = false,
177         };
178
179         CHUNK_ITERATE
180                 render_node(&data, (v3s32) {x, y, z});
181
182         if (!data.visible || (!data.batch->textures.siz && !data.batch_transparent->textures.siz)) {
183                 model_batch_free(data.batch);
184                 model_batch_free(data.batch_transparent);
185                 return NULL;
186         }
187
188         Model *model = model_create();
189         if (data.shown_edges)
190                 model->extra = chunk;
191         model->box = (aabb3f32) {
192                 v3f32_sub((v3f32) {-1.0f, -1.0f, -1.0f}, center_offset),
193                 v3f32_add((v3f32) {+1.0f, +1.0f, +1.0f}, center_offset)};
194         model->callbacks.step = animate ? &animate_chunk_model : NULL;
195         model->callbacks.delete = &model_free_meshes;
196         model->flags.frustum_culling = 1;
197         model->flags.transparent = data.transparent;
198
199         model->root->visible = data.visible;
200         model->root->pos = v3f32_add(v3s32_to_f32(data.chunkp), center_offset);
201         model->root->scale = (v3f32) {0.0f, 0.0f, 0.0f};
202
203         if (data.visible) {
204                 model_node_add_batch(model->root, data.batch);
205                 model_node_add_batch(model->root, data.batch_transparent);
206         } else {
207                 model_batch_free(data.batch);
208                 model_batch_free(data.batch_transparent);
209         }
210
211         for (int i = 0; i < 6; i++)
212                 if (data.nbrs[i])
213                         data.tried_nbrs[i] = true;
214
215         return model;
216 }
217
218 bool terrain_gfx_init()
219 {
220         GLint texture_units;
221         glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texture_units); GL_DEBUG
222
223         char *shader_defs;
224         asprintf(&shader_defs,
225                 "#define MAX_TEXTURE_UNITS %d\n"
226                 "#define VIEW_DISTANCE %lf\n",
227                 texture_units,
228                 client_config.view_distance
229         );
230
231         if (!shader_program_create(RESSOURCE_PATH "shaders/3d/terrain", &shader_prog, shader_defs)) {
232                 fprintf(stderr, "[error] failed to create terrain shader program\n");
233                 return false;
234         }
235
236         free(shader_defs);
237
238         loc_VP = glGetUniformLocation(shader_prog, "VP"); GL_DEBUG
239
240         GLint texture_indices[texture_units];
241         for (GLint i = 0; i < texture_units; i++)
242                 texture_indices[i] = i;
243
244         glProgramUniform1iv(shader_prog, glGetUniformLocation(shader_prog, "textures"), texture_units, texture_indices); GL_DEBUG
245
246         model_shader.prog = shader_prog;
247         model_shader.loc_transform = glGetUniformLocation(shader_prog, "model"); GL_DEBUG
248
249         light_shader.prog = shader_prog;
250         light_shader_locate(&light_shader);
251
252         return true;
253 }
254
255 void terrain_gfx_deinit()
256 {
257         glDeleteProgram(shader_prog); GL_DEBUG
258 }
259
260 void terrain_gfx_update()
261 {
262         glProgramUniformMatrix4fv(shader_prog, loc_VP, 1, GL_FALSE, frustum[0]); GL_DEBUG
263         light_shader_update(&light_shader);
264 }
265
266 void terrain_gfx_make_chunk_model(TerrainChunk *chunk)
267 {
268         TerrainChunkMeta *meta = chunk->extra;
269         pthread_mutex_lock(&chunk->mtx);
270
271         bool animate;
272         if (meta->model)
273                 animate = meta->model->callbacks.step ? true : false;
274         else
275                 animate = !meta->has_model;
276
277         pthread_mutex_unlock(&chunk->mtx);
278
279         bool depends[6] = {false};
280         Model *model = create_chunk_model(chunk, animate, depends);
281
282         pthread_mutex_lock(&chunk->mtx);
283
284         if (meta->model) {
285                 if (model) {
286                         model->callbacks.step = meta->model->callbacks.step;
287                         model->root->scale = meta->model->root->scale;
288                         model_node_transform(model->root);
289                 }
290
291                 meta->model->replace = model;
292                 meta->model->flags.delete = 1;
293         } else if (model) {
294                 model_scene_add(model);
295         }
296
297         meta->model = model;
298         meta->has_model = true;
299
300         for (int i = 0; i < 6; i++)
301                 meta->depends[i] = depends[i];
302
303         pthread_mutex_unlock(&chunk->mtx);
304 }