From adb558c67c61a6290fa7341f1326431530f65cb1 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 8 May 2021 14:11:27 +0900 Subject: [PATCH 1/2] Initial implementation of DrawDungeon.hll (Rance VI) In addition to DrawDungeon.c, this adds the following files in {src,include}/dungeon/: - dgn.{h,c}: Parser for .dgn (map data) - dtx.{h,c}: Parser for .dtx (textures) - tes.{h,c}: Parser for .tes (sound IDs associated with textures) - dungeon.{h,c}: Renderer main routines - mesh.{h,c}: Renderer for walls and stairs - skybox.{h,c}: Renderer for background cubemap - event_markers.{h,c}: Renderer for event markers And the following changes are made to the existing graphics code: - sact_Update() calls dungeon_update(), so that event markers animate - gfx_render_texture() renders vertically flipped image when texture.flip_y == true (see the comment in dungeon_context_create()) Some features are not implemented yet, including 2D maps and door animation. This makes cglm a dependency. --- README.md | 1 + include/dungeon/dgn.h | 119 +++++++++ include/dungeon/dtx.h | 50 ++++ include/dungeon/dungeon.h | 83 ++++++ include/dungeon/event_markers.h | 25 ++ include/dungeon/mesh.h | 49 ++++ include/dungeon/skybox.h | 24 ++ include/dungeon/tes.h | 39 +++ include/gfx/gfx.h | 1 + meson.build | 1 + shaders/dungeon.f.glsl | 35 +++ shaders/dungeon.v.glsl | 34 +++ shaders/dungeon_skybox.f.glsl | 27 ++ shaders/dungeon_skybox.v.glsl | 29 +++ shaders/dungeon_sprite.f.glsl | 33 +++ shaders/dungeon_sprite.v.glsl | 30 +++ src/dungeon/dgn.c | 122 +++++++++ src/dungeon/dtx.c | 79 ++++++ src/dungeon/dungeon.c | 441 ++++++++++++++++++++++++++++++++ src/dungeon/event_markers.c | 271 ++++++++++++++++++++ src/dungeon/mesh.c | 363 ++++++++++++++++++++++++++ src/dungeon/skybox.c | 132 ++++++++++ src/dungeon/tes.c | 46 ++++ src/ffi.c | 2 + src/hll/DrawDungeon.c | 377 +++++++++++++++++++++++++++ src/hll/SACT2.c | 4 +- src/meson.build | 11 +- src/video.c | 13 + subprojects/libsys4 | 2 +- 29 files changed, 2440 insertions(+), 3 deletions(-) create mode 100644 include/dungeon/dgn.h create mode 100644 include/dungeon/dtx.h create mode 100644 include/dungeon/dungeon.h create mode 100644 include/dungeon/event_markers.h create mode 100644 include/dungeon/mesh.h create mode 100644 include/dungeon/skybox.h create mode 100644 include/dungeon/tes.h create mode 100644 shaders/dungeon.f.glsl create mode 100644 shaders/dungeon.v.glsl create mode 100644 shaders/dungeon_skybox.f.glsl create mode 100644 shaders/dungeon_skybox.v.glsl create mode 100644 shaders/dungeon_sprite.f.glsl create mode 100644 shaders/dungeon_sprite.v.glsl create mode 100644 src/dungeon/dgn.c create mode 100644 src/dungeon/dtx.c create mode 100644 src/dungeon/dungeon.c create mode 100644 src/dungeon/event_markers.c create mode 100644 src/dungeon/mesh.c create mode 100644 src/dungeon/skybox.c create mode 100644 src/dungeon/tes.c create mode 100644 src/hll/DrawDungeon.c diff --git a/README.md b/README.md index 1592db8..ede9a8e 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ First install the dependencies (corresponding Debian package in parentheses): * chibi-scheme [optional, for debugger] * bison (bison) +* cglm (libcglm-dev) * flex (flex) * meson (meson) * libffi (libffi-dev) diff --git a/include/dungeon/dgn.h b/include/dungeon/dgn.h new file mode 100644 index 0000000..eef0dce --- /dev/null +++ b/include/dungeon/dgn.h @@ -0,0 +1,119 @@ +/* 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_DGN_H +#define SYSTEM4_DGN_H + +#include +#include +#include + +/* + * Dungeon data is a collection of cells indexed by three integers (x, y, z). + * X increases from west to east, Y increases from down to up, Z increases from + * south to north. + */ + +enum dgn_version { + DGN_VER_RANCE6 = 10, + DGN_VER_GALZOO = 13, +}; + +struct dgn_cell { + int32_t floor; + int32_t ceiling; + int32_t north_wall; + int32_t south_wall; + int32_t east_wall; + int32_t west_wall; + int32_t north_door; + int32_t south_door; + int32_t east_door; + 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 enterable; + int32_t enterable_north; + int32_t enterable_south; + int32_t enterable_east; + int32_t enterable_west; + int32_t floor_event; + int32_t north_event; + int32_t south_event; + int32_t east_event; + int32_t west_event; + // struct { + // uint32_t i; + // char *s; + // } pairs[6]; + int32_t unknown; + int32_t battle_background; + + // The below exist only in version 13 + int32_t polyobj_index; + float polyobj_scale; + float polyobj_rotation_y; + float polyobj_rotation_z; + float polyobj_rotation_x; + float polyobj_position_x; + float polyobj_position_y; + float polyobj_position_z; + int32_t roof_orientation; + int32_t roof_texture; + // int32_t unused1; // always -1 + int32_t roof_underside_texture; + // int32_t unused2; // always -1 + + // Runtime data (not stored in .dgn) + int32_t floor_event2; + int32_t north_event2; + int32_t south_event2; + int32_t east_event2; + int32_t west_event2; +}; + +struct dgn { + uint32_t version; // 10: Rance VI, 13: GALZOO Island + uint32_t size_x; + uint32_t size_y; + uint32_t size_z; + // uint32_t unknown[10]; + struct dgn_cell *cells; +}; + +struct dgn *dgn_parse(uint8_t *data, size_t size); +void dgn_free(struct dgn *dgn); +struct dgn_cell *dgn_cell_at(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z); + +static inline bool dgn_is_in_map(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z) +{ + return x < dgn->size_x && y < dgn->size_y && z < dgn->size_z; +} + +#endif /* SYSTEM4_DGN_H */ diff --git a/include/dungeon/dtx.h b/include/dungeon/dtx.h new file mode 100644 index 0000000..d4d321d --- /dev/null +++ b/include/dungeon/dtx.h @@ -0,0 +1,50 @@ +/* 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_DTX_H +#define SYSTEM4_DTX_H + +#include +#include + +struct cg; + +enum dtx_texture_type { + DTX_WALL = 0, + DTX_FLOOR = 1, + DTX_CEILING = 2, + DTX_STAIRS = 3, + DTX_DOOR = 4, + DTX_SKYBOX = 5, +}; + +struct dtx_entry { + uint32_t size; + uint8_t *data; +}; + +struct dtx { + uint32_t version; // 0: Rance IV, 1: GALZOO Island + int nr_rows; + int nr_columns; + struct dtx_entry *entries; +}; + +struct dtx *dtx_parse(uint8_t *data, size_t size); +void dtx_free(struct dtx *dtx); +struct cg *dtx_create_cg(struct dtx *dtx, int type, int index); + +#endif /* SYSTEM4_DTX_H */ diff --git a/include/dungeon/dungeon.h b/include/dungeon/dungeon.h new file mode 100644 index 0000000..ca79a4d --- /dev/null +++ b/include/dungeon/dungeon.h @@ -0,0 +1,83 @@ +/* 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_H +#define SYSTEM4_DUNGEON_H + +#include +#include +#include +#include + +struct dgn; +struct dtx; +struct tes; +struct mesh; +struct skybox; +struct event_markers; + +struct camera { + float pos[3]; + float angle; + float angle_p; +}; + +/* + * Dungeon rendering context. + * + * A dungeon cell at (x, y, z) is rendered in a 2x2x2 cube centered at + * (2*x, 2*y, -2*z) in the world coordinates. Note that Z is flipped, because + * OpenGL uses a right-handed coordinate system while dungeon data is left- + * handed. + */ + +struct dungeon_context { + int surface; + bool loaded; + bool draw_enabled; + struct camera camera; + struct dgn *dgn; + struct dtx *dtx; + struct tes *tes; + struct mesh **meshes; + int nr_meshes; + struct skybox *skybox; + struct event_markers *events; + GLuint depth_buffer; +}; + +struct dungeon_context *dungeon_context_create(int surface); +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); + +#endif /* SYSTEM4_DUNGEON_H */ diff --git a/include/dungeon/event_markers.h b/include/dungeon/event_markers.h new file mode 100644 index 0000000..f037775 --- /dev/null +++ b/include/dungeon/event_markers.h @@ -0,0 +1,25 @@ +/* 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 + +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 GLfloat *view_transform, const GLfloat *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); diff --git a/include/dungeon/mesh.h b/include/dungeon/mesh.h new file mode 100644 index 0000000..380d9de --- /dev/null +++ b/include/dungeon/mesh.h @@ -0,0 +1,49 @@ +/* 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 + +/* + * 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 GLfloat *local_transform, const GLfloat *view_transform, const GLfloat *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); diff --git a/include/dungeon/skybox.h b/include/dungeon/skybox.h new file mode 100644 index 0000000..58dea2b --- /dev/null +++ b/include/dungeon/skybox.h @@ -0,0 +1,24 @@ +/* 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 + +struct dtx; +struct skybox; + +struct skybox *skybox_create(struct dtx *dtx); +void skybox_free(struct skybox *s); +void skybox_render(struct skybox *s, const GLfloat *view_transform, const GLfloat *proj_transform); diff --git a/include/dungeon/tes.h b/include/dungeon/tes.h new file mode 100644 index 0000000..94f0142 --- /dev/null +++ b/include/dungeon/tes.h @@ -0,0 +1,39 @@ +/* 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_TES_H +#define SYSTEM4_TES_H + +#include +#include + +struct tes { + int nr_columns; + int nr_rows; + int *entries; +}; + +struct tes *tes_parse(uint8_t *data, size_t size); +void tes_free(struct tes *tes); + +static inline int tes_get(struct tes *tes, int type, int index) +{ + if (type < 0 || type >= tes->nr_rows || index < 0 || index >= tes->nr_columns) + return -1; + return tes->entries[type * tes->nr_columns + index]; +} + +#endif /* SYSTEM4_TES_H */ diff --git a/include/gfx/gfx.h b/include/gfx/gfx.h index 36c791e..6f51829 100644 --- a/include/gfx/gfx.h +++ b/include/gfx/gfx.h @@ -38,6 +38,7 @@ typedef struct texture { GLfloat world_transform[16]; int w, h; bool has_alpha; + bool flip_y; int alpha_mod; enum draw_method draw_method; } Texture; diff --git a/meson.build b/meson.build index d387875..c6512dc 100644 --- a/meson.build +++ b/meson.build @@ -15,6 +15,7 @@ tj = dependency('libturbojpeg') webp = dependency('libwebp') png = dependency('libpng') chibi = dependency('chibi-scheme', required : get_option('debugger')) +cglm = dependency('cglm') flex = find_program('flex') bison = find_program('bison') diff --git a/shaders/dungeon.f.glsl b/shaders/dungeon.f.glsl new file mode 100644 index 0000000..bd26448 --- /dev/null +++ b/shaders/dungeon.f.glsl @@ -0,0 +1,35 @@ +/* Copyright (C) 2021 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#version 140 + +uniform sampler2D tex; + +in float dist; +in vec2 tex_coord; +out vec4 frag_color; + +const float FOG_MAX_DIST = 20.0; // FIXME +const vec3 FOG_COLOR = vec3(0.0, 0.0, 0.0); + +void main() { + vec4 texel = texture(tex, tex_coord); + if (texel.a < 0.2) + discard; + float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST; + fog_factor = clamp(fog_factor, 0.0, 1.0); + frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a); +} diff --git a/shaders/dungeon.v.glsl b/shaders/dungeon.v.glsl new file mode 100644 index 0000000..4865581 --- /dev/null +++ b/shaders/dungeon.v.glsl @@ -0,0 +1,34 @@ +/* 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 local_transform; +uniform mat4 view_transform; +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); + dist = abs(pos.z); + gl_Position = proj_transform * pos; + tex_coord = vertex_uv; +} diff --git a/shaders/dungeon_skybox.f.glsl b/shaders/dungeon_skybox.f.glsl new file mode 100644 index 0000000..2ef08a0 --- /dev/null +++ b/shaders/dungeon_skybox.f.glsl @@ -0,0 +1,27 @@ +/* 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 samplerCube tex; + +in vec3 tex_coord; +out vec4 frag_color; + +void main() { + // Flip Z because the coordinate system for cubemaps is left-handed. + frag_color = texture(tex, vec3(tex_coord.xy, -tex_coord.z)); +} diff --git a/shaders/dungeon_skybox.v.glsl b/shaders/dungeon_skybox.v.glsl new file mode 100644 index 0000000..6f36502 --- /dev/null +++ b/shaders/dungeon_skybox.v.glsl @@ -0,0 +1,29 @@ +/* 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 vec3 tex_coord; + +void main() { + vec4 pos = proj_transform * mat4(mat3(view_transform)) * vec4(vertex_pos, 1.0); + gl_Position = pos.xyww; + tex_coord = vertex_pos; +} diff --git a/shaders/dungeon_sprite.f.glsl b/shaders/dungeon_sprite.f.glsl new file mode 100644 index 0000000..abca586 --- /dev/null +++ b/shaders/dungeon_sprite.f.glsl @@ -0,0 +1,33 @@ +/* Copyright (C) 2021 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#version 140 + +uniform sampler2D tex; +uniform float alpha_mod; + +in float dist; +out vec4 frag_color; + +const float FOG_MAX_DIST = 20.0; // FIXME +const vec3 FOG_COLOR = vec3(0.0, 0.0, 0.0); + +void main() { + vec4 texel = texture(tex, gl_PointCoord); + float fog_factor = (FOG_MAX_DIST - dist) / FOG_MAX_DIST; + fog_factor = clamp(fog_factor, 0.0, 1.0); + frag_color = vec4(mix(FOG_COLOR, vec3(texel), fog_factor), texel.a * alpha_mod); +} diff --git a/shaders/dungeon_sprite.v.glsl b/shaders/dungeon_sprite.v.glsl new file mode 100644 index 0000000..d928b63 --- /dev/null +++ b/shaders/dungeon_sprite.v.glsl @@ -0,0 +1,30 @@ +/* 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 new file mode 100644 index 0000000..c840e80 --- /dev/null +++ b/src/dungeon/dgn.c @@ -0,0 +1,122 @@ +/* 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 "system4.h" +#include "system4/buffer.h" + +#include "dungeon/dgn.h" +#include "vm.h" + +struct dgn *dgn_parse(uint8_t *data, size_t size) +{ + struct buffer r; + buffer_init(&r, data, size); + if (strncmp(buffer_strdata(&r), "DUGN", 4)) + return NULL; + buffer_skip(&r, 4); + + struct dgn *dgn = xcalloc(1, sizeof(struct dgn)); + dgn->version = buffer_read_int32(&r); + if (dgn->version != DGN_VER_RANCE6 && dgn->version != DGN_VER_GALZOO) { + WARNING("unknown DGN version: %d", dgn->version); + free(dgn); + return NULL; + } + dgn->size_x = buffer_read_int32(&r); + dgn->size_y = buffer_read_int32(&r); + dgn->size_z = buffer_read_int32(&r); + buffer_skip(&r, 40); + + int nr_cells = dgn->size_x * dgn->size_y * dgn->size_z; + dgn->cells = xcalloc(nr_cells, sizeof(struct dgn_cell)); + for (int i = 0; i < nr_cells; i++) { + struct dgn_cell *cell = &dgn->cells[i]; + cell->floor = buffer_read_int32(&r); + cell->ceiling = buffer_read_int32(&r); + cell->north_wall = buffer_read_int32(&r); + cell->south_wall = buffer_read_int32(&r); + cell->east_wall = buffer_read_int32(&r); + cell->west_wall = buffer_read_int32(&r); + cell->north_door = buffer_read_int32(&r); + cell->south_door = buffer_read_int32(&r); + cell->east_door = buffer_read_int32(&r); + 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); + cell->enterable = buffer_read_int32(&r); + cell->enterable_north = buffer_read_int32(&r); + cell->enterable_south = buffer_read_int32(&r); + cell->enterable_east = buffer_read_int32(&r); + cell->enterable_west = buffer_read_int32(&r); + cell->floor_event = buffer_read_int32(&r); + cell->north_event = buffer_read_int32(&r); + cell->south_event = buffer_read_int32(&r); + cell->east_event = buffer_read_int32(&r); + cell->west_event = buffer_read_int32(&r); + // Skip (int32, string) pairs + for (int j = 0; j < 6; j++) { + buffer_skip(&r, 4); + buffer_skip(&r, strlen(buffer_strdata(&r)) + 1); + } + cell->unknown = buffer_read_int32(&r); + cell->battle_background = buffer_read_int32(&r); + if (dgn->version != DGN_VER_GALZOO) + continue; + + cell->polyobj_index = buffer_read_int32(&r); + cell->polyobj_scale = buffer_read_float(&r); + cell->polyobj_rotation_y = buffer_read_float(&r); + cell->polyobj_rotation_z = buffer_read_float(&r); + cell->polyobj_rotation_x = buffer_read_float(&r); + cell->polyobj_position_x = buffer_read_float(&r); + cell->polyobj_position_y = buffer_read_float(&r); + cell->polyobj_position_z = buffer_read_float(&r); + cell->roof_orientation = buffer_read_int32(&r); + cell->roof_texture = buffer_read_int32(&r); + buffer_skip(&r, 4); + cell->roof_underside_texture = buffer_read_int32(&r); + buffer_skip(&r, 4); + } + return dgn; +} + +void dgn_free(struct dgn *dgn) +{ + free(dgn->cells); + free(dgn); +} + +struct dgn_cell *dgn_cell_at(struct dgn *dgn, uint32_t x, uint32_t y, uint32_t z) +{ + if (x >= dgn->size_x || y >= dgn->size_y || z >= dgn->size_z) + VM_ERROR("cell location out of range: (%d, %d, %d)", x, y, z); + return &dgn->cells[(z * dgn->size_y + y) * dgn->size_x + x]; +} diff --git a/src/dungeon/dtx.c b/src/dungeon/dtx.c new file mode 100644 index 0000000..c534c18 --- /dev/null +++ b/src/dungeon/dtx.c @@ -0,0 +1,79 @@ +/* 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 "system4.h" +#include "system4/buffer.h" +#include "system4/cg.h" + +#include "dungeon/dtx.h" + +struct dtx *dtx_parse(uint8_t *data, size_t size) +{ + struct buffer r; + buffer_init(&r, data, size); + if (strncmp(buffer_strdata(&r), "DTEX", 4)) { + WARNING("dtx_parse: not a DTX file"); + return NULL; + } + buffer_skip(&r, 4); + + struct dtx *dtx = xcalloc(1, sizeof(struct dtx)); + dtx->version = buffer_read_int32(&r); + dtx->nr_rows = buffer_read_int32(&r); + dtx->nr_columns = buffer_read_int32(&r); + dtx->entries = xcalloc(dtx->nr_rows * dtx->nr_columns, sizeof(struct dtx_entry)); + for (int i = 0; i < dtx->nr_rows; i++) { + int nr_items = buffer_read_int32(&r); + if (nr_items != dtx->nr_columns) { + WARNING("dtx_parse: unexpected number of items"); + dtx_free(dtx); + return NULL; + } + for (int j = 0; j < nr_items; j++) { + uint32_t size = buffer_read_int32(&r); + if (!size) + continue; + uint8_t *data = xmalloc(size); + buffer_read_bytes(&r, data, size); + dtx->entries[i * nr_items + j].size = size; + dtx->entries[i * nr_items + j].data = data; + } + } + return dtx; +} + +void dtx_free(struct dtx *dtx) +{ + int nr_entries = dtx->nr_rows * dtx->nr_columns; + for (int i = 0; i < nr_entries; i++) { + if (dtx->entries[i].data) + free(dtx->entries[i].data); + } + free(dtx->entries); + free(dtx); +} + +struct cg *dtx_create_cg(struct dtx *dtx, int type, int index) +{ + if (type < 0 || type >= dtx->nr_rows || index < 0 || index >= dtx->nr_columns) + return NULL; + struct dtx_entry *entry = &dtx->entries[type * dtx->nr_columns + index]; + if (!entry->data) + return NULL; + return cg_load_buffer(entry->data, entry->size); +} diff --git a/src/dungeon/dungeon.c b/src/dungeon/dungeon.c new file mode 100644 index 0000000..4da9fcd --- /dev/null +++ b/src/dungeon/dungeon.c @@ -0,0 +1,441 @@ +/* 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 +#include "system4.h" +#include "system4/archive.h" +#include "system4/cg.h" +#include "system4/dlf.h" + +#include "dungeon/dgn.h" +#include "dungeon/dtx.h" +#include "dungeon/dungeon.h" +#include "dungeon/event_markers.h" +#include "dungeon/mesh.h" +#include "dungeon/skybox.h" +#include "dungeon/tes.h" +#include "gfx/gfx.h" +#include "sact.h" +#include "sprite.h" +#include "vm.h" +#include "xsystem4.h" + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +struct dungeon_context *dungeon_context_create(int surface) +{ + struct dungeon_context *ctx = xcalloc(1, sizeof(struct dungeon_context)); + ctx->surface = surface; + struct sact_sprite *sp = sact_get_sprite(ctx->surface); + if (!sp) + VM_ERROR("DrawDungeon.Init: invalid surface %d", surface); + + // Dungeon scene will be rendered to this texture. Unlike other textures, + // its (0,0) is at the bottom-left, so it needs to be flipped vertically + // when displayed on the screen. + struct texture *texture = sprite_get_texture(sp); + texture->flip_y = true; + + glGenRenderbuffers(1, &ctx->depth_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, ctx->depth_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + mesh_init(); + return ctx; +} + +void dungeon_context_free(struct dungeon_context *ctx) +{ + if (ctx->dgn) + dgn_free(ctx->dgn); + if (ctx->dtx) + dtx_free(ctx->dtx); + 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); + + glDeleteRenderbuffers(1, &ctx->depth_buffer); + mesh_fini(); + + struct sact_sprite *sp = sact_get_sprite(ctx->surface); + if (sp) + sprite_get_texture(sp)->flip_y = false; + + free(ctx); +} + +static struct mesh **create_meshes(struct dtx *dtx, int *nr_meshes) +{ + 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); + } + } + *nr_meshes = nr_types * dtx->nr_columns; + return meshes; +} + +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); + } + } + } +} + +bool dungeon_load(struct dungeon_context *ctx, int num) +{ + if (ctx->loaded) + VM_ERROR("Dungeon is already loaded"); + + char *path = gamedir_path("Data/DungeonData.dlf"); + int error = ARCHIVE_SUCCESS; + struct archive *dlf = (struct archive *)dlf_open(path, ARCHIVE_MMAP, &error); + if (error == ARCHIVE_FILE_ERROR) { + WARNING("dlf_open(\"%s\"): %s", path, strerror(errno)); + } else if (error == ARCHIVE_BAD_ARCHIVE_ERROR) { + WARNING("dlf_open(\"%s\"): invalid .dlf file", path); + } + free(path); + if (!dlf) + return false; + + struct archive_data *dgn = archive_get(dlf, num * 3); + if (dgn) { + ctx->dgn = dgn_parse(dgn->data, dgn->size); + archive_free_data(dgn); + } + struct archive_data *dtx = archive_get(dlf, num * 3 + 1); + if (dtx) { + ctx->dtx = dtx_parse(dtx->data, dtx->size); + archive_free_data(dtx); + } + struct archive_data *tes = archive_get(dlf, num * 3 + 2); + if (tes) { + ctx->tes = tes_parse(tes->data, tes->size); + archive_free_data(tes); + } + archive_free(dlf); + if (!ctx->dgn || !ctx->dtx) { + WARNING("Cannot load dungeon %d", num); + return false; + } + + ctx->meshes = create_meshes(ctx->dtx, &ctx->nr_meshes); + ctx->skybox = skybox_create(ctx->dtx); + ctx->events = event_markers_create(); + + populate_meshes(ctx); + + ctx->loaded = true; + return true; +} + +void dungeon_set_camera(int surface, float x, float y, float z, float angle, float angle_p) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx) + return; + ctx->camera.pos[0] = x; + ctx->camera.pos[1] = y; + ctx->camera.pos[2] = -z; + ctx->camera.angle = -angle * M_PI / 180; + ctx->camera.angle_p = angle_p * M_PI / 180; +} + +static void model_view_matrix(struct camera *camera, mat4 out) +{ + // The actual camera position is slightly behind camera->pos. + float dist = 0.9; + vec3 d = { + dist * sin(camera->angle) * cos(camera->angle_p), + dist * sin(camera->angle_p), + dist * -cos(camera->angle) * cos(camera->angle_p), + }; + vec3 eye; + glm_vec3_sub(camera->pos, d, eye); + vec3 up = {0.0, 1.0, 0.0}; + glm_lookat(eye, camera->pos, up, out); +} + +void dungeon_render(struct dungeon_context *ctx) +{ + struct sact_sprite *sp = sact_get_sprite(ctx->surface); + sprite_dirty(sp); + struct texture *texture = sprite_get_texture(sp); + + GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, texture, 0, 0, texture->w, texture->h); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, ctx->depth_buffer); + if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + ERROR("Incomplete framebuffer"); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_DEPTH_TEST); + glEnable(GL_CULL_FACE); + + mat4 local_transform, view_transform, proj_transform; + glm_mat4_identity(local_transform); + 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[0], view_transform[0], proj_transform[0]); + } + + // Render the skybox. + skybox_render(ctx->skybox, view_transform[0], proj_transform[0]); + + // 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[0], view_transform[0], proj_transform[0]); + } + + // Render event markers. + event_markers_render(ctx->events, view_transform[0], proj_transform[0]); + + 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); +} + +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); +} + +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; + 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; + mesh_add_north_wall(ctx_mesh(ctx, DTX_WALL, cell->north_wall), 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; + mesh_add_south_wall(ctx_mesh(ctx, DTX_WALL, cell->south_wall), 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; + mesh_add_east_wall(ctx_mesh(ctx, DTX_WALL, cell->east_wall), 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; + mesh_add_west_wall(ctx_mesh(ctx, DTX_WALL, cell->west_wall), 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; + mesh_add_north_wall(ctx_mesh(ctx, DTX_DOOR, cell->north_door), 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; + mesh_add_south_wall(ctx_mesh(ctx, DTX_DOOR, cell->south_door), 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; + mesh_add_east_wall(ctx_mesh(ctx, DTX_DOOR, cell->east_door), 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; + mesh_add_west_wall(ctx_mesh(ctx, DTX_DOOR, cell->west_door), x, y, z); +} diff --git a/src/dungeon/event_markers.c b/src/dungeon/event_markers.c new file mode 100644 index 0000000..e91398b --- /dev/null +++ b/src/dungeon/event_markers.c @@ -0,0 +1,271 @@ +/* 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 texture_index; + int texture_count; + int floor_marker_type; +}; + +#define MAX_MARKER_INFO 19 + +static const struct marker_info rance6_marker_info[MAX_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 +}; + +const struct marker_info *get_marker_info(unsigned event_type) +{ + static const struct marker_info null_marker_info = {}; + if (event_type >= MAX_MARKER_INFO) + return &null_marker_info; + return &rance6_marker_info[event_type]; +} + +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 GLfloat *view_transform, const GLfloat *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[0], 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); + glUniformMatrix4fv(em->proj_transform, 1, GL_FALSE, proj_transform); + + 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->texture_count) { + // 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 new file mode 100644 index 0000000..cb8b11e --- /dev/null +++ b/src/dungeon/mesh.c @@ -0,0 +1,363 @@ +/* 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 GLfloat *local_transform, const GLfloat *view_transform, const GLfloat *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); + glUniformMatrix4fv(mesh_shader.s.view_transform, 1, GL_FALSE, view_transform); + glUniformMatrix4fv(mesh_shader.proj_transform, 1, GL_FALSE, proj_transform); + + 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/skybox.c b/src/dungeon/skybox.c new file mode 100644 index 0000000..db06c3c --- /dev/null +++ b/src/dungeon/skybox.c @@ -0,0 +1,132 @@ +/* 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 "system4.h" +#include "system4/cg.h" + +#include "dungeon/dtx.h" +#include "dungeon/skybox.h" +#include "gfx/gfx.h" + +struct skybox { + struct shader shader; + GLint proj_transform; + GLuint vao; + GLuint attr_buffer; + GLuint index_buffer; + GLuint texture; + int nr_indices; +}; + +struct skybox *skybox_create(struct dtx *dtx) +{ + struct skybox *s = xcalloc(1, sizeof(struct skybox)); + gfx_load_shader(&s->shader, "shaders/dungeon_skybox.v.glsl", "shaders/dungeon_skybox.f.glsl"); + s->proj_transform = glGetUniformLocation(s->shader.program, "proj_transform"); + + // Set up a vertex array. + const GLfloat vertices[] = { + -1.0, 1.0, -1.0, + -1.0, -1.0, -1.0, + 1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + -1.0, -1.0, 1.0, + -1.0, 1.0, 1.0, + 1.0, -1.0, 1.0, + 1.0, 1.0, 1.0, + }; + const GLushort indices[] = { + 0, 1, 2, 2, 3, 0, + 4, 1, 0, 0, 5, 4, + 2, 6, 7, 7, 3, 2, + 4, 5, 7, 7, 6, 4, + 0, 3, 7, 7, 5, 0, + 1, 4, 2, 2, 4, 6, + }; + glGenVertexArrays(1, &s->vao); + glBindVertexArray(s->vao); + glGenBuffers(1, &s->attr_buffer); + glBindBuffer(GL_ARRAY_BUFFER, s->attr_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + glEnableVertexAttribArray(s->shader.vertex); + glVertexAttribPointer(s->shader.vertex, 3, GL_FLOAT, GL_FALSE, 0, 0); + glGenBuffers(1, &s->index_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, s->index_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); + glBindVertexArray(0); + s->nr_indices = sizeof(indices) / sizeof(GLushort); + + // Set up a cubemap texture. + glGenTextures(1, &s->texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->texture); + const GLenum targets[6] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, // north + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, // west + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, // south + GL_TEXTURE_CUBE_MAP_POSITIVE_X, // east + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, // bottom + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, // top + }; + for (int i = 0; i < 6; i++) { + struct cg *cg = dtx_create_cg(dtx, DTX_SKYBOX, i); + if (!cg) { + WARNING("Cannot load skybox texture %d", i); + continue; + } + glTexImage2D(targets[i], 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels); + cg_free(cg); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + return s; +} + +void skybox_free(struct skybox *s) +{ + glDeleteVertexArrays(1, &s->vao); + glDeleteBuffers(1, &s->attr_buffer); + glDeleteBuffers(1, &s->index_buffer); + glDeleteTextures(1, &s->texture); + glDeleteProgram(s->shader.program); + free(s); +} + +void skybox_render(struct skybox *s, const GLfloat *view_transform, const GLfloat *proj_transform) +{ + glDepthFunc(GL_LEQUAL); + glUseProgram(s->shader.program); + + glUniformMatrix4fv(s->shader.view_transform, 1, GL_FALSE, view_transform); + glUniformMatrix4fv(s->proj_transform, 1, GL_FALSE, proj_transform); + + glBindVertexArray(s->vao); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, s->texture); + glUniform1i(s->shader.texture, 0); + + glDrawElements(GL_TRIANGLES, s->nr_indices, GL_UNSIGNED_SHORT, NULL); + + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glDepthFunc(GL_LESS); +} diff --git a/src/dungeon/tes.c b/src/dungeon/tes.c new file mode 100644 index 0000000..95c6401 --- /dev/null +++ b/src/dungeon/tes.c @@ -0,0 +1,46 @@ +/* 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 "system4.h" +#include "system4/buffer.h" + +#include "dungeon/tes.h" + +struct tes *tes_parse(uint8_t *data, size_t size) +{ + struct buffer r; + buffer_init(&r, data, size); + if (memcmp(buffer_strdata(&r), "TES\0\0\0\0\0", 8)) + return NULL; + buffer_skip(&r, 8); + + struct tes *tes = xcalloc(1, sizeof(struct tes)); + tes->nr_columns = buffer_read_int32(&r); + tes->nr_rows = buffer_read_int32(&r); + int nr_items = tes->nr_rows * tes->nr_columns; + tes->entries = xcalloc(nr_items, sizeof(int)); + for (int i = 0; i < nr_items; i++) + tes->entries[i] = buffer_read_int32(&r); + return tes; +} + +void tes_free(struct tes *tes) +{ + free(tes->entries); + free(tes); +} diff --git a/src/ffi.c b/src/ffi.c index 382badc..fc6ddd6 100644 --- a/src/ffi.c +++ b/src/ffi.c @@ -150,6 +150,7 @@ extern struct static_library lib_Confirm; extern struct static_library lib_Confirm2; extern struct static_library lib_CrayfishLogViewer; extern struct static_library lib_DataFile; +extern struct static_library lib_DrawDungeon; extern struct static_library lib_DrawGraph; extern struct static_library lib_DrawMovie2; extern struct static_library lib_DrawPluginManager; @@ -190,6 +191,7 @@ static struct static_library *static_libraries[] = { &lib_Confirm2, &lib_CrayfishLogViewer, &lib_DataFile, + &lib_DrawDungeon, &lib_DrawGraph, &lib_DrawMovie2, &lib_DrawPluginManager, diff --git a/src/hll/DrawDungeon.c b/src/hll/DrawDungeon.c new file mode 100644 index 0000000..44d9bec --- /dev/null +++ b/src/hll/DrawDungeon.c @@ -0,0 +1,377 @@ +/* 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/dgn.h" +#include "dungeon/dungeon.h" +#include "dungeon/tes.h" +#include "hll.h" + +static struct dungeon_context *current_context = NULL; + +struct dungeon_context *dungeon_get_context(int surface) +{ + if (!current_context || surface != current_context->surface) + return NULL; + return current_context; +} + +void dungeon_update(void) +{ + if (current_context && current_context->loaded && current_context->draw_enabled) + dungeon_render(current_context); +} + +static struct dgn_cell *dungeon_get_cell(int surface, int x, int y, int z) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->dgn || !dgn_is_in_map(ctx->dgn, x, y, z)) + return NULL; + return dgn_cell_at(ctx->dgn, x, y, z); +} + +static int DrawDungeon_Init(int surface) +{ + if (current_context) + VM_ERROR("Dungeon is already associated with surface %d", current_context->surface); + if (surface < 0) + return 0; + current_context = dungeon_context_create(surface); + return 1; +} + +static void DrawDungeon_Release(int surface) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx) + return; + dungeon_context_free(ctx); + current_context = NULL; +} + +static void DrawDungeon_SetDrawFlag(int surface, int flag) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx) + return; + ctx->draw_enabled = flag; +} + +static bool DrawDungeon_BeginLoad(int surface, int num) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx) + return false; + return dungeon_load(ctx, num); +} + +static bool DrawDungeon_IsLoading(int surface) +{ + return false; // We don't use background loading. +} + +static bool DrawDungeon_IsSucceededLoading(int surface) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx) + return false; + return ctx->loaded; +} + +//float DrawDungeon_GetLoadingPercent(int surface); +//int DrawDungeon_LoadFromFile(int surface, struct string *file_name, int num); +//int DrawDungeon_LoadTexture(int surface, struct string *file_name); +//int DrawDungeon_LoadEventTexture(int surface, struct string *file_name); + +static int DrawDungeon_GetMapX(int surface) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->dgn) + return -1; + return ctx->dgn->size_x; +} + +static int DrawDungeon_GetMapY(int surface) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->dgn) + return -1; + return ctx->dgn->size_y; +} + +static int DrawDungeon_GetMapZ(int surface) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->dgn) + return -1; + return ctx->dgn->size_z; +} + +static int DrawDungeon_IsInMap(int surface, int x, int y, int z) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->dgn) + return -1; + return dgn_is_in_map(ctx->dgn, x, y, z); +} + +#define CELL_GETTER(name, defval, expr) \ + static int name(int surface, int x, int y, int z) \ + { \ + struct dgn_cell *cell = dungeon_get_cell(surface, x, y, z); \ + if (!cell) \ + return defval; \ + return expr; \ + } + +CELL_GETTER(DrawDungeon_IsFloor, 0, cell->floor != -1); +//bool DrawDungeon_IsFloorFillTexture(int surface, int x, int y, int z); +CELL_GETTER(DrawDungeon_IsStair, 0, cell->stairs_orientation != -1); +CELL_GETTER(DrawDungeon_IsStairN, 0, cell->stairs_orientation == 0); +CELL_GETTER(DrawDungeon_IsStairW, 0, cell->stairs_orientation == 1); +CELL_GETTER(DrawDungeon_IsStairS, 0, cell->stairs_orientation == 2); +CELL_GETTER(DrawDungeon_IsStairE, 0, cell->stairs_orientation == 3); +CELL_GETTER(DrawDungeon_IsWallN, 0, cell->north_wall != -1); +CELL_GETTER(DrawDungeon_IsWallW, 0, cell->west_wall != -1); +CELL_GETTER(DrawDungeon_IsWallS, 0, cell->south_wall != -1); +CELL_GETTER(DrawDungeon_IsWallE, 0, cell->east_wall != -1); +CELL_GETTER(DrawDungeon_IsDoorN, 0, cell->north_door != -1); +CELL_GETTER(DrawDungeon_IsDoorW, 0, cell->west_door != -1); +CELL_GETTER(DrawDungeon_IsDoorS, 0, cell->south_door != -1); +CELL_GETTER(DrawDungeon_IsDoorE, 0, cell->east_door != -1); +CELL_GETTER(DrawDungeon_IsEnter, 0, cell->enterable); +CELL_GETTER(DrawDungeon_IsEnterN, 0, cell->enterable_north); +CELL_GETTER(DrawDungeon_IsEnterW, 0, cell->enterable_west); +CELL_GETTER(DrawDungeon_IsEnterS, 0, cell->enterable_south); +CELL_GETTER(DrawDungeon_IsEnterE, 0, cell->enterable_east); +CELL_GETTER(DrawDungeon_GetEvFloor, 0, cell->floor_event); +CELL_GETTER(DrawDungeon_GetEvWallN, 0, cell->north_event); +CELL_GETTER(DrawDungeon_GetEvWallW, 0, cell->west_event); +CELL_GETTER(DrawDungeon_GetEvWallS, 0, cell->south_event); +CELL_GETTER(DrawDungeon_GetEvWallE, 0, cell->east_event); +CELL_GETTER(DrawDungeon_GetEvFloor2, 0, cell->floor_event2); +CELL_GETTER(DrawDungeon_GetEvWallN2, 0, cell->north_event2); +CELL_GETTER(DrawDungeon_GetEvWallW2, 0, cell->west_event2); +CELL_GETTER(DrawDungeon_GetEvWallS2, 0, cell->south_event2); +CELL_GETTER(DrawDungeon_GetEvWallE2, 0, cell->east_event2); +CELL_GETTER(DrawDungeon_GetTexFloor, -1, cell->floor); +CELL_GETTER(DrawDungeon_GetTexCeiling, -1, cell->ceiling); +CELL_GETTER(DrawDungeon_GetTexWallN, -1, cell->north_wall); +CELL_GETTER(DrawDungeon_GetTexWallW, -1, cell->west_wall); +CELL_GETTER(DrawDungeon_GetTexWallS, -1, cell->south_wall); +CELL_GETTER(DrawDungeon_GetTexWallE, -1, cell->east_wall); +CELL_GETTER(DrawDungeon_GetTexDoorN, -1, cell->north_door); +CELL_GETTER(DrawDungeon_GetTexDoorW, -1, cell->west_door); +CELL_GETTER(DrawDungeon_GetTexDoorS, -1, cell->south_door); +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) \ + static void name(int surface, int x, int y, int z, int value) \ + { \ + struct dgn_cell *cell = dungeon_get_cell(surface, x, y, z); \ + if (cell) \ + expr = value; \ + } + +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); +//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); +//void DrawDungeon_SetSkyTextureSet(int surface, int set); + +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, DrawMap, int surface, int sprite); +//void DrawDungeon_SetMapAllViewFlag(int surfacce, int flag); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDrawMapFloor, int surface, int floor); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, DrawLMap, int surface, int sprite); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetMapCG, int surface, int index, int sprite); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDrawLMapFloor, int surface, int floor); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDrawMapRadar, int surface, int flag); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetDrawHalfFlag, int surface, int flag); +HLL_WARN_UNIMPLEMENTED(0, int, DrawDungeon, GetDrawHalfFlag, int surface); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetWalked, int surface, int x, int y, int z, int flag); +//void DrawDungeon_SetWalkedAll(int surface); +//int DrawDungeon_CalcNumofFloor(int surface); +//int DrawDungeon_CalcNumofWalk(int surface); +//int DrawDungeon_CalcNumofWalk2(int surface); +HLL_WARN_UNIMPLEMENTED(0, int, DrawDungeon, CalcConquer, int surface); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetInterlaceMode, int surface, int flag); +HLL_WARN_UNIMPLEMENTED(1, bool, DrawDungeon, SetDirect3DMode, int surface, int flag); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SaveDrawSettingFlag, int direct3D, int interlace, int half); +//void DrawDungeon_GetDrawSettingFlag(int *direct3D, int *half); +HLL_WARN_UNIMPLEMENTED(1, bool, DrawDungeon, IsDrawSettingFile, void); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, DeleteDrawSettingFile, void); +HLL_WARN_UNIMPLEMENTED(1, bool, DrawDungeon, IsSSEFlag, void); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, SetSSEFlag, bool flag); +bool DrawDungeon_IsPVSData(int surface) +{ + return false; +} + +static int DrawDungeon_GetTexSound(int surface, int type, int num) +{ + struct dungeon_context *ctx = dungeon_get_context(surface); + if (!ctx || !ctx->tes) + return -1; + return tes_get(ctx->tes, type, num); +} + +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, StopTimer, void); +HLL_WARN_UNIMPLEMENTED( , void, DrawDungeon, RestartTimer, void); +HLL_WARN_UNIMPLEMENTED(0, bool, DrawDungeon, LoadWalkData, int surface, int map, struct page **anWalkData); +HLL_WARN_UNIMPLEMENTED(1, bool, DrawDungeon, SaveWalkData, int surface, int map, struct page **anWalkData); + +HLL_LIBRARY(DrawDungeon, + HLL_EXPORT(Init, DrawDungeon_Init), + HLL_EXPORT(Release, DrawDungeon_Release), + HLL_EXPORT(SetDrawFlag, DrawDungeon_SetDrawFlag), + HLL_EXPORT(BeginLoad, DrawDungeon_BeginLoad), + HLL_EXPORT(IsLoading, DrawDungeon_IsLoading), + HLL_EXPORT(IsSucceededLoading, DrawDungeon_IsSucceededLoading), + HLL_TODO_EXPORT(GetLoadingPercent, DrawDungeon_GetLoadingPercent), + HLL_TODO_EXPORT(LoadFromFile, DrawDungeon_LoadFromFile), + HLL_TODO_EXPORT(LoadTexture, DrawDungeon_LoadTexture), + HLL_TODO_EXPORT(LoadEventTexture, DrawDungeon_LoadEventTexture), + HLL_EXPORT(GetMapX, DrawDungeon_GetMapX), + HLL_EXPORT(GetMapY, DrawDungeon_GetMapY), + HLL_EXPORT(GetMapZ, DrawDungeon_GetMapZ), + HLL_EXPORT(SetCamera, dungeon_set_camera), + HLL_EXPORT(IsInMap, DrawDungeon_IsInMap), + HLL_EXPORT(IsFloor, DrawDungeon_IsFloor), + HLL_TODO_EXPORT(IsFloorFillTexture, DrawDungeon_IsFloorFillTexture), + HLL_EXPORT(IsStair, DrawDungeon_IsStair), + HLL_EXPORT(IsStairN, DrawDungeon_IsStairN), + HLL_EXPORT(IsStairW, DrawDungeon_IsStairW), + HLL_EXPORT(IsStairS, DrawDungeon_IsStairS), + HLL_EXPORT(IsStairE, DrawDungeon_IsStairE), + HLL_EXPORT(IsWallN, DrawDungeon_IsWallN), + HLL_EXPORT(IsWallW, DrawDungeon_IsWallW), + HLL_EXPORT(IsWallS, DrawDungeon_IsWallS), + HLL_EXPORT(IsWallE, DrawDungeon_IsWallE), + HLL_EXPORT(IsDoorN, DrawDungeon_IsDoorN), + HLL_EXPORT(IsDoorW, DrawDungeon_IsDoorW), + HLL_EXPORT(IsDoorS, DrawDungeon_IsDoorS), + HLL_EXPORT(IsDoorE, DrawDungeon_IsDoorE), + HLL_EXPORT(IsEnter, DrawDungeon_IsEnter), + HLL_EXPORT(IsEnterN, DrawDungeon_IsEnterN), + HLL_EXPORT(IsEnterW, DrawDungeon_IsEnterW), + HLL_EXPORT(IsEnterS, DrawDungeon_IsEnterS), + HLL_EXPORT(IsEnterE, DrawDungeon_IsEnterE), + HLL_EXPORT(GetEvFloor, DrawDungeon_GetEvFloor), + HLL_EXPORT(GetEvWallN, DrawDungeon_GetEvWallN), + HLL_EXPORT(GetEvWallW, DrawDungeon_GetEvWallW), + HLL_EXPORT(GetEvWallS, DrawDungeon_GetEvWallS), + HLL_EXPORT(GetEvWallE, DrawDungeon_GetEvWallE), + HLL_EXPORT(GetEvFloor2, DrawDungeon_GetEvFloor2), + HLL_EXPORT(GetEvWallN2, DrawDungeon_GetEvWallN2), + HLL_EXPORT(GetEvWallW2, DrawDungeon_GetEvWallW2), + HLL_EXPORT(GetEvWallS2, DrawDungeon_GetEvWallS2), + HLL_EXPORT(GetEvWallE2, DrawDungeon_GetEvWallE2), + HLL_EXPORT(GetTexFloor, DrawDungeon_GetTexFloor), + HLL_EXPORT(GetTexCeiling, DrawDungeon_GetTexCeiling), + HLL_EXPORT(GetTexWallN, DrawDungeon_GetTexWallN), + HLL_EXPORT(GetTexWallW, DrawDungeon_GetTexWallW), + HLL_EXPORT(GetTexWallS, DrawDungeon_GetTexWallS), + HLL_EXPORT(GetTexWallE, DrawDungeon_GetTexWallE), + HLL_EXPORT(GetTexDoorN, DrawDungeon_GetTexDoorN), + HLL_EXPORT(GetTexDoorW, DrawDungeon_GetTexDoorW), + HLL_EXPORT(GetTexDoorS, DrawDungeon_GetTexDoorS), + HLL_EXPORT(GetTexDoorE, DrawDungeon_GetTexDoorE), + HLL_EXPORT(GetTexStair, DrawDungeon_GetTexStair), + HLL_EXPORT(GetBattleBack, DrawDungeon_GetBattleBack), + HLL_EXPORT(SetDoorNAngle, DrawDungeon_SetDoorNAngle), + HLL_EXPORT(SetDoorWAngle, DrawDungeon_SetDoorWAngle), + HLL_EXPORT(SetDoorSAngle, DrawDungeon_SetDoorSAngle), + HLL_EXPORT(SetDoorEAngle, DrawDungeon_SetDoorEAngle), + HLL_EXPORT(SetEvFloor, dungeon_set_event_floor), + HLL_EXPORT(SetEvWallN, DrawDungeon_SetEvWallN), + HLL_EXPORT(SetEvWallW, DrawDungeon_SetEvWallW), + HLL_EXPORT(SetEvWallS, DrawDungeon_SetEvWallS), + HLL_EXPORT(SetEvWallE, DrawDungeon_SetEvWallE), + HLL_EXPORT(SetEvFloor2, DrawDungeon_SetEvFloor2), + HLL_EXPORT(SetEvWallN2, DrawDungeon_SetEvWallN2), + HLL_EXPORT(SetEvWallW2, DrawDungeon_SetEvWallW2), + 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(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_TODO_EXPORT(SetSkyTextureSet, DrawDungeon_SetSkyTextureSet), + HLL_EXPORT(DrawMap, DrawDungeon_DrawMap), + HLL_TODO_EXPORT(SetMapAllViewFlag, DrawDungeon_SetMapAllViewFlag), + HLL_EXPORT(SetDrawMapFloor, DrawDungeon_SetDrawMapFloor), + HLL_EXPORT(DrawLMap, DrawDungeon_DrawLMap), + HLL_EXPORT(SetMapCG, DrawDungeon_SetMapCG), + HLL_EXPORT(SetDrawLMapFloor, DrawDungeon_SetDrawLMapFloor), + HLL_EXPORT(SetDrawMapRadar, DrawDungeon_SetDrawMapRadar), + HLL_EXPORT(SetDrawHalfFlag, DrawDungeon_SetDrawHalfFlag), + HLL_EXPORT(GetDrawHalfFlag, DrawDungeon_GetDrawHalfFlag), + HLL_EXPORT(SetWalked, DrawDungeon_SetWalked), + HLL_TODO_EXPORT(SetWalkedAll, DrawDungeon_SetWalkedAll), + HLL_TODO_EXPORT(CalcNumofFloor, DrawDungeon_CalcNumofFloor), + HLL_TODO_EXPORT(CalcNumofWalk, DrawDungeon_CalcNumofWalk), + HLL_TODO_EXPORT(CalcNumofWalk2, DrawDungeon_CalcNumofWalk2), + HLL_EXPORT(CalcConquer, DrawDungeon_CalcConquer), + HLL_EXPORT(SetInterlaceMode, DrawDungeon_SetInterlaceMode), + HLL_EXPORT(SetDirect3DMode, DrawDungeon_SetDirect3DMode), + HLL_EXPORT(SaveDrawSettingFlag, DrawDungeon_SaveDrawSettingFlag), + HLL_TODO_EXPORT(GetDrawSettingFlag, DrawDungeon_GetDrawSettingFlag), + HLL_EXPORT(IsDrawSettingFile, DrawDungeon_IsDrawSettingFile), + HLL_EXPORT(DeleteDrawSettingFile, DrawDungeon_DeleteDrawSettingFile), + HLL_EXPORT(IsSSEFlag, DrawDungeon_IsSSEFlag), + HLL_EXPORT(SetSSEFlag, DrawDungeon_SetSSEFlag), + HLL_EXPORT(IsPVSData, DrawDungeon_IsPVSData), + HLL_EXPORT(GetTexSound, DrawDungeon_GetTexSound), + HLL_EXPORT(StopTimer, DrawDungeon_StopTimer), + HLL_EXPORT(RestartTimer, DrawDungeon_RestartTimer), + HLL_EXPORT(LoadWalkData, DrawDungeon_LoadWalkData), + HLL_EXPORT(SaveWalkData, DrawDungeon_SaveWalkData) + ); diff --git a/src/hll/SACT2.c b/src/hll/SACT2.c index b46b035..2789d8e 100644 --- a/src/hll/SACT2.c +++ b/src/hll/SACT2.c @@ -30,6 +30,7 @@ #include "gfx/gfx.h" #include "sact.h" #include "vm/page.h" +#include "dungeon/dungeon.h" #include "xsystem4.h" static struct sact_sprite **sprites = NULL; @@ -63,7 +64,7 @@ static void realloc_sprite_table(int n) memset(sprites + old_nr_sprites, 0, sizeof(struct sact_sprite*) * (nr_sprites - old_nr_sprites)); } -// NOTE: Used externally by DrawGraph and SengokuRanceFont +// NOTE: Used externally by DrawGraph, SengokuRanceFont and dungeon.c struct sact_sprite *sact_get_sprite(int sp) { if (sp < -1) @@ -126,6 +127,7 @@ int sact_GetMainSurfaceNumber(void) int sact_Update(void) { handle_events(); + dungeon_update(); if (sact_dirty) { sprite_render_scene(); sprite_flip(); diff --git a/src/meson.build b/src/meson.build index a26a364..ecfcc59 100644 --- a/src/meson.build +++ b/src/meson.build @@ -17,6 +17,14 @@ xsystem4 = ['audio.c', 'video.c', 'vm.c', + 'dungeon/dgn.c', + 'dungeon/dtx.c', + 'dungeon/dungeon.c', + 'dungeon/event_markers.c', + 'dungeon/mesh.c', + 'dungeon/skybox.c', + 'dungeon/tes.c', + 'hll/ACXLoader.c', 'hll/ADVSYS.c', 'hll/AliceLogo.c', @@ -31,6 +39,7 @@ xsystem4 = ['audio.c', 'hll/Confirm2.c', 'hll/CrayfishLogViewer.c', 'hll/DataFile.c', + 'hll/DrawDungeon.c', 'hll/DrawGraph.c', 'hll/DrawMovie2.c', 'hll/DrawPluginManager.c', @@ -58,7 +67,7 @@ xsystem4 = ['audio.c', 'hll/VSFile.c', ] -xsystem4_deps = [libm, zlib, sdl2, sdl2ttf, sdl2mixer, ffi, gl, glew, libsys4_dep] +xsystem4_deps = [libm, zlib, sdl2, sdl2ttf, sdl2mixer, ffi, gl, glew, cglm, libsys4_dep] if chibi.found() xsystem4_deps += chibi xsystem4 += 'debugger.c' diff --git a/src/video.c b/src/video.c index 23842a2..a24ba92 100644 --- a/src/video.c +++ b/src/video.c @@ -155,6 +155,7 @@ static int gl_initialize(void) glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glBindVertexArray(0); return 0; } @@ -234,6 +235,7 @@ static void main_surface_init(int w, int h) main_surface.w = w; main_surface.h = h; main_surface.has_alpha = true; + main_surface.flip_y = false; main_surface.alpha_mod = 255; main_surface.draw_method = DRAW_METHOD_NORMAL; @@ -326,6 +328,7 @@ void gfx_prepare_job(struct gfx_render_job *job) */ void gfx_run_job(struct gfx_render_job *job) { + glBindVertexArray(sdl.gl.vao); glEnableVertexAttribArray(job->shader->vertex); glBindBuffer(GL_ARRAY_BUFFER, sdl.gl.vbo); @@ -335,6 +338,7 @@ void gfx_run_job(struct gfx_render_job *job) glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL); glDisableVertexAttribArray(job->shader->vertex); + glBindVertexArray(0); glUseProgram(0); } @@ -381,6 +385,10 @@ void gfx_render_texture(struct texture *t, Rectangle *r) t->world_transform[12] = 0; t->world_transform[13] = 0; } + if (t->flip_y) { + t->world_transform[13] += t->world_transform[5]; + t->world_transform[5] *= -1; + } struct gfx_render_job job = { .shader = &default_shader.s, @@ -391,6 +399,10 @@ void gfx_render_texture(struct texture *t, Rectangle *r) }; gfx_render(&job); + if (t->flip_y) { + t->world_transform[5] *= -1; + t->world_transform[13] -= t->world_transform[5]; + } if (t->draw_method != DRAW_METHOD_NORMAL || t->alpha_mod != 255) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); } @@ -415,6 +427,7 @@ static void init_texture(struct texture *t, int w, int h) t->h = h; t->has_alpha = true; + t->flip_y = false; t->alpha_mod = 255; t->draw_method = DRAW_METHOD_NORMAL; diff --git a/subprojects/libsys4 b/subprojects/libsys4 index d536a2e..44a027b 160000 --- a/subprojects/libsys4 +++ b/subprojects/libsys4 @@ -1 +1 @@ -Subproject commit d536a2eeedafe1b5075099c4af3a051ae009564d +Subproject commit 44a027b72a34dc2cfa8ec27f108750ab8119114c From 89e37c2428636b49467b06f2dd7857257b77c706 Mon Sep 17 00:00:00 2001 From: kichikuou Date: Sat, 8 May 2021 15:52:02 +0900 Subject: [PATCH 2/2] Add dependency wrap file for cglm Since libcglm-dev is not available in Debian buster (added in bullseye). --- README.md | 2 +- meson.build | 2 +- subprojects/cglm.wrap | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 subprojects/cglm.wrap diff --git a/README.md b/README.md index ede9a8e..d6371f1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ First install the dependencies (corresponding Debian package in parentheses): * chibi-scheme [optional, for debugger] * bison (bison) -* cglm (libcglm-dev) +* cglm (libcglm-dev) [optional, fetched by meson if not available] * flex (flex) * meson (meson) * libffi (libffi-dev) diff --git a/meson.build b/meson.build index c6512dc..d97d355 100644 --- a/meson.build +++ b/meson.build @@ -15,7 +15,7 @@ tj = dependency('libturbojpeg') webp = dependency('libwebp') png = dependency('libpng') chibi = dependency('chibi-scheme', required : get_option('debugger')) -cglm = dependency('cglm') +cglm = dependency('cglm', fallback : ['cglm', 'cglm_dep']) flex = find_program('flex') bison = find_program('bison') diff --git a/subprojects/cglm.wrap b/subprojects/cglm.wrap new file mode 100644 index 0000000..0d9dcd8 --- /dev/null +++ b/subprojects/cglm.wrap @@ -0,0 +1,3 @@ +[wrap-git] +url = https://github.com/recp/cglm.git +revision = v0.8.2