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/event_markers.h b/include/dungeon/event_markers.h deleted file mode 100644 index 2f60217..0000000 --- a/include/dungeon/event_markers.h +++ /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 . - */ - -#ifndef SYSTEM4_DUNGEON_EVENT_MARKERS_H -#define SYSTEM4_DUNGEON_EVENT_MARKERS_H - -#include - -struct event_markers; - -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); - -#endif /* SYSTEM4_DUNGEON_EVENT_MARKERS_H */ 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/shaders/dungeon_sprite.f.glsl b/include/dungeon/renderer.h similarity index 57% rename from shaders/dungeon_sprite.f.glsl rename to include/dungeon/renderer.h index abca586..014f5d8 100644 --- a/shaders/dungeon_sprite.f.glsl +++ b/include/dungeon/renderer.h @@ -14,20 +14,18 @@ * along with this program; if not, see . */ -#version 140 +#ifndef SYSTEM4_DUNGEON_RENDERER_H +#define SYSTEM4_DUNGEON_RENDERER_H -uniform sampler2D tex; -uniform float alpha_mod; +#include +#include -in float dist; -out vec4 frag_color; +struct dgn_cell; +struct dtx; +struct dungeon_renderer; -const float FOG_MAX_DIST = 20.0; // FIXME -const vec3 FOG_COLOR = vec3(0.0, 0.0, 0.0); +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); -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); -} +#endif /* SYSTEM4_DUNGEON_RENDERER_H */ diff --git a/shaders/dungeon.f.glsl b/shaders/dungeon.f.glsl index bd26448..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; @@ -27,9 +28,7 @@ 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); + frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a * alpha_mod); } 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/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/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..3570f63 --- /dev/null +++ b/src/dungeon/renderer.c @@ -0,0 +1,477 @@ +/* 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 geometry; +struct material; + +struct dungeon_renderer { + struct shader shader; + // Uniform variable locations + GLint local_transform; + GLint proj_transform; + GLuint alpha_mod; + // 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 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; +} + +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->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)); + 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; + + 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); + + 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_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) +{ + int frame = t / 100; // 100ms per frame + int texture = info->texture_index + frame % info->texture_count; + 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, mat4 view_transform) +{ + 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_floating_marker(r, info, x, y, z, cell->event_blend_rate, t, view_transform); + } + } +} + +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]); + 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, 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, view_transform); + + glUseProgram(0); +} 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',