mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
ReignEngine: Support billboard instance type
Used for 2D objects (e.g. NPCs in dungeons).
This commit is contained in:
@@ -134,6 +134,7 @@ 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);
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z);
|
||||
|
||||
int RE_motion_get_state(struct motion *motion);
|
||||
bool RE_motion_set_state(struct motion *motion, int state);
|
||||
|
||||
+10
-1
@@ -57,6 +57,7 @@ struct bone {
|
||||
};
|
||||
|
||||
struct motion {
|
||||
struct RE_instance *instance;
|
||||
struct mot *mot;
|
||||
char *name;
|
||||
int state;
|
||||
@@ -83,21 +84,29 @@ struct RE_renderer {
|
||||
GLint vertex_bone_weight;
|
||||
|
||||
mat4 bone_transforms[MAX_BONES];
|
||||
GLuint billboard_vao;
|
||||
GLuint billboard_attr_buffer;
|
||||
struct hash_table *billboard_textures; // cg_no -> struct billboard_texture*
|
||||
uint32_t last_frame_timestamp;
|
||||
};
|
||||
|
||||
struct billboard_texture {
|
||||
GLuint texture;
|
||||
};
|
||||
|
||||
// model.c
|
||||
|
||||
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);
|
||||
struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar);
|
||||
void motion_free(struct motion *motion);
|
||||
|
||||
// renderer.c
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture);
|
||||
void RE_renderer_free(struct RE_renderer *r);
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no);
|
||||
|
||||
// parser.c
|
||||
|
||||
|
||||
+8
-2
@@ -47,8 +47,14 @@ static const char *motion_state_name(enum RE_motion_state state)
|
||||
|
||||
static void print_motion(const char *name, struct motion *m, int indent)
|
||||
{
|
||||
indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n",
|
||||
name, m->name, motion_state_name(m->state), m->current_frame);
|
||||
if (m->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
indent_printf(indent, "%s = {state=%s, frame=%f, range=(%d,%d), loop_range=(%d,%d)},\n",
|
||||
name, motion_state_name(m->state), m->current_frame,
|
||||
(int)m->frame_begin, (int)m->frame_end, (int)m->loop_frame_begin, (int)m->loop_frame_end);
|
||||
} else {
|
||||
indent_printf(indent, "%s = {name=\"%s\", state=%s, frame=%f},\n",
|
||||
name, m->name, motion_state_name(m->state), m->current_frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_instance(struct RE_instance *inst, int index, int indent)
|
||||
|
||||
+9
-3
@@ -308,8 +308,11 @@ 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)
|
||||
struct motion *motion_load(const char *name, struct RE_instance *instance, struct archive *aar)
|
||||
{
|
||||
struct model *model = instance->model;
|
||||
if (!model)
|
||||
return NULL;
|
||||
char *motname = xmalloc(strlen(model->path) + strlen(name) + 6);
|
||||
sprintf(motname, "%s\\%s.MOT", model->path, name);
|
||||
|
||||
@@ -343,6 +346,7 @@ struct motion *motion_load(const char *name, struct model *model, struct archive
|
||||
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
|
||||
|
||||
struct motion *motion = xcalloc(1, sizeof(struct motion));
|
||||
motion->instance = instance;
|
||||
motion->mot = mot;
|
||||
motion->name = strdup(name);
|
||||
return motion;
|
||||
@@ -350,7 +354,9 @@ struct motion *motion_load(const char *name, struct model *model, struct archive
|
||||
|
||||
void motion_free(struct motion *motion)
|
||||
{
|
||||
mot_free(motion->mot);
|
||||
free(motion->name);
|
||||
if (motion->mot)
|
||||
mot_free(motion->mot);
|
||||
if (motion->name)
|
||||
free(motion->name);
|
||||
free(motion);
|
||||
}
|
||||
|
||||
+40
-4
@@ -14,6 +14,7 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -184,10 +185,18 @@ bool RE_instance_set_type(struct RE_instance *instance, int type)
|
||||
ERROR("instance type cannot be changed");
|
||||
switch (type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
break;
|
||||
case RE_ITYPE_SKINNED:
|
||||
break;
|
||||
case RE_ITYPE_BILLBOARD:
|
||||
assert(!instance->motion);
|
||||
instance->motion = xcalloc(1, sizeof(struct motion));
|
||||
instance->motion->instance = instance;
|
||||
break;
|
||||
case RE_ITYPE_DIRECTIONAL_LIGHT:
|
||||
break;
|
||||
case RE_ITYPE_SPECULAR_LIGHT:
|
||||
break;
|
||||
case RE_ITYPE_PARTICLE_EFFECT:
|
||||
break;
|
||||
default:
|
||||
@@ -210,21 +219,21 @@ bool RE_instance_load(struct RE_instance *instance, const char *name)
|
||||
|
||||
bool RE_instance_load_motion(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance || !instance->model)
|
||||
if (!instance)
|
||||
return false;
|
||||
if (instance->motion)
|
||||
motion_free(instance->motion);
|
||||
instance->motion = motion_load(name, instance->model, instance->plugin->aar);
|
||||
instance->motion = motion_load(name, instance, instance->plugin->aar);
|
||||
return !!instance->motion;
|
||||
}
|
||||
|
||||
bool RE_instance_load_next_motion(struct RE_instance *instance, const char *name)
|
||||
{
|
||||
if (!instance || !instance->model)
|
||||
if (!instance)
|
||||
return false;
|
||||
if (instance->next_motion)
|
||||
motion_free(instance->next_motion);
|
||||
instance->next_motion = motion_load(name, instance->model, instance->plugin->aar);
|
||||
instance->next_motion = motion_load(name, instance, instance->plugin->aar);
|
||||
return !!instance->next_motion;
|
||||
}
|
||||
|
||||
@@ -237,6 +246,21 @@ bool RE_instance_free_next_motion(struct RE_instance *instance)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z)
|
||||
{
|
||||
if (!instance)
|
||||
return false;
|
||||
if (instance->type != RE_ITYPE_BILLBOARD)
|
||||
ERROR("not implemented");
|
||||
// Hack: This works because SetInstanceVertexPos is only used to scale
|
||||
// billboards, and SetInstanceScale is not used for billboards.
|
||||
if (index == 1) {
|
||||
instance->scale[0] = x;
|
||||
instance->scale[1] = y / 2.0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int RE_motion_get_state(struct motion *motion)
|
||||
{
|
||||
return motion ? motion->state : RE_MOTION_STATE_STOP;
|
||||
@@ -269,6 +293,12 @@ bool RE_motion_set_frame_range(struct motion *motion, float begin, float end)
|
||||
return false;
|
||||
motion->frame_begin = begin;
|
||||
motion->frame_end = end;
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -278,6 +308,12 @@ bool RE_motion_set_loop_frame_range(struct motion *motion, float begin, float en
|
||||
return false;
|
||||
motion->loop_frame_begin = begin;
|
||||
motion->loop_frame_end = end;
|
||||
if (motion->instance->type == RE_ITYPE_BILLBOARD) {
|
||||
for (int i = begin; i < end; i++) {
|
||||
if (!RE_renderer_load_billboard_texture(motion->instance->plugin->renderer, i))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+151
-18
@@ -18,12 +18,46 @@
|
||||
#include <cglm/cglm.h>
|
||||
|
||||
#include "system4.h"
|
||||
#include "system4/cg.h"
|
||||
#include "system4/hashtable.h"
|
||||
|
||||
#include "3d_internal.h"
|
||||
#include "asset_manager.h"
|
||||
#include "reign.h"
|
||||
#include "sact.h"
|
||||
#include "vm.h"
|
||||
|
||||
static void init_billboard_mesh(struct RE_renderer *r)
|
||||
{
|
||||
static const GLfloat vertices[] = {
|
||||
// x, y, z, u, v
|
||||
-1.0, 2.0, 0.0, 0.0, 0.0,
|
||||
-1.0, 0.0, 0.0, 0.0, 1.0,
|
||||
1.0, 2.0, 0.0, 1.0, 0.0,
|
||||
1.0, 0.0, 0.0, 1.0, 1.0,
|
||||
};
|
||||
glGenVertexArrays(1, &r->billboard_vao);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
glGenBuffers(1, &r->billboard_attr_buffer);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, r->billboard_attr_buffer);
|
||||
|
||||
glEnableVertexAttribArray(r->shader.vertex);
|
||||
glVertexAttribPointer(r->shader.vertex, 3, GL_FLOAT, GL_FALSE, 20, (const void *)0);
|
||||
glEnableVertexAttribArray(r->vertex_uv);
|
||||
glVertexAttribPointer(r->vertex_uv, 2, GL_FLOAT, GL_FALSE, 20, (const void *)12);
|
||||
glDisableVertexAttribArray(r->vertex_normal);
|
||||
glVertexAttrib3f(r->vertex_normal, 0.0, 0.0, 1.0);
|
||||
glDisableVertexAttribArray(r->vertex_bone_index);
|
||||
glVertexAttribI4i(r->vertex_bone_index, 0, 0, 0, 0);
|
||||
glDisableVertexAttribArray(r->vertex_bone_weight);
|
||||
glVertexAttrib4f(r->vertex_bone_weight, 0.0, 0.0, 0.0, 0.0);
|
||||
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
{
|
||||
struct RE_renderer *r = xcalloc(1, sizeof(struct RE_renderer));
|
||||
@@ -44,14 +78,52 @@ struct RE_renderer *RE_renderer_new(struct texture *texture)
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, texture->w, texture->h);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
init_billboard_mesh(r);
|
||||
r->billboard_textures = ht_create(256);
|
||||
r->last_frame_timestamp = SDL_GetTicks();
|
||||
return r;
|
||||
}
|
||||
|
||||
bool RE_renderer_load_billboard_texture(struct RE_renderer *r, int cg_no)
|
||||
{
|
||||
if (ht_get_int(r->billboard_textures, cg_no, NULL))
|
||||
return true;
|
||||
|
||||
struct cg *cg = asset_cg_load(cg_no);
|
||||
if (!cg)
|
||||
return false;
|
||||
|
||||
struct billboard_texture *bt = xcalloc(1, sizeof(struct billboard_texture));
|
||||
glGenTextures(1, &bt->texture);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, cg->metrics.w, cg->metrics.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, cg->pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
cg_free(cg);
|
||||
ht_put_int(r->billboard_textures, cg_no, bt);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void free_billboard_texture(void *value)
|
||||
{
|
||||
struct billboard_texture *bt = value;
|
||||
glDeleteTextures(1, &bt->texture);
|
||||
free(bt);
|
||||
}
|
||||
|
||||
void RE_renderer_free(struct RE_renderer *r)
|
||||
{
|
||||
glDeleteProgram(r->shader.program);
|
||||
glDeleteRenderbuffers(1, &r->depth_buffer);
|
||||
ht_foreach_value(r->billboard_textures, free_billboard_texture);
|
||||
ht_free_int(r->billboard_textures);
|
||||
glDeleteVertexArrays(1, &r->billboard_vao);
|
||||
glDeleteBuffers(1, &r->billboard_attr_buffer);
|
||||
free(r);
|
||||
}
|
||||
|
||||
@@ -129,6 +201,9 @@ static void animate(struct RE_instance *inst, struct RE_renderer *r, float delta
|
||||
update_motion(inst->motion, delta_frame);
|
||||
update_motion(inst->next_motion, delta_frame);
|
||||
|
||||
if (inst->type == RE_ITYPE_BILLBOARD)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < inst->model->nr_bones; i++) {
|
||||
struct mot_frame mf;
|
||||
calc_motion_frame(inst->motion, i, &mf);
|
||||
@@ -151,30 +226,18 @@ static void animate(struct RE_instance *inst, struct RE_renderer *r, float delta
|
||||
}
|
||||
}
|
||||
|
||||
static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat4 view_transform, mat4 proj_transform, float delta_time)
|
||||
static void render_model(struct RE_instance *inst, struct RE_renderer *r)
|
||||
{
|
||||
if (!inst->draw || !inst->model)
|
||||
struct model *model = inst->model;
|
||||
if (!inst->model)
|
||||
return;
|
||||
|
||||
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]);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
|
||||
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];
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
@@ -186,8 +249,59 @@ static void render_instance(struct RE_instance *inst, struct RE_renderer *r, mat
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
}
|
||||
|
||||
glUseProgram(0);
|
||||
static void render_static_model(struct RE_instance *inst, struct RE_renderer *r)
|
||||
{
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
render_model(inst, r);
|
||||
}
|
||||
|
||||
static void render_skinned_model(struct RE_instance *inst, struct RE_renderer *r, float delta_time)
|
||||
{
|
||||
struct model *model = inst->model;
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
animate(inst, r, delta_time);
|
||||
|
||||
if (model->nr_bones > 0) {
|
||||
glUniform1i(r->has_bones, GL_TRUE);
|
||||
glUniformMatrix4fv(r->bone_matrices, model->nr_bones, GL_FALSE, r->bone_transforms[0][0]);
|
||||
} else {
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
}
|
||||
render_model(inst, r);
|
||||
}
|
||||
|
||||
static void render_billboard(struct RE_instance *inst, struct RE_renderer *r, mat4 view_mat, float delta_time)
|
||||
{
|
||||
animate(inst, r, delta_time);
|
||||
int cg_no = inst->motion->current_frame;
|
||||
struct billboard_texture *bt = ht_get_int(r->billboard_textures, cg_no, NULL);
|
||||
if (!bt)
|
||||
return;
|
||||
|
||||
mat4 local_transform;
|
||||
glm_translate_make(local_transform, inst->pos);
|
||||
mat3 rot;
|
||||
glm_mat4_pick3t(view_mat, rot);
|
||||
glm_mat4_ins3(rot, local_transform);
|
||||
glm_scale(local_transform, inst->scale);
|
||||
|
||||
glUniformMatrix4fv(r->local_transform, 1, GL_FALSE, local_transform[0]);
|
||||
glUniform1i(r->has_bones, GL_FALSE);
|
||||
glUniform1f(r->alpha_mod, inst->alpha);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, bt->texture);
|
||||
glUniform1i(r->shader.texture, 0);
|
||||
glBindVertexArray(r->billboard_vao);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static void render_back_cg(struct texture *dst, struct RE_back_cg *bcg, struct RE_renderer *r)
|
||||
@@ -242,18 +356,37 @@ void RE_render(struct sact_sprite *sp)
|
||||
plugin->proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CW);
|
||||
|
||||
glUseProgram(r->shader.program);
|
||||
glUniformMatrix4fv(r->shader.view_transform, 1, GL_FALSE, view_transform[0]);
|
||||
glUniformMatrix4fv(r->proj_transform, 1, GL_FALSE, plugin->proj_transform[0]);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
for (int i = 0; i < RE_MAX_INSTANCES; i++) {
|
||||
if (plugin->instances[i])
|
||||
render_instance(plugin->instances[i], r, view_transform, plugin->proj_transform, delta_time);
|
||||
struct RE_instance *inst = plugin->instances[i];
|
||||
if (!inst || !inst->draw)
|
||||
continue;
|
||||
switch (inst->type) {
|
||||
case RE_ITYPE_STATIC:
|
||||
render_static_model(inst, r);
|
||||
break;
|
||||
case RE_ITYPE_SKINNED:
|
||||
render_skinned_model(inst, r, delta_time);
|
||||
break;
|
||||
case RE_ITYPE_BILLBOARD:
|
||||
render_billboard(inst, r, view_transform, delta_time);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
plugin->proj_transform[1][1] *= -1;
|
||||
glFrontFace(GL_CCW);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glUseProgram(0);
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
|
||||
|
||||
@@ -218,7 +218,12 @@ static bool ReignEngine_SetInstanceScaleZ(int plugin, int instance, float scale_
|
||||
|
||||
//bool ReignEngine_SetInstanceZBias(int plugin, int instance, float fZBias);
|
||||
//float ReignEngine_GetInstanceZBias(int plugin, int instance);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceVertexPos, int plugin, int instance, int index, float x, float y, float z);
|
||||
|
||||
bool ReignEngine_SetInstanceVertexPos(int plugin, int instance, int index, float x, float y, float z)
|
||||
{
|
||||
return RE_instance_set_vertex_pos(get_instance(plugin, instance), index, x, y, z);
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceDiffuse, int plugin, int instance, float r, float g, float b);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceGlobeDiffuse, int plugin, int instance, float r, float g, float b);
|
||||
HLL_WARN_UNIMPLEMENTED(false, bool, ReignEngine, SetInstanceAmbient, int plugin, int instance, float r, float g, float b);
|
||||
|
||||
Reference in New Issue
Block a user