diff --git a/include/reign.h b/include/reign.h
index 029bdaa..e0ceb7a 100644
--- a/include/reign.h
+++ b/include/reign.h
@@ -195,6 +195,7 @@ struct RE_instance {
vec4 bounding_sphere;
struct RE_instance *shadow_volume_instance;
float z_from_camera;
+ int texture_animation_index;
};
struct RE_plugin *RE_plugin_new(enum RE_plugin_version version);
@@ -214,6 +215,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_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show);
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z);
int RE_instance_get_bone_index(struct RE_instance *instance, const char *name);
bool RE_instance_trans_local_pos_to_world_pos_by_bone(struct RE_instance *instance, int bone, vec3 offset, vec3 out);
diff --git a/shaders/reign.f.glsl b/shaders/reign.f.glsl
index ce603c8..067ce09 100644
--- a/shaders/reign.f.glsl
+++ b/shaders/reign.f.glsl
@@ -177,12 +177,12 @@ void main() {
// Alpha mapping
float alpha = texel.a * color_mod.a * alpha_mod;
if (alpha_mode == ALPHA_TEST) {
- if (alpha < 0.8)
+ if (alpha < 0.75)
discard;
} else if (alpha_mode == ALPHA_MAP_BLEND) {
alpha *= texture(alpha_texture, tex_coord).r;
} else if (alpha_mode == ALPHA_MAP_TEST) {
- if (texture(alpha_texture, tex_coord).r < 0.8)
+ if (texture(alpha_texture, tex_coord).r < 0.1)
discard;
}
diff --git a/src/3d/3d_internal.h b/src/3d/3d_internal.h
index a5287e7..7494e2f 100644
--- a/src/3d/3d_internal.h
+++ b/src/3d/3d_internal.h
@@ -50,8 +50,10 @@ struct model {
};
struct mesh {
+ char *name;
uint32_t flags;
bool is_transparent;
+ bool hidden;
GLuint vao;
GLuint attr_buffer;
GLuint index_buffer;
@@ -66,7 +68,8 @@ struct mesh {
struct material {
uint32_t flags;
bool is_transparent;
- GLuint color_map;
+ int nr_color_maps;
+ GLuint *color_maps;
GLuint specular_map;
GLuint alpha_map;
GLuint light_map;
@@ -92,6 +95,7 @@ struct motion {
float current_frame;
float frame_begin, frame_end;
float loop_frame_begin, loop_frame_end;
+ int texture_index;
};
// Attribute variable locations
@@ -498,6 +502,8 @@ struct mot {
char *name;
uint32_t nr_frames;
uint32_t nr_bones;
+ uint32_t nr_texture_indices;
+ uint32_t *texture_indices; // texture index for each frame
struct mot_bone *motions[];
};
@@ -549,6 +555,7 @@ void amt_free(struct amt *amt);
struct amt_material *amt_find_material(struct amt *amt, const char *name);
void opr_load(uint8_t *data, size_t size, struct pol *pol);
+void txa_load(uint8_t *data, size_t size, struct mot *mot);
// collision.c
diff --git a/src/3d/model.c b/src/3d/model.c
index c53c57a..cb105fb 100644
--- a/src/3d/model.c
+++ b/src/3d/model.c
@@ -83,7 +83,6 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na
{
struct archive_data *dfile = RE_get_aar_entry(aar, path, name, "");
if (!dfile) {
- WARNING("cannot load texture %s\\%s", path, name);
return 0;
}
struct cg *cg = cg_load_data(dfile);
@@ -110,6 +109,35 @@ static GLuint load_texture(struct archive *aar, const char *path, const char *na
return texture;
}
+static GLuint *load_texture_list(struct archive *aar, const char *path, const char *name, int *nr_textures_out, bool *has_alpha_out)
+{
+ // Load the base texture (e.g. face.png)
+ GLuint tex = load_texture(aar, path, name, has_alpha_out);
+ if (!tex)
+ return NULL;
+ GLuint *textures = xmalloc(10 * sizeof(GLuint));
+ int nr_textures = 0;
+ textures[nr_textures++] = tex;
+
+ // Try to load additional textures (e.g. face[1].png, face[2].png, etc.)
+ const char *ext = strrchr(name, '.');
+ if (ext) {
+ for (;;) {
+ char next_name[100];
+ snprintf(next_name, sizeof(next_name), "%.*s[%d]%s", (int)(ext - name), name, nr_textures, ext);
+ tex = load_texture(aar, path, next_name, NULL);
+ if (!tex)
+ break;
+ textures[nr_textures++] = tex;
+ if (nr_textures % 10 == 0) {
+ textures = xrealloc(textures, (nr_textures + 10) * sizeof(GLuint));
+ }
+ }
+ }
+ *nr_textures_out = nr_textures;
+ return textures;
+}
+
static bool init_material(struct material *material, const struct pol_material *m, struct amt *amt, struct archive *aar, const char *path)
{
material->flags = m->flags;
@@ -118,8 +146,8 @@ static bool init_material(struct material *material, const struct pol_material *
return false;
}
bool has_alpha;
- material->color_map = load_texture(aar, path, m->textures[COLOR_MAP], &has_alpha);
- if (!material->color_map)
+ material->color_maps = load_texture_list(aar, path, m->textures[COLOR_MAP], &material->nr_color_maps, &has_alpha);
+ if (!material->color_maps)
return false;
if (m->textures[SPECULAR_MAP])
@@ -162,8 +190,10 @@ static bool init_material(struct material *material, const struct pol_material *
static void destroy_material(struct material *material)
{
- if (material->color_map)
- glDeleteTextures(1, &material->color_map);
+ if (material->color_maps) {
+ glDeleteTextures(material->nr_color_maps, material->color_maps);
+ free(material->color_maps);
+ }
if (material->specular_map)
glDeleteTextures(1, &material->specular_map);
if (material->alpha_map)
@@ -317,6 +347,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
}
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->name = xstrdup(m->name);
mesh->flags = m->flags;
mesh->material = material;
if (re_plugin_version == RE_REIGN_PLUGIN)
@@ -393,6 +424,7 @@ static void add_mesh(struct model *model, struct pol_mesh *m, uint32_t material_
static void destroy_mesh(struct mesh *mesh)
{
+ free(mesh->name);
glDeleteVertexArrays(1, &mesh->vao);
glDeleteBuffers(1, &mesh->attr_buffer);
if (mesh->index_buffer)
@@ -661,8 +693,10 @@ struct model *model_create_sphere(int r, int g, int b, int a)
model->materials = xcalloc(1, sizeof(struct material));
struct material *material = &model->materials[0];
material->is_transparent = true;
- glGenTextures(1, &material->color_map);
- glBindTexture(GL_TEXTURE_2D, material->color_map);
+ material->color_maps = xmalloc(sizeof(GLuint));
+ material->nr_color_maps = 1;
+ glGenTextures(1, material->color_maps);
+ glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
uint8_t pixel[4] = {r, g, b, a};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
glBindTexture(GL_TEXTURE_2D, 0);
@@ -678,18 +712,18 @@ static int cmp_motions_by_bone_id(const void *lhs, const void *rhs)
static struct mot *mot_load(const char *name, struct model *model, struct archive *aar)
{
- struct archive_data *dfile = RE_get_aar_entry(aar, model->path, name, ".MOT");
- if (!dfile) {
+ struct archive_data *mot_file = RE_get_aar_entry(aar, model->path, name, ".MOT");
+ if (!mot_file) {
WARNING("%s\\%s.MOT: not found", model->path, name);
return NULL;
}
- struct mot *mot = mot_parse(dfile->data, dfile->size, name);
+ struct mot *mot = mot_parse(mot_file->data, mot_file->size, name);
if (!mot) {
- WARNING("%s: parse error", dfile->name);
- archive_free_data(dfile);
+ WARNING("%s: parse error", mot_file->name);
+ archive_free_data(mot_file);
return NULL;
}
- archive_free_data(dfile);
+ archive_free_data(mot_file);
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);
@@ -708,6 +742,13 @@ static struct mot *mot_load(const char *name, struct model *model, struct archiv
}
qsort(mot->motions, mot->nr_bones, sizeof(struct mot_bone *), cmp_motions_by_bone_id);
+ // Load optional .txa file.
+ struct archive_data *txa_file = RE_get_aar_entry(aar, model->path, name, ".txa");
+ if (txa_file) {
+ txa_load(txa_file->data, txa_file->size, mot);
+ archive_free_data(txa_file);
+ }
+
return mot;
}
diff --git a/src/3d/parser.c b/src/3d/parser.c
index 862a884..b03b7f2 100644
--- a/src/3d/parser.c
+++ b/src/3d/parser.c
@@ -14,6 +14,7 @@
* along with this program; if not, see .
*/
+#include
#include
#include
#include
@@ -424,6 +425,9 @@ void mot_free(struct mot *mot)
free(mot->motions[i]->name);
free(mot->motions[i]);
}
+ if (mot->texture_indices) {
+ free(mot->texture_indices);
+ }
free(mot);
}
@@ -487,7 +491,10 @@ struct amt_material *amt_find_material(struct amt *amt, const char *name)
void opr_load(uint8_t *data, size_t size, struct pol *pol)
{
- bool *selected = xcalloc(pol->nr_meshes, sizeof(bool));
+ bool *selected = xmalloc(pol->nr_meshes * sizeof(bool));
+ for (int i = 0; i < pol->nr_meshes; i++) {
+ selected[i] = !!pol->meshes[i];
+ }
while (size > 0) {
char line[200];
uint8_t *nl = memchr(data, '\n', size);
@@ -561,3 +568,30 @@ void opr_load(uint8_t *data, size_t size, struct pol *pol)
}
free(selected);
}
+
+void txa_load(uint8_t *data, size_t size, struct mot *mot)
+{
+ uint32_t count = 0;
+ uint32_t *indices = xmalloc(100 * sizeof(uint32_t));
+ const char *p = (const char *)data;
+ while (p < (const char *)data + size) {
+ if (isdigit(*p)) {
+ uint32_t n = 0;
+ while (p < (const char *)data + size && isdigit(*p)) {
+ n = n * 10 + (*p - '0');
+ p++;
+ }
+ indices[count++] = n;
+ if (count % 100 == 0) {
+ indices = xrealloc(indices, (count + 100) * sizeof(uint32_t));
+ }
+ } else if (isspace(*p)) {
+ p++;
+ } else {
+ WARNING("invalid character in txa file: '%c'", *p);
+ p++;
+ }
+ }
+ mot->nr_texture_indices = count;
+ mot->texture_indices = indices;
+}
diff --git a/src/3d/reign.c b/src/3d/reign.c
index 1c415be..472ba71 100644
--- a/src/3d/reign.c
+++ b/src/3d/reign.c
@@ -131,12 +131,18 @@ static void update_motion(struct motion *m, float delta_frame)
}
break;
case RE_MOTION_STATE_LOOP:
- if (m->current_frame > m->loop_frame_end)
+ if (m->loop_frame_begin == m->loop_frame_end)
+ m->current_frame = m->loop_frame_begin;
+ else 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);
}
+
+ uint32_t anim_frame = m->current_frame - m->frame_begin;
+ m->texture_index = (m->mot && anim_frame < m->mot->nr_texture_indices)
+ ? m->mot->texture_indices[anim_frame] : 0;
}
static void calc_motion_frame(struct motion *m, int bone, struct mot_frame *out)
@@ -326,6 +332,7 @@ bool RE_build_model(struct RE_plugin *plugin, int elapsed_ms)
if (inst->type == RE_ITYPE_SKINNED)
update_bones(inst);
+ inst->texture_animation_index = inst->motion ? inst->motion->texture_index : 0;
}
// Update particle effects in a second pass, because it uses the results of
@@ -502,6 +509,19 @@ bool RE_instance_free_next_motion(struct RE_instance *instance)
return true;
}
+bool RE_instance_set_mesh_show(struct RE_instance *instance, const char *mesh_name, bool show)
+{
+ if (!instance || !instance->model)
+ return false;
+ for (int i = 0; i < instance->model->nr_meshes; i++) {
+ if (!strcmp(instance->model->meshes[i].name, mesh_name)) {
+ instance->model->meshes[i].hidden = !show;
+ return true;
+ }
+ }
+ return false;
+}
+
bool RE_instance_set_vertex_pos(struct RE_instance *instance, int index, float x, float y, float z)
{
if (!instance)
diff --git a/src/3d/renderer.c b/src/3d/renderer.c
index 8abfa92..30cf0a8 100644
--- a/src/3d/renderer.c
+++ b/src/3d/renderer.c
@@ -335,6 +335,12 @@ void RE_calc_view_matrix(struct RE_camera *camera, vec3 up, mat4 out)
glm_look(camera->pos, front, up, out);
}
+static bool should_draw_shadow(struct mesh *mesh, struct material *material)
+{
+ return !(mesh->flags & (MESH_ALPHA | MESH_BOTH | MESH_SPRITE))
+ && !(material->flags & (MATERIAL_ALPHA | MATERIAL_SPRITE));
+}
+
static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum draw_phase phase)
{
struct model *model = inst->model;
@@ -362,6 +368,8 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
for (int i = 0; i < model->nr_meshes; i++) {
struct mesh *mesh = &model->meshes[i];
+ if (mesh->hidden)
+ continue;
struct material *material = &model->materials[mesh->material];
bool is_transparent = mesh->is_transparent || inst->alpha < 1.0f;
if (phase != (is_transparent ? DRAW_TRANSPARENT : DRAW_OPAQUE))
@@ -396,10 +404,12 @@ static void render_model(struct RE_instance *inst, struct RE_renderer *r, enum d
glUniform3fv(r->rim_color, 1, material->rim_color);
glActiveTexture(GL_TEXTURE0 + COLOR_TEXTURE_UNIT);
- glBindTexture(GL_TEXTURE_2D, material->color_map);
+ int animation_index = inst->texture_animation_index < material->nr_color_maps
+ ? inst->texture_animation_index : 0;
+ glBindTexture(GL_TEXTURE_2D, material->color_maps[animation_index]);
glUniform1i(r->texture, COLOR_TEXTURE_UNIT);
- if (draw_shadow && !(mesh->flags & MESH_BOTH))
+ if (draw_shadow && should_draw_shadow(mesh, material))
glUniform1f(r->shadow_darkness, material->shadow_darkness);
else
glUniform1f(r->shadow_darkness, 0.0f);
@@ -614,7 +624,7 @@ static void render_polygon_particles(struct RE_renderer *r, struct RE_instance *
struct material *material = &model->materials[mesh->material];
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, material->color_map);
+ glBindTexture(GL_TEXTURE_2D, material->color_maps[0]);
glUniform1i(r->texture, 0);
glBindVertexArray(mesh->vao);
diff --git a/src/hll/ReignEngine.c b/src/hll/ReignEngine.c
index 8ec90e7..f5a8ee4 100644
--- a/src/hll/ReignEngine.c
+++ b/src/hll/ReignEngine.c
@@ -1891,7 +1891,11 @@ static bool TapirEngine_GetInstancePathLine(int plugin, int instance, struct pag
}
//bool TapirEngine_CreateInstancePathLineList(int PluginNumber, int InstanceNumber, int PathInstanceNumber);
-//bool TapirEngine_SetInstanceMeshShow(int PluginNumber, int InstanceNumber, struct string *pIMeshName, bool Show);
+
+static bool TapirEngine_SetInstanceMeshShow(int plugin, int instance, struct string *mesh_name, bool show)
+{
+ return RE_instance_set_mesh_show(get_instance(plugin, instance), mesh_name->text, show);
+}
static bool TapirEngine_SetDrawOption(int plugin_number, int draw_option, int param)
{
@@ -2303,7 +2307,7 @@ HLL_LIBRARY(ReignEngine, REIGN_EXPORTS,
HLL_EXPORT(OptimizeInstancePathLine, TapirEngine_OptimizeInstancePathLine), \
HLL_EXPORT(GetInstancePathLine, TapirEngine_GetInstancePathLine), \
HLL_TODO_EXPORT(CreateInstancePathLineList, TapirEngine_CreateInstancePathLineList), \
- HLL_TODO_EXPORT(SetInstanceMeshShow, TapirEngine_SetInstanceMeshShow), \
+ HLL_EXPORT(SetInstanceMeshShow, TapirEngine_SetInstanceMeshShow), \
HLL_EXPORT(SetDrawOption, TapirEngine_SetDrawOption), \
HLL_EXPORT(GetDrawOption, TapirEngine_GetDrawOption), \
HLL_TODO_EXPORT(SetDebugMode, TapirEngine_SetDebugMode), \