diff --git a/include/swf.h b/include/swf.h index 8b42421..91e7606 100644 --- a/include/swf.h +++ b/include/swf.h @@ -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; diff --git a/src/parts/flash.c b/src/parts/flash.c index 853a334..910cdb8 100644 --- a/src/parts/flash.c +++ b/src/parts/flash.c @@ -17,6 +17,7 @@ #include #include #include +#include #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; diff --git a/src/parts/parts_internal.h b/src/parts/parts_internal.h index b57cfba..9aab74e 100644 --- a/src/parts/parts_internal.h +++ b/src/parts/parts_internal.h @@ -17,6 +17,7 @@ #ifndef SYSTEM4_PARTS_INTERNAL_H #define SYSTEM4_PARTS_INTERNAL_H +#include #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; }; diff --git a/src/parts/render.c b/src/parts/render.c index c0dd283..32a6ca8 100644 --- a/src/parts/render.c +++ b/src/parts/render.c @@ -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); } } }