diff --git a/include/reign.h b/include/reign.h
index 44e7cbf..a744788 100644
--- a/include/reign.h
+++ b/include/reign.h
@@ -23,6 +23,12 @@
#include "plugin.h"
+enum RE_motion_state {
+ RE_MOTION_STATE_STOP = 0,
+ RE_MOTION_STATE_NOLOOP = 1,
+ RE_MOTION_STATE_LOOP = 2,
+};
+
struct RE_options {
int anti_aliasing;
int wait_vsync;
@@ -71,14 +77,22 @@ struct RE_plugin {
struct RE_instance {
struct RE_plugin *plugin;
struct model *model;
+ struct motion *motion;
+ struct motion *next_motion;
+
int type;
vec3 pos;
float pitch, roll, yaw; // in degrees
vec3 vec;
vec3 scale;
bool draw;
+ float fps;
+ bool motion_blend;
+ float motion_blend_rate;
+
bool local_transform_needs_update;
mat4 local_transform;
+ mat4 *bone_transforms; // model->nr_bones elements
};
struct RE_plugin *RE_plugin_new(void);
@@ -90,6 +104,16 @@ int RE_create_instance(struct RE_plugin *plugin);
bool RE_release_instance(struct RE_plugin *plugin, int instance);
bool RE_instance_load(struct RE_instance *instance, const char *name);
+bool RE_instance_load_motion(struct RE_instance *instance, const char *name);
+bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name);
+bool RE_instance_free_next_motion(struct RE_instance *instance);
+
+int RE_motion_get_state(struct motion *motion);
+bool RE_motion_set_state(struct motion *motion, int state);
+float RE_motion_get_frame(struct motion *motion);
+bool RE_motion_set_frame(struct motion *motion, float frame);
+bool RE_motion_set_frame_range(struct motion *motion, float begin, float end);
+bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float end);
void RE_render(struct sact_sprite *sp);
void RE_debug_print(struct RE_plugin *p);
diff --git a/shaders/reign.v.glsl b/shaders/reign.v.glsl
index 935c4f8..363b568 100644
--- a/shaders/reign.v.glsl
+++ b/shaders/reign.v.glsl
@@ -18,12 +18,30 @@ uniform mat4 local_transform;
uniform mat4 view_transform;
uniform mat4 proj_transform;
+const int MAX_BONES = 211; // see 3d_internal.h
+const int NR_WEIGHTS = 4;
+uniform bool has_bones;
+uniform mat4 bone_matrices[MAX_BONES];
+
in vec3 vertex_pos;
in vec3 vertex_normal;
in vec2 vertex_uv;
+in ivec4 vertex_bone_index;
+in vec4 vertex_bone_weight;
+
out vec2 tex_coord;
void main() {
- gl_Position = proj_transform * view_transform * local_transform * vec4(vertex_pos, 1.0);
- tex_coord = vertex_uv;
+ mat4 bone_transform = mat4(1.0f);
+ if (has_bones) {
+ bone_transform = mat4(0.f);
+ for (int i = 0; i < NR_WEIGHTS; i++) {
+ if (vertex_bone_index[i] >= 0) {
+ bone_transform += bone_matrices[vertex_bone_index[i]] * vertex_bone_weight[i];
+ }
+ }
+ }
+
+ gl_Position = proj_transform * view_transform * local_transform * bone_transform * vec4(vertex_pos, 1.0);
+ tex_coord = vertex_uv;
}
diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h
index 3b19b5d..b55297b 100644
--- a/src/3d/3d_internal.h
+++ b/src/3d/3d_internal.h
@@ -24,12 +24,17 @@
#include "gfx/gl.h"
#include "gfx/gfx.h"
+#define MAX_BONES 211 // Maximum in TT3
+
struct model {
- char *name;
+ char *path;
int nr_meshes;
struct mesh *meshes;
int nr_materials;
struct material *materials;
+ int nr_bones;
+ struct bone *bones;
+ struct hash_table *bone_map; // bone id in POL/MOT -> struct bone *
};
struct mesh {
@@ -44,6 +49,22 @@ struct material {
bool opaque;
};
+struct bone {
+ char *name;
+ int index; // index on model->bones
+ int parent; // index on model->bones, or -1
+ mat4 inverse_bind_matrix;
+};
+
+struct motion {
+ struct mot *mot;
+ char *name;
+ int state;
+ float current_frame;
+ float frame_begin, frame_end;
+ float loop_frame_begin, loop_frame_end;
+};
+
struct RE_renderer {
struct shader shader;
GLuint depth_buffer;
@@ -51,10 +72,17 @@ struct RE_renderer {
// Uniform variable locations
GLint local_transform;
GLint proj_transform;
+ GLint has_bones;
+ GLint bone_matrices;
// Attribute variable locations
GLint vertex_normal;
GLint vertex_uv;
+ GLint vertex_bone_index;
+ GLint vertex_bone_weight;
+
+ mat4 bone_transforms[MAX_BONES];
+ uint32_t last_frame_timestamp;
};
// model.c
@@ -62,6 +90,9 @@ struct RE_renderer {
struct model *model_load(struct archive *aar, const char *path, struct RE_renderer *renderer);
void model_free(struct model *model);
+struct motion *motion_load(const char *name, struct model *model, struct archive *aar);
+void motion_free(struct motion *motion);
+
// renderer.c
struct RE_renderer *RE_renderer_new(struct texture *texture);
@@ -132,7 +163,29 @@ struct pol_bone {
versor rotq;
};
+struct mot {
+ uint32_t nr_frames;
+ uint32_t nr_bones;
+ struct mot_bone *motions[];
+};
+
+struct mot_frame {
+ vec3 pos;
+ versor rotq;
+};
+
+struct mot_bone {
+ char *name;
+ uint32_t id;
+ uint32_t parent;
+ struct mot_frame frames[];
+};
+
struct pol *pol_parse(uint8_t *data, size_t size);
void pol_free(struct pol *pol);
+struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id);
+
+struct mot *mot_parse(uint8_t *data, size_t size);
+void mot_free(struct mot *mot);
#endif /* SYSTEM4_3D_3D_INTERNAL_H */
diff --git a/src/3d/model.c b/src/3d/model.c
index 9706eca..939a5ac 100644
--- a/src/3d/model.c
+++ b/src/3d/model.c
@@ -21,14 +21,19 @@
#include "system4.h"
#include "system4/aar.h"
#include "system4/cg.h"
+#include "system4/hashtable.h"
#include "3d_internal.h"
#include "reign.h"
+#define NR_WEIGHTS 4
+
struct vertex {
GLfloat pos[3];
GLfloat normal[3];
GLfloat uv[2];
+ GLint bone_id[NR_WEIGHTS];
+ GLfloat bone_weight[NR_WEIGHTS];
};
static bool init_material(struct material *material, const struct pol_material *m, struct archive *aar, const char *path)
@@ -75,6 +80,25 @@ static void destroy_material(struct material *material)
glDeleteTextures(1, &material->color_map);
}
+static int cmp_by_bone_weight(const void *lhs, const void *rhs)
+{
+ float l = ((struct pol_bone_weight *)lhs)->weight;
+ float r = ((struct pol_bone_weight *)rhs)->weight;
+ return (l < r) - (l > r); // descending order.
+}
+
+static void sort_and_normalize_bone_weights(struct pol_vertex *v)
+{
+ qsort(v->weights, v->nr_weights, sizeof(struct pol_bone_weight), cmp_by_bone_weight);
+ float total = 0.0;
+ for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) {
+ total += v->weights[i].weight;
+ }
+ for (uint32_t i = 0; i < v->nr_weights && i < NR_WEIGHTS; i++) {
+ v->weights[i].weight /= total;
+ }
+}
+
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));
@@ -84,9 +108,23 @@ static void add_mesh(struct model *model, struct pol_mesh *m, int material_index
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);
+ struct pol_vertex *vert = &m->vertices[t->vert_index[j]];
+ glm_vec3_copy(vert->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);
+ sort_and_normalize_bone_weights(vert);
+ for (uint32_t k = 0; k < NR_WEIGHTS; k++) {
+ if (model->bone_map && k < vert->nr_weights) {
+ struct bone *bone = ht_get_int(model->bone_map, vert->weights[k].bone, NULL);
+ if (!bone)
+ WARNING("%s: invalid bone id in vertex data", model->path);
+ buf[v].bone_id[k] = bone ? bone->index : -1;
+ buf[v].bone_weight[k] = vert->weights[k].weight;
+ } else {
+ buf[v].bone_id[k] = -1;
+ buf[v].bone_weight[k] = 0.0;
+ }
+ }
v++;
}
}
@@ -107,9 +145,13 @@ static void add_mesh(struct model *model, struct pol_mesh *m, int material_index
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));
+ glVertexAttribPointer(r->vertex_normal, 3, GL_FLOAT, GL_FALSE, 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));
+ glEnableVertexAttribArray(r->vertex_bone_index);
+ glVertexAttribIPointer(r->vertex_bone_index, NR_WEIGHTS, GL_INT, sizeof(struct vertex), (const void *)offsetof(struct vertex, bone_id));
+ glEnableVertexAttribArray(r->vertex_bone_weight);
+ glVertexAttribPointer(r->vertex_bone_weight, NR_WEIGHTS, GL_FLOAT, GL_FALSE, sizeof(struct vertex), (const void *)offsetof(struct vertex, bone_weight));
glBufferData(GL_ARRAY_BUFFER, mesh->nr_vertices * sizeof(struct vertex), buf, GL_STATIC_DRAW);
glBindVertexArray(0);
@@ -124,6 +166,39 @@ static void destroy_mesh(struct mesh *mesh)
glDeleteBuffers(1, &mesh->attr_buffer);
}
+static struct bone *add_bone(struct model *model, struct pol *pol, struct pol_bone *pol_bone)
+{
+ struct bone *bone = ht_get_int(model->bone_map, pol_bone->id, NULL);
+ if (bone)
+ return bone; // already added.
+
+ struct bone *parent = NULL;
+ if (pol_bone->parent >= 0) {
+ // Parent bone must appear before its children in model->bones.
+ struct pol_bone *pol_parent = pol_find_bone(pol, pol_bone->parent);
+ if (!pol_parent)
+ ERROR("Parent bone of \"%s\" is not found", pol_bone->name);
+ parent = add_bone(model, pol, pol_parent);
+ }
+
+ bone = &model->bones[model->nr_bones];
+ ht_put_int(model->bone_map, pol_bone->id, bone);
+ bone->name = strdup(pol_bone->name);
+ bone->index = model->nr_bones;
+ bone->parent = parent ? parent->index : -1;
+
+ glm_quat_mat4(pol_bone->rotq, bone->inverse_bind_matrix);
+ glm_translate(bone->inverse_bind_matrix, pol_bone->pos);
+
+ model->nr_bones++;
+ return bone;
+}
+
+static void destroy_bone(struct bone *bone)
+{
+ free(bone->name);
+}
+
struct model *model_load(struct archive *aar, const char *path, struct RE_renderer *r)
{
if (path[0] == '\0')
@@ -148,9 +223,23 @@ struct model *model_load(struct archive *aar, const char *path, struct RE_render
free(polname);
return NULL;
}
+ free(polname);
struct model *model = xcalloc(1, sizeof(struct model));
- model->name = polname;
+ model->path = strdup(path);
+
+ // Bones
+ if (pol->nr_bones > 0) {
+ if (pol->nr_bones > MAX_BONES)
+ ERROR("%s: Too many bones (%u)", model->path, pol->nr_bones);
+ model->bone_map = ht_create(pol->nr_bones * 3 / 2);
+ model->bones = xcalloc(pol->nr_bones, sizeof(struct bone));
+ for (uint32_t i = 0; i < pol->nr_bones; i++) {
+ add_bone(model, pol, &pol->bones[i]);
+ }
+ if (model->nr_bones != (int)pol->nr_bones)
+ ERROR("%s: Broken bone data", model->path);
+ }
// Materials
int *material_offsets = xmalloc(pol->nr_materials * sizeof(int));
@@ -204,6 +293,64 @@ void model_free(struct model *model)
destroy_material(&model->materials[i]);
free(model->materials);
- free(model->name);
+ for (int i = 0; i < model->nr_bones; i++)
+ destroy_bone(&model->bones[i]);
+ free(model->bones);
+ if (model->bone_map)
+ ht_free_int(model->bone_map);
+
+ free(model->path);
free(model);
}
+
+static int cmp_motions_by_bone_id(const void *lhs, const void *rhs)
+{
+ return (*(struct mot_bone **)lhs)->id - (*(struct mot_bone **)rhs)->id;
+}
+
+struct motion *motion_load(const char *name, struct model *model, struct archive *aar)
+{
+ char *motname = xmalloc(strlen(model->path) + strlen(name) + 6);
+ sprintf(motname, "%s\\%s.MOT", model->path, name);
+
+ struct archive_data *dfile = archive_get_by_name(aar, motname);
+ if (!dfile) {
+ WARNING("%s: not found", motname);
+ free(motname);
+ return NULL;
+ }
+
+ struct mot *mot = mot_parse(dfile->data, dfile->size);
+ archive_free_data(dfile);
+ if (!mot) {
+ WARNING("%s: parse error", motname);
+ free(motname);
+ return NULL;
+ }
+ free(motname);
+
+ if (model->nr_bones != (int)mot->nr_bones)
+ ERROR("%s: wrong number of bones. Expected %d but got %d", name, model->nr_bones, mot->nr_bones);
+
+ // Reorder mot->motions so that motion for model->bones[i] can be
+ // accessed by mot->motions[i].
+ for (uint32_t i = 0; i < mot->nr_bones; i++) {
+ struct bone *bone = ht_get_int(model->bone_map, mot->motions[i]->id, NULL);
+ if (!bone)
+ ERROR("%s: invalid bone id %d", name, mot->motions[i]->id);
+ mot->motions[i]->id = bone->index;
+ }
+ qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
+
+ struct motion *motion = xcalloc(1, sizeof(struct motion));
+ motion->mot = mot;
+ motion->name = strdup(name);
+ return motion;
+}
+
+void motion_free(struct motion *motion)
+{
+ mot_free(motion->mot);
+ free(motion->name);
+ free(motion);
+}
diff --git a/src/3d/parser.c b/src/3d/parser.c
index 9548955..bac6d99 100644
--- a/src/3d/parser.c
+++ b/src/3d/parser.c
@@ -56,6 +56,7 @@ static void read_quaternion(struct buffer *r, versor q)
float y = -buffer_read_float(r);
float z = buffer_read_float(r);
glm_quat_init(q, x, y, z, w);
+ glm_quat_normalize(q);
}
static void parse_material(struct buffer *r, struct pol_material *m)
@@ -284,3 +285,57 @@ void pol_free(struct pol *pol)
free(pol->bones);
free(pol);
}
+
+struct pol_bone *pol_find_bone(struct pol *pol, uint32_t id)
+{
+ for (uint32_t i = 0; i < pol->nr_bones; i++) {
+ if (pol->bones[i].id == id)
+ return &pol->bones[i];
+ }
+ return NULL;
+}
+
+struct mot *mot_parse(uint8_t *data, size_t size)
+{
+ struct buffer r;
+ buffer_init(&r, data, size);
+ if (memcmp(buffer_strdata(&r), "MOT\0", 4))
+ return NULL;
+ buffer_skip(&r, 4);
+ uint32_t version = buffer_read_int32(&r);
+ if (version != 0) {
+ WARNING("unknown MOT version: %d", version);
+ return NULL;
+ }
+ uint32_t nr_frames = buffer_read_int32(&r);
+ uint32_t nr_bones = buffer_read_int32(&r);
+
+ struct mot *mot = xcalloc(1, sizeof(struct mot) + nr_bones * sizeof(struct mot_bone *));
+ mot->nr_frames = nr_frames;
+ mot->nr_bones = nr_bones;
+ for (uint32_t i = 0; i < nr_bones; i++) {
+ struct mot_bone *m = xcalloc(1, sizeof(struct mot_bone) + sizeof(struct mot_frame) * nr_frames);
+ m->name = read_cstring(&r);
+ m->id = buffer_read_int32(&r);
+ m->parent = buffer_read_int32(&r);
+ for (uint32_t j = 0; j < nr_frames; j++) {
+ read_position(&r, m->frames[j].pos);
+ read_quaternion(&r, m->frames[j].rotq);
+ buffer_skip(&r, 16); // another quaternion?
+ }
+ mot->motions[i] = m;
+ }
+ if (buffer_remaining(&r) != 0) {
+ WARNING("extra data at end");
+ }
+ return mot;
+}
+
+void mot_free(struct mot *mot)
+{
+ for (uint32_t i = 0; i < mot->nr_bones; i++) {
+ free(mot->motions[i]->name);
+ free(mot->motions[i]);
+ }
+ free(mot);
+}
diff --git a/src/3d/reign.c b/src/3d/reign.c
index a8f3e45..ebd4a67 100644
--- a/src/3d/reign.c
+++ b/src/3d/reign.c
@@ -35,14 +35,34 @@ static struct RE_instance *create_instance(struct RE_plugin *plugin)
instance->scale[0] = 1.0;
instance->scale[1] = 1.0;
instance->scale[2] = 1.0;
+ instance->fps = 30.0;
glm_mat4_identity(instance->local_transform);
return instance;
}
+static void unload_instance(struct RE_instance *instance)
+{
+ if (instance->motion) {
+ motion_free(instance->motion);
+ instance->motion = NULL;
+ }
+ if (instance->next_motion) {
+ motion_free(instance->next_motion);
+ instance->next_motion = NULL;
+ }
+ if (instance->model) {
+ model_free(instance->model);
+ instance->model = NULL;
+ }
+ if (instance->bone_transforms) {
+ free(instance->bone_transforms);
+ instance->bone_transforms = NULL;
+ }
+}
+
static void free_instance(struct RE_instance *instance)
{
- if (instance->model)
- model_free(instance->model);
+ unload_instance(instance);
free(instance);
}
@@ -138,29 +158,124 @@ bool RE_instance_load(struct RE_instance *instance, const char *name)
{
if (!instance)
return false;
- if (instance->model)
- model_free(instance->model);
+ unload_instance(instance);
instance->model = model_load(instance->plugin->aar, name, instance->plugin->renderer);
+ if (instance->model && instance->model->nr_bones > 0)
+ instance->bone_transforms = xcalloc(instance->model->nr_bones, sizeof(mat4));
return !!instance->model;
}
+bool RE_instance_load_motion(struct RE_instance *instance, const char *name)
+{
+ if (!instance || !instance->model)
+ return false;
+ instance->motion = motion_load(name, instance->model, instance->plugin->aar);
+ return !!instance->motion;
+}
+
+bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name)
+{
+ if (!instance || !instance->model)
+ return false;
+ instance->next_motion = motion_load(name, instance->model, instance->plugin->aar);
+ return !!instance->next_motion;
+}
+
+bool RE_instance_free_next_motion(struct RE_instance *instance)
+{
+ if (!instance || !instance->next_motion)
+ return false;
+ motion_free(instance->next_motion);
+ instance->next_motion = NULL;
+ return true;
+}
+
+int RE_motion_get_state(struct motion *motion)
+{
+ return motion ? motion->state : RE_MOTION_STATE_STOP;
+}
+
+bool RE_motion_set_state(struct motion *motion, int state)
+{
+ if (!motion)
+ return false;
+ motion->state = state;
+ return true;
+}
+
+float RE_motion_get_frame(struct motion *motion)
+{
+ return motion ? motion->current_frame : 0.0;
+}
+
+bool RE_motion_set_frame(struct motion *motion, float frame)
+{
+ if (!motion)
+ return false;
+ motion->current_frame = frame;
+ return true;
+}
+
+bool RE_motion_set_frame_range(struct motion *motion, float begin, float end)
+{
+ if (!motion)
+ return false;
+ motion->frame_begin = begin;
+ motion->frame_end = end;
+ return true;
+}
+
+bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float end)
+{
+ if (!motion)
+ return false;
+ motion->loop_frame_begin = begin;
+ motion->loop_frame_end = end;
+ return true;
+}
+
+static void print_motion(const char *name, struct motion *m, int indent)
+{
+ const char *state;
+ switch (m->state) {
+ case RE_MOTION_STATE_STOP: state = "STOP"; break;
+ case RE_MOTION_STATE_NOLOOP: state = "NOLOOP"; break;
+ case RE_MOTION_STATE_LOOP: state = "LOOP"; break;
+ default: state = "?"; break;
+ }
+ indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n",
+ name, m->name, state, m->current_frame);
+}
+
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);
+ int indent = 0;
+ indent_printf(indent, "camera = {x=%f, y=%f, z=%f, pitch=%f, roll=%f, yaw=%f},\n",
+ 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("}");
+ indent_printf(indent, "instance[%d] = {\n", i);
+ indent++;
+
+ indent_printf(indent, "path = \"%s\",\n", inst->model->path);
+ indent_printf(indent, "type = %d,\n", inst->type);
+ indent_printf(indent, "draw = %d,\n", inst->draw);
+ indent_printf(indent, "pos = {x=%f, y=%f, z=%f},\n", inst->pos[0], inst->pos[1], inst->pos[2]);
+ indent_printf(indent, "vec = {x=%f, y=%f, z=%f},\n", inst->vec[0], inst->vec[1], inst->vec[2]);
+ indent_printf(indent, "scale = {x=%f, y=%f, z=%f},\n", inst->scale[0], inst->scale[1], inst->scale[2]);
+ if (inst->motion)
+ print_motion("motion", inst->motion, indent);
+ if (inst->next_motion)
+ print_motion("next_motion", inst->next_motion, indent);
+ indent_printf(indent, "fps = %f,\n", inst->fps);
+ if (inst->motion_blend)
+ indent_printf(indent, "motion_blend_rate = %f,\n", inst->motion_blend_rate);
+
+ indent--;
+ indent_printf(indent, "},\n");
}
}
diff --git a/src/3d/renderer.c b/src/3d/renderer.c
index 932414f..40af51a 100644
--- a/src/3d/renderer.c
+++ b/src/3d/renderer.c
@@ -14,6 +14,7 @@
* along with this program; if not, see .
*/
+#include
#include
#include "system4.h"
@@ -21,6 +22,7 @@
#include "3d_internal.h"
#include "reign.h"
#include "sact.h"
+#include "vm.h"
struct RE_renderer *RE_renderer_new(struct texture *texture)
{
@@ -29,14 +31,19 @@ struct RE_renderer *RE_renderer_new(struct texture *texture)
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->has_bones = glGetUniformLocation(r->shader.program, "has_bones");
+ r->bone_matrices = glGetUniformLocation(r->shader.program, "bone_matrices");
r->vertex_normal = glGetAttribLocation(r->shader.program, "vertex_normal");
r->vertex_uv = glGetAttribLocation(r->shader.program, "vertex_uv");
+ r->vertex_bone_index = glGetAttribLocation(r->shader.program, "vertex_bone_index");
+ r->vertex_bone_weight = glGetAttribLocation(r->shader.program, "vertex_bone_weight");
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);
+ r->last_frame_timestamp = SDL_GetTicks();
return r;
}
@@ -79,7 +86,71 @@ static void update_local_transform(struct RE_instance *inst)
inst->local_transform_needs_update = false;
}
-static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_transform, mat4 proj_transform)
+static void interpolate_motion_frame(struct mot_frame *m1, struct mot_frame *m2, float t, struct mot_frame *out)
+{
+ glm_vec3_lerp(m1->pos, m2->pos, t, out->pos);
+ glm_quat_nlerp(m1->rotq, m2->rotq, t, out->rotq);
+}
+
+static void update_motion(struct motion *m, float delta_frame)
+{
+ if (!m || m->state == RE_MOTION_STATE_STOP)
+ return;
+ m->current_frame += delta_frame;
+ switch (m->state) {
+ case RE_MOTION_STATE_NOLOOP:
+ if (m->current_frame > m->frame_end) {
+ m->current_frame = m->frame_end;
+ m->state = RE_MOTION_STATE_STOP;
+ }
+ break;
+ case RE_MOTION_STATE_LOOP:
+ if (m->current_frame > m->loop_frame_end)
+ m->current_frame = m->loop_frame_begin + fmodf(m->current_frame - m->loop_frame_begin, m->loop_frame_end - m->loop_frame_begin);
+ break;
+ default:
+ VM_ERROR("Invalid motion state %d", m->state);
+ }
+}
+
+static void calc_motion_frame(struct motion *m, int bone, struct mot_frame *out)
+{
+ int cur_frame = m->current_frame;
+ int next_frame = ceilf(m->current_frame);
+ float t = m->current_frame - cur_frame;
+ struct mot_frame *frames = m->mot->motions[bone]->frames;
+ interpolate_motion_frame(&frames[cur_frame], &frames[next_frame], t, out);
+}
+
+static void animate(struct RE_instance *inst, struct RE_renderer *r, float delta_time)
+{
+ float delta_frame = delta_time * inst->fps;
+ update_motion(inst->motion, delta_frame);
+ update_motion(inst->next_motion, delta_frame);
+
+ for (int i = 0; i < inst->model->nr_bones; i++) {
+ struct mot_frame mf;
+ calc_motion_frame(inst->motion, i, &mf);
+ if (inst->motion_blend && inst->next_motion) {
+ struct mot_frame next_mf;
+ calc_motion_frame(inst->next_motion, i, &next_mf);
+ interpolate_motion_frame(&mf, &next_mf, inst->motion_blend_rate, &mf);
+ }
+ mat4 bone_transform;
+ glm_translate_make(bone_transform, mf.pos);
+ glm_quat_rotate(bone_transform, mf.rotq, bone_transform);
+
+ if (inst->model->bones[i].parent >= 0) {
+ assert(inst->model->bones[i].parent < i);
+ glm_mat4_mul(inst->bone_transforms[inst->model->bones[i].parent], bone_transform, bone_transform);
+ }
+ glm_mat4_copy(bone_transform, inst->bone_transforms[i]);
+
+ glm_mat4_mul(bone_transform, inst->model->bones[i].inverse_bind_matrix, r->bone_transforms[i]);
+ }
+}
+
+static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_transform, mat4 proj_transform, float delta_time)
{
if (!inst->draw || !inst->model)
return;
@@ -87,11 +158,20 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat
if (inst->local_transform_needs_update)
update_local_transform(inst);
+ animate(inst, r, delta_time);
+
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]);
+ if (inst->model->nr_bones > 0) {
+ glUniform1i(r->has_bones, GL_TRUE);
+ glUniformMatrix4fv(r->bone_matrices, inst->model->nr_bones, GL_FALSE, r->bone_transforms[0][0]);
+ } else {
+ glUniform1i(r->has_bones, GL_FALSE);
+ }
+
struct model *model = inst->model;
for (int i = 0; i < model->nr_meshes; i++) {
struct mesh *mesh = &model->meshes[i];
@@ -110,13 +190,19 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat
void RE_render(struct sact_sprite *sp)
{
+ uint32_t timestamp = SDL_GetTicks();
+
struct RE_plugin *plugin = (struct RE_plugin *)sp->plugin;
struct RE_renderer *r = plugin->renderer;
- if (!r)
+ if (!r) {
+ r->last_frame_timestamp = timestamp;
return;
+ }
sprite_dirty(sp);
struct texture *texture = sprite_get_texture(sp);
+ float delta_time = (timestamp - r->last_frame_timestamp) / 1000.0;
+
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)
@@ -140,7 +226,7 @@ void RE_render(struct sact_sprite *sp)
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
if (plugin->instances[i])
- render_instance(plugin->instances[i], r, view_transform, plugin->proj_transform);
+ render_instance(plugin->instances[i], r, view_transform, plugin->proj_transform, delta_time);
}
plugin->proj_transform[1][1] *= -1;
@@ -149,4 +235,6 @@ void RE_render(struct sact_sprite *sp)
glDisable(GL_CULL_FACE);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
+
+ r->last_frame_timestamp = timestamp;
}
diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c
index 52dc47b..5ce8b02 100644
--- a/src/hll/ReignEngine.c
+++ b/src/hll/ReignEngine.c
@@ -42,6 +42,18 @@ static struct RE_instance *get_instance(unsigned plugin, unsigned instance)
return instance < RE_MAX_INSTANCES ? rp->instances[instance] : NULL;
}
+static struct motion *get_motion(unsigned plugin, unsigned instance)
+{
+ struct RE_instance *inst = get_instance(plugin, instance);
+ return inst ? inst->motion : NULL;
+}
+
+static struct motion *get_next_motion(unsigned plugin, unsigned instance)
+{
+ struct RE_instance *inst = get_instance(plugin, instance);
+ return inst ? inst->next_motion : NULL;
+}
+
static int ReignEngine_CreatePlugin(void)
{
for (int i = 0; i < RE_MAX_PLUGINS; i++) {
@@ -239,25 +251,110 @@ static bool ReignEngine_GetInstanceDraw(int plugin, int instance)
//bool ReignEngine_GetInstanceDrawBump(int plugin, int instance);
//int ReignEngine_GetInstanceDrawType(int plugin, int instance);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDrawType, int plugin, int instance, int draw_type);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, LoadInstanceMotion, int plugin, int instance, struct string *name);
-HLL_WARN_UNIMPLEMENTED(0, int, ReignEngine, GetInstanceMotionState, int plugin, int instance);
-HLL_WARN_UNIMPLEMENTED(0.0, float, ReignEngine, GetInstanceMotionFrame, int plugin, int instance);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionState, int plugin, int instance, int state);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionFrame, int plugin, int instance, float frame);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionFrameRange, int plugin, int instance, float begin_frame, float end_frame);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionLoopFrameRange, int plugin, int instance, float begin_frame, float end_frame);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, LoadInstanceNextMotion, int plugin, int instance, struct string *name);
-//bool ReignEngine_SetInstanceNextMotionState(int plugin, int instance, int nState);
-//bool ReignEngine_SetInstanceNextMotionFrame(int plugin, int instance, float fFrame);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceNextMotionFrameRange, int plugin, int instance, float begin_frame, float end_frame);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceNextMotionLoopFrameRange, int plugin, int instance, float begin_frame, float end_frame);
-//bool ReignEngine_SetInstanceMotionBlendRate(int plugin, int instance, float fRate);
-//bool ReignEngine_SetInstanceMotionBlend(int plugin, int instance, bool bMotionBlend);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, IsInstanceMotionBlend, int plugin, int instance);
+
+bool ReignEngine_LoadInstanceMotion(int plugin, int instance, struct string *name)
+{
+ return RE_instance_load_motion(get_instance(plugin, instance), name->text);
+}
+
+static int ReignEngine_GetInstanceMotionState(int plugin, int instance)
+{
+ return RE_motion_get_state(get_motion(plugin, instance));
+}
+
+static float ReignEngine_GetInstanceMotionFrame(int plugin, int instance)
+{
+ return RE_motion_get_frame(get_motion(plugin, instance));
+}
+
+static bool ReignEngine_SetInstanceMotionState(int plugin, int instance, int state)
+{
+ return RE_motion_set_state(get_motion(plugin, instance), state);
+}
+
+static bool ReignEngine_SetInstanceMotionFrame(int plugin, int instance, float frame)
+{
+ return RE_motion_set_frame(get_motion(plugin, instance), frame);
+}
+
+static bool ReignEngine_SetInstanceMotionFrameRange(int plugin, int instance, float begin_frame, float end_frame)
+{
+ return RE_motion_set_frame_range(get_motion(plugin, instance), begin_frame, end_frame);
+}
+
+static bool ReignEngine_SetInstanceMotionLoopFrameRange(int plugin, int instance, float begin_frame, float end_frame)
+{
+ return RE_motion_set_loop_frame_range(get_motion(plugin, instance), begin_frame, end_frame);
+}
+
+static bool ReignEngine_LoadInstanceNextMotion(int plugin, int instance, struct string *name)
+{
+ return RE_instance_load_next_motion(get_instance(plugin, instance), name->text);
+}
+
+static bool ReignEngine_SetInstanceNextMotionState(int plugin, int instance, int state)
+{
+ return RE_motion_set_state(get_next_motion(plugin, instance), state);
+}
+
+static bool ReignEngine_SetInstanceNextMotionFrame(int plugin, int instance, float frame)
+{
+ return RE_motion_set_frame(get_next_motion(plugin, instance), frame);
+}
+
+static bool ReignEngine_SetInstanceNextMotionFrameRange(int plugin, int instance, float begin_frame, float end_frame)
+{
+ return RE_motion_set_frame_range(get_next_motion(plugin, instance), begin_frame, end_frame);
+}
+
+static bool ReignEngine_SetInstanceNextMotionLoopFrameRange(int plugin, int instance, float begin_frame, float end_frame)
+{
+ return RE_motion_set_loop_frame_range(get_next_motion(plugin, instance), begin_frame, end_frame);
+}
+
+static bool ReignEngine_SetInstanceMotionBlendRate(int plugin, int instance, float rate)
+{
+ struct RE_instance *ri = get_instance(plugin, instance);
+ if (!ri)
+ return false;
+ ri->motion_blend_rate = rate;
+ return true;
+}
+
+static bool ReignEngine_SetInstanceMotionBlend(int plugin, int instance, bool motion_blend)
+{
+ struct RE_instance *ri = get_instance(plugin, instance);
+ if (!ri)
+ return false;
+ ri->motion_blend = motion_blend;
+ return true;
+}
+
+static bool ReignEngine_IsInstanceMotionBlend(int plugin, int instance)
+{
+ struct RE_instance *ri = get_instance(plugin, instance);
+ return ri ? ri->motion_blend : false;
+}
+
//bool ReignEngine_GetInstanceMotionBlendPutTime(int plugin, int instance);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceMotionBlendPutTime, int plugin, int instance, bool flag);
-//bool ReignEngine_SwapInstanceMotion(int plugin, int instance);
-//bool ReignEngine_FreeInstanceNextMotion(int plugin, int instance);
+
+static bool ReignEngine_SwapInstanceMotion(int plugin, int instance)
+{
+ struct RE_instance *ri = get_instance(plugin, instance);
+ if (!ri)
+ return false;
+ struct motion *tmp = ri->motion;
+ ri->motion = ri->next_motion;
+ ri->next_motion = tmp;
+ return true;
+}
+
+static bool ReignEngine_FreeInstanceNextMotion(int plugin, int instance)
+{
+ return RE_instance_free_next_motion(get_instance(plugin, instance));
+}
+
//int ReignEngine_GetInstanceNumofMaterial(int plugin, int instance);
//bool ReignEngine_GetInstanceMaterialName(int plugin, int instance, int num, struct string **pIName);
//float ReignEngine_GetInstanceMaterialParam(int plugin, int instance, int nMaterial, int type);
@@ -281,8 +378,22 @@ HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceColumnAngle, int plu
//bool ReignEngine_SetInstanceDrawColumn(int plugin, int instance, bool bDraw);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceTarget, int plugin, int instance, int index, int target);
//int ReignEngine_GetInstanceTarget(int plugin, int instance, int index);
-//float ReignEngine_GetInstanceFPS(int plugin, int instance);
-HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceFPS, int plugin, int instance, float fps);
+
+static float ReignEngine_GetInstanceFPS(int plugin, int instance)
+{
+ struct RE_instance *ri = get_instance(plugin, instance);
+ return ri ? ri->fps : 0.0;
+}
+
+static bool ReignEngine_SetInstanceFPS(int plugin, int instance, float fps)
+{
+ struct RE_instance *ri = get_instance(plugin, instance);
+ if (!ri)
+ return false;
+ ri->fps = fps;
+ return true;
+}
+
HLL_WARN_UNIMPLEMENTED(0, int, ReignEngine, GetInstanceBoneIndex, int plugin, int instance, struct string *name);
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, TransInstanceLocalPosToWorldPosByBone, int plugin, int instance, int bone, float offset_x, float offset_y, float offset_z, float *x, float *y, float *z);
//int ReignEngine_GetInstanceNumofPolygon(int plugin, int instance);
@@ -304,7 +415,7 @@ HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, GetEffectFrameRange, int plugin
//float ReignEngine_GetEffectObjectMoveCurve(int plugin, int instance, int nObject);
//void ReignEngine_GetEffectObjectFrame(int plugin, int instance, int nObject, int *pnBeginFrame, int *pnEndFrame);
//int ReignEngine_GetEffectObjectStopFrame(int plugin, int instance, int nObject);
-//int ReignEngine_GetEffectNumofObject(int plugin, int instance);
+HLL_WARN_UNIMPLEMENTED(0, int, ReignEngine, GetEffectNumofObject, int plugin, int instance);
//void ReignEngine_GetEffectObjectName(int plugin, int instance, int nObject, struct string **pIName);
//int ReignEngine_GetEffectNumofObjectPos(int plugin, int instance, int nObject);
//int ReignEngine_GetEffectNumofObjectPosUnit(int plugin, int instance, int nObject, int nPos);
@@ -417,8 +528,8 @@ HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, GetEffectFrameRange, int plugin
//float ReignEngine_GetEffectObjectOffsetY(int plugin, int instance, int nObject);
//bool ReignEngine_SetEffectObjectOffsetX(int plugin, int instance, int nObject, float fOffsetX);
//bool ReignEngine_SetEffectObjectOffsetY(int plugin, int instance, int nObject, float fOffsetY);
-//bool ReignEngine_GetCameraQuakeEffectFlag(int plugin, int instance);
-//bool ReignEngine_SetCameraQuakeEffectFlag(int plugin, int instance, bool flag);
+HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, GetCameraQuakeEffectFlag, int plugin, int instance);
+HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetCameraQuakeEffectFlag, int plugin, int instance, bool flag);
bool ReignEngine_SetCameraPos(int plugin, float x, float y, float z)
{
@@ -928,17 +1039,17 @@ HLL_LIBRARY(ReignEngine,
HLL_EXPORT(SetInstanceMotionFrameRange, ReignEngine_SetInstanceMotionFrameRange),
HLL_EXPORT(SetInstanceMotionLoopFrameRange, ReignEngine_SetInstanceMotionLoopFrameRange),
HLL_EXPORT(LoadInstanceNextMotion, ReignEngine_LoadInstanceNextMotion),
- HLL_TODO_EXPORT(SetInstanceNextMotionState, ReignEngine_SetInstanceNextMotionState),
- HLL_TODO_EXPORT(SetInstanceNextMotionFrame, ReignEngine_SetInstanceNextMotionFrame),
+ HLL_EXPORT(SetInstanceNextMotionState, ReignEngine_SetInstanceNextMotionState),
+ HLL_EXPORT(SetInstanceNextMotionFrame, ReignEngine_SetInstanceNextMotionFrame),
HLL_EXPORT(SetInstanceNextMotionFrameRange, ReignEngine_SetInstanceNextMotionFrameRange),
HLL_EXPORT(SetInstanceNextMotionLoopFrameRange, ReignEngine_SetInstanceNextMotionLoopFrameRange),
- HLL_TODO_EXPORT(SetInstanceMotionBlendRate, ReignEngine_SetInstanceMotionBlendRate),
- HLL_TODO_EXPORT(SetInstanceMotionBlend, ReignEngine_SetInstanceMotionBlend),
+ HLL_EXPORT(SetInstanceMotionBlendRate, ReignEngine_SetInstanceMotionBlendRate),
+ HLL_EXPORT(SetInstanceMotionBlend, ReignEngine_SetInstanceMotionBlend),
HLL_EXPORT(IsInstanceMotionBlend, ReignEngine_IsInstanceMotionBlend),
HLL_TODO_EXPORT(GetInstanceMotionBlendPutTime, ReignEngine_GetInstanceMotionBlendPutTime),
HLL_EXPORT(SetInstanceMotionBlendPutTime, ReignEngine_SetInstanceMotionBlendPutTime),
- HLL_TODO_EXPORT(SwapInstanceMotion, ReignEngine_SwapInstanceMotion),
- HLL_TODO_EXPORT(FreeInstanceNextMotion, ReignEngine_FreeInstanceNextMotion),
+ HLL_EXPORT(SwapInstanceMotion, ReignEngine_SwapInstanceMotion),
+ HLL_EXPORT(FreeInstanceNextMotion, ReignEngine_FreeInstanceNextMotion),
HLL_TODO_EXPORT(GetInstanceNumofMaterial, ReignEngine_GetInstanceNumofMaterial),
HLL_TODO_EXPORT(GetInstanceMaterialName, ReignEngine_GetInstanceMaterialName),
HLL_TODO_EXPORT(GetInstanceMaterialParam, ReignEngine_GetInstanceMaterialParam),
@@ -962,7 +1073,7 @@ HLL_LIBRARY(ReignEngine,
HLL_TODO_EXPORT(SetInstanceDrawColumn, ReignEngine_SetInstanceDrawColumn),
HLL_EXPORT(SetInstanceTarget, ReignEngine_SetInstanceTarget),
HLL_TODO_EXPORT(GetInstanceTarget, ReignEngine_GetInstanceTarget),
- HLL_TODO_EXPORT(GetInstanceFPS, ReignEngine_GetInstanceFPS),
+ HLL_EXPORT(GetInstanceFPS, ReignEngine_GetInstanceFPS),
HLL_EXPORT(SetInstanceFPS, ReignEngine_SetInstanceFPS),
HLL_EXPORT(GetInstanceBoneIndex, ReignEngine_GetInstanceBoneIndex),
HLL_EXPORT(TransInstanceLocalPosToWorldPosByBone, ReignEngine_TransInstanceLocalPosToWorldPosByBone),
@@ -985,7 +1096,7 @@ HLL_LIBRARY(ReignEngine,
HLL_TODO_EXPORT(GetEffectObjectMoveCurve, ReignEngine_GetEffectObjectMoveCurve),
HLL_TODO_EXPORT(GetEffectObjectFrame, ReignEngine_GetEffectObjectFrame),
HLL_TODO_EXPORT(GetEffectObjectStopFrame, ReignEngine_GetEffectObjectStopFrame),
- HLL_TODO_EXPORT(GetEffectNumofObject, ReignEngine_GetEffectNumofObject),
+ HLL_EXPORT(GetEffectNumofObject, ReignEngine_GetEffectNumofObject),
HLL_TODO_EXPORT(GetEffectObjectName, ReignEngine_GetEffectObjectName),
HLL_TODO_EXPORT(GetEffectNumofObjectPos, ReignEngine_GetEffectNumofObjectPos),
HLL_TODO_EXPORT(GetEffectNumofObjectPosUnit, ReignEngine_GetEffectNumofObjectPosUnit),
@@ -1098,8 +1209,8 @@ HLL_LIBRARY(ReignEngine,
HLL_TODO_EXPORT(GetEffectObjectOffsetY, ReignEngine_GetEffectObjectOffsetY),
HLL_TODO_EXPORT(SetEffectObjectOffsetX, ReignEngine_SetEffectObjectOffsetX),
HLL_TODO_EXPORT(SetEffectObjectOffsetY, ReignEngine_SetEffectObjectOffsetY),
- HLL_TODO_EXPORT(GetCameraQuakeEffectFlag, ReignEngine_GetCameraQuakeEffectFlag),
- HLL_TODO_EXPORT(SetCameraQuakeEffectFlag, ReignEngine_SetCameraQuakeEffectFlag),
+ HLL_EXPORT(GetCameraQuakeEffectFlag, ReignEngine_GetCameraQuakeEffectFlag),
+ HLL_EXPORT(SetCameraQuakeEffectFlag, ReignEngine_SetCameraQuakeEffectFlag),
HLL_EXPORT(SetCameraPos, ReignEngine_SetCameraPos),
HLL_EXPORT(SetCameraAngle, ReignEngine_SetCameraAngle),
HLL_EXPORT(SetCameraAngleP, ReignEngine_SetCameraAngleP),