0. BEFORE USING THE LIBRARY, READ THE PCE OFFICIAL/UNOFFICIAL HARDWARE DOCUMNETATION TO UNDERSTAND HOW PCE HARDWARE WORKSThe sprite/meta-sprite library for
HuC.
The library works with data exported from
SPReD-PCE.
History:v0.7- reduced the number of HuC functions (.proc/.endp):
[macro] spd_init()
[macro] spd_SG_bank_get_ind()
[macro] spd_sprite_params(...)
[macro] spd_get_dbl_buff_ind()
[macro] spd_SATB_get_pos()
[macro] spd_SATB_set_pos(...)
[macro] spd_SATB_to_VRAM()
[macro] spd_set_palette_LT(...)
[macro] spd_get_palette_LT()
[macro] spd_set_pri_LT(...)
[macro] spd_set_x_LT(...)
[macro] spd_get_x_LT()
[macro] spd_set_y_LT(...)
[macro] spd_get_y_LT()
[macro] spd_show_LT()
[macro] spd_hide_LT()
[macro] spd_SG_data_params(...)
[macro] spd_copy_SG_data_to_VRAM.4(...)
v0.6- added 'General information' section
- added 'spd_SATB_to_VRAM()' and 'spd_SATB_to_VRAM( _spr_cnt )' functions
- optimized 'spd_SATB_clear_from( _pos )'
- added 'spd_SATB_set_sprite_LT' for simple sprites, it takes a less processing time compared to 'spd_SATB_push_sprite' and allows you to use sprite offset values unlike HuC sprite functions
- added functions for simple sprites:
spd_set_palette_LT( ind ),
spd_get_palette_LT(),
spd_set_pri_LT( SPD_SPR_PRI_HIGH/SPD_SPR_PRI_LOW ),
spd_set_x_LT( X ),
spd_get_x_LT(),
spd_set_y_LT( Y ),
spd_get_y_LT(),
spd_show_LT(),
spd_hide_LT()
- added a little note about using the 'SPD_DEBUG' flag
- added cyan border color as attributes transformation indicator
- added 'SPD_SG_BANK_INIT_VAL' as initial value for the '_last_bank_ind' in 'spd_sprite_params(...)'
- the double-buffer index in 'spd_get_dbl_buff_ind()' and the second argument in 'spd_dbl_buff_VRAM_addr(...)' changed to a byte value
- the type of the second argument for 'spd_copy_SG_data_to_VRAM(...)' and 'spd_SG_data_params(...)' has changed, now '_src_bank' is a byte and therefore a pointer to a byte
v0.5
- changes in SPD_DEBUG - now you can see how often and how much data loads ROM->VRAM and how long does 'spd_SATB_push_sprite' take (use Mednafen); SPD_DEBUG is enabled in all animated samples by default
- added second argument to the 'spd_dbl_buff_VRAM_addr( VADDR_dbl_buff, _dbl_buff_ind )' function and added 'spd_get_dbl_buff_ind()' function to make double-buffered meta-sprites more independent in processing
- optimized loading of SG data to VRAM for double-buffered meta-sprites; added 'SPD_DBL_BUFF_INIT_VAL' as initial value for a double-buffer index
- added 'spd_alt_VRAM_addr( _alt_VADDR )' function to use VRAM address other than exported one; now it is legal and can be used for sprite instancing
- added 'spd_change_palette( _plt_ind )' function to change a sprite palette after the 'spd_SATB_push_sprite' function call
- added meta-sprite instancing demo (.\samples\pce\sprite_render\animation_test\huc3\); each meta-sprite has its own behaviour, palette and share the same graphics data
v0.4
- added a debug flag 'SPD_DEBUG' that shows when gfx data is being loaded to VRAM
- added a flag 'SPD_SG_NEW_DATA' to the 'spd_SATB_push_sprite' function result
- added fourth argument to the 'spd_sprite_params( <exported_name>_SG_arr, <EXPORTED_NAME>_SPR_VADDR, _flag, _last_bank_ind )' and also added 'spd_SG_bank_get_ind()'
- changed 'spd_copy_SG_data_to_VRAM( _SG_ind )' to 'spd_copy_SG_data_to_VRAM( <exported_name>_frames_data, _spr_ind )' and added 'spd_copy_SG_data_to_VRAM( <animation_name>_frame )'
- changed exported data, now '<exported_name>_PALETTE_SLOT' includes sprite palette offset (16) and '<exported_name>_palette_size' is the number of active palettes
- fixed _spd_farptr_add_offset
v0.3
- added 'spd_copy_SG_to_VRAM( _SG_ind )'
v0.2
- optimization of '__attr_loop_XY', '__attr_loop_XY_IND' loops
- added 'SPD_FLAG_IGNORE_SG'
v0.1
- initial release
Debug info (use Mednafen):
- pink border color - ROM-VRAM data copying
- white border color - spd_SATB_push_sprite
Place '
#define SPD_DEBUG' before '
#include "spd.h"':
[upd] v0.6
NOTE: After enabling you will see two border lines: pink - ROM-VRAM data copying and white/cyan - spd_SATB_push_sprite.
Pay attention to the pink border line. The normal behaviour is when the pink line flashes sometimes (see sample
projects). When you see a bright pink border, this means that SG data copies to VRAM each frame. If this is not
expected behavior, then there is a bug somewhere in your program logic.
General information:
~~~~~~~~~~~~~~~
1. The library works with both simple sprites and meta-sprites.
The only difference is the use of the following functions: '
spd_SATB_push_sprite' for meta-sprites and '
spd_SATB_set_sprite_LT' for simple sprites.
'
spd_SATB_set_sprite_LT' - optimized to work with simple sprites. It supports sprite offset values, but does not support double-buffering and doesn't increment SATB position, unlike '
spd_SATB_push_sprite'.
2. The
SPReD-PCE exports both meta-sprites and simple sprites (
16x16,16x32,16x64,32x16,32x32,32x64).
The
CGX/CGY flags are automatically applied to exported sprites. So you don't need to configure anything in your HuC program.
3. The library supports an arbitrary number of sprite sets, which are switched between using the '
spd_sprite_params' function.
4. Double-buffering is supported for meta-sprites. This requires 2x video memory for SG data, but ensures that there are no glitches when synchronizing SG and VRAM inner SATB.
Compare both meta-sprites with and without double-buffering and decide which one is better in your case. As a rule, double-buffering helps when meta-sprites are at the top of the screen.
But it also depends on the amount of SG data is being loaded to VRAM.
[upd]5. Supports changing the color palette for meta-sprites. Use '
spd_change_palette' right after '
spd_SATB_push_sprite'.
[upd]6. Simple or meta-sprite graphics can be loaded to any VRAM address. Use '
spd_alt_VRAM_addr' right after 'spd_sprite_params' and before '
spd_copy_SG_data_to_VRAM', '
spd_SATB_push_sprite' and '
spd_SATB_set_sprite_LT'.
7. There are two ways to load SG data to VRAM:
- automatically, when calling '
spd_SATB_push_sprite' or '
spd_SATB_set_sprite_LT';
- manually with '
spd_copy_SG_data_to_VRAM' for graphics caching before further use, as a rule for simple sprites, or to load a meta-sprite graphics without double-buffering after
VBLANK/vsync();
Keep this in mind when planning the logic of your program.
8. The library can be used in combination with HuC sprite functions. This makes it much easier to initialize data for HuC sprites.
You can also use similar SPD library functions:
(!) Functions with the '
_LT' postfix are designed to work with simple sprites.
spr_set( N )
-
spd_SATB_set_pos( N )
spr_pal( _plt_ind )
-
spd_set_palette_LT( _plt_ind )
spr_get_pal()
-
spd_get_palette_LT()
spr_pri( val )
-
spd_set_pri_LT( SPD_SPR_PRI_HIGH/SPD_SPR_PRI_LOW )
spr_x( _x )
-
spd_set_x_LT( _x ) -
doesn't support sprite offset value
spr_get_x()
-
spd_get_x_LT()
spr_y( _y )
-
spd_set_y_LT( _y ) -
doesn't support sprite offset value
spr_get_y()
-
spd_get_y_LT()
spr_show()
-
spd_show_LT()
spr_hide()
-
spd_hide_LT()
There are also following optimized similar functions:
init_satb()/
reset_satb()
-
spd_SATB_clear_from( N )
satb_update()
-
spd_SATB_to_VRAM()
9. Performance. Starting with the fastest function:
[upd] Direct writing to SATB using: '
spd_set_palette_LT', '
spd_set_pri_LT', '
spd_set_x_LT', '
spd_set_y_LT' etc. or HuC functions.
spd_SATB_set_sprite_LT( <sprite_name>, X, Y )
spd_SATB_set_sprite_LT( <exported_name>_frames_arr, spr_ind, X, Y )
spd_SATB_push_sprite( <sprite_name>, X, Y )
spd_SATB_push_sprite( <exported_name>_frames_arr, spr_ind, X, Y )
Use the following functions, if you don't use animations:
spd_SATB_set_sprite_LT( <sprite_name>, X, Y )
spd_SATB_push_sprite( <sprite_name>, X, Y )
This will work faster, than functions that use sprite indexing:
spd_SATB_set_sprite_LT( <exported_name>_frames_arr, spr_ind, X, Y )
spd_SATB_push_sprite( <exported_name>_frames_arr, spr_ind, X, Y )
The sprite indexing is designed specifically to make working with animations easier. So use these functions only for animated sprites.
10. Use '
SPD_DEBUG' to keep track of how efficiently/frequently SG data is loaded to VRAM.
Examples of SPD library using:
~~~~~~~~~~~~~~~~~~~~~~
There are two data types the
SPD-render works with.
1.
PACKED sprites data. All exported
SG data are stored in a single file. It were packed in the
SPReD-PCE before exporting.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The main logic is:
// SPD-render initialization.
spd_init();
// Initialization of exported sprite set.
{
// Load palette in the usual way.
load_palette( <EXPORTED_NAME>_PALETTE_SLOT, <exported_name>_palette, <exported_name>_PALETTE_SIZE );
// Set up exported sprite set with SG data array and VRAM address to load SG data to.
// NOTE: You can combine any number of exported sprite sets in your program.
// Call the 'spd_sprite_params' to switch between them.
[upd] v0.5 // NOTE: Passing ZERO as the third parameter, means that SG data will be automatically
---> // loaded to VRAM on 'spd_SATB_push_sprite'.
[upd] v0.2 // NOTE: Passing 'SPD_FLAG_IGNORE_SG' as the third parameter will ignore loading SG to VRAM.
// It's useful for PACKED(!) sprites when you are switching to a sprite set and SG data already loaded to VRAM.
---> // Such way you avoid loading SG to VRAM twice.
[upd] v0.4 // NOTE: Passing 'last_bank_ind' allows to avoid loading SG data to VRAM twice when you are switching back from another data set.
[upd] v0.6 // The last value can be obtained using 'spd_SG_bank_get_ind()'. The initial value is 'SPD_SG_BANK_INIT_VAL'.
---> spd_sprite_params( <exported_name>_SG_arr, <EXPORTED_NAME>_SPR_VADDR, 0, last_bank_ind );
[upd] v0.3 // NOTE: There are two ways to load SG data to VRAM:
// 1. Indirect loading, when you push the first sprite by calling 'spd_SATB_push_sprite'.
// The third argument for the 'spd_sprite_params' must be ZERO.
---> //
[upd] v0.4 // 2. Direct loading, when you call 'spd_copy_SG_data_to_VRAM' with a sprite name/index.
// The third argument for the 'spd_sprite_params' must be 'SPD_FLAG_IGNORE_SG'.
//
---> // spd_copy_SG_data_to_VRAM( <exported_name>_frames_data, _spr_ind )
[upd] v0.6 // spd_copy_SG_data_to_VRAM( <sprite_name> )
}
[upd] v0.6
// SATB initialization.
spd_SATB_clear_from( 0 );
--->
// Here you can use HuC's sprite calls...
// SPD calls
{
// Now you can set a local SATB position to push your exported sprites/meta-sprites to.
// NOTE: The SATB position will be automatically incremented with each call to 'spd_SATB_push_sprite' (!)
[upd] v0.6 // NOTE: The SATB position will NOT be automatically incremented when using 'spd_SATB_set_sprite_LT' (!)
spd_SATB_set_pos( <SATB_pos[0...63]> );
[upd] v0.6
// NOTE: There are two ways to push sprite to SATB:
// 1. spd_SATB_push_sprite - for meta-sprites
//
// 2. spd_SATB_set_sprite_LT - for simple sprites, it takes a little less processing time
// compared to 'spd_SATB_push_sprite' and allows you to use sprite offset values, that will be
// zeroed out when using HuC sprite functions.
//
// NOTE: Double-buffering and 'spd_change_palette' aren't supported with 'spd_SATB_set_sprite_LT'.
// Use 'spd_set_palette_LT' instead of 'spd_change_palette' for simple sprites.
--->
// There are two ways to show a sprite:
// 1. By index in a sprite array. Suitable for animation sequences.
// (see exported .h file for generated constants)
spd_SATB_push_sprite( <exported_name>_frames_data, _ind, _x, _y );
OR
[upd] v0.6 spd_SATB_set_sprite_LT( <exported_name>_frames_data, _ind, _x, _y );
// 2. By sprite data pointer or by sprite name (it is faster than by index).
// (see exported .h file for generated data)
spd_SATB_push_sprite( <sprite_name>, _x, _y );
OR
spd_SATB_set_sprite_LT( <sprite_name>, _x, _y );
// NOTE: SG data will be automatically loaded once to VRAM at first call to the 'spd_SATB_push_sprite' or 'spd_SATB_set_sprite_LT',
---> // when the third parameter passed to the 'spd_sprite_params' is ZERO (!)
// NOTE: If meta-sprite does not fit into SATB, it will be ignored!
[upd] v0.4 // NOTE: 'spd_SATB_push_sprite' returns: 1-Ok + ORed flag 'SPD_SG_NEW_DATA' when a new SG data already or must be loaded to VRAM; 0-SATB overflow
// Save SG bank index, especially if you are using more than one sprite set(!)
---> last_bank_ind = spd_SG_bank_get_ind();
}
// Then call 'spd_SATB_to_VRAM' to push your sprite data to VRAM-SAT.
[upd] v0.6
// NOTE: After pushing all sprites, `spd_SATB_get_pos()` returns the number of sprites in SATB when using the 'spd_SATB_push_sprite'.
// NOTE: But when using 'spd_SATB_set_sprite_LT' you need to know how many sprites were used.
// Move the entire SATB - 64 sprites to VRAM-SAT
spd_SATB_to_VRAM();
OR
// Move a certain number of sprites to VRAM-SAT
spd_SATB_to_VRAM( spr_cnt );
--->
[upd] v0.6
// NOTE: As mentioned before, you can combine the SPD calls with the HuC ones or use SPD analogue functions.
// For example, you can do the following for simple static sprites:
// Initialization at startup
load_palette( ...
spd_sprite_params( ...
// SATB initialization.
spd_SATB_clear_from( 0 );
// set SATB position to initialize a sprite data to
spd_SATB_set_pos( 0 );
spd_SATB_set_sprite_LT( my_sprite_16x32, init_x, init_y );
...
// In update loop
spd_SATB_set_pos( 0 );
spd_set_x_LT( new_x );
spd_set_y_LT( new_y );
// I recommend using simple sprites this way, as it simplifies their initialization and is optimal for runtime.
// NOTE: The functions for using with simple sprites:
//
// void spd_set_palette_LT( unsigned char _plt_ind );
// unsigned char spd_get_palette_LT();
// void spd_set_pri_LT( SPD_SPR_PRI_HIGH/SPD_SPR_PRI_LOW );
// void spd_set_x_LT( unsigned short _x ); <--- doesn't support sprite offset value
// unsigned short spd_get_x_LT();
// void spd_set_y_LT( unsigned short _y ); <--- doesn't support sprite offset value
// unsigned short spd_get_y_LT();
// void spd_show_LT();
// void spd_hide_LT();
--->
2.
UNPACKED sprites data. All exported
SG data are stored in separate files. It were not packed in the
SPReD-PCE.
Unpacked data are suitable for dynamic
SG data, that can be loaded to VRAM dynamically to save video memory. It`s
useful when you have a lot of animations that don`t fit into VRAM, like in fighting games.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The main logic is:
// SPD-render initialization.
spd_init();
// Initialization of exported sprite set.
{
// Load palette in the usual way.
load_palette( <EXPORTED_NAME>_PALETTE_SLOT, <exported_name>_palette, <exported_name>_PALETTE_SIZE );
// Set up exported sprite set with SG data array and VRAM address to load SG data to.
#if DEF_SG_DBL_BUFF
// NOTE: Using the `SPD_FLAG_DBL_BUFF` flag means double-buffering for sprite graphics.
// It costs x2 of dynamic SG data in VRAM, but glitches free. You have to compare the results
// of using 'SPD_FLAG_DBL_BUFF' and 'SPD_FLAG_PEND_SG_DATA' and decide which is better in your case.
[upd] v0.4 // NOTE: Passing 'last_bank_ind' allows to avoid loading SG data to VRAM twice when you are switching back from another data set.
[upd] v0.6 // The last value can be obtained using 'spd_SG_bank_get_ind()'. The initial value is 'SPD_SG_BANK_INIT_VAL'.
---> spd_sprite_params( <exported_name>_SG_arr, <EXPORTED_NAME>_SPR_VADDR, SPD_FLAG_DBL_BUFF, last_bank_ind );
[upd] v0.5 // Set the second VRAM address for double-buffering (SPD_FLAG_DBL_BUFF) and the last double-buffer index value (initial value is 'SPD_DBL_BUFF_INIT_VAL').
---> spd_dbl_buff_VRAM_addr( VADDR_dbl_buff, last_dbl_buff_ind );
#else
// NOTE: Using the `SPD_FLAG_PEND_SG_DATA` flag means that SG data will not be loaded
// to VRAM automatically. You should do that manually on VBLANK.
spd_sprite_params( <exported_name>_SG_arr, <EXPORTED_NAME>_SPR_VADDR, SPD_FLAG_PEND_SG_DATA, last_bank_ind );
// Set pointers to SG data for delayed use (SPD_FLAG_PEND_SG_DATA).
spd_SG_data_params( &SG_DATA_SRC_ADDR, &SG_DATA_SRC_BANK, &SG_DATA_DST_ADDR, &SG_DATA_LEN );
#endif
// NOTE: Single- and double-buffered meta-sprites can be combined in runtime.
// NOTE: You can combine any number of exported sprite sets in your program.
// Call the 'spd_sprite_params' to switch between them.
[upd] v0.5 // NOTE: To use multiple instances of the same sprite set, for example, for a sprite cache,
---> // use 'spd_alt_VRAM_addr( _alt_VADDR )' which replaces the '<EXPORTED_NAME>_SPR_VADDR'.
}
[upd] v0.6
// SATB initialization.
spd_SATB_clear_from( 0 );
--->
// Here you can use HuC's sprite calls...
// SPD calls
{
// Now you can set a local SATB position to push your exported sprites/meta-sprites to.
// NOTE: The SATB position will be automatically incremented with each call to 'spd_SATB_push_sprite' (!)
spd_SATB_set_pos( <SATB_pos[0...63]> );
// There are two ways to show a sprite:
// 1. By index in a sprite array. Suitable for animation sequences.
// (see exported .h file for generated constants)
spd_SATB_push_sprite( <exported_name>_frames_data, _ind, _x, _y );
[upd] v0.6 // 2. By sprite data pointer or by sprite name (it is faster than by index).
// (see exported .h file for generated data)
---> spd_SATB_push_sprite( <sprite_name>, _x, _y );
// NOTE: If meta-sprite does not fit into SATB, it will be ignored!
[upd] v0.4 // NOTE: 'spd_SATB_push_sprite' returns: 1-Ok + ORed flag 'SPD_SG_NEW_DATA' when a new SG data already or must be loaded to VRAM; 0-SATB overflow
// Save SG bank index, especially if you are using more than one sprite set(!)
---> last_bank_ind = spd_SG_bank_get_ind();
[upd] v0.5
#if DEF_SG_DBL_BUFF
last_dbl_buff_ind = spd_get_dbl_buff_ind();
#endif
--->
}
// 'VRAM-SATB Transfer Auto-Repeat on VBLANK' (DCR ROF-$10) is enabled by default in HuC at startup, so we skip this step.
// Main loop
for( ;; )
{
// Update your sprite animation
update_frame( &test_anim );
// clear SATB
spd_SATB_clear_from( <SATB_pos[0...63]> );// to save sprites before 'SATB_pos', and clear memory after to avoid graphical glitches with variable sized meta-sprites
// Set the SATB position to push your sprite to.
spd_SATB_set_pos( <SATB_pos[0...63]> );
// Push your sprite to the local RAM SATB.
[upd] v0.4 res_byte = spd_SATB_push_sprite( <exported_name>_frames_data, test_anim.start_frame + test_anim.curr_frame, _x, _y );
---> last_bank_ind = spd_SG_bank_get_ind();
[upd] v0.5 // NOTE: To change sprite palette, use 'spd_change_palette( _plt_ind )' function immediately after calling the 'spd_SATB_push_sprite',
// if the result isn't zero. See './samples/pce/sprite_render/animation_test/huc3' and '/huc4' for details.
if( res_byte )
{
spd_change_palette( _plt_ind ); // _plt_ind - 0...15
}
#if DEF_SG_DBL_BUFF
last_dbl_buff_ind = spd_get_dbl_buff_ind();
#endif
--->
[upd] v0.6 // Move the entire SATB - 64 sprites to VRAM-SAT
spd_SATB_to_VRAM();
OR
// Move a certain number of sprites to VRAM-SAT
spd_SATB_to_VRAM( spr_cnt );
--->
vsync();
#if !DEF_SG_DBL_BUFF
// The HuC doesn't allow to handle VBLANK directly, so we use 'vsync' instead for synchronization of sprite graphics and the inner SATB.
// This may cause some graphical glitches at the upper part of the screen.
// Delayed copying of SG data to VRAM to synchronize it with the inner SATB
[upd] v0.4 if( res_byte & SPD_SG_NEW_DATA )
{
spd_copy_SG_data_to_VRAM( SG_DATA_SRC_ADDR, SG_DATA_SRC_BANK, SG_DATA_DST_ADDR, SG_DATA_LEN );
}
#endif
}
[upd] v0.6
3. See the sample projects for implementation details.
[upd] v0.4
4. Also you can use PACKED and UNPACKED data in one data set (in one SPReD-PCE project) by combining the approaches described above.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*SG - sprite graphics
[upd] The main
SPD-render library file you can find here:
`./samples/pce/common/spd.h`There are samples that demonstrate all of the above. You can find them with compiled binaries in the latest development build
here.
`./samples/pce/sprite_render/show_sprites/huc/` - simple show meta-sprites demo
`./samples/pce/sprite_render/animation_test/huc/` - simple animation demo with a big meta-sprite
`./samples/pce/sprite_render/animation_test/huc2/` - animation demo that shows 3 independent, dynamic meta-sprite sets that fill the entire
SATB with optional double-buffering
`
./samples/pce/sprite_render/animation_test/huc3/` - meta-sprite instancing demo; each meta-sprite has its own unique behaviour, palette and share the same graphics data
`./samples/pce/sprite_render/player_cntrl/huc/` - simple character controller with a big meta-sprite character, dynamic sprite data and optional double-buffering;
Controls: LEFT, RIGHT, UP...and other samples in the
`./samples/pce/sprite_render' directory.
Compiled binaries you can find here:
`./samples/pce/bin`