diff --git a/include/dungeon/polyobj.h b/include/dungeon/polyobj.h new file mode 100644 index 0000000..015ec5e --- /dev/null +++ b/include/dungeon/polyobj.h @@ -0,0 +1,65 @@ +/* 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_POLYOBJ_H +#define SYSTEM4_POLYOBJ_H + +#include +#include +#include + +struct polyobj_location { + uint32_t offset; + uint32_t length; +}; + +struct polyobj { + uint8_t *data; + size_t size; + int nr_textures; + struct polyobj_location *textures; + int nr_objects; + struct polyobj_location *objects; +}; + +struct polyobj_triangle { + int material; + int index[3]; + vec2 uv[3]; +}; + +struct polyobj_part { + int nr_vertices; + vec3 *vertices; + int nr_triangles; + struct polyobj_triangle *triangles; +}; + +struct polyobj_object { + char *name; + int nr_materials; + int *materials; + int nr_parts; + struct polyobj_part *parts; +}; + +struct polyobj *polyobj_load(const char *path); +void polyobj_free(struct polyobj *po); +struct cg *polyobj_get_texture(struct polyobj *po, int index); +struct polyobj_object *polyobj_get_object(struct polyobj *po, int index); +void polyobj_object_free(struct polyobj_object *obj); + +#endif /* SYSTEM4_POLYOBJ_H */ diff --git a/include/dungeon/renderer.h b/include/dungeon/renderer.h index 2e4c993..c2ab08b 100644 --- a/include/dungeon/renderer.h +++ b/include/dungeon/renderer.h @@ -22,9 +22,10 @@ struct dgn_cell; struct dtx; +struct polyobj; struct dungeon_renderer; -struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures); +struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po); void dungeon_renderer_free(struct dungeon_renderer *r); void dungeon_renderer_render(struct dungeon_renderer *r, struct dgn_cell **cells, int nr_cells, mat4 view_transform, mat4 proj_transform); void dungeon_renderer_enable_event_markers(struct dungeon_renderer *r, bool enable); diff --git a/src/dungeon/dungeon.c b/src/dungeon/dungeon.c index e8263b8..3dcf193 100644 --- a/src/dungeon/dungeon.c +++ b/src/dungeon/dungeon.c @@ -31,6 +31,7 @@ #include "dungeon/dtx.h" #include "dungeon/dungeon.h" #include "dungeon/map.h" +#include "dungeon/polyobj.h" #include "dungeon/renderer.h" #include "dungeon/tes.h" #include "gfx/gfx.h" @@ -127,6 +128,8 @@ static GLuint *load_event_textures(int *nr_textures_out) bool dungeon_load(struct dungeon_context *ctx, int num) { + struct polyobj *polyobj = NULL; + if (ctx->loaded) VM_ERROR("Dungeon is already loaded"); @@ -185,6 +188,14 @@ bool dungeon_load(struct dungeon_context *ctx, int num) free(tes); } free(path); + + char *polyobj_path = gamedir_path("Data/PolyObj.lin"); + polyobj = polyobj_load(polyobj_path); + free(polyobj_path); + if (!polyobj) { + WARNING("Cannot load Data/PolyObj.lin"); + return false; + } } else { ERROR("Invalid DrawDungeon version %d", ctx->version); } @@ -199,7 +210,10 @@ bool dungeon_load(struct dungeon_context *ctx, int num) if (!event_textures) return false; - ctx->renderer = dungeon_renderer_create(ctx->version, ctx->dtx, event_textures, nr_event_textures); + ctx->renderer = dungeon_renderer_create(ctx->version, ctx->dtx, event_textures, nr_event_textures, polyobj); + + if (polyobj) + polyobj_free(polyobj); dungeon_map_init(ctx); diff --git a/src/dungeon/polyobj.c b/src/dungeon/polyobj.c new file mode 100644 index 0000000..be978ac --- /dev/null +++ b/src/dungeon/polyobj.c @@ -0,0 +1,133 @@ +/* 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 "system4/file.h" + +#include "dungeon/polyobj.h" + +struct polyobj *polyobj_load(const char *path) +{ + size_t size; + uint8_t *data = file_read(path, &size); + if (!data) + return NULL; + struct buffer r; + buffer_init(&r, data, size); + if (memcmp(buffer_strdata(&r), "POL\0\0\0\0\0", 8)) + return NULL; + buffer_skip(&r, 8); + + struct polyobj *po = xcalloc(1, sizeof(struct polyobj)); + po->data = data; + po->size = size; + po->nr_textures = buffer_read_int32(&r); + po->textures = xcalloc(po->nr_textures, sizeof(struct polyobj_location)); + for (int i = 0; i < po->nr_textures; i++) { + po->textures[i].offset = buffer_read_int32(&r); + po->textures[i].length = buffer_read_int32(&r); + } + po->nr_objects = buffer_read_int32(&r); + po->objects = xcalloc(po->nr_objects, sizeof(struct polyobj_location)); + for (int i = 0; i < po->nr_objects; i++) { + po->objects[i].offset = buffer_read_int32(&r); + po->objects[i].length = buffer_read_int32(&r); + } + return po; +} + +void polyobj_free(struct polyobj *po) +{ + free(po->textures); + free(po->objects); + free(po->data); + free(po); +} + +struct cg *polyobj_get_texture(struct polyobj *po, int index) +{ + if (index < 0 || index >= po->nr_textures) + return NULL; + struct polyobj_location *loc = &po->textures[index]; + return cg_load_buffer(po->data + loc->offset, loc->length); +} + +struct polyobj_object *polyobj_get_object(struct polyobj *po, int index) +{ + if (index < 0 || index >= po->nr_objects) + return NULL; + struct polyobj_location *loc = &po->objects[index]; + struct buffer r; + buffer_init(&r, po->data + loc->offset, loc->length); + + struct polyobj_object *obj = xcalloc(1, sizeof(struct polyobj_object)); + obj->name = strdup(buffer_strdata(&r)); + buffer_skip(&r, strlen(obj->name) + 1); + if (memcmp(buffer_strdata(&r), "POO\0\0\0\0\0", 8)) { + polyobj_object_free(obj); + return NULL; + } + buffer_skip(&r, 8); + obj->nr_materials = buffer_read_int32(&r); + obj->materials = xcalloc(obj->nr_materials, sizeof(int)); + for (int i = 0; i < obj->nr_materials; i++) + obj->materials[i] = buffer_read_int32(&r); + obj->nr_parts = buffer_read_int32(&r); + obj->parts = xcalloc(obj->nr_parts, sizeof(struct polyobj_part)); + for (int i = 0; i < obj->nr_parts; i++) { + struct polyobj_part *part = &obj->parts[i]; + part->nr_vertices = buffer_read_int32(&r); + part->vertices = xcalloc(part->nr_vertices, sizeof(vec3)); + for (int j = 0; j < part->nr_vertices; j++) { + part->vertices[j][0] = buffer_read_float(&r); + part->vertices[j][1] = buffer_read_float(&r); + part->vertices[j][2] = buffer_read_float(&r); + } + + part->nr_triangles = buffer_read_int32(&r); + part->triangles = xcalloc(part->nr_triangles, sizeof(struct polyobj_triangle)); + for (int j = 0; j < part->nr_triangles; j++) { + struct polyobj_triangle *triangle = &part->triangles[j]; + triangle->material = buffer_read_int32(&r); + triangle->index[0] = buffer_read_int32(&r); + triangle->index[1] = buffer_read_int32(&r); + triangle->index[2] = buffer_read_int32(&r); + triangle->uv[0][0] = buffer_read_float(&r); + triangle->uv[0][1] = buffer_read_float(&r); + triangle->uv[1][0] = buffer_read_float(&r); + triangle->uv[1][1] = buffer_read_float(&r); + triangle->uv[2][0] = buffer_read_float(&r); + triangle->uv[2][1] = buffer_read_float(&r); + } + } + return obj; +} + +void polyobj_object_free(struct polyobj_object *obj) +{ + for (int i = 0; i < obj->nr_parts; i++) { + struct polyobj_part *part = &obj->parts[i]; + free(part->vertices); + free(part->triangles); + } + free(obj->parts); + free(obj->materials); + free(obj->name); + free(obj); +} diff --git a/src/dungeon/renderer.c b/src/dungeon/renderer.c index 4f90a16..d3a2113 100644 --- a/src/dungeon/renderer.c +++ b/src/dungeon/renderer.c @@ -20,11 +20,13 @@ #include #include #include "system4.h" +#include "system4/buffer.h" #include "system4/cg.h" #include "dungeon/dungeon.h" #include "dungeon/dgn.h" #include "dungeon/dtx.h" +#include "dungeon/polyobj.h" #include "dungeon/renderer.h" #include "dungeon/skybox.h" #include "gfx/gfx.h" @@ -73,6 +75,7 @@ static const struct marker_info *marker_tables[] = { struct geometry; struct material; +struct object3d; struct dungeon_renderer { struct shader shader; @@ -99,6 +102,11 @@ struct dungeon_renderer { const struct marker_info *marker_table; bool draw_event_markers; + int nr_polyobjs; + struct object3d *polyobjs; + int nr_polyobj_materials; + struct material *polyobj_materials; + struct skybox *skybox; }; @@ -189,12 +197,14 @@ struct geometry { GLuint vao; GLuint attr_buffer; int nr_vertices; + GLenum mode; }; -static struct geometry *geometry_create(struct dungeon_renderer *r, const struct vertex *vertices, size_t size) +static struct geometry *geometry_create(struct dungeon_renderer *r, const struct vertex *vertices, size_t size, GLenum mode) { struct geometry *g = xcalloc(1, sizeof(struct geometry)); g->nr_vertices = size / sizeof(struct vertex); + g->mode = mode; glGenVertexArrays(1, &g->vao); glBindVertexArray(g->vao); @@ -224,11 +234,8 @@ struct material { bool opaque; }; -static void init_material(struct material *m, struct dtx *dtx, int type, int index) +static void init_material(struct material *m, struct cg *cg) { - struct cg *cg = dtx_create_cg(dtx, type, index); - if (!cg) - return; glGenTextures(1, &m->texture); glBindTexture(GL_TEXTURE_2D, m->texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels); @@ -239,7 +246,6 @@ static void init_material(struct material *m, struct dtx *dtx, int type, int ind glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); m->opaque = !cg->metrics.has_alpha; - cg_free(cg); } static void delete_material(struct material *m) @@ -256,7 +262,77 @@ static struct material *get_material(struct dungeon_renderer *r, int type, int i return m; } -struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures) +struct mesh { + struct geometry *geometry; + int material; +}; + +struct object3d { + int nr_meshes; + struct mesh *meshes; +}; + +static void init_object3d(struct object3d *o3d, struct dungeon_renderer *r, const struct polyobj_object *obj) +{ + o3d->nr_meshes = obj->nr_materials; + o3d->meshes = xcalloc(obj->nr_materials, sizeof(struct mesh)); + for (int i = 0; i < obj->nr_materials; i++) + o3d->meshes[i].material = obj->materials[i]; + + struct buffer *bufs = xcalloc(obj->nr_materials, sizeof(struct buffer)); + for (int i = 0; i < obj->nr_parts; i++) { + const struct polyobj_part *part = &obj->parts[i]; + for (int j = 0; j < part->nr_triangles; j++) { + const struct polyobj_triangle *triangle = &part->triangles[j]; + for (int k = 0; k < 3; k++) { + const int ltor[] = {0, 2, 1}; // left->right handed system + const vec3 *pos = &part->vertices[triangle->index[ltor[k]]]; + const vec2 *uv = &triangle->uv[ltor[k]]; + buffer_write_float(&bufs[triangle->material], (*pos)[0]); // x + buffer_write_float(&bufs[triangle->material], (*pos)[1]); // y + buffer_write_float(&bufs[triangle->material], -(*pos)[2]); // z + buffer_write_float(&bufs[triangle->material], (*uv)[0]); // u + buffer_write_float(&bufs[triangle->material], (*uv)[1]); // v + } + } + } + + for (int i = 0; i < obj->nr_materials; i++) { + o3d->meshes[i].geometry = geometry_create(r, (const struct vertex *)bufs[i].buf, bufs[i].index, GL_TRIANGLES); + free(bufs[i].buf); + } + free(bufs); +} + +static void delete_object3d(struct object3d *o3d) +{ + for (int i = 0; i < o3d->nr_meshes; i++) + geometry_free(o3d->meshes[i].geometry); + free(o3d->meshes); +} + +static void init_polyobjs(struct dungeon_renderer *r, struct polyobj *po) +{ + r->nr_polyobj_materials = po->nr_textures; + r->polyobj_materials = xcalloc(po->nr_textures, sizeof(struct material)); + for (int i = 0; i < po->nr_textures; i++) { + struct cg *cg = polyobj_get_texture(po, i); + init_material(&r->polyobj_materials[i], cg); + cg_free(cg); + } + + r->nr_polyobjs = po->nr_objects; + r->polyobjs = xcalloc(po->nr_objects, sizeof(struct object3d)); + for (int i = 0; i < po->nr_objects; i++) { + struct polyobj_object *obj = polyobj_get_object(po, i); + if (!obj) + continue; + init_object3d(&r->polyobjs[i], r, obj); + polyobj_object_free(obj); + } +} + +struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version version, struct dtx *dtx, GLuint *event_textures, int nr_event_textures, struct polyobj *po) { struct dungeon_renderer *r = xcalloc(1, sizeof(struct dungeon_renderer)); @@ -266,18 +342,22 @@ struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version versi r->alpha_mod = glGetUniformLocation(r->shader.program, "alpha_mod"); r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv"); - r->wall_geometry = geometry_create(r, wall_vertices, sizeof(wall_vertices)); - r->door_left_geometry = geometry_create(r, door_left_vertices, sizeof(door_left_vertices)); - r->door_right_geometry = geometry_create(r, door_right_vertices, sizeof(door_right_vertices)); - r->roof_geometry = geometry_create(r, roof_vertices, sizeof(roof_vertices)); - r->stairs_geometry = geometry_create(r, stairs_vertices, sizeof(stairs_vertices)); - r->floating_marker_geometry = geometry_create(r, floating_marker_vertices, sizeof(floating_marker_vertices)); + r->wall_geometry = geometry_create(r, wall_vertices, sizeof(wall_vertices), GL_TRIANGLE_STRIP); + r->door_left_geometry = geometry_create(r, door_left_vertices, sizeof(door_left_vertices), GL_TRIANGLE_STRIP); + r->door_right_geometry = geometry_create(r, door_right_vertices, sizeof(door_right_vertices), GL_TRIANGLE_STRIP); + r->roof_geometry = geometry_create(r, roof_vertices, sizeof(roof_vertices), GL_TRIANGLE_STRIP); + r->stairs_geometry = geometry_create(r, stairs_vertices, sizeof(stairs_vertices), GL_TRIANGLE_STRIP); + r->floating_marker_geometry = geometry_create(r, floating_marker_vertices, sizeof(floating_marker_vertices), GL_TRIANGLE_STRIP); const int nr_types = DTX_DOOR + 1; r->materials = xcalloc(nr_types * dtx->nr_columns, sizeof(struct material)); for (int type = 0; type < nr_types; type++) { for (int i = 0; i < dtx->nr_columns; i++) { - init_material(&r->materials[type * dtx->nr_columns + i], dtx, type, i); + struct cg *cg = dtx_create_cg(dtx, type, i); + if (!cg) + continue; + init_material(&r->materials[type * dtx->nr_columns + i], cg); + cg_free(cg); } } r->nr_dtx_columns = dtx->nr_columns; @@ -288,6 +368,9 @@ struct dungeon_renderer *dungeon_renderer_create(enum draw_dungeon_version versi r->marker_table = marker_tables[version]; r->draw_event_markers = true; + if (po) + init_polyobjs(r, po); + r->skybox = skybox_create(dtx); return r; @@ -311,6 +394,13 @@ void dungeon_renderer_free(struct dungeon_renderer *r) glDeleteTextures(r->nr_event_textures, r->event_textures); free(r->event_textures); + for (int i = 0; i < r->nr_polyobjs; i++) + delete_object3d(&r->polyobjs[i]); + free(r->polyobjs); + for (int i = 0; i < r->nr_polyobj_materials; i++) + delete_material(&r->polyobj_materials[i]); + free(r->polyobj_materials); + skybox_free(r->skybox); free(r); @@ -326,7 +416,7 @@ static void draw(struct dungeon_renderer *r, struct geometry *geometry, GLuint t glUniform1i(r->shader.texture, 0); glBindVertexArray(geometry->vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, geometry->nr_vertices); + glDrawArrays(geometry->mode, 0, geometry->nr_vertices); glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); } @@ -397,6 +487,22 @@ static void draw_floating_marker(struct dungeon_renderer *r, const struct marker glUniform1f(r->alpha_mod, 1.0); } +static void draw_object3d(struct dungeon_renderer *r, struct object3d *o3d, bool render_opaque, vec3 pos, float scale, float rotation_y) +{ + mat4 m; + glm_translate_make(m, pos); + glm_rotate_y(m, glm_rad(rotation_y), m); + glm_scale_uni(m, scale); + + for (int i = 0; i < o3d->nr_meshes; i++) { + struct mesh *mesh = &o3d->meshes[i]; + struct material *material = &r->polyobj_materials[mesh->material]; + if (material->opaque != render_opaque) + continue; + draw(r, mesh->geometry, material->texture, m); + } +} + static void stairs_matrix(mat4 dst, float x, float y, float z, int orientation) { vec3 pos = {x, y, z}; @@ -551,6 +657,14 @@ static void draw_cell(struct dungeon_renderer *r, struct dgn_cell *cell, bool re glCullFace(GL_BACK); } } + if (cell->polyobj_index >= 0) { + vec3 pos = { + x + cell->polyobj_position_x, + y + cell->polyobj_position_y - 1, + z - cell->polyobj_position_z + }; + draw_object3d(r, &r->polyobjs[cell->polyobj_index], render_opaque, pos, cell->polyobj_scale, cell->polyobj_rotation_y); + } if (r->draw_event_markers && cell->floor_event && !render_opaque) { const struct marker_info *info = get_marker_info(r, cell->floor_event); if (info) { diff --git a/src/meson.build b/src/meson.build index ad68edc..199cb77 100644 --- a/src/meson.build +++ b/src/meson.build @@ -23,6 +23,7 @@ xsystem4 = ['audio.c', 'dungeon/dtx.c', 'dungeon/dungeon.c', 'dungeon/map.c', + 'dungeon/polyobj.c', 'dungeon/renderer.c', 'dungeon/skybox.c', 'dungeon/tes.c',