DrawDungeon14: Draw 3D objects in PolyObj.lin

This commit is contained in:
kichikuou
2021-06-19 22:56:06 +09:00
parent 5adf8f090e
commit 057862b533
6 changed files with 345 additions and 17 deletions
+65
View File
@@ -0,0 +1,65 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#ifndef SYSTEM4_POLYOBJ_H
#define SYSTEM4_POLYOBJ_H
#include <stddef.h>
#include <stdint.h>
#include <cglm/types.h>
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 */
+2 -1
View File
@@ -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);
+15 -1
View File
@@ -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);
+133
View File
@@ -0,0 +1,133 @@
/* Copyright (C) 2021 kichikuou <KichikuouChrome@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#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);
}
+129 -15
View File
@@ -20,11 +20,13 @@
#include <cglm/cglm.h>
#include <GL/glew.h>
#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) {
+1
View File
@@ -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',