mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
Merge pull request #337 from kichikuou/flat
Parts: Stop motion support and rendering fixes
This commit is contained in:
+84
-4
@@ -62,10 +62,15 @@ void parts_flat_free(struct parts_flat *f)
|
||||
free_string(f->name);
|
||||
flat_layer_state_free(f->root_state);
|
||||
if (f->textures) {
|
||||
for (size_t i = 0; i < f->nr_textures; i++)
|
||||
for (size_t i = 0; i < f->nr_libraries; i++)
|
||||
gfx_delete_texture(&f->textures[i]);
|
||||
free(f->textures);
|
||||
}
|
||||
if (f->stop_motion_frames) {
|
||||
for (size_t i = 0; i < f->nr_libraries; i++)
|
||||
free(f->stop_motion_frames[i].lib_indices);
|
||||
free(f->stop_motion_frames);
|
||||
}
|
||||
}
|
||||
|
||||
int parts_flat_find_library(struct flat *fl, const char *name)
|
||||
@@ -192,6 +197,75 @@ static bool flat_advance_children(struct flat *fl,
|
||||
return changed;
|
||||
}
|
||||
|
||||
// Build the CG list for a STOP_MOTION library: frame 0 is the base
|
||||
// library named in `sm->library_name`; subsequent frames are numbered
|
||||
// variants `${stem}_NN.${ext}`, terminated by the first missing entry.
|
||||
static void build_stop_motion_frames(struct parts_flat *f, int sm_lib_idx)
|
||||
{
|
||||
struct flat_stop_motion *sm = &f->flat->libraries[sm_lib_idx].stop_motion;
|
||||
const char *base = sm->library_name->text;
|
||||
|
||||
int base_idx = parts_flat_find_library(f->flat, base);
|
||||
if (base_idx < 0)
|
||||
return;
|
||||
|
||||
const char *dot = strrchr(base, '.');
|
||||
if (!dot) {
|
||||
WARNING("flat: invalid STOP_MOTION library name '%s'", base);
|
||||
return;
|
||||
}
|
||||
|
||||
int *indices = xmalloc(sizeof(int));
|
||||
indices[0] = base_idx;
|
||||
int count = 1;
|
||||
|
||||
char name_buf[512];
|
||||
for (int i = 1; ; i++) {
|
||||
snprintf(name_buf, sizeof(name_buf), "%.*s_%02d.%s",
|
||||
(int)(dot - base), base, i, dot + 1);
|
||||
int idx = parts_flat_find_library(f->flat, name_buf);
|
||||
if (idx < 0)
|
||||
break;
|
||||
indices = xrealloc(indices, (count + 1) * sizeof(int));
|
||||
indices[count++] = idx;
|
||||
}
|
||||
|
||||
f->stop_motion_frames[sm_lib_idx].lib_indices = indices;
|
||||
f->stop_motion_frames[sm_lib_idx].count = count;
|
||||
}
|
||||
|
||||
static int stop_motion_frame_index(int loop_type, int span, int local, int total)
|
||||
{
|
||||
if (total <= 1)
|
||||
return 0;
|
||||
int idx = (span > 0) ? local / span : 0;
|
||||
switch (loop_type) {
|
||||
case 0: // stop at last frame
|
||||
return min(idx, total - 1);
|
||||
case 1: // loop
|
||||
return idx % total;
|
||||
case 2: { // ping-pong
|
||||
int period = total * 2 - 2;
|
||||
idx = idx % period;
|
||||
return idx < total ? idx : period - idx;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int parts_flat_stop_motion_get_cg_lib(struct parts_flat *f, int sm_lib_idx, int local)
|
||||
{
|
||||
if (sm_lib_idx < 0 || (size_t)sm_lib_idx >= f->nr_libraries)
|
||||
return -1;
|
||||
struct flat_stop_motion_frames *frames = &f->stop_motion_frames[sm_lib_idx];
|
||||
if (frames->count <= 0)
|
||||
return -1;
|
||||
struct flat_stop_motion *sm = &f->flat->libraries[sm_lib_idx].stop_motion;
|
||||
int frame = stop_motion_frame_index(sm->loop_type, sm->span, local, frames->count);
|
||||
return frames->lib_indices[frame];
|
||||
}
|
||||
|
||||
bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *filename)
|
||||
{
|
||||
if (f->flat) {
|
||||
@@ -221,8 +295,10 @@ bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *f
|
||||
f->root_state = flat_layer_state_new(f->flat->nr_timelines);
|
||||
|
||||
// Load textures for CG libraries.
|
||||
f->nr_textures = f->flat->nr_libraries;
|
||||
f->textures = xcalloc(f->nr_textures, sizeof(Texture));
|
||||
f->nr_libraries = f->flat->nr_libraries;
|
||||
f->textures = xcalloc(f->nr_libraries, sizeof(Texture));
|
||||
f->stop_motion_frames = xcalloc(f->nr_libraries,
|
||||
sizeof(struct flat_stop_motion_frames));
|
||||
for (size_t i = 0; i < f->flat->nr_libraries; i++) {
|
||||
struct flat_library *lib = &f->flat->libraries[i];
|
||||
if (lib->type != FLAT_LIB_CG)
|
||||
@@ -235,6 +311,10 @@ bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *f
|
||||
gfx_init_texture_with_cg(&f->textures[i], cg);
|
||||
cg_free(cg);
|
||||
}
|
||||
for (size_t i = 0; i < f->flat->nr_libraries; i++) {
|
||||
if (f->flat->libraries[i].type == FLAT_LIB_STOP_MOTION)
|
||||
build_stop_motion_frames(f, (int)i);
|
||||
}
|
||||
|
||||
// TODO: The original engine performs per-frame hit testing against
|
||||
// the flat's visible sprites (with full transform chain), rather than
|
||||
@@ -244,7 +324,7 @@ bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *f
|
||||
if (w <= 0 || h <= 0) {
|
||||
w = 0;
|
||||
h = 0;
|
||||
for (size_t i = 0; i < f->nr_textures; i++) {
|
||||
for (size_t i = 0; i < f->nr_libraries; i++) {
|
||||
if (f->textures[i].w > w)
|
||||
w = f->textures[i].w;
|
||||
if (f->textures[i].h > h)
|
||||
|
||||
@@ -310,6 +310,13 @@ struct flat_layer_state {
|
||||
size_t nr_timelines;
|
||||
};
|
||||
|
||||
// Per-frame CG list for a STOP_MOTION library. lib_indices[k] is
|
||||
// the library index of the CG to display at frame k.
|
||||
struct flat_stop_motion_frames {
|
||||
int *lib_indices;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct parts_flat {
|
||||
struct parts_common common;
|
||||
struct string *name;
|
||||
@@ -320,8 +327,10 @@ struct parts_flat {
|
||||
int end_frame;
|
||||
int pending_seek_delta;
|
||||
struct flat_layer_state *root_state;
|
||||
size_t nr_libraries;
|
||||
Texture *textures; // indexed by library index (only CG libs have valid textures)
|
||||
size_t nr_textures;
|
||||
// Indexed by library index. Only entries for STOP_MOTION libraries have lib_indices populated.
|
||||
struct flat_stop_motion_frames *stop_motion_frames;
|
||||
};
|
||||
|
||||
struct parts_movie {
|
||||
@@ -559,6 +568,7 @@ void parts_flat_free(struct parts_flat *f);
|
||||
bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *filename);
|
||||
bool parts_flat_update(struct parts_flat *f, int passed_time);
|
||||
int parts_flat_find_library(struct flat *fl, const char *name);
|
||||
int parts_flat_stop_motion_get_cg_lib(struct parts_flat *f, int sm_lib_idx, int local);
|
||||
|
||||
// layoutbox.c
|
||||
void parts_do_layout(struct parts *parts);
|
||||
|
||||
+76
-49
@@ -30,15 +30,6 @@
|
||||
|
||||
#include "parts_internal.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI (3.14159265358979323846)
|
||||
#endif
|
||||
|
||||
static inline float deg2rad(float deg)
|
||||
{
|
||||
return deg * (M_PI / 180.0);
|
||||
}
|
||||
|
||||
static struct {
|
||||
struct shader shader;
|
||||
GLint blend_rate;
|
||||
@@ -97,7 +88,7 @@ static void parts_render_texture(struct texture *texture, mat4 mw_transform, Rec
|
||||
// Calculate the inverse of the clipper's world matrix.
|
||||
mat4 clip_mw = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(clip_mw, (vec3) { clipper->global.pos.x, clipper->global.pos.y, 0 });
|
||||
glm_rotate_z(clip_mw, clipper->local.rotation.z * (M_PI/180.0), clip_mw);
|
||||
glm_rotate_z(clip_mw, glm_rad(clipper->local.rotation.z), clip_mw);
|
||||
glm_scale(clip_mw, (vec3){ clipper->global.scale.x, clipper->global.scale.y, 1.0 });
|
||||
glm_translate(clip_mw, (vec3){ c_common->origin_offset.x, c_common->origin_offset.y, 0 });
|
||||
glm_scale(clip_mw, (vec3){ c_common->w, c_common->h, 1.0 });
|
||||
@@ -157,7 +148,7 @@ static void parts_render_cg(struct parts *parts, struct parts_common *common)
|
||||
// FIXME: need perspective for 3D rotate
|
||||
//glm_rotate_x(mw_transform, parts->rotation.x, mw_transform);
|
||||
//glm_rotate_y(mw_transform, parts->rotation.y, mw_transform);
|
||||
glm_rotate_z(mw_transform, parts->local.rotation.z * (M_PI/180.0), mw_transform);
|
||||
glm_rotate_z(mw_transform, glm_rad(parts->local.rotation.z), mw_transform);
|
||||
glm_scale(mw_transform, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0 });
|
||||
glm_translate(mw_transform, (vec3){ common->origin_offset.x, common->origin_offset.y, 0 });
|
||||
glm_scale(mw_transform, (vec3){ common->w, common->h, 1.0 });
|
||||
@@ -185,14 +176,49 @@ static void parts_render_cg(struct parts *parts, struct parts_common *common)
|
||||
static void render_flat_layer(struct parts *parts, struct parts_flat *f,
|
||||
struct flat_layer_state *state,
|
||||
struct flat_timeline *timelines, size_t nr_timelines,
|
||||
mat4 parent, float parent_alpha);
|
||||
mat4 parent, float parent_alpha, int parent_draw_filter);
|
||||
|
||||
static void render_flat_cg(struct parts *parts, Texture *tex,
|
||||
struct flat_key_data_graphic *key, mat4 combined, float alpha, int draw_filter)
|
||||
{
|
||||
if (!tex->handle)
|
||||
return;
|
||||
|
||||
set_draw_filter_blend_func(draw_filter);
|
||||
|
||||
mat4 render_m;
|
||||
glm_mat4_copy(combined, render_m);
|
||||
// Kill the Z-output row to pin clip_z at the near plane, avoiding
|
||||
// near/far clipping of 3D-rotated sprites.
|
||||
render_m[0][2] = render_m[1][2] = render_m[2][2] = render_m[3][2] = 0.0f;
|
||||
// area_x/area_y select a sub-rectangle of the texture atlas, but
|
||||
// should not shift the on-screen position. This translation cancels
|
||||
// the offset that the sub-rect's top-left would otherwise introduce.
|
||||
glm_translate(render_m, (vec3){ -(float)key->area_x, -(float)key->area_y, 0.0f });
|
||||
glm_scale(render_m, (vec3){ tex->w, tex->h, 1.0f });
|
||||
|
||||
Rectangle rect;
|
||||
if (key->area_width && key->area_height) {
|
||||
rect = (Rectangle){ key->area_x, key->area_y, key->area_width, key->area_height };
|
||||
} else {
|
||||
rect = (Rectangle){ 0, 0, tex->w, tex->h };
|
||||
}
|
||||
|
||||
vec3 add_color = { key->add_r / 255.0f, key->add_g / 255.0f, key->add_b / 255.0f };
|
||||
vec3 mul_color = { key->mul_r / 255.0f, key->mul_g / 255.0f, key->mul_b / 255.0f };
|
||||
parts_render_texture(tex, render_m, &rect, alpha, add_color, mul_color,
|
||||
draw_filter, parts->alpha_clipper_parts_no);
|
||||
|
||||
if (draw_filter != PARTS_DRAW_FILTER_NORMAL)
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
|
||||
static void render_flat_item(struct parts *parts, struct parts_flat *f,
|
||||
struct flat_layer_state *state, size_t tl_idx,
|
||||
struct flat_timeline *tl,
|
||||
struct flat_key_data_graphic *key,
|
||||
mat4 parent, float parent_alpha)
|
||||
struct flat_timeline *tl, int local,
|
||||
mat4 parent, float parent_alpha, int parent_draw_filter)
|
||||
{
|
||||
struct flat_key_data_graphic *key = &tl->graphic.keys[local];
|
||||
int lib_idx = parts_flat_find_library(f->flat, tl->library_name->text);
|
||||
if (lib_idx < 0 || (size_t)lib_idx >= f->flat->nr_libraries)
|
||||
return;
|
||||
@@ -202,56 +228,58 @@ static void render_flat_item(struct parts *parts, struct parts_flat *f,
|
||||
float pos_x = (f->flat->hdr.version > 4) ? key->pos_x.f : (float)key->pos_x.i;
|
||||
float pos_y = (f->flat->hdr.version > 4) ? key->pos_y.f : (float)key->pos_y.i;
|
||||
|
||||
// Per-key local matrix:
|
||||
// M_layer = T(pos) * Rz * Rx * Ry * Scale * T(-origin) * ReverseScale
|
||||
// The original engine projects 3D-rotated sprites through a proper
|
||||
// perspective, but we use an orthographic approximation for simplicity.
|
||||
mat4 layer_m = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(layer_m, (vec3){ pos_x, pos_y, 0 });
|
||||
glm_rotate_z(layer_m, deg2rad(key->angle_z), layer_m);
|
||||
if (key->angle_z != 0)
|
||||
glm_rotate_z(layer_m, glm_rad(key->angle_z), layer_m);
|
||||
if (key->angle_x != 0)
|
||||
glm_rotate_x(layer_m, glm_rad(-key->angle_x), layer_m);
|
||||
if (key->angle_y != 0)
|
||||
glm_rotate_y(layer_m, glm_rad(key->angle_y), layer_m);
|
||||
glm_scale(layer_m, (vec3){ key->scale_x, key->scale_y, 1.0f });
|
||||
glm_translate(layer_m, (vec3){ -(float)key->origin_x, -(float)key->origin_y, 0 });
|
||||
if (key->reverse_lr || key->reverse_tb) {
|
||||
glm_scale(layer_m, (vec3){
|
||||
key->reverse_lr ? -1.0f : 1.0f,
|
||||
key->reverse_tb ? -1.0f : 1.0f,
|
||||
1.0f });
|
||||
}
|
||||
|
||||
mat4 combined;
|
||||
glm_mat4_mul(parent, layer_m, combined);
|
||||
|
||||
float alpha = parent_alpha * key->alpha / 255.0f;
|
||||
|
||||
// Effective draw filter: this key's own filter overrides for its subtree;
|
||||
// otherwise the cascading parent filter persists.
|
||||
int draw_filter = key->draw_filter != PARTS_DRAW_FILTER_NORMAL
|
||||
? key->draw_filter : parent_draw_filter;
|
||||
|
||||
switch (lib->type) {
|
||||
case FLAT_LIB_CG: {
|
||||
if ((size_t)lib_idx >= f->nr_textures || !f->textures[lib_idx].handle)
|
||||
return;
|
||||
Texture *tex = &f->textures[lib_idx];
|
||||
|
||||
set_draw_filter_blend_func(key->draw_filter);
|
||||
|
||||
mat4 render_m;
|
||||
glm_mat4_copy(combined, render_m);
|
||||
glm_scale(render_m, (vec3){ tex->w, tex->h, 1.0f });
|
||||
|
||||
Rectangle rect;
|
||||
if (key->area_width && key->area_height) {
|
||||
rect = (Rectangle){ key->area_x, key->area_y, key->area_width, key->area_height };
|
||||
} else {
|
||||
rect = (Rectangle){ 0, 0, tex->w, tex->h };
|
||||
}
|
||||
|
||||
vec3 add_color = { key->add_r / 255.0f, key->add_g / 255.0f, key->add_b / 255.0f };
|
||||
vec3 mul_color = { key->mul_r / 255.0f, key->mul_g / 255.0f, key->mul_b / 255.0f };
|
||||
parts_render_texture(tex, render_m, &rect, alpha, add_color, mul_color,
|
||||
key->draw_filter, parts->alpha_clipper_parts_no);
|
||||
|
||||
if (key->draw_filter != PARTS_DRAW_FILTER_NORMAL)
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
case FLAT_LIB_CG:
|
||||
render_flat_cg(parts, &f->textures[lib_idx], key, combined, alpha, draw_filter);
|
||||
break;
|
||||
}
|
||||
case FLAT_LIB_TIMELINE: {
|
||||
struct flat_layer_state *child = state->children[tl_idx];
|
||||
if (child) {
|
||||
render_flat_layer(parts, f, child,
|
||||
lib->timeline.timelines,
|
||||
lib->timeline.nr_timelines,
|
||||
combined, alpha);
|
||||
combined, alpha, draw_filter);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// TODO: support FLAT_LIB_STOP_MOTION and FLAT_LIB_EMITTER
|
||||
case FLAT_LIB_STOP_MOTION: {
|
||||
int cg_idx = parts_flat_stop_motion_get_cg_lib(f, lib_idx, local);
|
||||
if (cg_idx >= 0 && (size_t)cg_idx < f->nr_libraries)
|
||||
render_flat_cg(parts, &f->textures[cg_idx], key, combined, alpha, draw_filter);
|
||||
break;
|
||||
}
|
||||
// TODO: support FLAT_LIB_EMITTER
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -260,7 +288,7 @@ static void render_flat_item(struct parts *parts, struct parts_flat *f,
|
||||
static void render_flat_layer(struct parts *parts, struct parts_flat *f,
|
||||
struct flat_layer_state *state,
|
||||
struct flat_timeline *timelines, size_t nr_timelines,
|
||||
mat4 parent, float parent_alpha)
|
||||
mat4 parent, float parent_alpha, int parent_draw_filter)
|
||||
{
|
||||
// reverse order for correct z-ordering
|
||||
for (size_t i = nr_timelines; i-- > 0;) {
|
||||
@@ -273,9 +301,8 @@ static void render_flat_layer(struct parts *parts, struct parts_flat *f,
|
||||
|
||||
if (local >= (int)tl->graphic.count)
|
||||
continue;
|
||||
struct flat_key_data_graphic *key = &tl->graphic.keys[local];
|
||||
|
||||
render_flat_item(parts, f, state, i, tl, key, parent, parent_alpha);
|
||||
render_flat_item(parts, f, state, i, tl, local, parent, parent_alpha, parent_draw_filter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,12 +313,12 @@ static void parts_render_flat(struct parts *parts, struct parts_flat *f)
|
||||
|
||||
mat4 base = GLM_MAT4_IDENTITY_INIT;
|
||||
glm_translate(base, (vec3){ parts->global.pos.x, parts->global.pos.y, 0 });
|
||||
glm_rotate_z(base, deg2rad(parts->local.rotation.z), base);
|
||||
glm_rotate_z(base, glm_rad(parts->local.rotation.z), base);
|
||||
glm_scale(base, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0f });
|
||||
|
||||
render_flat_layer(parts, f, f->root_state,
|
||||
f->flat->timelines, f->flat->nr_timelines,
|
||||
base, parts->global.alpha / 255.0f);
|
||||
base, parts->global.alpha / 255.0f, PARTS_DRAW_FILTER_NORMAL);
|
||||
}
|
||||
|
||||
static void parts_render_flash_shape(struct parts *parts, struct parts_flash *f, struct parts_flash_object *obj, struct swf_tag_define_shape *tag)
|
||||
|
||||
Reference in New Issue
Block a user