mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-10-13 09:48:23 +03:00
Initial implementation of ReignEngine HLL
ReignEngine is the 3D rendering engine used in Toushin Toshi III, and is the predecessor of the 3D engines for Rance Quest, Rance 9, Evenicle, etc. This implements minimal functionality to be able to draw the first 3D dungeon scene. Many important parts such as motion and lighting are not implemented yet.
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
/* Copyright (C) 2022 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_3D_3D_INTERNAL_H
|
||||
#define SYSTEM4_3D_3D_INTERNAL_H
|
||||
|
||||
#include <cglm/types.h>
|
||||
|
||||
#include "system4/archive.h"
|
||||
|
||||
#include "gfx/gl.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
struct model {
|
||||
char *name;
|
||||
int nr_meshes;
|
||||
struct mesh *meshes;
|
||||
int nr_materials;
|
||||
struct material *materials;
|
||||
};
|
||||
|
||||
struct mesh {
|
||||
GLuint vao;
|
||||
GLuint attr_buffer;
|
||||
int nr_vertices;
|
||||
int material;
|
||||
};
|
||||
|
||||
struct material {
|
||||
GLuint color_map;
|
||||
bool opaque;
|
||||
};
|
||||
|
||||
struct RE_renderer {
|
||||
struct shader shader;
|
||||
GLuint depth_buffer;
|
||||
|
||||
// Uniform variable locations
|
||||
GLint local_transform;
|
||||
GLint proj_transform;
|
||||
|
||||
// Attribute variable locations
|
||||
GLint vertex_normal;
|
||||
GLint vertex_uv;
|
||||
};
|
||||
|
||||
// model.c
|
||||
|
||||
struct model *model_load(struct archive *aar, const char *path, struct RE_renderer *renderer);
|
||||
void model_free(struct model *model);
|
||||
|
||||
// renderer.c
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture);
|
||||
void RE_renderer_free(struct RE_renderer *r);
|
||||
|
||||
// parser.c
|
||||
|
||||
struct pol {
|
||||
int32_t version;
|
||||
uint32_t nr_materials;
|
||||
struct pol_material_group *materials;
|
||||
uint32_t nr_meshes;
|
||||
struct pol_mesh **meshes;
|
||||
uint32_t nr_bones;
|
||||
struct pol_bone *bones;
|
||||
};
|
||||
|
||||
enum pol_texture_type {
|
||||
COLOR_MAP = 1,
|
||||
MAX_TEXTURE_TYPE
|
||||
};
|
||||
|
||||
struct pol_material {
|
||||
char *name;
|
||||
char *textures[MAX_TEXTURE_TYPE];
|
||||
};
|
||||
|
||||
struct pol_material_group {
|
||||
struct pol_material m;
|
||||
uint32_t nr_children;
|
||||
struct pol_material *children;
|
||||
};
|
||||
|
||||
struct pol_mesh {
|
||||
char *name;
|
||||
uint32_t material;
|
||||
uint32_t nr_vertices;
|
||||
struct pol_vertex *vertices;
|
||||
uint32_t nr_uvs;
|
||||
vec2 *uvs;
|
||||
uint32_t nr_triangles;
|
||||
struct pol_triangle *triangles;
|
||||
};
|
||||
|
||||
struct pol_vertex {
|
||||
vec3 pos;
|
||||
uint32_t nr_weights;
|
||||
struct pol_bone_weight *weights;
|
||||
};
|
||||
|
||||
struct pol_bone_weight {
|
||||
uint32_t bone;
|
||||
float weight;
|
||||
};
|
||||
|
||||
struct pol_triangle {
|
||||
uint32_t vert_index[3];
|
||||
uint32_t uv_index[3];
|
||||
vec3 normals[3];
|
||||
uint32_t material; // index in the material group
|
||||
};
|
||||
|
||||
struct pol_bone {
|
||||
char *name;
|
||||
uint32_t id;
|
||||
int32_t parent;
|
||||
vec3 pos;
|
||||
versor rotq;
|
||||
};
|
||||
|
||||
struct pol *pol_parse(uint8_t *data, size_t size);
|
||||
void pol_free(struct pol *pol);
|
||||
|
||||
#endif /* SYSTEM4_3D_3D_INTERNAL_H */
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/* Copyright (C) 2022 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 <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
#include "system4/cg.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
|
||||
struct vertex {
|
||||
GLfloat pos[3];
|
||||
GLfloat normal[3];
|
||||
GLfloat uv[2];
|
||||
};
|
||||
|
||||
static bool init_material(struct material *material, const struct pol_material *m, struct archive *aar, const char *path)
|
||||
{
|
||||
if (!m->textures[COLOR_MAP]) {
|
||||
WARNING("No color texture");
|
||||
return false;
|
||||
}
|
||||
char *texture_path = xmalloc(strlen(path) + strlen(m->textures[COLOR_MAP]) + 2);
|
||||
sprintf(texture_path, "%s\\%s", path, m->textures[COLOR_MAP]);
|
||||
struct archive_data *dfile = archive_get_by_name(aar, texture_path);
|
||||
if (!dfile) {
|
||||
WARNING("cannot load texture %s", texture_path);
|
||||
free(texture_path);
|
||||
return false;
|
||||
}
|
||||
struct cg *cg = cg_load_data(dfile);
|
||||
archive_free_data(dfile);
|
||||
if (!cg) {
|
||||
WARNING("cg_load_data failed: %s", texture_path);
|
||||
free(texture_path);
|
||||
return false;
|
||||
}
|
||||
free(texture_path);
|
||||
|
||||
glGenTextures(1, &material->color_map);
|
||||
glBindTexture(GL_TEXTURE_2D, material->color_map);
|
||||
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_REPEAT);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
material->opaque = !cg->metrics.has_alpha;
|
||||
|
||||
cg_free(cg);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void destroy_material(struct material *material)
|
||||
{
|
||||
if (material->color_map)
|
||||
glDeleteTextures(1, &material->color_map);
|
||||
}
|
||||
|
||||
static void add_mesh(struct model *model, struct pol_mesh *m, int material_index, int material, struct RE_renderer *r)
|
||||
{
|
||||
struct vertex *buf = xmalloc(m->nr_triangles * 3 * sizeof(struct vertex));
|
||||
int v = 0;
|
||||
for (uint32_t i = 0; i < m->nr_triangles; i++) {
|
||||
struct pol_triangle *t = &m->triangles[i];
|
||||
if (material_index >= 0 && t->material != (uint32_t)material_index)
|
||||
continue;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
glm_vec3_copy(m->vertices[t->vert_index[j]].pos, buf[v].pos);
|
||||
glm_vec3_copy(t->normals[j], buf[v].normal);
|
||||
glm_vec2_copy(m->uvs[t->uv_index[j]], buf[v].uv);
|
||||
v++;
|
||||
}
|
||||
}
|
||||
if (v == 0) {
|
||||
free(buf);
|
||||
return;
|
||||
}
|
||||
model->meshes = xrealloc_array(model->meshes, model->nr_meshes, model->nr_meshes + 1, sizeof(struct mesh));
|
||||
struct mesh *mesh = &model->meshes[model->nr_meshes++];
|
||||
mesh->material = material;
|
||||
mesh->nr_vertices = v;
|
||||
|
||||
glGenVertexArrays(1, &mesh->vao);
|
||||
glBindVertexArray(mesh->vao);
|
||||
glGenBuffers(1, &mesh->attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, mesh->attr_buffer);
|
||||
|
||||
glEnableVertexAttribArray(r->shader.vertex);
|
||||
glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)0);
|
||||
glEnableVertexAttribArray(r->vertex_normal);
|
||||
glVertexAttribPointer(r->vertex_normal, 3, GL_FLOAT, GL_TRUE, sizeof(struct vertex), (const void *)offsetof(struct vertex, normal));
|
||||
glEnableVertexAttribArray(r->vertex_uv);
|
||||
glVertexAttribPointer(r->vertex_uv, 2, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)offsetof(struct vertex, uv));
|
||||
glBufferData(GL_ARRAY_BUFFER, mesh->nr_vertices * sizeof(struct vertex), buf, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void destroy_mesh(struct mesh *mesh)
|
||||
{
|
||||
glDeleteVertexArrays(1, &mesh->vao);
|
||||
glDeleteBuffers(1, &mesh->attr_buffer);
|
||||
}
|
||||
|
||||
struct model *model_load(struct archive *aar, const char *path, struct RE_renderer *r)
|
||||
{
|
||||
if (path[0] == '\0')
|
||||
return NULL;
|
||||
|
||||
const char *basename = strrchr(path, '\\');
|
||||
basename = basename ? basename + 1 : path;
|
||||
char *polname = xmalloc(strlen(path) + strlen(basename) + 6);
|
||||
sprintf(polname, "%s\\%s.POL", path, basename);
|
||||
|
||||
struct archive_data *dfile = archive_get_by_name(aar, polname);
|
||||
if (!dfile) {
|
||||
WARNING("%s: not found", polname);
|
||||
free(polname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pol *pol = pol_parse(dfile->data, dfile->size);
|
||||
archive_free_data(dfile);
|
||||
if (!pol) {
|
||||
WARNING("%s: parse error", polname);
|
||||
free(polname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct model *model = xcalloc(1, sizeof(struct model));
|
||||
model->name = polname;
|
||||
|
||||
// Materials
|
||||
int *material_offsets = xmalloc(pol->nr_materials * sizeof(int));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
material_offsets[i] = model->nr_materials;
|
||||
if (pol->materials[i].nr_children)
|
||||
model->nr_materials += pol->materials[i].nr_children;
|
||||
else
|
||||
model->nr_materials++;
|
||||
}
|
||||
model->materials = xcalloc(model->nr_materials, sizeof(struct material));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
if (pol->materials[i].nr_children == 0) {
|
||||
init_material(&model->materials[material_offsets[i]],
|
||||
&pol->materials[i].m, aar, path);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < pol->materials[i].nr_children; j++) {
|
||||
init_material(&model->materials[material_offsets[i] + j],
|
||||
&pol->materials[i].children[j], aar, path);
|
||||
}
|
||||
}
|
||||
|
||||
// Meshes
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
if (!pol->meshes[i])
|
||||
continue;
|
||||
struct pol_material_group *mg = &pol->materials[pol->meshes[i]->material];
|
||||
int m_off = material_offsets[pol->meshes[i]->material];
|
||||
if (mg->nr_children == 0) {
|
||||
add_mesh(model, pol->meshes[i], -1, m_off, r);
|
||||
continue;
|
||||
}
|
||||
for (uint32_t j = 0; j < mg->nr_children; j++) {
|
||||
add_mesh(model, pol->meshes[i], j, m_off + j, r);
|
||||
}
|
||||
}
|
||||
|
||||
free(material_offsets);
|
||||
pol_free(pol);
|
||||
return model;
|
||||
}
|
||||
|
||||
void model_free(struct model *model)
|
||||
{
|
||||
for (int i = 0; i < model->nr_meshes; i++)
|
||||
destroy_mesh(&model->meshes[i]);
|
||||
free(model->meshes);
|
||||
|
||||
for (int i = 0; i < model->nr_materials; i++)
|
||||
destroy_material(&model->materials[i]);
|
||||
free(model->materials);
|
||||
|
||||
free(model->name);
|
||||
free(model);
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/* Copyright (C) 2022 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 <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/buffer.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
|
||||
#define METERS_PER_INCH 0.0254
|
||||
|
||||
static char *read_cstring(struct buffer *r)
|
||||
{
|
||||
char *s = strdup(buffer_strdata(r));
|
||||
buffer_skip(r, strlen(s) + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
static void read_position(struct buffer *r, vec3 v)
|
||||
{
|
||||
// left->right handed system
|
||||
v[0] = buffer_read_float(r) * METERS_PER_INCH;
|
||||
v[1] = buffer_read_float(r) * METERS_PER_INCH;
|
||||
v[2] = -buffer_read_float(r) * METERS_PER_INCH;
|
||||
}
|
||||
|
||||
static void read_direction(struct buffer *r, vec3 v)
|
||||
{
|
||||
// left->right handed system
|
||||
v[0] = buffer_read_float(r);
|
||||
v[1] = buffer_read_float(r);
|
||||
v[2] = -buffer_read_float(r);
|
||||
}
|
||||
|
||||
static void read_quaternion(struct buffer *r, versor q)
|
||||
{
|
||||
// left->right handed system
|
||||
float w = buffer_read_float(r);
|
||||
float x = -buffer_read_float(r);
|
||||
float y = -buffer_read_float(r);
|
||||
float z = buffer_read_float(r);
|
||||
glm_quat_init(q, x, y, z, w);
|
||||
}
|
||||
|
||||
static void parse_material(struct buffer *r, struct pol_material *m)
|
||||
{
|
||||
m->name = read_cstring(r);
|
||||
|
||||
int nr_textures = buffer_read_int32(r);
|
||||
for (int i = 0; i < nr_textures; i++) {
|
||||
char *filename = read_cstring(r);
|
||||
int type = buffer_read_int32(r);
|
||||
if ((unsigned)type < MAX_TEXTURE_TYPE) {
|
||||
m->textures[type] = filename;
|
||||
} else {
|
||||
WARNING("invalid texture type %d", type);
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_material(struct pol_material *m)
|
||||
{
|
||||
free(m->name);
|
||||
for (int i = 0; i < MAX_TEXTURE_TYPE; i++)
|
||||
free(m->textures[i]);
|
||||
}
|
||||
|
||||
static void parse_material_group(struct buffer *r, struct pol_material_group *mg)
|
||||
{
|
||||
parse_material(r, &mg->m);
|
||||
|
||||
mg->nr_children = buffer_read_int32(r);
|
||||
if (mg->nr_children > 0) {
|
||||
mg->children = xcalloc(mg->nr_children, sizeof(struct pol_material));
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++) {
|
||||
parse_material(r, &mg->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_material_group(struct pol_material_group *mg)
|
||||
{
|
||||
destroy_material(&mg->m);
|
||||
if (mg->children) {
|
||||
for (uint32_t i = 0; i < mg->nr_children; i++)
|
||||
destroy_material(&mg->children[i]);
|
||||
free(mg->children);
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_vertex(struct buffer *r, int pol_version, struct pol_vertex *v)
|
||||
{
|
||||
read_position(r, v->pos);
|
||||
v->nr_weights = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r);
|
||||
if (v->nr_weights) {
|
||||
v->weights = xcalloc(v->nr_weights, sizeof(struct pol_bone_weight));
|
||||
for (uint32_t i = 0; i < v->nr_weights; i++) {
|
||||
v->weights[i].bone = pol_version == 1 ? buffer_read_int32(r) : buffer_read_u16(r);
|
||||
v->weights[i].weight = buffer_read_float(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_vertex(struct pol_vertex *v)
|
||||
{
|
||||
if (v->weights)
|
||||
free(v->weights);
|
||||
}
|
||||
|
||||
static void parse_triangle(struct buffer *r, int unknowns_length, struct pol_triangle *t)
|
||||
{
|
||||
t->vert_index[0] = buffer_read_int32(r);
|
||||
t->vert_index[1] = buffer_read_int32(r);
|
||||
t->vert_index[2] = buffer_read_int32(r);
|
||||
t->uv_index[0] = buffer_read_int32(r);
|
||||
t->uv_index[1] = buffer_read_int32(r);
|
||||
t->uv_index[2] = buffer_read_int32(r);
|
||||
|
||||
buffer_skip(r, unknowns_length);
|
||||
|
||||
read_direction(r, t->normals[0]);
|
||||
read_direction(r, t->normals[1]);
|
||||
read_direction(r, t->normals[2]);
|
||||
t->material = buffer_read_int32(r);
|
||||
}
|
||||
|
||||
static struct pol_mesh *parse_mesh(struct buffer *r, int pol_version)
|
||||
{
|
||||
int type = buffer_read_int32(r);
|
||||
if (type != 0) {
|
||||
if (type != -1)
|
||||
WARNING("unknown mesh type: %d", type);
|
||||
return NULL;
|
||||
}
|
||||
struct pol_mesh *mesh = xcalloc(1, sizeof(struct pol_mesh));
|
||||
mesh->name = read_cstring(r);
|
||||
mesh->material = buffer_read_int32(r);
|
||||
|
||||
mesh->nr_vertices = buffer_read_int32(r);
|
||||
mesh->vertices = xcalloc(mesh->nr_vertices, sizeof(struct pol_vertex));
|
||||
for (uint32_t i = 0; i < mesh->nr_vertices; i++) {
|
||||
parse_vertex(r, pol_version, &mesh->vertices[i]);
|
||||
}
|
||||
|
||||
mesh->nr_uvs = buffer_read_int32(r);
|
||||
mesh->uvs = xcalloc(mesh->nr_uvs, sizeof(vec2));
|
||||
for (uint32_t i = 0; i < mesh->nr_uvs; i++) {
|
||||
mesh->uvs[i][0] = buffer_read_float(r);
|
||||
mesh->uvs[i][1] = buffer_read_float(r);
|
||||
}
|
||||
|
||||
int triangle_unknowns_length = 12;
|
||||
|
||||
int nr_unknown_f64s = buffer_read_int32(r);
|
||||
if (nr_unknown_f64s) {
|
||||
buffer_skip(r, nr_unknown_f64s * 8);
|
||||
triangle_unknowns_length += 12;
|
||||
}
|
||||
|
||||
if (pol_version == 1) {
|
||||
int nr_unknown_vecs = buffer_read_int32(r);
|
||||
buffer_skip(r, nr_unknown_vecs * 12);
|
||||
} else {
|
||||
int nr_unknown_ints = buffer_read_int32(r);
|
||||
buffer_skip(r, nr_unknown_ints * 4);
|
||||
int nr_unknown_bytes = buffer_read_int32(r);
|
||||
if (nr_unknown_bytes) {
|
||||
buffer_skip(r, nr_unknown_bytes);
|
||||
triangle_unknowns_length += 12;
|
||||
}
|
||||
}
|
||||
|
||||
mesh->nr_triangles = buffer_read_int32(r);
|
||||
mesh->triangles = xcalloc(mesh->nr_triangles, sizeof(struct pol_triangle));
|
||||
for (uint32_t i = 0; i < mesh->nr_triangles; i++) {
|
||||
parse_triangle(r, triangle_unknowns_length, &mesh->triangles[i]);
|
||||
}
|
||||
|
||||
if (pol_version == 1) {
|
||||
if (buffer_read_int32(r) != 1)
|
||||
WARNING("unexpected mesh footer");
|
||||
if (buffer_read_int32(r) != 0)
|
||||
WARNING("unexpected mesh footer");
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static void free_mesh(struct pol_mesh *mesh)
|
||||
{
|
||||
if (!mesh)
|
||||
return;
|
||||
free(mesh->name);
|
||||
for (uint32_t i = 0; i < mesh->nr_vertices; i++)
|
||||
destroy_vertex(&mesh->vertices[i]);
|
||||
free(mesh->vertices);
|
||||
free(mesh->uvs);
|
||||
free(mesh->triangles);
|
||||
free(mesh);
|
||||
}
|
||||
|
||||
static void parse_bone(struct buffer *r, struct pol_bone *bone)
|
||||
{
|
||||
bone->name = read_cstring(r);
|
||||
bone->id = buffer_read_int32(r);
|
||||
bone->parent = buffer_read_int32(r);
|
||||
read_position(r, bone->pos);
|
||||
read_quaternion(r, bone->rotq);
|
||||
}
|
||||
|
||||
static void destroy_bone(struct pol_bone *bone)
|
||||
{
|
||||
free(bone->name);
|
||||
}
|
||||
|
||||
struct pol *pol_parse(uint8_t *data, size_t size)
|
||||
{
|
||||
struct buffer r;
|
||||
buffer_init(&r, data, size);
|
||||
if (memcmp(buffer_strdata(&r), "POL\0", 4))
|
||||
return NULL;
|
||||
buffer_skip(&r, 4);
|
||||
|
||||
struct pol *pol = xcalloc(1, sizeof(struct pol));
|
||||
pol->version = buffer_read_int32(&r);
|
||||
if (pol->version != 1 && pol->version != 2) {
|
||||
WARNING("unknown POL version: %d", pol->version);
|
||||
free(pol);
|
||||
return NULL;
|
||||
}
|
||||
pol->nr_materials = buffer_read_int32(&r);
|
||||
pol->materials = xcalloc(pol->nr_materials, sizeof(struct pol_material_group));
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++) {
|
||||
parse_material_group(&r, &pol->materials[i]);
|
||||
}
|
||||
|
||||
pol->nr_meshes = buffer_read_int32(&r);
|
||||
pol->meshes = xcalloc(pol->nr_meshes, sizeof(struct pol_mesh *));
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++) {
|
||||
pol->meshes[i] = parse_mesh(&r, pol->version);
|
||||
}
|
||||
|
||||
pol->nr_bones = buffer_read_int32(&r);
|
||||
if (pol->nr_bones) {
|
||||
pol->bones = xcalloc(pol->nr_bones, sizeof(struct pol_bone));
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++) {
|
||||
parse_bone(&r, &pol->bones[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_remaining(&r) != 0) {
|
||||
WARNING("extra data at end");
|
||||
}
|
||||
return pol;
|
||||
}
|
||||
|
||||
void pol_free(struct pol *pol)
|
||||
{
|
||||
for (uint32_t i = 0; i < pol->nr_materials; i++)
|
||||
destroy_material_group(&pol->materials[i]);
|
||||
free(pol->materials);
|
||||
for (uint32_t i = 0; i < pol->nr_meshes; i++)
|
||||
free_mesh(pol->meshes[i]);
|
||||
free(pol->meshes);
|
||||
for (uint32_t i = 0; i < pol->nr_bones; i++)
|
||||
destroy_bone(&pol->bones[i]);
|
||||
free(pol->bones);
|
||||
free(pol);
|
||||
}
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/* Copyright (C) 2022 kichikuou <KichikuouChrome@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/aar.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
#include "xsystem4.h"
|
||||
|
||||
static struct RE_instance *create_instance(struct RE_plugin *plugin)
|
||||
{
|
||||
struct RE_instance *instance = xcalloc(1, sizeof(struct RE_instance));
|
||||
instance->plugin = plugin;
|
||||
instance->scale[0] = 1.0;
|
||||
instance->scale[1] = 1.0;
|
||||
instance->scale[2] = 1.0;
|
||||
glm_mat4_identity(instance->local_transform);
|
||||
return instance;
|
||||
}
|
||||
|
||||
static void free_instance(struct RE_instance *instance)
|
||||
{
|
||||
if (instance->model)
|
||||
model_free(instance->model);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
struct RE_plugin *RE_plugin_new(void)
|
||||
{
|
||||
char *aar_path = gamedir_path("Data/ReignData.red");
|
||||
int error = ARCHIVE_SUCCESS;
|
||||
struct archive *aar = (struct archive *)aar_open(aar_path, ARCHIVE_MMAP, &error);
|
||||
if (error == ARCHIVE_FILE_ERROR) {
|
||||
WARNING("aar_open(\"%s\"): %s", display_utf0(aar_path), strerror(errno));
|
||||
} else if (error == ARCHIVE_BAD_ARCHIVE_ERROR) {
|
||||
WARNING("aar_open(\"%s\"): invalid AAR archive", display_utf0(aar_path));
|
||||
}
|
||||
free(aar_path);
|
||||
if (!aar)
|
||||
return NULL;
|
||||
|
||||
struct RE_plugin *plugin = xcalloc(1, sizeof(struct RE_plugin));
|
||||
plugin->plugin.name = "ReignEngine";
|
||||
plugin->plugin.update = RE_render;
|
||||
plugin->aar = aar;
|
||||
return plugin;
|
||||
}
|
||||
|
||||
void RE_plugin_free(struct RE_plugin *plugin)
|
||||
{
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
|
||||
if (plugin->instances[i])
|
||||
free_instance(plugin->instances[i]);
|
||||
}
|
||||
archive_free(plugin->aar);
|
||||
if (plugin->renderer)
|
||||
RE_renderer_free(plugin->renderer);
|
||||
free(plugin);
|
||||
}
|
||||
|
||||
bool RE_plugin_bind(struct RE_plugin *plugin, int sprite)
|
||||
{
|
||||
struct sact_sprite *sp = sact_try_get_sprite(sprite);
|
||||
if (!sp)
|
||||
return false;
|
||||
plugin->renderer = RE_renderer_new(sprite_get_texture(sp));
|
||||
plugin->sprite = sprite;
|
||||
sprite_bind_plugin(sp, &plugin->plugin);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_plugin_unbind(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin || !plugin->renderer)
|
||||
return false;
|
||||
struct sact_sprite *sp = sact_try_get_sprite(plugin->sprite);
|
||||
if (sp)
|
||||
sprite_bind_plugin(sp, NULL);
|
||||
RE_renderer_free(plugin->renderer);
|
||||
plugin->renderer = NULL;
|
||||
plugin->sprite = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_set_projection(struct RE_plugin *plugin, float width, float height, float near, float far, float deg)
|
||||
{
|
||||
if (!plugin)
|
||||
return false;
|
||||
glm_perspective(glm_rad(deg), width / height, near, far, plugin->proj_transform);
|
||||
return true;
|
||||
}
|
||||
|
||||
int RE_create_instance(struct RE_plugin *plugin)
|
||||
{
|
||||
if (!plugin)
|
||||
return -1;
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
|
||||
if (!plugin->instances[i]) {
|
||||
plugin->instances[i] = create_instance(plugin);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
WARNING("too many instances");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool RE_release_instance(struct RE_plugin *plugin, int instance)
|
||||
{
|
||||
if (!plugin || !plugin->instances[instance])
|
||||
return false;
|
||||
free_instance(plugin->instances[instance]);
|
||||
plugin->instances[instance] = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_load(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance)
|
||||
return false;
|
||||
if (instance->model)
|
||||
model_free(instance->model);
|
||||
instance->model = model_load(instance->plugin->aar, name, instance->plugin->renderer);
|
||||
return !!instance->model;
|
||||
}
|
||||
|
||||
void RE_debug_print(struct RE_plugin *p)
|
||||
{
|
||||
printf("camera = { x: %f, y: %f, z: %f, pitch: %f, roll: %f, yaw: %f }",
|
||||
p->camera.pos[0], p->camera.pos[1], p->camera.pos[2],
|
||||
p->camera.pitch, p->camera.roll, p->camera.yaw);
|
||||
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
|
||||
struct RE_instance *inst = p->instances[i];
|
||||
if (!inst || !inst->model)
|
||||
continue;
|
||||
printf("[%d] = {", i);
|
||||
printf(" name: \"%s\",", inst->model->name);
|
||||
printf(" type: %d,", inst->type);
|
||||
printf(" draw: %d,", inst->draw);
|
||||
printf(" pos: { x: %f, y: %f, z: %f },", inst->pos[0], inst->pos[1], inst->pos[2]);
|
||||
printf(" vec: { x: %f, y: %f, z: %f },", inst->vec[0], inst->vec[1], inst->vec[2]);
|
||||
printf(" scale: { x: %f, y: %f, z: %f },", inst->scale[0], inst->scale[1], inst->scale[2]);
|
||||
printf("}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/* Copyright (C) 2022 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 <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
{
|
||||
struct RE_renderer *r = xcalloc(1, sizeof(struct RE_renderer));
|
||||
|
||||
gfx_load_shader(&r->shader, "shaders/reign.v.glsl", "shaders/reign.f.glsl");
|
||||
r->local_transform = glGetUniformLocation(r->shader.program, "local_transform");
|
||||
r->proj_transform = glGetUniformLocation(r->shader.program, "proj_transform");
|
||||
r->vertex_normal = glGetAttribLocation(r->shader.program, "vertex_normal");
|
||||
r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv");
|
||||
|
||||
glGenRenderbuffers(1, &r->depth_buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, r->depth_buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void RE_renderer_free(struct RE_renderer *r)
|
||||
{
|
||||
glDeleteProgram(r->shader.program);
|
||||
glDeleteRenderbuffers(1, &r->depth_buffer);
|
||||
free(r);
|
||||
}
|
||||
|
||||
static void calc_view_matrix(struct RE_camera *camera, mat4 out)
|
||||
{
|
||||
vec3 front = { 0.0, 0.0, -1.0 };
|
||||
vec3 euler = {
|
||||
glm_rad(camera->pitch),
|
||||
glm_rad(camera->yaw),
|
||||
glm_rad(camera->roll)
|
||||
};
|
||||
mat4 rot;
|
||||
glm_euler(euler, rot);
|
||||
glm_mat4_mulv3(rot, front, 0.0, front);
|
||||
vec3 up = {0.0, 1.0, 0.0};
|
||||
glm_look(camera->pos, front, up, out);
|
||||
}
|
||||
|
||||
static void update_local_transform(struct RE_instance *inst)
|
||||
{
|
||||
vec3 euler = {
|
||||
glm_rad(inst->pitch),
|
||||
glm_rad(inst->yaw),
|
||||
glm_rad(inst->roll)
|
||||
};
|
||||
mat4 rot;
|
||||
glm_euler(euler, rot);
|
||||
|
||||
glm_translate_make(inst->local_transform, inst->pos);
|
||||
glm_mat4_mul(inst->local_transform, rot, inst->local_transform);
|
||||
glm_scale(inst->local_transform, inst->scale);
|
||||
|
||||
inst->local_transform_needs_update = false;
|
||||
}
|
||||
|
||||
static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_transform, mat4 proj_transform)
|
||||
{
|
||||
if (!inst->draw || !inst->model)
|
||||
return;
|
||||
|
||||
if (inst->local_transform_needs_update)
|
||||
update_local_transform(inst);
|
||||
|
||||
glUseProgram(r->shader.program);
|
||||
glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, proj_transform[0]);
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, inst->local_transform[0]);
|
||||
|
||||
struct model *model = inst->model;
|
||||
for (int i = 0; i < model->nr_meshes; i++) {
|
||||
struct mesh *mesh = &model->meshes[i];
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, model->materials[mesh->material].color_map);
|
||||
glUniform1i(r->shader.texture, 0);
|
||||
|
||||
glBindVertexArray(mesh->vao);
|
||||
glDrawArrays(GL_TRIANGLES, 0, mesh->nr_vertices);
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
void RE_render(struct sact_sprite *sp)
|
||||
{
|
||||
struct RE_plugin *plugin = (struct RE_plugin *)sp->plugin;
|
||||
struct RE_renderer *r = plugin->renderer;
|
||||
if (!r)
|
||||
return;
|
||||
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, r->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 view_transform;
|
||||
calc_view_matrix(&plugin->camera, view_transform);
|
||||
|
||||
// Tweak the projection transform so that the rendering result is vertically
|
||||
// flipped. If we render the scene normally, the resulting image will be
|
||||
// bottom-up (the first pixel is at the bottom-left), but we want a top-down
|
||||
// image (the first pixel is at the top-left). This changes the coordinate
|
||||
// system from right-handed to left-handed, we also need to reverse the
|
||||
// winding order.
|
||||
plugin->proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
|
||||
if (plugin->instances[i])
|
||||
render_instance(plugin->instances[i], r, view_transform, plugin->proj_transform);
|
||||
}
|
||||
|
||||
plugin->proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CCW);
|
||||
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);
|
||||
}
|
||||
@@ -45,7 +45,7 @@
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
static void dungeon_render(struct draw_plugin *plugin);
|
||||
static void dungeon_render(struct sact_sprite *sp);
|
||||
|
||||
struct dungeon_context *dungeon_context_create(enum draw_dungeon_version version, int surface)
|
||||
{
|
||||
@@ -257,12 +257,11 @@ static void model_view_matrix(struct camera *camera, mat4 out)
|
||||
glm_lookat(eye, camera->pos, up, out);
|
||||
}
|
||||
|
||||
static void dungeon_render(struct draw_plugin *plugin)
|
||||
static void dungeon_render(struct sact_sprite *sp)
|
||||
{
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)plugin;
|
||||
struct dungeon_context *ctx = (struct dungeon_context *)sp->plugin;
|
||||
if (!ctx->loaded || !ctx->draw_enabled)
|
||||
return;
|
||||
struct sact_sprite *sp = sact_get_sprite(ctx->surface);
|
||||
sprite_dirty(sp);
|
||||
struct texture *texture = sprite_get_texture(sp);
|
||||
|
||||
|
||||
@@ -338,6 +338,7 @@ extern struct static_library lib_OutputLog;
|
||||
extern struct static_library lib_PassRegister;
|
||||
extern struct static_library lib_PlayDemo;
|
||||
extern struct static_library lib_PlayMovie;
|
||||
extern struct static_library lib_ReignEngine;
|
||||
extern struct static_library lib_SACT2;
|
||||
extern struct static_library lib_SACTDX;
|
||||
extern struct static_library lib_SengokuRanceFont;
|
||||
@@ -401,6 +402,7 @@ static struct static_library *static_libraries[] = {
|
||||
&lib_PassRegister,
|
||||
&lib_PlayDemo,
|
||||
&lib_PlayMovie,
|
||||
&lib_ReignEngine,
|
||||
&lib_SACT2,
|
||||
&lib_SACTDX,
|
||||
&lib_SengokuRanceFont,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,11 @@ xsystem4 = [version_h,
|
||||
'video.c',
|
||||
'vm.c',
|
||||
|
||||
'3d/model.c',
|
||||
'3d/parser.c',
|
||||
'3d/reign.c',
|
||||
'3d/renderer.c',
|
||||
|
||||
'dungeon/dgn.c',
|
||||
'dungeon/dtx.c',
|
||||
'dungeon/dungeon.c',
|
||||
@@ -98,6 +103,7 @@ xsystem4 = [version_h,
|
||||
'hll/PassRegister.c',
|
||||
'hll/PlayDemo.c',
|
||||
'hll/PlayMovie.c',
|
||||
'hll/ReignEngine.c',
|
||||
'hll/SACT2.c',
|
||||
'hll/SengokuRanceFont.c',
|
||||
'hll/SoundFilePlayer.c',
|
||||
|
||||
+1
-1
@@ -338,7 +338,7 @@ void sprite_call_plugins(void)
|
||||
{
|
||||
struct sact_sprite *sp;
|
||||
LIST_FOREACH(sp, &sprites_with_plugins, entry) {
|
||||
sp->plugin->update(sp->plugin);
|
||||
sp->plugin->update(sp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user