1 #include <asprintf/asprintf.h>
2 #include <linmath.h/linmath.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"
20 ModelBatch *batch_transparent;
23 TerrainChunk *nbrs[6];
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
38 .size = sizeof(TerrainVertex),
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,
47 static GLuint shader_prog;
50 static LightShader light_shader;
51 static ModelShader model_shader;
53 static inline bool show_face(NodeType self, NodeType nbr)
55 switch (client_node_def[self].visibility) {
59 case VISIBILITY_BLEND:
62 case VISIBILITY_SOLID:
63 return nbr != NODE_UNLOADED && client_node_def[nbr].visibility != VISIBILITY_SOLID;
65 default: // impossible
73 static inline void render_node(ChunkRenderData *data, v3s32 offset)
77 args.node = &data->chunk->data[offset.x][offset.y][offset.z];
79 ClientNodeDef *def = &client_node_def[args.node->type];
80 if (def->visibility == VISIBILITY_NONE)
83 v3f32 vertex_offset = v3f32_sub(v3s32_to_f32(offset), center_offset);
85 args.pos = v3s32_add(offset, data->chunkp);
87 for (args.f = 0; args.f < 6; args.f++) {
88 v3s32 nbr_offset = v3s32_add(offset, facedir[args.f]);
90 TerrainChunk *nbr_chunk;
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;
102 NodeType nbr_node = NODE_UNLOADED;
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;
109 bool show = show_face(args.node->type, nbr_node);
112 data->visible = true;
113 else if (data->show_edges && nbr_chunk != data->chunk && def->visibility == VISIBILITY_SOLID)
114 data->shown_edges = show = true;
119 ModelBatch *batch = data->batch;
121 if (def->visibility == VISIBILITY_BLEND) {
122 data->transparent = true;
123 batch = data->batch_transparent;
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};
134 model_batch_add_vertex(batch, def->tiles.textures[args.f]->txo, &args.vertex);
139 static void animate_chunk_model(Model *model, f64 dtime)
141 bool finished = (model->root->scale.x += dtime * 2.0f) > 1.0f;
143 model->root->scale.x = 1.0f;
146 = model->root->scale.y
147 = model->root->scale.x;
149 model_node_transform(model->root);
152 model->callbacks.step = NULL;
155 TerrainChunk *chunk = model->extra;
157 pthread_mutex_lock(&chunk->mtx);
158 client_terrain_meshgen_task(chunk, false);
159 pthread_mutex_unlock(&chunk->mtx);
164 static Model *create_chunk_model(TerrainChunk *chunk, bool animate, bool *depends)
166 ChunkRenderData data = {
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)),
172 .chunkp = v3s32_scale(chunk->pos, CHUNK_SIZE),
174 .tried_nbrs = depends,
175 .show_edges = animate,
176 .shown_edges = false,
180 render_node(&data, (v3s32) {x, y, z});
182 for (int i = 0; i < 6; i++)
186 if (!data.visible || (!data.batch->textures.siz && !data.batch_transparent->textures.siz)) {
187 model_batch_free(data.batch);
188 model_batch_free(data.batch_transparent);
192 Model *model = model_create();
193 if (data.shown_edges)
194 model->extra = chunk;
195 model->box = (aabb3f32) {
196 v3f32_sub((v3f32) {-1.0f, -1.0f, -1.0f}, center_offset),
197 v3f32_add((v3f32) {+1.0f, +1.0f, +1.0f}, center_offset)};
198 model->callbacks.step = animate ? &animate_chunk_model : NULL;
199 model->callbacks.delete = &model_free_meshes;
200 model->flags.frustum_culling = 1;
201 model->flags.transparent = data.transparent;
203 model->root->visible = data.visible;
204 model->root->pos = v3f32_add(v3s32_to_f32(data.chunkp), center_offset);
205 model->root->scale = (v3f32) {0.0f, 0.0f, 0.0f};
208 model_node_add_batch(model->root, data.batch);
209 model_node_add_batch(model->root, data.batch_transparent);
211 model_batch_free(data.batch);
212 model_batch_free(data.batch_transparent);
218 bool terrain_gfx_init()
221 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texture_units); GL_DEBUG
224 asprintf(&shader_def,
225 "#define MAX_TEXTURE_UNITS %d\n"
226 "#define VIEW_DISTANCE %lf\n",
228 client_config.view_distance
231 if (!shader_program_create(RESSOURCE_PATH "shaders/3d/terrain", &shader_prog, shader_def)) {
232 fprintf(stderr, "[error] failed to create terrain shader program\n");
238 loc_VP = glGetUniformLocation(shader_prog, "VP"); GL_DEBUG
240 GLint texture_indices[texture_units];
241 for (GLint i = 0; i < texture_units; i++)
242 texture_indices[i] = i;
244 glProgramUniform1iv(shader_prog, glGetUniformLocation(shader_prog, "textures"), texture_units, texture_indices); GL_DEBUG
246 model_shader.prog = shader_prog;
247 model_shader.loc_transform = glGetUniformLocation(shader_prog, "model"); GL_DEBUG
249 light_shader.prog = shader_prog;
250 light_shader_locate(&light_shader);
255 void terrain_gfx_deinit()
257 glDeleteProgram(shader_prog); GL_DEBUG
260 void terrain_gfx_update()
262 glProgramUniformMatrix4fv(shader_prog, loc_VP, 1, GL_FALSE, frustum[0]); GL_DEBUG
263 light_shader_update(&light_shader);
266 void terrain_gfx_make_chunk_model(TerrainChunk *chunk)
268 TerrainChunkMeta *meta = chunk->extra;
269 pthread_mutex_lock(&chunk->mtx);
273 animate = meta->model->callbacks.step ? true : false;
275 animate = !meta->has_model;
277 pthread_mutex_unlock(&chunk->mtx);
279 bool depends[6] = {false};
280 Model *model = create_chunk_model(chunk, animate, depends);
282 pthread_mutex_lock(&chunk->mtx);
286 model->callbacks.step = meta->model->callbacks.step;
287 model->root->scale = meta->model->root->scale;
288 model_node_transform(model->root);
291 meta->model->replace = model;
292 meta->model->flags.delete = 1;
294 model_scene_add(model);
298 meta->has_model = true;
300 for (int i = 0; i < 6; i++)
301 meta->depends[i] = depends[i];
303 pthread_mutex_unlock(&chunk->mtx);