PartsEngine: Implement movie parts

This adds PARTS_MOVIE type which creates a SACT sprite as a render
target for DrawMovie.

The GL texture is shared between the SACT sprite and parts_common, so
movie frames rendered to the sprite are directly visible through the
parts engine's rendering path.
This commit is contained in:
kichikuou
2026-04-09 13:06:07 +09:00
parent 9192bc80c5
commit 96d46069ce
7 changed files with 113 additions and 2 deletions
+3
View File
@@ -116,6 +116,9 @@ bool PE_Load(struct page **buffer);
// GUIEngine
int PE_GetFreeNumber(void);
bool PE_IsExist(int parts_no);
// PartsFunc interface
bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state);
int PE_get_movie_sprite(int parts_no, int state);
// construction.c
bool PE_AddCreateToPartsConstructionProcess(int parts_no, int w, int h, int state);
+34 -1
View File
@@ -18,6 +18,7 @@
#include "system4/ain.h"
#include "vm/page.h"
#include "parts.h"
#include "hll.h"
@@ -96,7 +97,39 @@ static void PartsEngine_AddComponentMotionPos(int parts_no, float begin_x, float
begin_t, end_t, curve_name);
}
HLL_WARN_UNIMPLEMENTED(0, int, PartsEngine, PartsFunc, int func_id, struct page **array_int, struct page **array_float, struct page **array_string);
// Generic dispatch function for PartsEngine operations.
// func_id selects the operation; arguments and return values are passed
// through three typed arrays (int/bool, float, string).
//
// Calling convention (from game script):
// 1. Push input values into the appropriate arrays.
// 2. Push a placeholder (0 or "") for each output slot.
// 3. Call PartsFunc; outputs are written back into the placeholder slots.
static int PartsEngine_PartsFunc(int func_id, struct page **array_int,
struct page **array_float, struct page **array_string)
{
int nr_ints = (array_int && *array_int) ? (*array_int)->nr_vars : 0;
union vm_value *ints = nr_ints ? (*array_int)->values : NULL;
#define REQUIRE_INTS(n) \
if (nr_ints != (n)) VM_ERROR("Invalid arguments for PartsFunc %d: expected %d ints, got %d", func_id, (n), nr_ints)
switch (func_id) {
case 162: // bool InitPartsMovie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state)
REQUIRE_INTS(8);
ints[7].i = PE_init_parts_movie(ints[0].i, ints[1].i, ints[2].i, ints[3].i, ints[4].i, ints[5].i, ints[6].i);
return 1;
case 163: // int GetMovieSprite(int parts_no, int state)
REQUIRE_INTS(3);
ints[2].i = PE_get_movie_sprite(ints[0].i, ints[1].i);
return 1;
default:
WARNING("Unknown func_id: %d", func_id);
return 0;
}
#undef REQUIRE_INTS
}
HLL_WARN_UNIMPLEMENTED(, void, PartsEngine, StopSoundWithoutSystemSound);
HLL_WARN_UNIMPLEMENTED(, void, PartsEngine, ReleaseMessage, void);
HLL_WARN_UNIMPLEMENTED(0, int, PartsEngine, GetMessageType, void);
+11
View File
@@ -209,6 +209,11 @@ static void parts_flat_to_json(struct parts_flat *flat, cJSON *out, bool verbose
cJSON_AddNumberToObject(out, "end_frame", flat->end_frame);
}
static void parts_movie_to_json(struct parts_movie *movie, cJSON *out, bool verbose)
{
cJSON_AddNumberToObject(out, "sprite_no", movie->sprite_no);
}
static cJSON *parts_state_to_json(struct parts_state *state, bool verbose)
{
static const char *state_types[PARTS_NR_TYPES] = {
@@ -262,6 +267,9 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose)
case PARTS_FLAT:
parts_flat_to_json(&state->flat, obj, verbose);
break;
case PARTS_MOVIE:
parts_movie_to_json(&state->movie, obj, verbose);
break;
}
return obj;
@@ -545,6 +553,9 @@ static void parts_list_print(struct parts *parts, int indent)
case PARTS_FLAT:
sys_message("(flat %s)", state->flat.name ? display_sjis0(state->flat.name->text) : "(null)");
break;
case PARTS_MOVIE:
sys_message("(movie sp=%d)", state->movie.sprite_no);
break;
}
sys_message(" @ z=%d (%d)\n", parts->global.z, parts->local.z);
+51
View File
@@ -26,6 +26,7 @@
#include "vm/page.h"
#include "xsystem4.h"
#include "sact.h"
#include "sprite.h"
#include "parts.h"
#include "parts_internal.h"
@@ -178,6 +179,12 @@ static void parts_state_free(struct parts_state *state)
case PARTS_FLAT:
parts_flat_free(&state->flat);
break;
case PARTS_MOVIE:
// The texture is owned by the SACT sprite.
state->common.texture.handle = 0;
if (state->movie.sprite_no >= 0)
sact_SP_Delete(state->movie.sprite_no);
break;
}
memset(state, 0, sizeof(struct parts_state));
}
@@ -225,6 +232,9 @@ void parts_state_reset(struct parts_state *state, enum parts_type type)
case PARTS_FLASH:
case PARTS_FLAT:
break;
case PARTS_MOVIE:
state->movie.sprite_no = -1;
break;
}
}
@@ -300,6 +310,14 @@ struct parts_flat *parts_get_flat(struct parts *parts, int state)
return &parts->states[state].flat;
}
struct parts_movie *parts_get_movie(struct parts *parts, int state)
{
if (parts->states[state].type != PARTS_MOVIE) {
parts_state_reset(&parts->states[state], PARTS_MOVIE);
}
return &parts->states[state].movie;
}
static Point calculate_offset(int mode, int w, int h)
{
switch (mode) {
@@ -1861,3 +1879,36 @@ void PE_SetSpeedupRateByMessageSkip(int parts_no, int rate)
if (rate != 1)
UNIMPLEMENTED("(%d, %d)");
}
bool PE_init_parts_movie(int parts_no, int width, int height, int bg_r, int bg_g, int bg_b, int state)
{
if (!parts_state_valid(--state))
return false;
struct parts *parts = parts_get(parts_no);
struct parts_movie *movie = parts_get_movie(parts, state);
int sp_no = sact_SP_GetUnuseNum(0);
struct sact_sprite *sp = sact_create_sprite(sp_no, width, height, bg_r, bg_g, bg_b, 255);
if (!sp)
return false;
struct texture *tex = sprite_get_texture(sp);
movie->sprite_no = sp_no;
movie->common.texture = *tex; // XXX: textures normally shouldn't be copied like this...
parts_set_dims(parts, &movie->common, width, height);
parts_dirty(parts);
return true;
}
int PE_get_movie_sprite(int parts_no, int state)
{
if (!parts_state_valid(--state))
return -1;
struct parts *parts = parts_try_get(parts_no);
if (!parts || parts->states[state].type != PARTS_MOVIE)
return -1;
return parts->states[state].movie.sprite_no;
}
+9 -1
View File
@@ -104,7 +104,8 @@ enum parts_type {
PARTS_CONSTRUCTION_PROCESS,
PARTS_FLASH,
PARTS_FLAT,
#define PARTS_NR_TYPES (PARTS_FLAT+1)
PARTS_MOVIE,
#define PARTS_NR_TYPES (PARTS_MOVIE+1)
};
struct parts_common {
@@ -313,6 +314,11 @@ struct parts_flat {
size_t nr_textures;
};
struct parts_movie {
struct parts_common common;
int sprite_no; // SACT sprite number used as movie render target
};
struct parts_state {
enum parts_type type;
union {
@@ -325,6 +331,7 @@ struct parts_state {
struct parts_construction_process cproc;
struct parts_flash flash;
struct parts_flat flat;
struct parts_movie movie;
};
};
@@ -385,6 +392,7 @@ struct parts_gauge *parts_get_vgauge(struct parts *parts, int state);
struct parts_construction_process *parts_get_construction_process(struct parts *parts, int state);
struct parts_flash *parts_get_flash(struct parts *parts, int state);
struct parts_flat *parts_get_flat(struct parts *parts, int state);
struct parts_movie *parts_get_movie(struct parts *parts, int state);
void parts_set_pos(struct parts *parts, Point pos);
void parts_set_global_pos(Point pos);
void parts_set_dims(struct parts *parts, struct parts_common *common, int w, int h);
+1
View File
@@ -419,6 +419,7 @@ void parts_render(struct parts *parts)
case PARTS_HGAUGE:
case PARTS_VGAUGE:
case PARTS_CONSTRUCTION_PROCESS:
case PARTS_MOVIE:
if (state->common.texture.handle)
parts_render_cg(parts, &state->common);
break;
+4
View File
@@ -386,6 +386,8 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state)
case PARTS_FLAT:
save_parts_flat(w, &state->flat);
break;
case PARTS_MOVIE:
break;
}
}
@@ -428,6 +430,8 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts,
case PARTS_FLAT:
load_parts_flat(r, parts, &state->flat);
break;
case PARTS_MOVIE:
break;
}
}