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