Flash: add support for sprite objects

This implementation supports only single-frame sprites with a single
child object. As far as I know, all Flash effects in AliceSoft games
meet this requirement.
This commit is contained in:
kichikuou
2023-07-09 17:05:25 +09:00
parent 9543bf1761
commit 4cb374dd5d
4 changed files with 114 additions and 22 deletions
+16
View File
@@ -27,6 +27,22 @@ typedef uint16_t fixedu16;
typedef uint32_t rgb_t; // 0x00BBGGRR
typedef uint32_t rgba_t; // 0xAABBGGRR
static inline float twips_to_float(int32_t twips) {
return twips / 20.0f;
}
static inline float fixed32_to_float(fixed32 f32) {
return f32 / 65536.0f;
}
static inline float fixed16_to_float(fixed16 f16) {
return f16 / 256.0f;
}
static inline fixed16 fixed16_mul(fixed16 a, fixed16 b) {
return (int32_t)a * (int32_t)b / 256;
}
struct swf_rect {
// in twips (1/20 pixel)
int32_t x_min;
+48 -7
View File
@@ -17,6 +17,7 @@
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <cglm/cglm.h>
#include "system4.h"
#include "system4/archive.h"
#include "system4/hashtable.h"
@@ -60,6 +61,10 @@ void parts_flash_free(struct parts_flash *f)
ht_foreach_value(f->bitmaps, free_bitmap);
ht_free_int(f->bitmaps);
}
if (f->sprites) {
ht_foreach_value(f->sprites, free);
ht_free_int(f->sprites);
}
}
bool parts_flash_load(struct parts *parts, struct parts_flash *f, struct string *filename)
@@ -88,6 +93,7 @@ bool parts_flash_load(struct parts *parts, struct parts_flash *f, struct string
f->current_frame = 0;
f->dictionary = ht_create(256);
f->bitmaps = ht_create(64);
f->sprites = ht_create(64);
int width = f->swf->frame_size.x_max / 20;
int height = f->swf->frame_size.y_max / 20;
@@ -147,17 +153,20 @@ static struct parts_flash_object *update_object(struct parts_flash_object *obj,
}
if (tag->flags & PLACE_FLAG_HAS_CHARACTER) {
obj->character_id = tag->character_id;
obj->matrix = (struct swf_matrix) {
.scale_x = 1 << 16,
.scale_y = 1 << 16,
};
glm_mat4_identity(obj->matrix);
obj->color_transform = (struct swf_cxform_with_alpha) {
.mult_terms = { 256, 256, 256, 256 }
};
}
if (tag->flags & PLACE_FLAG_HAS_MATRIX)
obj->matrix = tag->matrix;
if (tag->flags & PLACE_FLAG_HAS_MATRIX) {
obj->matrix[0][0] = fixed32_to_float(tag->matrix.scale_x);
obj->matrix[1][1] = fixed32_to_float(tag->matrix.scale_y);
obj->matrix[0][1] = fixed32_to_float(tag->matrix.rotate_skew_0);
obj->matrix[1][0] = fixed32_to_float(tag->matrix.rotate_skew_1);
obj->matrix[3][0] = twips_to_float(tag->matrix.translate_x);
obj->matrix[3][1] = twips_to_float(tag->matrix.translate_y);
}
if (tag->flags & PLACE_FLAG_HAS_COLOR_TRANSFORM)
obj->color_transform = tag->color_transform;
if (tag->flags & PLACE_FLAG_HAS_BLEND_MODE)
@@ -253,6 +262,35 @@ static void start_sound(struct parts_flash *f, struct swf_tag_start_sound *tag)
audio_play_archive_data(dfile);
}
static void define_sprite(struct parts_flash *f, struct swf_tag_define_sprite *tag)
{
ht_put_int(f->dictionary, tag->sprite_id, tag);
struct ht_slot *slot = ht_put_int(f->sprites, tag->sprite_id, NULL);
if (slot->value)
return; // already defined.
if (tag->frame_count != 1)
ERROR("sprites with multiple frames are not supported");
struct parts_flash_object *obj = NULL;
for (struct swf_tag *t = tag->tags; t && t->type != TAG_END; t = t->next) {
switch (t->type) {
case TAG_SOUND_STREAM_HEAD2:
case TAG_SHOW_FRAME:
// ignored.
break;
case TAG_PLACE_OBJECT2:
if (obj)
ERROR("sprites with multiple objects are not supported");;
obj = update_object(NULL, (struct swf_tag_place_object*)t);
break;
default:
ERROR("unsupported tag %d in sprite", t->type);
}
}
if (!obj)
ERROR("sprite has no PlaceObject2 tag");
slot->value = obj;
}
bool parts_flash_seek(struct parts_flash *f, int frame)
{
if (!f->swf || f->current_frame == frame)
@@ -285,6 +323,9 @@ bool parts_flash_seek(struct parts_flash *f, int frame)
case TAG_DEFINE_SHAPE:
define_shape(f, (struct swf_tag_define_shape*)t);
break;
case TAG_DEFINE_SPRITE:
define_sprite(f, (struct swf_tag_define_sprite*)t);
break;
case TAG_DEFINE_SOUND:
define_sound(f, (struct swf_tag_define_sound*)t);
break;
@@ -302,7 +343,7 @@ bool parts_flash_seek(struct parts_flash *f, int frame)
start_sound(f, (struct swf_tag_start_sound*)t);
break;
default:
ERROR("unknown tag 0x%x", t->type);
ERROR("unknown tag %d", t->type);
}
}
f->tag = t;
+3 -1
View File
@@ -17,6 +17,7 @@
#ifndef SYSTEM4_PARTS_INTERNAL_H
#define SYSTEM4_PARTS_INTERNAL_H
#include <cglm/types.h>
#include "gfx/gfx.h"
#include "gfx/font.h"
#include "queue.h"
@@ -217,7 +218,7 @@ struct parts_flash_object {
TAILQ_ENTRY(parts_flash_object) entry;
uint16_t depth;
uint16_t character_id;
struct swf_matrix matrix;
mat4 matrix;
struct swf_cxform_with_alpha color_transform;
};
@@ -232,6 +233,7 @@ struct parts_flash {
int current_frame;
struct hash_table *dictionary;
struct hash_table *bitmaps; // bitmap character id -> struct texture *
struct hash_table *sprites; // sprite character id -> struct parts_flash_object *
TAILQ_HEAD(, parts_flash_object) display_list;
};
+47 -14
View File
@@ -122,30 +122,60 @@ static void parts_render_flash_shape(struct parts *parts, struct parts_flash *f,
glm_translate(mw_transform, (vec3) { parts->global.pos.x, parts->global.pos.y, 0 });
glm_rotate_z(mw_transform, parts->local.rotation.z, mw_transform);
glm_scale(mw_transform, (vec3){ parts->global.scale.x, parts->global.scale.y, 1.0 });
int x = f->common.origin_offset.x + (obj->matrix.translate_x + tag->bounds.x_min) / 20;
int y = f->common.origin_offset.y + (obj->matrix.translate_y + tag->bounds.y_min) / 20;
int width = (tag->bounds.x_max - tag->bounds.x_min) / 20 * (obj->matrix.scale_x / 65536.0f);
int height = (tag->bounds.y_max - tag->bounds.y_min) / 20 * (obj->matrix.scale_y / 65536.0f);
// TODO: consider matrix.roate_skew
glm_translate(mw_transform, (vec3){ x, y, 0 });
glm_scale(mw_transform, (vec3){ width, height, 1.0 });
glm_translate(mw_transform, (vec3){ f->common.origin_offset.x, f->common.origin_offset.y, 0 });
Rectangle r = { 0, 0, width, height };
glm_mul(mw_transform, obj->matrix, mw_transform);
float blend_rate = (parts->global.alpha / 255.0f) * (obj->color_transform.mult_terms[3] / 256.0f);
glm_translate(mw_transform, (vec3){
twips_to_float(tag->bounds.x_min),
twips_to_float(tag->bounds.y_min),
0});
glm_scale(mw_transform, (vec3){
twips_to_float(tag->bounds.x_max - tag->bounds.x_min),
twips_to_float(tag->bounds.y_max - tag->bounds.y_min),
1.0});
Rectangle r = { 0, 0, src->w, src->h };
float blend_rate = (parts->global.alpha / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[3]);
vec3 add_color = {
(parts->global.add_color.r + obj->color_transform.add_terms[0]) / 255.0f,
(parts->global.add_color.g + obj->color_transform.add_terms[1]) / 255.0f,
(parts->global.add_color.b + obj->color_transform.add_terms[2]) / 255.0f
};
vec3 multiply_color = {
(parts->global.multiply_color.r / 255.0f) * (obj->color_transform.mult_terms[0] / 256.0f),
(parts->global.multiply_color.g / 255.0f) * (obj->color_transform.mult_terms[1] / 256.0f),
(parts->global.multiply_color.b / 255.0f) * (obj->color_transform.mult_terms[2] / 256.0f)
(parts->global.multiply_color.r / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[0]),
(parts->global.multiply_color.g / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[1]),
(parts->global.multiply_color.b / 255.0f) * fixed16_to_float(obj->color_transform.mult_terms[2])
};
parts_render_texture(src, mw_transform, &r, blend_rate, add_color, multiply_color);
}
static void parts_render_flash_sprite(struct parts *parts, struct parts_flash *f, struct parts_flash_object *obj, struct swf_tag_define_sprite *tag)
{
struct parts_flash_object *obj2 = ht_get_int(f->sprites, tag->sprite_id, NULL);
if (!obj)
ERROR("undefined sprite id %d", tag->sprite_id);
struct swf_tag *child = ht_get_int(f->dictionary, obj2->character_id, NULL);
if (!child)
ERROR("character %d is not defined", obj2->character_id);
if (child->type != TAG_DEFINE_SHAPE)
ERROR("unexpected child type %d", child->type);
struct parts_flash_object transform;
glm_mul(obj->matrix, obj2->matrix, transform.matrix);
for (int i = 0; i < 4; i++) {
transform.color_transform.add_terms[i] =
obj->color_transform.add_terms[i] + obj2->color_transform.add_terms[i];
}
for (int i = 0; i < 4; i++) {
transform.color_transform.mult_terms[i] =
fixed16_mul(obj->color_transform.mult_terms[i], obj2->color_transform.mult_terms[i]);
}
parts_render_flash_shape(parts, f, &transform, (struct swf_tag_define_shape*)child);
}
static void parts_render_flash(struct parts *parts, struct parts_flash *f)
{
struct parts_flash_object *obj;
@@ -157,10 +187,13 @@ static void parts_render_flash(struct parts *parts, struct parts_flash *f)
}
switch (tag->type) {
case TAG_DEFINE_SHAPE:
parts_render_flash_shape(parts, f, obj, (struct swf_tag_define_shape *)tag);
parts_render_flash_shape(parts, f, obj, (struct swf_tag_define_shape*)tag);
break;
case TAG_DEFINE_SPRITE:
parts_render_flash_sprite(parts, f, obj, (struct swf_tag_define_sprite*)tag);
break;
default:
ERROR("unknown tag 0x%x", tag->type);
ERROR("unknown tag %d", tag->type);
}
}
}