From 98798a6ab99817a5968d67df4eb2503258d37d31 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 30 May 2021 13:53:19 +0900 Subject: [PATCH 1/2] DrawDungeon: A major rewrite of dungeon renderer Before this, all objects that share a texture were drawn at once. Objects were not depth-sorted, so if a transparent object was drawn first, objects behind it were not drawn. After this, dungeon is drawn for each cell, sorted by distance from the camera position. To reduce the number of cells to be drawn, PVS (Potentially Visible Set) stored in dgn files is used. --- include/dungeon/dgn.h | 34 +- include/dungeon/dungeon.h | 21 +- include/dungeon/mesh.h | 55 -- .../dungeon/{event_markers.h => renderer.h} | 21 +- shaders/dungeon.f.glsl | 2 - shaders/dungeon.v.glsl | 3 +- src/dungeon/dgn.c | 120 +++- src/dungeon/dungeon.c | 324 ++--------- src/dungeon/event_markers.c | 273 --------- src/dungeon/mesh.c | 363 ------------ src/dungeon/renderer.c | 522 ++++++++++++++++++ src/hll/DrawDungeon.c | 69 ++- src/meson.build | 3 +- 13 files changed, 754 insertions(+), 1056 deletions(-) delete mode 100644 include/dungeon/mesh.h rename include/dungeon/{event_markers.h => renderer.h} (56%) delete mode 100644 src/dungeon/event_markers.c delete mode 100644 src/dungeon/mesh.c create mode 100644 src/dungeon/renderer.c diff --git a/include/dungeon/dgn.h b/include/dungeon/dgn.h index 965c414..12a553b 100644 --- a/include/dungeon/dgn.h +++ b/include/dungeon/dgn.h @@ -45,19 +45,7 @@ struct dgn_cell { int32_t west_door; int32_t stairs_texture; int32_t stairs_orientation; - int32_t attr12; - int32_t attr13; - int32_t attr14; - int32_t attr15; - int32_t attr16; - int32_t attr17; - int32_t attr18; - int32_t attr19; - int32_t attr20; - int32_t attr21; - int32_t attr22; - int32_t attr23; - int32_t attr24; + // int32_t unknown[13]; int32_t enterable; int32_t enterable_north; int32_t enterable_south; @@ -72,7 +60,7 @@ struct dgn_cell { // uint32_t i; // char *s; // } pairs[6]; - int32_t unknown; + // int32_t unknown; int32_t battle_background; // The below exist only in version 13 @@ -91,11 +79,25 @@ struct dgn_cell { // int32_t unused2; // always -1 // Runtime data (not stored in .dgn) + int x, y, z; + int event_blend_rate; int32_t floor_event2; int32_t north_event2; int32_t south_event2; int32_t east_event2; int32_t west_event2; + struct dgn_cell **visible_cells; +}; + +struct pvs_run_lengths { + int32_t invisible_cells; + int32_t visible_cells; +}; + +struct packed_pvs { + struct pvs_run_lengths *run_lengths; + int nr_run_lengths; + int nr_visible_cells; }; struct dgn { @@ -105,6 +107,7 @@ struct dgn { uint32_t size_z; // uint32_t unknown[10]; struct dgn_cell *cells; + struct packed_pvs *pvs; }; struct dgn *dgn_parse(uint8_t *data, size_t size); @@ -118,4 +121,7 @@ static inline bool dgn_is_in_map(struct dgn *dgn, uint32_t x, uint32_t y, uint32 return x < dgn->size_x && y < dgn->size_y && z < dgn->size_z; } +// Returns a list of cells visible from (x, y, z), sorted by distance from (x, y, z). +struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out); + #endif /* SYSTEM4_DGN_H */ diff --git a/include/dungeon/dungeon.h b/include/dungeon/dungeon.h index 74e7c0e..79bc0f0 100644 --- a/include/dungeon/dungeon.h +++ b/include/dungeon/dungeon.h @@ -25,9 +25,7 @@ struct dgn; struct dtx; struct tes; -struct mesh; -struct skybox; -struct event_markers; +struct dungeon_renderer; struct dungeon_map; struct camera { @@ -54,10 +52,7 @@ struct dungeon_context { struct dgn *dgn; struct dtx *dtx; struct tes *tes; - struct mesh **meshes; - int nr_meshes; - struct skybox *skybox; - struct event_markers *events; + struct dungeon_renderer *renderer; GLuint depth_buffer; }; @@ -66,18 +61,6 @@ void dungeon_context_free(struct dungeon_context *ctx); bool dungeon_load(struct dungeon_context *ctx, int num); void dungeon_set_camera(int surface, float x, float y, float z, float angle, float angle_p); void dungeon_render(struct dungeon_context *ctx); -void dungeon_set_event_floor(int surface, int x, int y, int z, int event); -void dungeon_set_event_blend_rate(int surface, int x, int y, int z, int rate); -void dungeon_set_texture_floor(int surface, int x, int y, int z, int texture); -void dungeon_set_texture_ceiling(int surface, int x, int y, int z, int texture); -void dungeon_set_texture_north(int surface, int x, int y, int z, int texture); -void dungeon_set_texture_south(int surface, int x, int y, int z, int texture); -void dungeon_set_texture_east(int surface, int x, int y, int z, int texture); -void dungeon_set_texture_west(int surface, int x, int y, int z, int texture); -void dungeon_set_door_north(int surface, int x, int y, int z, int texture); -void dungeon_set_door_south(int surface, int x, int y, int z, int texture); -void dungeon_set_door_east(int surface, int x, int y, int z, int texture); -void dungeon_set_door_west(int surface, int x, int y, int z, int texture); struct dungeon_context *dungeon_get_context(int surface); void dungeon_update(void); diff --git a/include/dungeon/mesh.h b/include/dungeon/mesh.h deleted file mode 100644 index 89222e7..0000000 --- a/include/dungeon/mesh.h +++ /dev/null @@ -1,55 +0,0 @@ -/* Copyright (C) 2021 kichikuou - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#ifndef SYSTEM4_DUNGEON_MESH_H -#define SYSTEM4_DUNGEON_MESH_H - -#include -#include - -/* - * A mesh represents a set of objects with the same shape and texture. - */ - -struct mesh; - -enum mesh_type { - MESH_WALL, - MESH_STAIRS, - MESH_TYPE_MAX, -}; - -void mesh_init(void); -void mesh_fini(void); -struct mesh *mesh_create(enum mesh_type type, GLuint texture, bool has_alpha); -void mesh_free(struct mesh *m); -bool mesh_is_transparent(struct mesh *m); -void mesh_render(struct mesh *m, const mat4 local_transform, const mat4 view_transform, const mat4 proj_transform); -void mesh_add_floor(struct mesh *m, int x, int y, int z); -void mesh_add_ceiling(struct mesh *m, int x, int y, int z); -void mesh_add_north_wall(struct mesh *m, int x, int y, int z); -void mesh_add_south_wall(struct mesh *m, int x, int y, int z); -void mesh_add_east_wall(struct mesh *m, int x, int y, int z); -void mesh_add_west_wall(struct mesh *m, int x, int y, int z); -void mesh_add_stairs(struct mesh *m, int x, int y, int z, int orientation); -void mesh_remove_floor(struct mesh *m, int x, int y, int z); -void mesh_remove_ceiling(struct mesh *m, int x, int y, int z); -void mesh_remove_north_wall(struct mesh *m, int x, int y, int z); -void mesh_remove_south_wall(struct mesh *m, int x, int y, int z); -void mesh_remove_east_wall(struct mesh *m, int x, int y, int z); -void mesh_remove_west_wall(struct mesh *m, int x, int y, int z); - -#endif /* SYSTEM4_DUNGEON_MESH_H */ diff --git a/include/dungeon/event_markers.h b/include/dungeon/renderer.h similarity index 56% rename from include/dungeon/event_markers.h rename to include/dungeon/renderer.h index 2f60217..014f5d8 100644 --- a/include/dungeon/event_markers.h +++ b/include/dungeon/renderer.h @@ -14,17 +14,18 @@ * along with this program; if not, see . */ -#ifndef SYSTEM4_DUNGEON_EVENT_MARKERS_H -#define SYSTEM4_DUNGEON_EVENT_MARKERS_H +#ifndef SYSTEM4_DUNGEON_RENDERER_H +#define SYSTEM4_DUNGEON_RENDERER_H -#include +#include +#include -struct event_markers; +struct dgn_cell; +struct dtx; +struct dungeon_renderer; -struct event_markers *event_markers_create(void); -void event_markers_free(struct event_markers *em); -void event_markers_render(struct event_markers *em, const mat4 view_transform, const mat4 proj_transform); -void event_markers_set(struct event_markers *em, int x, int y, int z, int event_type); -void event_markers_set_blend_rate(struct event_markers *em, int x, int y, int z, int rate); +struct dungeon_renderer *dungeon_renderer_create(struct dtx *dtx, GLuint *event_textures, int nr_event_textures); +void dungeon_renderer_free(struct dungeon_renderer *r); +void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, mat4 view_transform, mat4 proj_transform); -#endif /* SYSTEM4_DUNGEON_EVENT_MARKERS_H */ +#endif /* SYSTEM4_DUNGEON_RENDERER_H */ diff --git a/shaders/dungeon.f.glsl b/shaders/dungeon.f.glsl index bd26448..d0d95f4 100644 --- a/shaders/dungeon.f.glsl +++ b/shaders/dungeon.f.glsl @@ -27,8 +27,6 @@ const vec3 FOG_COLOR = vec3(0.0, 0.0, 0.0); void main() { vec4 texel = texture(tex, tex_coord); - if (texel.a < 0.2) - discard; float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST; fog_factor = clamp(fog_factor, 0.0, 1.0); frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a); diff --git a/shaders/dungeon.v.glsl b/shaders/dungeon.v.glsl index 4865581..4cfe35f 100644 --- a/shaders/dungeon.v.glsl +++ b/shaders/dungeon.v.glsl @@ -22,12 +22,11 @@ uniform mat4 proj_transform; in vec3 vertex_pos; in vec2 vertex_uv; -in mat4 instance_transform; out float dist; out vec2 tex_coord; void main() { - vec4 pos = view_transform * instance_transform * local_transform * vec4(vertex_pos, 1.0); + vec4 pos = view_transform * local_transform * vec4(vertex_pos, 1.0); dist = abs(pos.z); gl_Position = proj_transform * pos; tex_coord = vertex_uv; diff --git a/src/dungeon/dgn.c b/src/dungeon/dgn.c index fa30762..bf432ad 100644 --- a/src/dungeon/dgn.c +++ b/src/dungeon/dgn.c @@ -44,8 +44,21 @@ struct dgn *dgn_parse(uint8_t *data, size_t size) int nr_cells = dgn->size_x * dgn->size_y * dgn->size_z; dgn->cells = xcalloc(nr_cells, sizeof(struct dgn_cell)); + uint32_t x = 0, y = 0, z = 0; for (int i = 0; i < nr_cells; i++) { struct dgn_cell *cell = &dgn->cells[i]; + cell->x = x; + cell->y = y; + cell->z = z; + if (++x == dgn->size_x) { + x = 0; + if (++y == dgn->size_y) { + y = 0; + z++; + } + } + cell->event_blend_rate = 255; + cell->floor = buffer_read_int32(&r); cell->ceiling = buffer_read_int32(&r); cell->north_wall = buffer_read_int32(&r); @@ -58,19 +71,7 @@ struct dgn *dgn_parse(uint8_t *data, size_t size) cell->west_door = buffer_read_int32(&r); cell->stairs_texture = buffer_read_int32(&r); cell->stairs_orientation = buffer_read_int32(&r); - cell->attr12 = buffer_read_int32(&r); - cell->attr13 = buffer_read_int32(&r); - cell->attr14 = buffer_read_int32(&r); - cell->attr15 = buffer_read_int32(&r); - cell->attr16 = buffer_read_int32(&r); - cell->attr17 = buffer_read_int32(&r); - cell->attr18 = buffer_read_int32(&r); - cell->attr19 = buffer_read_int32(&r); - cell->attr20 = buffer_read_int32(&r); - cell->attr21 = buffer_read_int32(&r); - cell->attr22 = buffer_read_int32(&r); - cell->attr23 = buffer_read_int32(&r); - cell->attr24 = buffer_read_int32(&r); + buffer_skip(&r, 13 * 4); cell->enterable = buffer_read_int32(&r); cell->enterable_north = buffer_read_int32(&r); cell->enterable_south = buffer_read_int32(&r); @@ -86,7 +87,7 @@ struct dgn *dgn_parse(uint8_t *data, size_t size) buffer_skip(&r, 4); buffer_skip(&r, strlen(buffer_strdata(&r)) + 1); } - cell->unknown = buffer_read_int32(&r); + buffer_skip(&r, 4); cell->battle_background = buffer_read_int32(&r); if (dgn->version != DGN_VER_GALZOO) continue; @@ -105,12 +106,60 @@ struct dgn *dgn_parse(uint8_t *data, size_t size) cell->roof_underside_texture = buffer_read_int32(&r); buffer_skip(&r, 4); } + if (buffer_read_u8(&r) != 0 || buffer_read_int32(&r) != 1) { + dgn_free(dgn); + return NULL; + } + dgn->pvs = xcalloc(nr_cells, sizeof(struct packed_pvs)); + for (int i = 0; i < nr_cells; i++) { + struct packed_pvs *pvs = &dgn->pvs[i]; + int32_t len = buffer_read_int32(&r); + if (len % 8 != 4) { + WARNING("dgn_parse: unexpected PVS length"); + dgn_free(dgn); + return NULL; + } + if (buffer_read_int32(&r) != nr_cells) { + WARNING("dgn_parse: bad PVS"); + dgn_free(dgn); + return NULL; + } + pvs->nr_run_lengths = (len - 4) / 8; + pvs->run_lengths = xmalloc(pvs->nr_run_lengths * sizeof(struct pvs_run_lengths)); + int32_t total = 0; + for (int j = 0; j < pvs->nr_run_lengths; j++) { + int32_t invisible = buffer_read_int32(&r); + int32_t visible = buffer_read_int32(&r); + pvs->run_lengths[j].invisible_cells = invisible; + pvs->run_lengths[j].visible_cells = visible; + pvs->nr_visible_cells += visible; + total += invisible + visible; + } + if (total != nr_cells) { + WARNING("dgn_parse: bad PVS"); + dgn_free(dgn); + return NULL; + } + } return dgn; } void dgn_free(struct dgn *dgn) { + int nr_cells = dgn->size_x * dgn->size_y * dgn->size_z; + + for (int i = 0; i < nr_cells; i++) { + if (dgn->cells[i].visible_cells) + free(dgn->cells[i].visible_cells); + } free(dgn->cells); + + if (dgn->pvs) { + for (int i = 0; i < nr_cells; i++) + free(dgn->pvs[i].run_lengths); + free(dgn->pvs); + } + free(dgn); } @@ -125,3 +174,46 @@ struct dgn_cell *dgn_cell_at(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z { return &dgn->cells[dgn_cell_index(dgn, x, y, z)]; } + +struct pvs_cell { + struct dgn_cell *cell; + int distance; +}; + +static int pvs_cell_compare(const void *a, const void *b) +{ + return ((struct pvs_cell *)a)->distance - ((struct pvs_cell *)b)->distance; +} + +struct dgn_cell **dgn_get_visible_cells(struct dgn *dgn, int x, int y, int z, int *nr_cells_out) +{ + int cell_index = dgn_cell_index(dgn, x, y, z); + struct dgn_cell *cell = &dgn->cells[cell_index]; + struct packed_pvs *pvs = &dgn->pvs[cell_index]; + if (!cell->visible_cells) { + struct pvs_cell *pvs_cells = xmalloc(pvs->nr_visible_cells * sizeof(struct pvs_cell)); + + struct pvs_cell *dst = pvs_cells; + struct dgn_cell *src = dgn->cells; + for (int i = 0; i < pvs->nr_run_lengths; i++) { + src += pvs->run_lengths[i].invisible_cells; + for (int j = 0; j < pvs->run_lengths[i].visible_cells; j++) { + int dx = src->x - x; + int dy = src->y - y; + int dz = src->z - z; + *dst++ = (struct pvs_cell){ + .cell = src++, + .distance = dx * dx + dy * dy + dz * dz + }; + } + } + qsort(pvs_cells, pvs->nr_visible_cells, sizeof(struct pvs_cell), pvs_cell_compare); + + cell->visible_cells = xmalloc(pvs->nr_visible_cells * sizeof(struct cell *)); + for (int i = 0; i < pvs->nr_visible_cells; i++) + cell->visible_cells[i] = pvs_cells[i].cell; + free(pvs_cells); + } + *nr_cells_out = pvs->nr_visible_cells; + return cell->visible_cells; +} diff --git a/src/dungeon/dungeon.c b/src/dungeon/dungeon.c index fa8ed66..3cb1003 100644 --- a/src/dungeon/dungeon.c +++ b/src/dungeon/dungeon.c @@ -21,6 +21,7 @@ #include #include #include "system4.h" +#include "system4/alk.h" #include "system4/archive.h" #include "system4/cg.h" #include "system4/dlf.h" @@ -28,10 +29,8 @@ #include "dungeon/dgn.h" #include "dungeon/dtx.h" #include "dungeon/dungeon.h" -#include "dungeon/event_markers.h" #include "dungeon/map.h" -#include "dungeon/mesh.h" -#include "dungeon/skybox.h" +#include "dungeon/renderer.h" #include "dungeon/tes.h" #include "gfx/gfx.h" #include "sact.h" @@ -63,7 +62,6 @@ struct dungeon_context *dungeon_context_create(int surface) glBindRenderbuffer(GL_RENDERBUFFER, 0); ctx->map = dungeon_map_create(); - mesh_init(); return ctx; } @@ -76,14 +74,10 @@ void dungeon_context_free(struct dungeon_context *ctx) if (ctx->tes) tes_free(ctx->tes); - for (int i = 0; i < ctx->nr_meshes; i++) - mesh_free(ctx->meshes[i]); - free(ctx->meshes); - skybox_free(ctx->skybox); - event_markers_free(ctx->events); + if (ctx->renderer) + dungeon_renderer_free(ctx->renderer); glDeleteRenderbuffers(1, &ctx->depth_buffer); - mesh_fini(); dungeon_map_free(ctx->map); struct sact_sprite *sp = sact_get_sprite(ctx->surface); @@ -93,72 +87,45 @@ void dungeon_context_free(struct dungeon_context *ctx) free(ctx); } -static struct mesh **create_meshes(struct dtx *dtx, int *nr_meshes) +static GLuint *load_event_textures(int *nr_textures_out) { - const int nr_types = DTX_DOOR + 1; - struct mesh **meshes = xcalloc(nr_types * dtx->nr_columns, sizeof(struct mesh *)); - for (int type = 0; type < nr_types; type++) { - enum mesh_type mtype = type == DTX_STAIRS ? MESH_STAIRS : MESH_WALL; - for (int i = 0; i < dtx->nr_columns; i++) { - struct cg *cg = dtx_create_cg(dtx, type, i); - if (!cg) - continue; - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - meshes[type * dtx->nr_columns + i] = mesh_create(mtype, texture, cg->metrics.has_alpha); - cg_free(cg); - } + char *path = gamedir_path("Data/Event.alk"); + int error = ARCHIVE_SUCCESS; + struct alk_archive *alk = alk_open(path, ARCHIVE_MMAP, &error); + if (error == ARCHIVE_FILE_ERROR) { + WARNING("alk_open(\"%s\"): %s", path, strerror(errno)); + } else if (error == ARCHIVE_BAD_ARCHIVE_ERROR) { + WARNING("alk_open(\"%s\"): invalid .alk file", path); } - *nr_meshes = nr_types * dtx->nr_columns; - return meshes; -} + free(path); + if (!alk) + return NULL; -static inline struct mesh *ctx_mesh(struct dungeon_context *ctx, int type, int index) -{ - return ctx->meshes[type * ctx->dtx->nr_columns + index]; -} - -static void populate_meshes(struct dungeon_context *ctx) -{ - struct dgn_cell *cell = ctx->dgn->cells; - for (uint32_t z = 0; z < ctx->dgn->size_z; z++) { - for (uint32_t y = 0; y < ctx->dgn->size_y; y++) { - for (uint32_t x = 0; x < ctx->dgn->size_x; x++, cell++) { - if (cell->floor >= 0) - mesh_add_floor(ctx_mesh(ctx, DTX_FLOOR, cell->floor), x, y, z); - if (cell->ceiling >= 0) - mesh_add_ceiling(ctx_mesh(ctx, DTX_CEILING, cell->ceiling), x, y, z); - if (cell->north_wall >= 0) - mesh_add_north_wall(ctx_mesh(ctx, DTX_WALL, cell->north_wall), x, y, z); - if (cell->south_wall >= 0) - mesh_add_south_wall(ctx_mesh(ctx, DTX_WALL, cell->south_wall), x, y, z); - if (cell->east_wall >= 0) - mesh_add_east_wall(ctx_mesh(ctx, DTX_WALL, cell->east_wall), x, y, z); - if (cell->west_wall >= 0) - mesh_add_west_wall(ctx_mesh(ctx, DTX_WALL, cell->west_wall), x, y, z); - if (cell->north_door >= 0) - mesh_add_north_wall(ctx_mesh(ctx, DTX_DOOR, cell->north_door), x, y, z); - if (cell->south_door >= 0) - mesh_add_south_wall(ctx_mesh(ctx, DTX_DOOR, cell->south_door), x, y, z); - if (cell->east_door >= 0) - mesh_add_east_wall(ctx_mesh(ctx, DTX_DOOR, cell->east_door), x, y, z); - if (cell->west_door >= 0) - mesh_add_west_wall(ctx_mesh(ctx, DTX_DOOR, cell->west_door), x, y, z); - if (cell->stairs_texture >= 0) - mesh_add_stairs(ctx_mesh(ctx, DTX_STAIRS, cell->stairs_texture), x, y, z, cell->stairs_orientation); - if (cell->floor_event) - event_markers_set(ctx->events, x, y, z, cell->floor_event); - } + GLuint *textures = xcalloc(alk->nr_files, sizeof(GLuint)); + glGenTextures(alk->nr_files, textures); + for (int i = 0; i < alk->nr_files; i++) { + struct archive_data *dfile = archive_get(&alk->ar, i); + if (!dfile) + continue; + struct cg *cg = cg_load_data(dfile); + archive_free_data(dfile); + if (!cg) { + WARNING("Event.alk: cannot load cg %d", i); + continue; } + glBindTexture(GL_TEXTURE_2D, textures[i]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + cg_free(cg); } + *nr_textures_out = alk->nr_files; + archive_free(&alk->ar); + return textures; } bool dungeon_load(struct dungeon_context *ctx, int num) @@ -199,11 +166,13 @@ bool dungeon_load(struct dungeon_context *ctx, int num) return false; } - ctx->meshes = create_meshes(ctx->dtx, &ctx->nr_meshes); - ctx->skybox = skybox_create(ctx->dtx); - ctx->events = event_markers_create(); + int nr_event_textures; + GLuint *event_textures = load_event_textures(&nr_event_textures); + if (!event_textures) + return false; + + ctx->renderer = dungeon_renderer_create(ctx->dtx, event_textures, nr_event_textures); - populate_meshes(ctx); dungeon_map_init(ctx); ctx->loaded = true; @@ -257,208 +226,15 @@ void dungeon_render(struct dungeon_context *ctx) model_view_matrix(&ctx->camera, view_transform); glm_perspective(M_PI / 3.0, (float)texture->w / texture->h, 0.5, 100.0, proj_transform); - // Render opaque objects. - glDisable(GL_BLEND); - for (int i = 0; i < ctx->nr_meshes; i++) { - struct mesh *m = ctx->meshes[i]; - if (m && !mesh_is_transparent(m)) - mesh_render(m, local_transform, view_transform, proj_transform); - } - - // Render the skybox. - skybox_render(ctx->skybox, view_transform, proj_transform); - - // Render transparent objects. - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - for (int i = 0; i < ctx->nr_meshes; i++) { - struct mesh *m = ctx->meshes[i]; - if (m && mesh_is_transparent(m)) - mesh_render(m, local_transform, view_transform, proj_transform); - } - - // Render event markers. - event_markers_render(ctx->events, view_transform, proj_transform); + int dgn_x = round(ctx->camera.pos[0] / 2.0); + int dgn_y = round(ctx->camera.pos[1] / 2.0); + int dgn_z = round(ctx->camera.pos[2] / -2.0); + int nr_cells; + struct dgn_cell **cells = dgn_get_visible_cells(ctx->dgn, dgn_x, dgn_y, dgn_z, &nr_cells); + dungeon_renderer_render(ctx->renderer, cells, nr_cells, view_transform, proj_transform); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo); } - -void dungeon_set_event_floor(int surface, int x, int y, int z, int event) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - cell->floor_event = event; - event_markers_set(ctx->events, x, y, z, cell->floor_event); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_event_blend_rate(int surface, int x, int y, int z, int rate) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx) - return; - event_markers_set_blend_rate(ctx->events, x, y, z, rate); -} - -void dungeon_set_texture_floor(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->floor == texture) - return; - if (cell->floor >= 0) - mesh_remove_floor(ctx_mesh(ctx, DTX_FLOOR, cell->floor), x, y, z); - cell->floor = texture; - mesh_add_floor(ctx_mesh(ctx, DTX_FLOOR, cell->floor), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_texture_ceiling(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->ceiling == texture) - return; - if (cell->ceiling >= 0) - mesh_remove_ceiling(ctx_mesh(ctx, DTX_CEILING, cell->ceiling), x, y, z); - cell->ceiling = texture; - if (texture >= 0) - mesh_add_ceiling(ctx_mesh(ctx, DTX_CEILING, cell->ceiling), x, y, z); -} - -void dungeon_set_texture_north(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->north_wall == texture) - return; - if (cell->north_wall >= 0) - mesh_remove_north_wall(ctx_mesh(ctx, DTX_WALL, cell->north_wall), x, y, z); - cell->north_wall = texture; - if (texture >= 0) - mesh_add_north_wall(ctx_mesh(ctx, DTX_WALL, cell->north_wall), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_texture_south(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->south_wall == texture) - return; - if (cell->south_wall >= 0) - mesh_remove_south_wall(ctx_mesh(ctx, DTX_WALL, cell->south_wall), x, y, z); - cell->south_wall = texture; - if (texture >= 0) - mesh_add_south_wall(ctx_mesh(ctx, DTX_WALL, cell->south_wall), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_texture_east(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->east_wall == texture) - return; - if (cell->east_wall >= 0) - mesh_remove_east_wall(ctx_mesh(ctx, DTX_WALL, cell->east_wall), x, y, z); - cell->east_wall = texture; - if (texture >= 0) - mesh_add_east_wall(ctx_mesh(ctx, DTX_WALL, cell->east_wall), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_texture_west(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->west_wall == texture) - return; - if (cell->west_wall >= 0) - mesh_remove_west_wall(ctx_mesh(ctx, DTX_WALL, cell->west_wall), x, y, z); - cell->west_wall = texture; - if (texture >= 0) - mesh_add_west_wall(ctx_mesh(ctx, DTX_WALL, cell->west_wall), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_door_north(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->north_door == texture) - return; - if (cell->north_door >= 0) - mesh_remove_north_wall(ctx_mesh(ctx, DTX_DOOR, cell->north_door), x, y, z); - cell->north_door = texture; - if (texture >= 0) - mesh_add_north_wall(ctx_mesh(ctx, DTX_DOOR, cell->north_door), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_door_south(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->south_door == texture) - return; - if (cell->south_door >= 0) - mesh_remove_south_wall(ctx_mesh(ctx, DTX_DOOR, cell->south_door), x, y, z); - cell->south_door = texture; - if (texture >= 0) - mesh_add_south_wall(ctx_mesh(ctx, DTX_DOOR, cell->south_door), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_door_east(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->east_door == texture) - return; - if (cell->east_door >= 0) - mesh_remove_east_wall(ctx_mesh(ctx, DTX_DOOR, cell->east_door), x, y, z); - cell->east_door = texture; - if (texture >= 0) - mesh_add_east_wall(ctx_mesh(ctx, DTX_DOOR, cell->east_door), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} - -void dungeon_set_door_west(int surface, int x, int y, int z, int texture) -{ - struct dungeon_context *ctx = dungeon_get_context(surface); - if (!ctx || !ctx->dgn) - return; - struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); - if (cell->west_door == texture) - return; - if (cell->west_door >= 0) - mesh_remove_west_wall(ctx_mesh(ctx, DTX_DOOR, cell->west_door), x, y, z); - cell->west_door = texture; - if (texture >= 0) - mesh_add_west_wall(ctx_mesh(ctx, DTX_DOOR, cell->west_door), x, y, z); - dungeon_map_update_cell(ctx, x, y, z); -} diff --git a/src/dungeon/event_markers.c b/src/dungeon/event_markers.c deleted file mode 100644 index d0123e9..0000000 --- a/src/dungeon/event_markers.c +++ /dev/null @@ -1,273 +0,0 @@ -/* Copyright (C) 2021 kichikuou - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include -#include "system4.h" -#include "system4/alk.h" -#include "system4/cg.h" - -#include "dungeon/event_markers.h" -#include "dungeon/mesh.h" -#include "gfx/gfx.h" -#include "xsystem4.h" - -enum floor_marker_type { - MAGIC_CIRCLE, - RED_OCTAGRAM, - NR_FLOOR_MARKER_TYPE -}; - -struct marker_info { - int event_type; - int texture_index; - int texture_count; - int floor_marker_type; -}; - -static const struct marker_info rance6_marker_info[] = { - { 11, 41, 3, MAGIC_CIRCLE}, // treasure chest - { 12, 51, 7, RED_OCTAGRAM}, // enemy - { 14, 8, 5, MAGIC_CIRCLE}, // green star - { 15, 21, 10, MAGIC_CIRCLE}, // heart - { 16, 45, 5, MAGIC_CIRCLE}, // BP cross - { 17, 58, 5, MAGIC_CIRCLE}, // exit - { 18, 31, 10, MAGIC_CIRCLE}, // teleporter - {140, 13, 5, RED_OCTAGRAM}, // red star - {0}, -}; - -const struct marker_info *get_marker_info(int event_type) -{ - for (const struct marker_info *info = rance6_marker_info; info->event_type; info++) { - if (info->event_type == event_type) - return info; - } - return NULL; -} - -static inline uint32_t cell_id(int x, int y, int z) { - return x << 16 | y << 8 | z; -} - -struct event_sprite { - uint32_t cell_id; - int event_type; - GLfloat pos[3]; - int alpha_mod; // 0-255 -}; - -struct event_markers { - GLuint *event_textures; - int nr_event_textures; - struct mesh *floor_markers[NR_FLOOR_MARKER_TYPE]; - - // For rendering sprites - struct shader sprite_shader; - GLint proj_transform; - GLuint alpha_mod; - struct event_sprite *sprites; - int nr_sprites; - int cap_sprites; - GLuint vao; - GLuint pos_buffer; -}; - -static int find_event_sprite(struct event_markers *em, int x, int y, int z) -{ - uint32_t id = cell_id(x, y, z); - for (int i = 0; i < em->nr_sprites; i++) { - if (em->sprites[i].cell_id == id) - return i; - } - return -1; -} - -static GLuint *load_event_textures(int *nr_textures_out) -{ - char *path = gamedir_path("Data/Event.alk"); - int error = ARCHIVE_SUCCESS; - struct alk_archive *alk = alk_open(path, ARCHIVE_MMAP, &error); - if (error == ARCHIVE_FILE_ERROR) { - WARNING("alk_open(\"%s\"): %s", path, strerror(errno)); - } else if (error == ARCHIVE_BAD_ARCHIVE_ERROR) { - WARNING("alk_open(\"%s\"): invalid .alk file", path); - } - free(path); - if (!alk) - return NULL; - - GLuint *textures = xcalloc(alk->nr_files, sizeof(GLuint)); - glGenTextures(alk->nr_files, textures); - for (int i = 0; i < alk->nr_files; i++) { - struct archive_data *dfile = archive_get(&alk->ar, i); - if (!dfile) - continue; - struct cg *cg = cg_load_data(dfile); - archive_free_data(dfile); - if (!cg) { - WARNING("Event.alk: cannot load cg %d", i); - continue; - } - glBindTexture(GL_TEXTURE_2D, textures[i]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); - cg_free(cg); - } - *nr_textures_out = alk->nr_files; - archive_free(&alk->ar); - return textures; -} - -struct event_markers *event_markers_create(void) -{ - struct event_markers *em = xcalloc(1, sizeof(struct event_markers)); - gfx_load_shader(&em->sprite_shader, "shaders/dungeon_sprite.v.glsl", "shaders/dungeon_sprite.f.glsl"); - em->proj_transform = glGetUniformLocation(em->sprite_shader.program, "proj_transform"); - em->alpha_mod = glGetUniformLocation(em->sprite_shader.program, "alpha_mod"); - - em->event_textures = load_event_textures(&em->nr_event_textures); - if (!em->event_textures) - return em; - - em->floor_markers[MAGIC_CIRCLE] = mesh_create(MESH_WALL, em->event_textures[1], true); - em->floor_markers[RED_OCTAGRAM] = mesh_create(MESH_WALL, em->event_textures[63], true); - - em->nr_sprites = 0; - em->cap_sprites = 16; - em->sprites = xcalloc(em->cap_sprites, sizeof(struct event_sprite)); - glGenVertexArrays(1, &em->vao); - glBindVertexArray(em->vao); - glGenBuffers(1, &em->pos_buffer); - glBindBuffer(GL_ARRAY_BUFFER, em->pos_buffer); - glEnableVertexAttribArray(em->sprite_shader.vertex); - glVertexAttribPointer(em->sprite_shader.vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - glBindVertexArray(0); - return em; -} - -void event_markers_free(struct event_markers *em) -{ - for (int i = 0; i < NR_FLOOR_MARKER_TYPE; i++) - mesh_free(em->floor_markers[i]); - glDeleteVertexArrays(1, &em->vao); - glDeleteBuffers(1, &em->pos_buffer); - glDeleteProgram(em->sprite_shader.program); - glDeleteTextures(em->nr_event_textures, em->event_textures); - free(em->event_textures); - free(em->sprites); - free(em); -} - - -void event_markers_render(struct event_markers *em, const mat4 view_transform, const mat4 proj_transform) -{ - uint32_t t = SDL_GetTicks(); - - // Draw floor markers - mat4 local_transform; - glm_mat4_identity(local_transform); - float rad = t / 400.0; - local_transform[0][0] = cos(rad); - local_transform[0][1] = sin(rad); - local_transform[1][0] = -sin(rad); - local_transform[1][1] = cos(rad); - local_transform[3][3] /= 0.75; - - glDepthFunc(GL_LEQUAL); - glEnable(GL_POLYGON_OFFSET_FILL); - glPolygonOffset(-1.0, -2.0); - for (int i = 0; i < NR_FLOOR_MARKER_TYPE; i++) - mesh_render(em->floor_markers[i], local_transform, view_transform, proj_transform); - glDisable(GL_POLYGON_OFFSET_FILL); - glDepthFunc(GL_LESS); - - // Draw sprites - glUseProgram(em->sprite_shader.program); - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - glEnable(GL_POINT_SPRITE); - glUniformMatrix4fv(em->sprite_shader.view_transform, 1, GL_FALSE, view_transform[0]); - glUniformMatrix4fv(em->proj_transform, 1, GL_FALSE, proj_transform[0]); - - glBindVertexArray(em->vao); - glActiveTexture(GL_TEXTURE0); - glUniform1i(em->sprite_shader.texture, 0); - glBindBuffer(GL_ARRAY_BUFFER, em->pos_buffer); - - int frame = t / 100; // 100ms per frame - struct event_sprite *sp = em->sprites; - for (int i = 0; i < em->nr_sprites; i++, sp++) { - const struct marker_info *info = get_marker_info(sp->event_type); - int texture = info->texture_index + frame % info->texture_count; - glUniform1f(em->alpha_mod, sp->alpha_mod / 255.0); - glBindTexture(GL_TEXTURE_2D, em->event_textures[texture]); - glBufferData(GL_ARRAY_BUFFER, 3 * sizeof(GLfloat), sp->pos, GL_STREAM_DRAW); - glDrawArrays(GL_POINTS, 0, 1); - } - - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); - glDisable(GL_POINT_SPRITE); -} - -void event_markers_set(struct event_markers *em, int x, int y, int z, int event_type) -{ - int i = find_event_sprite(em, x, y, z); - if (i >= 0) { - if (event_type == em->sprites[i].event_type) - return; - // Remove old marker - const struct marker_info *old_info = get_marker_info(em->sprites[i].event_type); - mesh_remove_floor(em->floor_markers[old_info->floor_marker_type], x, y, z); - if (i != em->nr_sprites - 1) - em->sprites[i] = em->sprites[em->nr_sprites - 1]; - em->nr_sprites--; - } - - const struct marker_info *info = get_marker_info(event_type); - if (info) { - // Add a new marker - if (em->nr_sprites == em->cap_sprites) { - em->cap_sprites *= 2; - em->sprites = xrealloc_array(em->sprites, em->nr_sprites, em->cap_sprites, sizeof(struct event_sprite)); - } - struct event_sprite *sp = &em->sprites[em->nr_sprites++]; - sp->cell_id = cell_id(x, y, z); - sp->event_type = event_type; - sp->pos[0] = x * 2.0; - sp->pos[1] = y * 2.0; - sp->pos[2] = z * -2.0; - sp->alpha_mod = 255; - mesh_add_floor(em->floor_markers[info->floor_marker_type], x, y, z); - } -} - -void event_markers_set_blend_rate(struct event_markers *em, int x, int y, int z, int alpha_mod) -{ - int i = find_event_sprite(em, x, y, z); - if (i < 0) - return; - em->sprites[i].alpha_mod = alpha_mod; -} diff --git a/src/dungeon/mesh.c b/src/dungeon/mesh.c deleted file mode 100644 index 71eae9d..0000000 --- a/src/dungeon/mesh.c +++ /dev/null @@ -1,363 +0,0 @@ -/* Copyright (C) 2021 kichikuou - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#include -#include -#include -#include "system4.h" - -#include "dungeon/mesh.h" -#include "gfx/gfx.h" - -struct mesh_shader { - struct shader s; - // Uniform variable locations - GLint local_transform; - GLint proj_transform; - // Attribute variable locations - GLint vertex_uv; - GLint instance_transform; -}; -static struct mesh_shader mesh_shader; - -struct vertex { - GLfloat x, y, z, u, v; -}; - -static const struct vertex wall_vertices[] = { - {-1.0, 1.0, 0.0, 0.0, 0.0}, - {-1.0, -1.0, 0.0, 0.0, 1.0}, - { 1.0, 1.0, 0.0, 1.0, 0.0}, - { 1.0, -1.0, 0.0, 1.0, 1.0}, -}; - -static const struct vertex stairs_vertices[] = { - { 1, 3/3.f, -3/3.f, 1, 0/12.f}, - {-1, 3/3.f, -3/3.f, 0, 0/12.f}, - { 1, 3/3.f, -2/3.f, 1, 1/12.f}, - {-1, 3/3.f, -2/3.f, 0, 1/12.f}, - { 1, 2/3.f, -2/3.f, 1, 2/12.f}, - {-1, 2/3.f, -2/3.f, 0, 2/12.f}, - { 1, 2/3.f, -1/3.f, 1, 3/12.f}, - {-1, 2/3.f, -1/3.f, 0, 3/12.f}, - { 1, 1/3.f, -1/3.f, 1, 4/12.f}, - {-1, 1/3.f, -1/3.f, 0, 4/12.f}, - { 1, 1/3.f, 0/3.f, 1, 5/12.f}, - {-1, 1/3.f, 0/3.f, 0, 5/12.f}, - { 1, 0/3.f, 0/3.f, 1, 6/12.f}, - {-1, 0/3.f, 0/3.f, 0, 6/12.f}, - { 1, 0/3.f, 1/3.f, 1, 7/12.f}, - {-1, 0/3.f, 1/3.f, 0, 7/12.f}, - { 1, -1/3.f, 1/3.f, 1, 8/12.f}, - {-1, -1/3.f, 1/3.f, 0, 8/12.f}, - { 1, -1/3.f, 2/3.f, 1, 9/12.f}, - {-1, -1/3.f, 2/3.f, 0, 9/12.f}, - { 1, -2/3.f, 2/3.f, 1, 10/12.f}, - {-1, -2/3.f, 2/3.f, 0, 10/12.f}, - { 1, -2/3.f, 3/3.f, 1, 11/12.f}, - {-1, -2/3.f, 3/3.f, 0, 11/12.f}, - { 1, -3/3.f, 3/3.f, 1, 12/12.f}, - {-1, -3/3.f, 3/3.f, 0, 12/12.f}, -}; - -struct geometry { - GLuint attr_buffer; - int nr_vertices; -}; -static struct geometry *geometries[MESH_TYPE_MAX]; - -static struct geometry *geometry_create(const struct vertex *vertices, size_t size) -{ - struct geometry *g = xcalloc(1, sizeof(struct geometry)); - g->nr_vertices = size / sizeof(struct vertex); - glGenBuffers(1, &g->attr_buffer); - glBindBuffer(GL_ARRAY_BUFFER, g->attr_buffer); - glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - return g; -} - -static void geometry_free(struct geometry *g) -{ - glDeleteBuffers(1, &g->attr_buffer); - free(g); -} - -static void geometry_bind(const struct geometry *g) -{ - glBindBuffer(GL_ARRAY_BUFFER, g->attr_buffer); - glEnableVertexAttribArray(mesh_shader.s.vertex); - glVertexAttribPointer(mesh_shader.s.vertex, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0); - glEnableVertexAttribArray(mesh_shader.vertex_uv); - glVertexAttribPointer(mesh_shader.vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)(3 * sizeof(GLfloat))); -} - -void mesh_init(void) -{ - gfx_load_shader(&mesh_shader.s, "shaders/dungeon.v.glsl", "shaders/dungeon.f.glsl"); - mesh_shader.local_transform = glGetUniformLocation(mesh_shader.s.program, "local_transform"); - mesh_shader.proj_transform = glGetUniformLocation(mesh_shader.s.program, "proj_transform"); - mesh_shader.vertex_uv = glGetAttribLocation(mesh_shader.s.program, "vertex_uv"); - mesh_shader.instance_transform = glGetAttribLocation(mesh_shader.s.program, "instance_transform"); - - geometries[MESH_WALL] = geometry_create(wall_vertices, sizeof(wall_vertices)); - geometries[MESH_STAIRS] = geometry_create(stairs_vertices, sizeof(stairs_vertices)); -} - -void mesh_fini(void) -{ - for (int i = 0; i < MESH_TYPE_MAX; i++) - geometry_free(geometries[i]); - glDeleteProgram(mesh_shader.s.program); -} - -struct mesh { - const struct geometry *geometry; - GLuint vao; - GLuint texture; - GLuint instance_buffer; - mat4 *instances; - int nr_instances; - int cap_instances; - uint32_t *cell_ids; - bool has_alpha; - bool dirty; -}; - -struct mesh *mesh_create(enum mesh_type type, GLuint texture, bool has_alpha) -{ - struct mesh *m = xcalloc(1, sizeof(struct mesh)); - m->geometry = geometries[type]; - m->texture = texture; - m->has_alpha = has_alpha; - m->nr_instances = 0; - m->cap_instances = 16; - m->instances = xcalloc(m->cap_instances, sizeof(mat4)); - m->cell_ids = xcalloc(m->cap_instances, sizeof(uint32_t)); - m->dirty = false; - glGenVertexArrays(1, &m->vao); - glBindVertexArray(m->vao); - geometry_bind(m->geometry); - glGenBuffers(1, &m->instance_buffer); - glBindBuffer(GL_ARRAY_BUFFER, m->instance_buffer); - for (int i = 0; i < 4; i++) { - GLuint index = mesh_shader.instance_transform + i; - glEnableVertexAttribArray(index); - glVertexAttribPointer(index, 4, GL_FLOAT, GL_FALSE, 16 * sizeof(GLfloat), (const void *)(i * 4 * sizeof(GLfloat))); - glVertexAttribDivisor(index, 1); - } - glBindVertexArray(0); - return m; -} - -void mesh_free(struct mesh *m) -{ - if (!m) - return; - glDeleteTextures(1, &m->texture); - glDeleteBuffers(1, &m->instance_buffer); - glDeleteVertexArrays(1, &m->vao); - free(m->instances); - free(m->cell_ids); - free(m); -} - -bool mesh_is_transparent(struct mesh *m) -{ - return m->has_alpha; -} - -void mesh_render(struct mesh *m, const mat4 local_transform, const mat4 view_transform, const mat4 proj_transform) -{ - if (!m || !m->nr_instances) - return; - - if (m->dirty) { - glBindBuffer(GL_ARRAY_BUFFER, m->instance_buffer); - glBufferData(GL_ARRAY_BUFFER, m->nr_instances * sizeof(mat4), m->instances, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - m->dirty = false; - } - - glUseProgram(mesh_shader.s.program); - glUniformMatrix4fv(mesh_shader.local_transform, 1, GL_FALSE, local_transform[0]); - glUniformMatrix4fv(mesh_shader.s.view_transform, 1, GL_FALSE, view_transform[0]); - glUniformMatrix4fv(mesh_shader.proj_transform, 1, GL_FALSE, proj_transform[0]); - - glBindVertexArray(m->vao); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, m->texture); - glUniform1i(mesh_shader.s.texture, 0); - - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, m->geometry->nr_vertices, m->nr_instances); - - glBindVertexArray(0); - glBindTexture(GL_TEXTURE_2D, 0); -} - -enum wall_face { - NORTH, - SOUTH, - EAST, - WEST, - FLOOR, - CEILING -}; - -static inline uint32_t cell_id(int x, int y, int z, int face) -{ - return x << 24 | y << 16 | z << 8 | face; -} - -static void mesh_add(struct mesh *m, mat4 instance, uint32_t cell_id) -{ - if (!m) - return; - if (m->nr_instances == m->cap_instances) { - m->cap_instances *= 2; - m->instances = xrealloc_array(m->instances, m->nr_instances, m->cap_instances, sizeof(mat4)); - m->cell_ids = xrealloc_array(m->cell_ids, m->nr_instances, m->cap_instances, sizeof(uint32_t)); - } - memcpy(m->instances[m->nr_instances], instance, sizeof(mat4)); - m->cell_ids[m->nr_instances] = cell_id; - m->nr_instances++; - m->dirty = true; -} - -static void mesh_remove(struct mesh *m, uint32_t cell_id) -{ - if (!m) - return; - for (int i = 0; i < m->nr_instances; i++) { - if (m->cell_ids[i] != cell_id) - continue; - if (i != m->nr_instances - 1) { - memcpy(m->instances[i], m->instances[m->nr_instances - 1], sizeof(mat4)); - m->cell_ids[i] = m->cell_ids[m->nr_instances - 1]; - } - m->nr_instances--; - m->dirty = true; - return; - } - ERROR("mesh_remove: cannot find cell id %x", cell_id); -} - -void mesh_add_floor(struct mesh *m, int x, int y, int z) -{ - mat4 mat = { - { 1, 0, 0, 0}, - { 0, 0, -1, 0}, - { 0, 1, 0, 0}, - { 2*x, 2*y-1, -2*z, 1} - }; - mesh_add(m, mat, cell_id(x, y, z, FLOOR)); -} - -void mesh_add_ceiling(struct mesh *m, int x, int y, int z) -{ - mat4 mat = { - { -1, 0, 0, 0}, - { 0, 0, -1, 0}, - { 0, -1, 0, 0}, - {2*x, 2*y+1, -2*z, 1} - }; - mesh_add(m, mat, cell_id(x, y, z, CEILING)); -} - -void mesh_add_north_wall(struct mesh *m, int x, int y, int z) -{ - mat4 mat = { - { 1, 0, 0, 0}, - { 0, 1, 0, 0}, - { 0, 0, 1, 0}, - {2*x, 2*y, -2*z-1, 1} - }; - mesh_add(m, mat, cell_id(x, y, z, NORTH)); -} - -void mesh_add_south_wall(struct mesh *m, int x, int y, int z) -{ - mat4 mat = { - { -1, 0, 0, 0}, - { 0, 1, 0, 0}, - { 0, 0, -1, 0}, - {2*x, 2*y, -2*z+1, 1} - }; - mesh_add(m, mat, cell_id(x, y, z, SOUTH)); -} - -void mesh_add_east_wall(struct mesh *m, int x, int y, int z) -{ - mat4 mat = { - { 0, 0, 1, 0}, - { 0, 1, 0, 0}, - { -1, 0, 0, 0}, - {2*x+1, 2*y, -2*z, 1} - }; - mesh_add(m, mat, cell_id(x, y, z, EAST)); -} - -void mesh_add_west_wall(struct mesh *m, int x, int y, int z) -{ - mat4 mat = { - { 0, 0, -1, 0}, - { 0, 1, 0, 0}, - { 1, 0, 0, 0}, - {2*x-1, 2*y, -2*z, 1} - }; - mesh_add(m, mat, cell_id(x, y, z, WEST)); -} - -void mesh_add_stairs(struct mesh *m, int x, int y, int z, int orientation) -{ - mat4 matrices[4] = { - {{ 1, 0, 0, 0}, {0, 1, 0, 0}, { 0, 0, 1, 0}, {2*x, 2*y, -2*z, 1}}, - {{ 0, 0, -1, 0}, {0, 1, 0, 0}, { 1, 0, 0, 0}, {2*x, 2*y, -2*z, 1}}, - {{-1, 0, 0, 0}, {0, 1, 0, 0}, { 0, 0, -1, 0}, {2*x, 2*y, -2*z, 1}}, - {{ 0, 0, 1, 0}, {0, 1, 0, 0}, {-1, 0, 0, 0}, {2*x, 2*y, -2*z, 1}} - }; - mesh_add(m, matrices[orientation], 0); -} - -void mesh_remove_floor(struct mesh *m, int x, int y, int z) -{ - mesh_remove(m, cell_id(x, y, z, FLOOR)); -} - -void mesh_remove_ceiling(struct mesh *m, int x, int y, int z) -{ - mesh_remove(m, cell_id(x, y, z, CEILING)); -} - -void mesh_remove_north_wall(struct mesh *m, int x, int y, int z) -{ - mesh_remove(m, cell_id(x, y, z, NORTH)); -} - -void mesh_remove_south_wall(struct mesh *m, int x, int y, int z) -{ - mesh_remove(m, cell_id(x, y, z, SOUTH)); -} - -void mesh_remove_east_wall(struct mesh *m, int x, int y, int z) -{ - mesh_remove(m, cell_id(x, y, z, EAST)); -} - -void mesh_remove_west_wall(struct mesh *m, int x, int y, int z) -{ - mesh_remove(m, cell_id(x, y, z, WEST)); -} diff --git a/src/dungeon/renderer.c b/src/dungeon/renderer.c new file mode 100644 index 0000000..f43e9f2 --- /dev/null +++ b/src/dungeon/renderer.c @@ -0,0 +1,522 @@ +/* Copyright (C) 2021 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include "system4.h" +#include "system4/cg.h" + +#include "dungeon/dgn.h" +#include "dungeon/dtx.h" +#include "dungeon/renderer.h" +#include "dungeon/skybox.h" +#include "gfx/gfx.h" + +struct marker_info { + int event_type; + int texture_index; + int texture_count; + int floor_marker; +}; + +enum rance6_floor_marker { + MAGIC_CIRCLE = 1, + RED_OCTAGRAM = 63, +}; + +static const struct marker_info rance6_marker_info[] = { + { 11, 41, 3, MAGIC_CIRCLE}, // treasure chest + { 12, 51, 7, RED_OCTAGRAM}, // enemy + { 14, 8, 5, MAGIC_CIRCLE}, // green star + { 15, 21, 10, MAGIC_CIRCLE}, // heart + { 16, 45, 5, MAGIC_CIRCLE}, // BP cross + { 17, 58, 5, MAGIC_CIRCLE}, // exit + { 18, 31, 10, MAGIC_CIRCLE}, // teleporter + {140, 13, 5, RED_OCTAGRAM}, // red star + {0}, +}; + +static const struct marker_info *get_marker_info(int event_type) +{ + for (const struct marker_info *info = rance6_marker_info; info->event_type; info++) { + if (info->event_type == event_type) + return info; + } + return NULL; +} + +struct sprite_renderer { + struct shader shader; + GLint proj_transform; + GLuint alpha_mod; + GLuint vao; + GLuint pos_buffer; +}; + +struct geometry; +struct material; + +struct dungeon_renderer { + struct shader shader; + // Uniform variable locations + GLint local_transform; + GLint proj_transform; + // Attribute variable locations + GLint vertex_uv; + + struct geometry *wall_geometry; + struct geometry *stairs_geometry; + + struct material *materials; + int nr_materials; + int nr_dtx_columns; + + GLuint *event_textures; + int nr_event_textures; + + struct sprite_renderer sprite; + struct skybox *skybox; +}; + +struct vertex { + GLfloat x, y, z, u, v; +}; + +static const struct vertex wall_vertices[] = { + {-1.0, 1.0, 0.0, 0.0, 0.0}, + {-1.0, -1.0, 0.0, 0.0, 1.0}, + { 1.0, 1.0, 0.0, 1.0, 0.0}, + { 1.0, -1.0, 0.0, 1.0, 1.0}, +}; + +static const struct vertex stairs_vertices[] = { + { 1, 3/3.f, -3/3.f, 1, 0/12.f}, + {-1, 3/3.f, -3/3.f, 0, 0/12.f}, + { 1, 3/3.f, -2/3.f, 1, 1/12.f}, + {-1, 3/3.f, -2/3.f, 0, 1/12.f}, + { 1, 2/3.f, -2/3.f, 1, 2/12.f}, + {-1, 2/3.f, -2/3.f, 0, 2/12.f}, + { 1, 2/3.f, -1/3.f, 1, 3/12.f}, + {-1, 2/3.f, -1/3.f, 0, 3/12.f}, + { 1, 1/3.f, -1/3.f, 1, 4/12.f}, + {-1, 1/3.f, -1/3.f, 0, 4/12.f}, + { 1, 1/3.f, 0/3.f, 1, 5/12.f}, + {-1, 1/3.f, 0/3.f, 0, 5/12.f}, + { 1, 0/3.f, 0/3.f, 1, 6/12.f}, + {-1, 0/3.f, 0/3.f, 0, 6/12.f}, + { 1, 0/3.f, 1/3.f, 1, 7/12.f}, + {-1, 0/3.f, 1/3.f, 0, 7/12.f}, + { 1, -1/3.f, 1/3.f, 1, 8/12.f}, + {-1, -1/3.f, 1/3.f, 0, 8/12.f}, + { 1, -1/3.f, 2/3.f, 1, 9/12.f}, + {-1, -1/3.f, 2/3.f, 0, 9/12.f}, + { 1, -2/3.f, 2/3.f, 1, 10/12.f}, + {-1, -2/3.f, 2/3.f, 0, 10/12.f}, + { 1, -2/3.f, 3/3.f, 1, 11/12.f}, + {-1, -2/3.f, 3/3.f, 0, 11/12.f}, + { 1, -3/3.f, 3/3.f, 1, 12/12.f}, + {-1, -3/3.f, 3/3.f, 0, 12/12.f}, +}; + +struct geometry { + GLuint vao; + GLuint attr_buffer; + int nr_vertices; +}; + +static struct geometry *geometry_create(struct dungeon_renderer *r, const struct vertex *vertices, size_t size) +{ + struct geometry *g = xcalloc(1, sizeof(struct geometry)); + g->nr_vertices = size / sizeof(struct vertex); + + glGenVertexArrays(1, &g->vao); + glBindVertexArray(g->vao); + glGenBuffers(1, &g->attr_buffer); + glBindBuffer(GL_ARRAY_BUFFER, g->attr_buffer); + + glEnableVertexAttribArray(r->shader.vertex); + glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0); + glEnableVertexAttribArray(r->vertex_uv); + glVertexAttribPointer(r->vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)(3 * sizeof(GLfloat))); + glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + return g; +} + +static void geometry_free(struct geometry *g) +{ + glDeleteVertexArrays(1, &g->vao); + glDeleteBuffers(1, &g->attr_buffer); + free(g); +} + +struct material { + GLuint texture; + bool opaque; +}; + +static void init_material(struct material *m, struct dtx *dtx, int type, int index) +{ + struct cg *cg = dtx_create_cg(dtx, type, index); + if (!cg) + return; + glGenTextures(1, &m->texture); + glBindTexture(GL_TEXTURE_2D, m->texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + m->opaque = !cg->metrics.has_alpha; + cg_free(cg); +} + +static void delete_material(struct material *m) +{ + if (m->texture) + glDeleteTextures(1, &m->texture); +} + +static struct material *get_material(struct dungeon_renderer *r, int type, int index) +{ + struct material *m = &r->materials[type * r->nr_dtx_columns + index]; + if (!m->texture) + return NULL; + return m; +} + +static void init_sprite_renderer(struct sprite_renderer *sr) +{ + gfx_load_shader(&sr->shader, "shaders/dungeon_sprite.v.glsl", "shaders/dungeon_sprite.f.glsl"); + sr->proj_transform = glGetUniformLocation(sr->shader.program, "proj_transform"); + sr->alpha_mod = glGetUniformLocation(sr->shader.program, "alpha_mod"); + + glGenVertexArrays(1, &sr->vao); + glBindVertexArray(sr->vao); + glGenBuffers(1, &sr->pos_buffer); + glBindBuffer(GL_ARRAY_BUFFER, sr->pos_buffer); + glEnableVertexAttribArray(sr->shader.vertex); + glVertexAttribPointer(sr->shader.vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +static void delete_sprite_renderer(struct sprite_renderer *sr) +{ + glDeleteVertexArrays(1, &sr->vao); + glDeleteBuffers(1, &sr->pos_buffer); + glDeleteProgram(sr->shader.program); +} + +struct dungeon_renderer *dungeon_renderer_create(struct dtx *dtx, GLuint *event_textures, int nr_event_textures) +{ + struct dungeon_renderer *r = xcalloc(1, sizeof(struct dungeon_renderer)); + + gfx_load_shader(&r->shader, "shaders/dungeon.v.glsl", "shaders/dungeon.f.glsl"); + r->local_transform = glGetUniformLocation(r->shader.program, "local_transform"); + r->proj_transform = glGetUniformLocation(r->shader.program, "proj_transform"); + r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv"); + + r->wall_geometry = geometry_create(r, wall_vertices, sizeof(wall_vertices)); + r->stairs_geometry = geometry_create(r, stairs_vertices, sizeof(stairs_vertices)); + + const int nr_types = DTX_DOOR + 1; + r->materials = xcalloc(nr_types * dtx->nr_columns, sizeof(struct material)); + for (int type = 0; type < nr_types; type++) { + for (int i = 0; i < dtx->nr_columns; i++) { + init_material(&r->materials[type * dtx->nr_columns + i], dtx, type, i); + } + } + r->nr_dtx_columns = dtx->nr_columns; + r->nr_materials = nr_types * dtx->nr_columns; + + r->event_textures = event_textures; + r->nr_event_textures = nr_event_textures; + + init_sprite_renderer(&r->sprite); + r->skybox = skybox_create(dtx); + + return r; +} + +void dungeon_renderer_free(struct dungeon_renderer *r) +{ + glDeleteProgram(r->shader.program); + + geometry_free(r->wall_geometry); + geometry_free(r->stairs_geometry); + + for (int i = 0; i < r->nr_materials; i++) + delete_material(&r->materials[i]); + free(r->materials); + + glDeleteTextures(r->nr_event_textures, r->event_textures); + free(r->event_textures); + + delete_sprite_renderer(&r->sprite); + skybox_free(r->skybox); + + free(r); +} + +static void draw(struct dungeon_renderer *r, struct geometry *geometry, GLuint texture, mat4 transform) +{ + glUseProgram(r->shader.program); + glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, transform[0]); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + glUniform1i(r->shader.texture, 0); + + glBindVertexArray(geometry->vao); + glDrawArrays(GL_TRIANGLE_STRIP, 0, geometry->nr_vertices); + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static void draw_floor_marker(struct dungeon_renderer *r, const struct marker_info *info, float x, float y, float z, uint32_t t) +{ + float rad = t / 400.0; + mat4 rot; + glm_euler((vec3){0, 0, rad}, rot); + rot[3][3] /= 0.75; // scale + mat4 trans = MAT4( + 1, 0, 0, x, + 0, 0, 1, y-1, + 0, -1, 0, z, + 0, 0, 0, 1); + glm_mul(trans, rot, trans); + + glDepthFunc(GL_LEQUAL); + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(-1.0, -2.0); + draw(r, r->wall_geometry, r->event_textures[info->floor_marker], trans); + glDisable(GL_POLYGON_OFFSET_FILL); + glDepthFunc(GL_LESS); +} + +static void draw_sprite(struct dungeon_renderer *r, const struct marker_info *info, float x, float y, float z, int blend_rate, uint32_t t) +{ + struct sprite_renderer *sr = &r->sprite; + glUseProgram(sr->shader.program); + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + glEnable(GL_POINT_SPRITE); + + glBindVertexArray(sr->vao); + glActiveTexture(GL_TEXTURE0); + glUniform1i(sr->shader.texture, 0); + glUniform1f(sr->alpha_mod, blend_rate / 255.0); + glBindBuffer(GL_ARRAY_BUFFER, sr->pos_buffer); + + int frame = t / 100; // 100ms per frame + int texture = info->texture_index + frame % info->texture_count; + glBindTexture(GL_TEXTURE_2D, r->event_textures[texture]); + GLfloat pos[3] = {x, y, z}; + glBufferData(GL_ARRAY_BUFFER, 3 * sizeof(GLfloat), pos, GL_STREAM_DRAW); + glDrawArrays(GL_POINTS, 0, 1); + + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + glDisable(GL_POINT_SPRITE); +} + +static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool render_opaque) +{ + float x = 2.0 * cell->x; + float y = 2.0 * cell->y; + float z = -2.0 * cell->z; + + if (cell->floor >= 0) { + struct material *material = get_material(r, DTX_FLOOR, cell->floor); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + 1, 0, 0, x, + 0, 0, 1, y-1, + 0, -1, 0, z, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->ceiling >= 0) { + struct material *material = get_material(r, DTX_CEILING, cell->ceiling); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + -1, 0, 0, x, + 0, 0, -1, y+1, + 0, -1, 0, z, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->north_wall >= 0) { + struct material *material = get_material(r, DTX_WALL, cell->north_wall); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z-1, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->south_wall >= 0) { + struct material *material = get_material(r, DTX_WALL, cell->south_wall); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + -1, 0, 0, x, + 0, 1, 0, y, + 0, 0, -1, z+1, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->east_wall >= 0) { + struct material *material = get_material(r, DTX_WALL, cell->east_wall); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + 0, 0, -1, x+1, + 0, 1, 0, y, + 1, 0, 0, z, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->west_wall >= 0) { + struct material *material = get_material(r, DTX_WALL, cell->west_wall); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + 0, 0, 1, x-1, + 0, 1, 0, y, + -1, 0, 0, z, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->north_door >= 0) { + struct material *material = get_material(r, DTX_DOOR, cell->north_door); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z-1, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->south_door >= 0) { + struct material *material = get_material(r, DTX_DOOR, cell->south_door); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + -1, 0, 0, x, + 0, 1, 0, y, + 0, 0, -1, z+1, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->east_door >= 0) { + struct material *material = get_material(r, DTX_DOOR, cell->east_door); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + 0, 0, -1, x+1, + 0, 1, 0, y, + 1, 0, 0, z, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->west_door >= 0) { + struct material *material = get_material(r, DTX_DOOR, cell->west_door); + if (material && material->opaque == render_opaque) { + mat4 m = MAT4( + 0, 0, 1, x-1, + 0, 1, 0, y, + -1, 0, 0, z, + 0, 0, 0, 1); + draw(r, r->wall_geometry, material->texture, m); + } + } + if (cell->stairs_texture >= 0) { + struct material *material = get_material(r, DTX_STAIRS, cell->stairs_texture); + if (material && material->opaque == render_opaque) { + mat4 matrices[4] = { + MAT4( + 1, 0, 0, x, + 0, 1, 0, y, + 0, 0, 1, z, + 0, 0, 0, 1), + MAT4( + 0, 0, 1, x, + 0, 1, 0, y, + -1, 0, 0, z, + 0, 0, 0, 1), + MAT4( + -1, 0, 0, x, + 0, 1, 0, y, + 0, 0, -1, z, + 0, 0, 0, 1), + MAT4( + 0, 0, -1, x, + 0, 1, 0, y, + 1, 0, 0, z, + 0, 0, 0, 1) + }; + draw(r, r->stairs_geometry, material->texture, matrices[cell->stairs_orientation]); + } + } + if (cell->floor_event && !render_opaque) { + const struct marker_info *info = get_marker_info(cell->floor_event); + if (info) { + uint32_t t = SDL_GetTicks(); + draw_floor_marker(r, info, x, y, z, t); + draw_sprite(r, info, x, y, z, cell->event_blend_rate, t); + } + } +} + +void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, mat4 view_transform, mat4 proj_transform) +{ + glUseProgram(r->shader.program); + glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]); + glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, proj_transform[0]); + glUseProgram(r->sprite.shader.program); + glUniformMatrix4fv(r->sprite.shader.view_transform, 1, GL_FALSE, view_transform[0]); + glUniformMatrix4fv(r->sprite.proj_transform, 1, GL_FALSE, proj_transform[0]); + glUseProgram(0); + + // Render opaque objects, from near to far. + glDisable(GL_BLEND); + for (int i = 0; i < nr_cells; i++) + draw_cell(r, cells[i], true); + + // Render the skybox. + skybox_render(r->skybox, view_transform, proj_transform); + + // Render transparent objects, from far to near. + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + for (int i = nr_cells - 1; i >= 0; i--) + draw_cell(r, cells[i], false); +} diff --git a/src/hll/DrawDungeon.c b/src/hll/DrawDungeon.c index 8fb2aa1..434f88b 100644 --- a/src/hll/DrawDungeon.c +++ b/src/hll/DrawDungeon.c @@ -186,7 +186,7 @@ CELL_GETTER(DrawDungeon_GetTexDoorE, -1, cell->east_door); CELL_GETTER(DrawDungeon_GetTexStair, -1, cell->stairs_texture); CELL_GETTER(DrawDungeon_GetBattleBack, -1, cell->battle_background); -#define CELL_SETTER(name, expr) \ +#define CELL_SETTER(name, expr, update_map) \ static void name(int surface, int x, int y, int z, int value) \ { \ struct dungeon_context *ctx = dungeon_get_context(surface); \ @@ -194,28 +194,41 @@ CELL_GETTER(DrawDungeon_GetBattleBack, -1, cell->battle_background); return; \ struct dgn_cell *cell = dgn_cell_at(ctx->dgn, x, y, z); \ expr = value; \ - dungeon_map_update_cell(ctx, x, y, z); \ + if (update_map) \ + dungeon_map_update_cell(ctx, x, y, z); \ } HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDoorNAngle, int surface, int x, int y, int z, float angle); HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDoorWAngle, int surface, int x, int y, int z, float angle); HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDoorSAngle, int surface, int x, int y, int z, float angle); HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDoorEAngle, int surface, int x, int y, int z, float angle); -CELL_SETTER(DrawDungeon_SetEvWallN, cell->north_event); -CELL_SETTER(DrawDungeon_SetEvWallW, cell->west_event); -CELL_SETTER(DrawDungeon_SetEvWallS, cell->south_event); -CELL_SETTER(DrawDungeon_SetEvWallE, cell->east_event); -CELL_SETTER(DrawDungeon_SetEvFloor2, cell->floor_event2); -CELL_SETTER(DrawDungeon_SetEvWallN2, cell->north_event2); -CELL_SETTER(DrawDungeon_SetEvWallW2, cell->west_event2); -CELL_SETTER(DrawDungeon_SetEvWallS2, cell->south_event2); -CELL_SETTER(DrawDungeon_SetEvWallE2, cell->east_event2); +CELL_SETTER(DrawDungeon_SetEvFloor, cell->floor_event, true); +CELL_SETTER(DrawDungeon_SetEvWallN, cell->north_event, true); +CELL_SETTER(DrawDungeon_SetEvWallW, cell->west_event, true); +CELL_SETTER(DrawDungeon_SetEvWallS, cell->south_event, true); +CELL_SETTER(DrawDungeon_SetEvWallE, cell->east_event, true); +CELL_SETTER(DrawDungeon_SetEvFloor2, cell->floor_event2, false); +CELL_SETTER(DrawDungeon_SetEvWallN2, cell->north_event2, false); +CELL_SETTER(DrawDungeon_SetEvWallW2, cell->west_event2, false); +CELL_SETTER(DrawDungeon_SetEvWallS2, cell->south_event2, false); +CELL_SETTER(DrawDungeon_SetEvWallE2, cell->east_event2, false); //void DrawDungeon_SetEvMag(int surface, int x, int y, int z, float mag); -CELL_SETTER(DrawDungeon_SetEnter, cell->enterable); -CELL_SETTER(DrawDungeon_SetEnterN, cell->enterable_north); -CELL_SETTER(DrawDungeon_SetEnterW, cell->enterable_west); -CELL_SETTER(DrawDungeon_SetEnterS, cell->enterable_south); -CELL_SETTER(DrawDungeon_SetEnterE, cell->enterable_east); +CELL_SETTER(DrawDungeon_SetEvRate, cell->event_blend_rate, false); +CELL_SETTER(DrawDungeon_SetEnter, cell->enterable, false); +CELL_SETTER(DrawDungeon_SetEnterN, cell->enterable_north, true); +CELL_SETTER(DrawDungeon_SetEnterW, cell->enterable_west, true); +CELL_SETTER(DrawDungeon_SetEnterS, cell->enterable_south, true); +CELL_SETTER(DrawDungeon_SetEnterE, cell->enterable_east, true); +CELL_SETTER(DrawDungeon_SetTexFloor, cell->floor, true); +CELL_SETTER(DrawDungeon_SetTexCeiling, cell->ceiling, false); +CELL_SETTER(DrawDungeon_SetTexWallN, cell->north_wall, true); +CELL_SETTER(DrawDungeon_SetTexWallW, cell->west_wall, true); +CELL_SETTER(DrawDungeon_SetTexWallS, cell->south_wall, true); +CELL_SETTER(DrawDungeon_SetTexWallE, cell->east_wall, true); +CELL_SETTER(DrawDungeon_SetTexDoorN, cell->north_door, true); +CELL_SETTER(DrawDungeon_SetTexDoorW, cell->west_door, true); +CELL_SETTER(DrawDungeon_SetTexDoorS, cell->south_door, true); +CELL_SETTER(DrawDungeon_SetTexDoorE, cell->east_door, true); //void DrawDungeon_SetSkyTextureSet(int surface, int set); HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDrawMapRadar, int surface, int flag); @@ -311,7 +324,7 @@ HLL_LIBRARY(DrawDungeon, HLL_EXPORT(SetDoorWAngle, DrawDungeon_SetDoorWAngle), HLL_EXPORT(SetDoorSAngle, DrawDungeon_SetDoorSAngle), HLL_EXPORT(SetDoorEAngle, DrawDungeon_SetDoorEAngle), - HLL_EXPORT(SetEvFloor, dungeon_set_event_floor), + HLL_EXPORT(SetEvFloor, DrawDungeon_SetEvFloor), HLL_EXPORT(SetEvWallN, DrawDungeon_SetEvWallN), HLL_EXPORT(SetEvWallW, DrawDungeon_SetEvWallW), HLL_EXPORT(SetEvWallS, DrawDungeon_SetEvWallS), @@ -322,22 +335,22 @@ HLL_LIBRARY(DrawDungeon, HLL_EXPORT(SetEvWallS2, DrawDungeon_SetEvWallS2), HLL_EXPORT(SetEvWallE2, DrawDungeon_SetEvWallE2), HLL_TODO_EXPORT(SetEvMag, DrawDungeon_SetEvMag), - HLL_EXPORT(SetEvRate, dungeon_set_event_blend_rate), + HLL_EXPORT(SetEvRate, DrawDungeon_SetEvRate), HLL_EXPORT(SetEnter, DrawDungeon_SetEnter), HLL_EXPORT(SetEnterN, DrawDungeon_SetEnterN), HLL_EXPORT(SetEnterW, DrawDungeon_SetEnterW), HLL_EXPORT(SetEnterS, DrawDungeon_SetEnterS), HLL_EXPORT(SetEnterE, DrawDungeon_SetEnterE), - HLL_EXPORT(SetTexFloor, dungeon_set_texture_floor), - HLL_EXPORT(SetTexCeiling, dungeon_set_texture_ceiling), - HLL_EXPORT(SetTexWallN, dungeon_set_texture_north), - HLL_EXPORT(SetTexWallW, dungeon_set_texture_west), - HLL_EXPORT(SetTexWallS, dungeon_set_texture_south), - HLL_EXPORT(SetTexWallE, dungeon_set_texture_east), - HLL_EXPORT(SetTexDoorN, dungeon_set_door_north), - HLL_EXPORT(SetTexDoorW, dungeon_set_door_west), - HLL_EXPORT(SetTexDoorS, dungeon_set_door_south), - HLL_EXPORT(SetTexDoorE, dungeon_set_door_east), + HLL_EXPORT(SetTexFloor, DrawDungeon_SetTexFloor), + HLL_EXPORT(SetTexCeiling, DrawDungeon_SetTexCeiling), + HLL_EXPORT(SetTexWallN, DrawDungeon_SetTexWallN), + HLL_EXPORT(SetTexWallW, DrawDungeon_SetTexWallW), + HLL_EXPORT(SetTexWallS, DrawDungeon_SetTexWallS), + HLL_EXPORT(SetTexWallE, DrawDungeon_SetTexWallE), + HLL_EXPORT(SetTexDoorN, DrawDungeon_SetTexDoorN), + HLL_EXPORT(SetTexDoorW, DrawDungeon_SetTexDoorW), + HLL_EXPORT(SetTexDoorS, DrawDungeon_SetTexDoorS), + HLL_EXPORT(SetTexDoorE, DrawDungeon_SetTexDoorE), HLL_TODO_EXPORT(SetSkyTextureSet, DrawDungeon_SetSkyTextureSet), HLL_EXPORT(DrawMap, dungeon_map_draw), HLL_EXPORT(SetMapAllViewFlag, dungeon_map_set_all_view), diff --git a/src/meson.build b/src/meson.build index 1d8ebf5..01b2a3d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -22,9 +22,8 @@ xsystem4 = ['audio.c', 'dungeon/dgn.c', 'dungeon/dtx.c', 'dungeon/dungeon.c', - 'dungeon/event_markers.c', 'dungeon/map.c', - 'dungeon/mesh.c', + 'dungeon/renderer.c', 'dungeon/skybox.c', 'dungeon/tes.c', From c54f2493d10efa77fa34a2a7474a853a16c1798c Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sun, 30 May 2021 15:46:43 +0900 Subject: [PATCH 2/2] DrawDungeon: Use billboarding for floating event markers Before this, floating markers were rendered as OpenGL's point sprites. But point sprites has an implementation-dependent maximum size, and Intel driver's maximum (256 pixels) is too small for us. After this, markers are drawn with the same shaders as normal objects, using the billboarding technique. --- shaders/dungeon.f.glsl | 3 +- shaders/dungeon_sprite.f.glsl | 33 -------------- shaders/dungeon_sprite.v.glsl | 30 ------------- src/dungeon/renderer.c | 83 ++++++++--------------------------- 4 files changed, 21 insertions(+), 128 deletions(-) delete mode 100644 shaders/dungeon_sprite.f.glsl delete mode 100644 shaders/dungeon_sprite.v.glsl diff --git a/shaders/dungeon.f.glsl b/shaders/dungeon.f.glsl index d0d95f4..ddffffa 100644 --- a/shaders/dungeon.f.glsl +++ b/shaders/dungeon.f.glsl @@ -17,6 +17,7 @@ #version 140 uniform sampler2D tex; +uniform float alpha_mod; in float dist; in vec2 tex_coord; @@ -29,5 +30,5 @@ void main() { vec4 texel = texture(tex, tex_coord); float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST; fog_factor = clamp(fog_factor, 0.0, 1.0); - frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a); + frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a * alpha_mod); } diff --git a/shaders/dungeon_sprite.f.glsl b/shaders/dungeon_sprite.f.glsl deleted file mode 100644 index abca586..0000000 --- a/shaders/dungeon_sprite.f.glsl +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright (C) 2021 kichikuou - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#version 140 - -uniform sampler2D tex; -uniform float alpha_mod; - -in float dist; -out vec4 frag_color; - -const float FOG_MAX_DIST = 20.0; // FIXME -const vec3 FOG_COLOR = vec3(0.0, 0.0, 0.0); - -void main() { - vec4 texel = texture(tex, gl_PointCoord); - float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST; - fog_factor = clamp(fog_factor, 0.0, 1.0); - frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a * alpha_mod); -} diff --git a/shaders/dungeon_sprite.v.glsl b/shaders/dungeon_sprite.v.glsl deleted file mode 100644 index d928b63..0000000 --- a/shaders/dungeon_sprite.v.glsl +++ /dev/null @@ -1,30 +0,0 @@ -/* Copyright (C) 2021 kichikuou - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . - */ - -#version 140 - -uniform mat4 view_transform; -uniform mat4 proj_transform; - -in vec3 vertex_pos; -out float dist; - -void main() { - vec4 pos = view_transform * vec4(vertex_pos, 1.0); - dist = abs(pos.z); - gl_Position = proj_transform * pos; - gl_PointSize = 600 / dist; // FIXME -} diff --git a/src/dungeon/renderer.c b/src/dungeon/renderer.c index f43e9f2..3570f63 100644 --- a/src/dungeon/renderer.c +++ b/src/dungeon/renderer.c @@ -61,14 +61,6 @@ static const struct marker_info *get_marker_info(int event_type) return NULL; } -struct sprite_renderer { - struct shader shader; - GLint proj_transform; - GLuint alpha_mod; - GLuint vao; - GLuint pos_buffer; -}; - struct geometry; struct material; @@ -77,6 +69,7 @@ struct dungeon_renderer { // Uniform variable locations GLint local_transform; GLint proj_transform; + GLuint alpha_mod; // Attribute variable locations GLint vertex_uv; @@ -90,7 +83,6 @@ struct dungeon_renderer { GLuint *event_textures; int nr_event_textures; - struct sprite_renderer sprite; struct skybox *skybox; }; @@ -205,29 +197,6 @@ static struct material *get_material(struct dungeon_renderer *r, int type, int i return m; } -static void init_sprite_renderer(struct sprite_renderer *sr) -{ - gfx_load_shader(&sr->shader, "shaders/dungeon_sprite.v.glsl", "shaders/dungeon_sprite.f.glsl"); - sr->proj_transform = glGetUniformLocation(sr->shader.program, "proj_transform"); - sr->alpha_mod = glGetUniformLocation(sr->shader.program, "alpha_mod"); - - glGenVertexArrays(1, &sr->vao); - glBindVertexArray(sr->vao); - glGenBuffers(1, &sr->pos_buffer); - glBindBuffer(GL_ARRAY_BUFFER, sr->pos_buffer); - glEnableVertexAttribArray(sr->shader.vertex); - glVertexAttribPointer(sr->shader.vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -static void delete_sprite_renderer(struct sprite_renderer *sr) -{ - glDeleteVertexArrays(1, &sr->vao); - glDeleteBuffers(1, &sr->pos_buffer); - glDeleteProgram(sr->shader.program); -} - struct dungeon_renderer *dungeon_renderer_create(struct dtx *dtx, GLuint *event_textures, int nr_event_textures) { struct dungeon_renderer *r = xcalloc(1, sizeof(struct dungeon_renderer)); @@ -235,6 +204,7 @@ struct dungeon_renderer *dungeon_renderer_create(struct dtx *dtx, GLuint *event_ gfx_load_shader(&r->shader, "shaders/dungeon.v.glsl", "shaders/dungeon.f.glsl"); r->local_transform = glGetUniformLocation(r->shader.program, "local_transform"); r->proj_transform = glGetUniformLocation(r->shader.program, "proj_transform"); + r->alpha_mod = glGetUniformLocation(r->shader.program, "alpha_mod"); r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv"); r->wall_geometry = geometry_create(r, wall_vertices, sizeof(wall_vertices)); @@ -253,7 +223,6 @@ struct dungeon_renderer *dungeon_renderer_create(struct dtx *dtx, GLuint *event_ r->event_textures = event_textures; r->nr_event_textures = nr_event_textures; - init_sprite_renderer(&r->sprite); r->skybox = skybox_create(dtx); return r; @@ -273,7 +242,6 @@ void dungeon_renderer_free(struct dungeon_renderer *r) glDeleteTextures(r->nr_event_textures, r->event_textures); free(r->event_textures); - delete_sprite_renderer(&r->sprite); skybox_free(r->skybox); free(r); @@ -315,34 +283,21 @@ static void draw_floor_marker(struct dungeon_renderer *r, const struct marker_in glDepthFunc(GL_LESS); } -static void draw_sprite(struct dungeon_renderer *r, const struct marker_info *info, float x, float y, float z, int blend_rate, uint32_t t) +static void draw_floating_marker(struct dungeon_renderer *r, const struct marker_info *info, float x, float y, float z, int blend_rate, uint32_t t, mat4 v) { - struct sprite_renderer *sr = &r->sprite; - glUseProgram(sr->shader.program); - glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); - glEnable(GL_POINT_SPRITE); - - glBindVertexArray(sr->vao); - glActiveTexture(GL_TEXTURE0); - glUniform1i(sr->shader.texture, 0); - glUniform1f(sr->alpha_mod, blend_rate / 255.0); - glBindBuffer(GL_ARRAY_BUFFER, sr->pos_buffer); - int frame = t / 100; // 100ms per frame int texture = info->texture_index + frame % info->texture_count; - glBindTexture(GL_TEXTURE_2D, r->event_textures[texture]); - GLfloat pos[3] = {x, y, z}; - glBufferData(GL_ARRAY_BUFFER, 3 * sizeof(GLfloat), pos, GL_STREAM_DRAW); - glDrawArrays(GL_POINTS, 0, 1); - - glBindVertexArray(0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); - glDisable(GL_POINT_SPRITE); + mat4 billboard = MAT4( + v[0][0], v[0][1], v[0][2], x, + v[1][0], v[1][1], v[1][2], y, + v[2][0], v[2][1], v[2][2], z, + 0, 0, 0, 1); + glUniform1f(r->alpha_mod, blend_rate / 255.0); + draw(r, r->wall_geometry, r->event_textures[texture], billboard); + glUniform1f(r->alpha_mod, 1.0); } -static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool render_opaque) +static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool render_opaque, mat4 view_transform) { float x = 2.0 * cell->x; float y = 2.0 * cell->y; @@ -491,7 +446,7 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re if (info) { uint32_t t = SDL_GetTicks(); draw_floor_marker(r, info, x, y, z, t); - draw_sprite(r, info, x, y, z, cell->event_blend_rate, t); + draw_floating_marker(r, info, x, y, z, cell->event_blend_rate, t, view_transform); } } } @@ -501,22 +456,22 @@ void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells glUseProgram(r->shader.program); glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]); glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, proj_transform[0]); - glUseProgram(r->sprite.shader.program); - glUniformMatrix4fv(r->sprite.shader.view_transform, 1, GL_FALSE, view_transform[0]); - glUniformMatrix4fv(r->sprite.proj_transform, 1, GL_FALSE, proj_transform[0]); - glUseProgram(0); + glUniform1f(r->alpha_mod, 1.0); // Render opaque objects, from near to far. glDisable(GL_BLEND); for (int i = 0; i < nr_cells; i++) - draw_cell(r, cells[i], true); + draw_cell(r, cells[i], true, view_transform); // Render the skybox. skybox_render(r->skybox, view_transform, proj_transform); + glUseProgram(r->shader.program); // Render transparent objects, from far to near. glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); for (int i = nr_cells - 1; i >= 0; i--) - draw_cell(r, cells[i], false); + draw_cell(r, cells[i], false, view_transform); + + glUseProgram(0); }