This commit is contained in:
Nunuhara Cabbage
2021-05-30 18:49:49 -07:00
15 changed files with 711 additions and 1120 deletions
+20 -14
View File
@@ -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 */
+2 -19
View File
@@ -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);
-30
View File
@@ -1,30 +0,0 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* 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 <http://gnu.org/licenses/>.
*/
#ifndef SYSTEM4_DUNGEON_EVENT_MARKERS_H
#define SYSTEM4_DUNGEON_EVENT_MARKERS_H
#include <cglm/types.h>
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 */
-55
View File
@@ -1,55 +0,0 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* 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 <http://gnu.org/licenses/>.
*/
#ifndef SYSTEM4_DUNGEON_MESH_H
#define SYSTEM4_DUNGEON_MESH_H
#include <GL/glew.h>
#include <cglm/types.h>
/*
* 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 */
@@ -14,20 +14,18 @@
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#version 140
#ifndef SYSTEM4_DUNGEON_RENDERER_H
#define SYSTEM4_DUNGEON_RENDERER_H
uniform sampler2D tex;
uniform float alpha_mod;
#include <cglm/cglm.h>
#include <GL/glew.h>
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 */
+2 -3
View File
@@ -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);
}
+1 -2
View File
@@ -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;
-30
View File
@@ -1,30 +0,0 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* 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 <http://gnu.org/licenses/>.
*/
#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
}
+106 -14
View File
@@ -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;
}
+50 -274
View File
@@ -21,6 +21,7 @@
#include <cglm/cglm.h>
#include <GL/glew.h>
#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);
}
-273
View File
@@ -1,273 +0,0 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* 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 <http://gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdlib.h>
#include <cglm/cglm.h>
#include <GL/glew.h>
#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;
}
-363
View File
@@ -1,363 +0,0 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* 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 <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <cglm/cglm.h>
#include <GL/glew.h>
#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));
}
+477
View File
@@ -0,0 +1,477 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* 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 <http://gnu.org/licenses/>.
*/
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <cglm/cglm.h>
#include <GL/glew.h>
#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);
}
+41 -28
View File
@@ -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),
+1 -2
View File
@@ -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',