mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
642cd48f7d
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
120 lines
5.5 KiB
C
120 lines
5.5 KiB
C
#ifndef PSX_RENDER_PASS_MOTION_H
|
|
#define PSX_RENDER_PASS_MOTION_H
|
|
/*
|
|
* Motion kit for render-pass plugins (docs/RENDER_PASSES.md, "Blending game
|
|
* state"). A pass redraws the scene with the game's own code between two
|
|
* logic ticks; what makes the image an in-between one is the game state the
|
|
* draw code reads, placed part of the way from the previous tick to the
|
|
* current one. This kit owns that part for any title:
|
|
*
|
|
* per game frame, at the plugin's capture point
|
|
* psx_motion_begin(set, tick) the game's logic tick counter
|
|
* psx_motion_track(set, kind, addr, identity) for each value the draw
|
|
* code reads (object matrices,
|
|
* positions, angles, the camera)
|
|
* psx_motion_prepare(set, &limits, &stats) pair with the previous frame
|
|
* in each pass
|
|
* psx_motion_apply(set, alpha) write the blends into guest RAM
|
|
* psx_motion_blend_at(set, addr, ...) or blend one value a hook hands on
|
|
*
|
|
* Values are matched by address AND identity: game objects are heap blocks,
|
|
* and a freed address reused by another object must never blend between the
|
|
* two. A value that moved further than the limits allow in the ticks between
|
|
* the frames was placed (respawn, teleport, camera cut), not moved; it keeps
|
|
* its current value, as do values without history. Nothing is written
|
|
* outside psx_motion_apply, which a pass's sandbox rolls back.
|
|
*
|
|
* Guest RAM is read and written through psx_mod_read_* / psx_mod_write_*.
|
|
*/
|
|
#include <stdint.h>
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/* What lives at a tracked address (guest RAM layout). */
|
|
enum {
|
|
/* PsyQ MATRIX: 3x3 s16 rotation in 4.12 at +0x00..+0x11, s32 t[3] at
|
|
* +0x14. Rotation slerps when both rows are orthonormal, otherwise it
|
|
* blends elementwise; translation blends linearly. */
|
|
PSX_MOTION_MATRIX = 1,
|
|
/* Only the translation of a MATRIX (+0x14..+0x1F): for matrices whose
|
|
* rotation the draw code rebuilds itself (billboards). */
|
|
PSX_MOTION_MATRIX_TRANSLATION = 2,
|
|
PSX_MOTION_VECTOR = 3, /* s32 x, y, z */
|
|
PSX_MOTION_SVECTOR = 4, /* s16 x, y, z */
|
|
/* s16 x, y, z angles, 4096 per turn: each blends along the shorter arc. */
|
|
PSX_MOTION_ANGLES = 5,
|
|
PSX_MOTION_SCALAR = 6 /* s32 */
|
|
};
|
|
|
|
typedef struct PSXMotionLimits {
|
|
/* Distance one value may move per logic tick and still blend
|
|
* (translation, VECTOR, SVECTOR, SCALAR, in the game's units). */
|
|
double move_per_tick;
|
|
/* Rotation between the two frames beyond which a value snaps
|
|
* (MATRIX rotation, ANGLES). 0 means pi (any turn up to a half turn). */
|
|
double turn_radians;
|
|
} PSXMotionLimits;
|
|
|
|
typedef struct PSXMotionStats {
|
|
uint32_t tracked; /* values captured this frame */
|
|
uint32_t blended; /* ... that will blend */
|
|
uint32_t placed; /* ... that moved or turned past the limits */
|
|
uint32_t unmatched; /* ... new, or a different identity at the address */
|
|
uint32_t ticks; /* logic ticks between the two frames (0 = no history) */
|
|
} PSXMotionStats;
|
|
|
|
typedef struct PSXMotionMatrix {
|
|
int16_t r[9];
|
|
int32_t t[3];
|
|
} PSXMotionMatrix;
|
|
|
|
typedef struct PSXMotionSet PSXMotionSet;
|
|
|
|
PSXMotionSet* psx_motion_set_create(uint32_t capacity);
|
|
void psx_motion_set_destroy(PSXMotionSet* set);
|
|
|
|
/* Start this frame's capture. `tick` is the game's logic tick counter at
|
|
* this frame; the previous capture becomes the history. */
|
|
void psx_motion_begin(PSXMotionSet* set, uint32_t tick);
|
|
/* Record the value of `kind` at `addr` as the draw code will read it.
|
|
* `identity` names what lives there (psx_motion_identity of its type,
|
|
* handler, model...). One value per (addr, kind) per frame; a repeat
|
|
* replaces the earlier one. Returns 0 when the set is full. */
|
|
int psx_motion_track(PSXMotionSet* set, uint32_t kind, uint32_t addr,
|
|
uint32_t identity);
|
|
/* Pair this capture with the previous one. Returns how many values will
|
|
* blend. History counts only when it is the capture just before this one
|
|
* with 1..8 ticks between them; otherwise nothing blends. */
|
|
uint32_t psx_motion_prepare(PSXMotionSet* set, const PSXMotionLimits* limits,
|
|
PSXMotionStats* stats);
|
|
/* Write every prepared value at t (0 = previous frame, 1 = this frame). */
|
|
void psx_motion_apply(const PSXMotionSet* set, double t);
|
|
/* Blend one prepared value at t into `out` without writing guest RAM:
|
|
* a PSXMotionMatrix for the MATRIX kinds, int32_t[3] for VECTOR / SVECTOR /
|
|
* ANGLES, int32_t for SCALAR. Returns 0 when that value does not blend this
|
|
* frame (out then holds its current value) or was not tracked (out
|
|
* untouched, returns -1). */
|
|
int psx_motion_blend_at(const PSXMotionSet* set, uint32_t kind, uint32_t addr,
|
|
double t, void* out);
|
|
/* Forget all history (scene change, load, a frame the plugin skipped). */
|
|
void psx_motion_invalidate(PSXMotionSet* set);
|
|
|
|
/* Mix up to three words into an identity tag. */
|
|
uint32_t psx_motion_identity(uint32_t a, uint32_t b, uint32_t c);
|
|
|
|
/* The math, for plugins that blend values the set does not hold. Returns 0
|
|
* when the rotation turned past turn_radians (out = b). */
|
|
int psx_motion_blend_matrix(const PSXMotionMatrix* a, const PSXMotionMatrix* b,
|
|
double t, double turn_radians,
|
|
PSXMotionMatrix* out);
|
|
void psx_motion_read_matrix(uint32_t addr, PSXMotionMatrix* out);
|
|
void psx_motion_write_matrix(uint32_t addr, const PSXMotionMatrix* m,
|
|
int rotation);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|