death animation and start of end screen
This commit is contained in:
parent
7c9a0de14c
commit
2d04d54a8e
|
@ -9,6 +9,8 @@ find_package(Gint 2.8 REQUIRED)
|
|||
find_package(LibProf 2.1 REQUIRED)
|
||||
|
||||
set(SOURCES
|
||||
src/fsblend.cpp
|
||||
src/fsblend.S
|
||||
src/game.cpp
|
||||
src/generator/gen1.cpp
|
||||
src/generator/gen2.cpp
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
.global _bosonx_shader_fsblend
|
||||
.align 4
|
||||
|
||||
#define _fg r2
|
||||
#define _alpha r3 /* Opacity of the colored overlay (0..31) */
|
||||
#define _N r4 /* Number of pixels in fragment */
|
||||
#define _cmd r5 /* Command */
|
||||
#define _frag r6 /* Fragment */
|
||||
#define _mask r7 /* Pixel expansion mask */
|
||||
|
||||
/* Possible optimizations:
|
||||
- Remove the huge sts -> EX delay by tiling. This is the only way. */
|
||||
|
||||
_bosonx_shader_fsblend:
|
||||
/* Preload and duplicate the first pixel while we're setting up */
|
||||
mov.w @_frag, r0
|
||||
add #2, r5
|
||||
|
||||
mov.l r8, @-r15
|
||||
nop
|
||||
|
||||
mov.w @r5+, _alpha /* alpha */
|
||||
swap.w r0, r8
|
||||
|
||||
mov.l .mask, _mask
|
||||
xtrct r0, r8
|
||||
|
||||
mov.l @r5+, _fg /* color_expanded */
|
||||
mov r8, r0
|
||||
|
||||
mov.l .xram, r8
|
||||
nop
|
||||
|
||||
mov.l r9, @-r15
|
||||
mov #-5, r9
|
||||
|
||||
/* --- */
|
||||
|
||||
/* We use a DSP loop to avoid spending EX cycles on decrementing the
|
||||
counter. However the DSP loop supports only up to 4096 cycles and we
|
||||
have more than that. Do two passes. */
|
||||
|
||||
mov.l r10, @-r15
|
||||
shlr _N
|
||||
|
||||
mov #2, r10
|
||||
ldrs 2f
|
||||
ldre 3f
|
||||
|
||||
1: ldrc _N
|
||||
nop
|
||||
|
||||
/* --- */
|
||||
|
||||
2: mov r0, r1 /* x */
|
||||
and _mask, r1
|
||||
|
||||
mov _fg, r0
|
||||
sub r1, r0
|
||||
|
||||
mul.l r0, _alpha
|
||||
mov.w @_frag+, r5 /* LS-based add #2, _frag */
|
||||
|
||||
sts macl, r5
|
||||
nop
|
||||
|
||||
/* 2 cycles lost to the sts -> EX delay!
|
||||
Not much can be done without tiling because this loop is already a
|
||||
strict EX chain; everything else parallelizes with it. */
|
||||
|
||||
shld r9, r5
|
||||
mov.w @(2, _frag), r0 /* (next pixel) */
|
||||
|
||||
add r5, r1
|
||||
mov.w r0, @r8 /* (next pixel) */
|
||||
|
||||
and _mask, r1
|
||||
mov.w r0, @(2, r8) /* (next pixel) */
|
||||
|
||||
swap.w r1, r5
|
||||
mov.l @r8, r0 /* (next pixel) */
|
||||
|
||||
or r5, r1
|
||||
3: mov.w r1, @_frag
|
||||
|
||||
/* --- */
|
||||
|
||||
dt r10
|
||||
bf 1b
|
||||
|
||||
mov.l @r15+, r10
|
||||
mov.l @r15+, r9
|
||||
rts
|
||||
mov.l @r15+, r8
|
||||
|
||||
.balign 4
|
||||
.mask:
|
||||
.long 0x07e0f81f
|
||||
.xram:
|
||||
.long 0xe500e000
|
|
@ -0,0 +1,42 @@
|
|||
#include <azur/gint/render.h>
|
||||
|
||||
uint8_t BOSONX_SHADER_FSBLEND = -1;
|
||||
extern "C" azrp_shader_t bosonx_shader_fsblend;
|
||||
|
||||
static void configure(void)
|
||||
{
|
||||
int pixels_in_fragment = azrp_width * azrp_frag_height;
|
||||
azrp_set_uniforms(BOSONX_SHADER_FSBLEND, (void *)pixels_in_fragment);
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void register_shader(void)
|
||||
{
|
||||
BOSONX_SHADER_FSBLEND =
|
||||
azrp_register_shader(bosonx_shader_fsblend, configure);
|
||||
configure();
|
||||
}
|
||||
|
||||
//---
|
||||
|
||||
struct command {
|
||||
uint8_t shader_id;
|
||||
uint8_t _;
|
||||
/* Alpha value between 0 and 31 */
|
||||
uint16_t alpha;
|
||||
/* Expanded foreground color (XGXRXB565565) */
|
||||
uint32_t color_expanded;
|
||||
};
|
||||
|
||||
void shader_fsblend(uint16_t color, int alpha)
|
||||
{
|
||||
prof_enter(azrp_perf_cmdgen);
|
||||
|
||||
struct command cmd;
|
||||
cmd.shader_id = BOSONX_SHADER_FSBLEND;
|
||||
cmd.alpha = (alpha & 31);
|
||||
cmd.color_expanded = ((color << 16) | color) & 0x07e0f81f;
|
||||
|
||||
azrp_queue_command(&cmd, sizeof cmd, 0, azrp_frag_count);
|
||||
prof_leave(azrp_perf_cmdgen);
|
||||
}
|
|
@ -27,6 +27,7 @@ struct player
|
|||
Running, /* Running on the ground */
|
||||
Jumping, /* Jumping, with jump key still pressed */
|
||||
Falling, /* Falling, typically after releasing jump key */
|
||||
Collided, /* Death animation */
|
||||
} stance;
|
||||
|
||||
/* Determine the player's animation and frame. */
|
||||
|
@ -83,8 +84,8 @@ struct game
|
|||
|
||||
/* Absolute time spent in the level. */
|
||||
num t;
|
||||
/* Number of sections passed. */
|
||||
int sections_passed;
|
||||
/* Time in the death animation. */
|
||||
num t_death;
|
||||
|
||||
struct {
|
||||
bool footer; /* Show performance footer */
|
||||
|
|
87
src/main.cpp
87
src/main.cpp
|
@ -27,7 +27,7 @@ bool volatile screenshot = false;
|
|||
/* Send next frame as video to fxlink */
|
||||
bool volatile videocapture = false;
|
||||
|
||||
static bool async_event_filter(key_event_t ev)
|
||||
GUNUSED static bool async_event_filter(key_event_t ev)
|
||||
{
|
||||
if(ev.key == KEY_7) {
|
||||
if(ev.type == KEYEV_DOWN)
|
||||
|
@ -42,7 +42,7 @@ static bool async_event_filter(key_event_t ev)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void hook_prefrag(int id, void *fragment, int size)
|
||||
GUNUSED static void hook_prefrag(int id, void *fragment, int size)
|
||||
{
|
||||
if(!screenshot && !videocapture)
|
||||
return;
|
||||
|
@ -74,7 +74,7 @@ static void hook_prefrag(int id, void *fragment, int size)
|
|||
}
|
||||
}
|
||||
|
||||
static void hook_dupdate(void)
|
||||
GUNUSED static void hook_dupdate(void)
|
||||
{
|
||||
if(screenshot) {
|
||||
usb_fxlink_screenshot(true);
|
||||
|
@ -95,7 +95,7 @@ int play_level(int level_id)
|
|||
|
||||
game.level = level_create(level_id);
|
||||
game.t = 0.0;
|
||||
game.sections_passed = 0;
|
||||
game.t_death = 0.0;
|
||||
game.debug.footer = false;
|
||||
game.debug.invincible = false;
|
||||
|
||||
|
@ -132,8 +132,6 @@ int play_level(int level_id)
|
|||
camera->platform,
|
||||
str(camera->angle_vector));
|
||||
|
||||
image_t *effect_bpp = NULL;
|
||||
|
||||
bool game_run = true;
|
||||
while(game_run) {
|
||||
while(!need_frame) sleep();
|
||||
|
@ -145,7 +143,6 @@ int play_level(int level_id)
|
|||
game.t += dt;
|
||||
prof_t perf_frame = prof_make();
|
||||
prof_enter_norec(perf_frame);
|
||||
game.perf.effect_bpp = prof_make();
|
||||
|
||||
level_update(&game.level, player->z);
|
||||
camera_track(camera, player);
|
||||
|
@ -155,54 +152,8 @@ int play_level(int level_id)
|
|||
|
||||
//======= Rendering =======//
|
||||
|
||||
azrp_perf_clear();
|
||||
azrp_clear(game.level.bgcolor);
|
||||
|
||||
prof_t perf_comp = prof_make();
|
||||
prof_enter_norec(perf_comp);
|
||||
|
||||
for(auto it = game.level.platform_buffer.rbegin();
|
||||
it != game.level.platform_buffer.rend();
|
||||
++it) {
|
||||
int color = render_platform_color(&*it, &game.camera, game.t);
|
||||
struct prect p = space_platform_position(&*it);
|
||||
|
||||
/* Near plane clipping */
|
||||
num near = camera->pos.z + camera->near_plane();
|
||||
if(p.fl.z <= near) continue;
|
||||
if(p.nl.z <= near) p.nl.z = near;
|
||||
if(p.nr.z <= near) p.nr.z = near;
|
||||
camera_project_prect(&game.camera, &p);
|
||||
|
||||
render_triangle(&p.nl, &p.fr, &p.fl, color);
|
||||
render_triangle(&p.fr, &p.nl, &p.nr, color);
|
||||
}
|
||||
|
||||
/* Render player */
|
||||
vec3 player_dot = camera_project_point(&game.camera, player->pos());
|
||||
struct anim *player_anim;
|
||||
int player_frame;
|
||||
player->get_anim(&player_anim, &player_frame);
|
||||
render_anim_frame((int)player_dot.x, (int)player_dot.y,
|
||||
player_anim, player_frame);
|
||||
|
||||
if(standing_on && standing_on->type == PLATFORM_BLUE) {
|
||||
prof_enter(game.perf.effect_bpp);
|
||||
render_effect_bpp(&effect_bpp, game.t);
|
||||
azrp_image((int)player_dot.x - effect_bpp->width / 2,
|
||||
(int)player_dot.y - effect_bpp->height + 2,
|
||||
effect_bpp);
|
||||
prof_leave(game.perf.effect_bpp);
|
||||
}
|
||||
|
||||
/* Render energy score */
|
||||
char energy_str[32];
|
||||
sprintf(energy_str, "%.2f%%", (float)player->energy_percent);
|
||||
azrp_rect(DWIDTH-100, 4, 96, 20, AZRP_RECT_DARKEN);
|
||||
render_energy_str(DWIDTH-8, 8, DTEXT_RIGHT, energy_str);
|
||||
|
||||
prof_leave_norec(perf_comp);
|
||||
azrp_update();
|
||||
render_game(game, &perf_comp);
|
||||
|
||||
num floor = 0;
|
||||
|
||||
|
@ -277,8 +228,14 @@ int play_level(int level_id)
|
|||
//======= Simulation =======//
|
||||
|
||||
num vz = player->vz * (num(1) + player->energy_percent / 200);
|
||||
player->z += vz * dt;
|
||||
player->height += player->vheight * dt;
|
||||
|
||||
if(player->stance == player::Collided) {
|
||||
game.t_death += dt;
|
||||
}
|
||||
else {
|
||||
player->z += vz * dt;
|
||||
player->height += player->vheight * dt;
|
||||
}
|
||||
|
||||
/* Apply gravity */
|
||||
num gravity = 0.0;
|
||||
|
@ -325,7 +282,7 @@ int play_level(int level_id)
|
|||
player->height = 0;
|
||||
}
|
||||
}
|
||||
else if(floor < player->height) {
|
||||
else if(floor < player->height && player->stance != player::Collided) {
|
||||
player->stance = player::Falling;
|
||||
player->vheight = 0.0;
|
||||
}
|
||||
|
@ -336,13 +293,13 @@ int play_level(int level_id)
|
|||
/* Death condition #2/2: below the kill plane */
|
||||
bool death_2 = (player->height < LEVEL_RADIUS - KILL_PLANE_RADIUS);
|
||||
|
||||
if(!game.debug.invincible && (death_1 || death_2))
|
||||
break;
|
||||
|
||||
if(standing_on && standing_on->type == PLATFORM_BLUE) {
|
||||
if(!game.debug.invincible && (death_1 || death_2)) {
|
||||
player->stance = player::Collided;
|
||||
}
|
||||
else if(standing_on && standing_on->type == PLATFORM_BLUE) {
|
||||
player->energy_percent += dt * vz * 5.5;
|
||||
}
|
||||
if(standing_on && standing_on->type == PLATFORM_RED) {
|
||||
else if(standing_on && standing_on->type == PLATFORM_RED) {
|
||||
standing_on->falling = true;
|
||||
}
|
||||
|
||||
|
@ -354,14 +311,14 @@ int play_level(int level_id)
|
|||
}
|
||||
}
|
||||
|
||||
if(game.t_death >= TIME_DEATH_ANIMATION)
|
||||
break;
|
||||
|
||||
game_advance(&game);
|
||||
prof_leave_norec(perf_frame);
|
||||
last_frame_us = prof_time(perf_frame);
|
||||
}
|
||||
|
||||
if(effect_bpp)
|
||||
image_free(effect_bpp);
|
||||
|
||||
timer_stop(timer);
|
||||
return 0;
|
||||
}
|
||||
|
|
130
src/render.cpp
130
src/render.cpp
|
@ -266,3 +266,133 @@ void render_effect_bpp(image_t **image, num t)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void render_effect_dp(int x, int y, int count, num t, int color)
|
||||
{
|
||||
for(int i = 0; i < count; i++) {
|
||||
num lifetime = (num(37 * i) / 16).frac() + num(0.75);
|
||||
if(t >= lifetime)
|
||||
continue;
|
||||
|
||||
num v = (num(113 * i * i) / 24) % num(8);
|
||||
num t0 = v / 8;
|
||||
v = v * v + num(4);
|
||||
|
||||
num alpha = num((163 * i) / 32) % num(6.28);
|
||||
num vx = v * num_cos(alpha);
|
||||
num vy = v * num_sin(alpha);
|
||||
|
||||
num local_t = TIME_DEATH_ANIMATION * TIME_DEATH_ANIMATION -
|
||||
(TIME_DEATH_ANIMATION - t) * (TIME_DEATH_ANIMATION - t);
|
||||
|
||||
int rx = x + (int)((t0 + local_t) * vx);
|
||||
int ry = y + (int)((t0 + local_t) * vy);
|
||||
azrp_rect(rx, ry, 3, 3, color);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t render_blend(uint16_t c1, uint16_t c2, int t)
|
||||
{
|
||||
uint32_t o1 = ((c1 << 16) | c1) & 0x07e0f81f;
|
||||
uint32_t o2 = ((c2 << 16) | c2) & 0x07e0f81f;
|
||||
|
||||
uint32_t r = (t * (o2 - o1) >> 5) + o1;
|
||||
r &= 0x07e0f81f;
|
||||
return (r >> 16) | r;
|
||||
}
|
||||
|
||||
static void render_game_platforms(struct game &game)
|
||||
{
|
||||
for(auto it = game.level.platform_buffer.rbegin();
|
||||
it != game.level.platform_buffer.rend();
|
||||
++it) {
|
||||
int color = render_platform_color(&*it, &game.camera, game.t);
|
||||
struct prect p = space_platform_position(&*it);
|
||||
|
||||
/* Near plane clipping */
|
||||
num near = game.camera.pos.z + game.camera.near_plane();
|
||||
if(p.fl.z <= near) continue;
|
||||
if(p.nl.z <= near) p.nl.z = near;
|
||||
if(p.nr.z <= near) p.nr.z = near;
|
||||
camera_project_prect(&game.camera, &p);
|
||||
|
||||
render_triangle(&p.nl, &p.fr, &p.fl, color);
|
||||
render_triangle(&p.fr, &p.nl, &p.nr, color);
|
||||
}
|
||||
}
|
||||
|
||||
/* Blue platform particle effect's buffer image */
|
||||
static image_t *_effect_bpp = NULL;
|
||||
|
||||
void render_game(struct game &game, prof_t *perf_comp)
|
||||
{
|
||||
azrp_perf_clear();
|
||||
azrp_clear(game.level.bgcolor);
|
||||
game.perf.effect_bpp = prof_make();
|
||||
|
||||
struct player *player = &game.player;
|
||||
vec3 player_dot = camera_project_point(&game.camera, player->pos());
|
||||
bool alive = (player->stance != player::Collided);
|
||||
struct platform *standing_on = game_platform_under_player(&game, player);
|
||||
|
||||
/* Standard gameplay before collision */
|
||||
if(alive) {
|
||||
prof_enter_norec(*perf_comp);
|
||||
render_game_platforms(game);
|
||||
prof_leave_norec(*perf_comp);
|
||||
|
||||
struct anim *player_anim;
|
||||
int player_frame;
|
||||
player->get_anim(&player_anim, &player_frame);
|
||||
render_anim_frame((int)player_dot.x, (int)player_dot.y,
|
||||
player_anim, player_frame);
|
||||
|
||||
if(standing_on && standing_on->type == PLATFORM_BLUE) {
|
||||
prof_enter(game.perf.effect_bpp);
|
||||
render_effect_bpp(&_effect_bpp, game.t);
|
||||
azrp_image((int)player_dot.x - _effect_bpp->width / 2,
|
||||
(int)player_dot.y - _effect_bpp->height + 2,
|
||||
_effect_bpp);
|
||||
prof_leave(game.perf.effect_bpp);
|
||||
}
|
||||
|
||||
/* Render energy score */
|
||||
char energy_str[32];
|
||||
sprintf(energy_str, "%.2f%%", (float)player->energy_percent);
|
||||
azrp_rect(DWIDTH-100, 4, 96, 20, AZRP_RECT_DARKEN);
|
||||
render_energy_str(DWIDTH-8, 8, DTEXT_RIGHT, energy_str);
|
||||
}
|
||||
/* End transition and end screen */
|
||||
else {
|
||||
/* Background flash of light */
|
||||
if(game.t_death < num(0.25)) {
|
||||
int alpha = 31 - (int)(31 * (game.t_death / num(0.25)));
|
||||
shader_fsblend(C_WHITE, alpha);
|
||||
}
|
||||
/* Background fading to end screen */
|
||||
if(game.t_death > num(0.75)) {
|
||||
num t_fadeout = (game.t_death - num(0.75)) * num(2.0);
|
||||
int alpha = (int)(20 * min(t_fadeout, num(1.0)));
|
||||
shader_fsblend(C_BLACK, alpha);
|
||||
}
|
||||
|
||||
/* Particle explosion */
|
||||
if(game.t_death < TIME_DEATH_ANIMATION) {
|
||||
int color_t = (int)(min(game.t_death, num(1.0)) * 32);
|
||||
render_effect_dp((int)player_dot.x, (int)player_dot.y - 40,
|
||||
256, game.t_death, render_blend(C_WHITE, 0x5555, color_t));
|
||||
}
|
||||
|
||||
/* End screen elements appearing in order */
|
||||
num t_endscreen = game.t_death - TIME_DEATH_ANIMATION - num(0.5);
|
||||
|
||||
if(t_endscreen >= num(0.0)) {
|
||||
}
|
||||
if(t_endscreen >= num(0.1)) {
|
||||
}
|
||||
if(t_endscreen >= num(0.2)) {
|
||||
}
|
||||
}
|
||||
|
||||
azrp_update();
|
||||
}
|
||||
|
|
16
src/render.h
16
src/render.h
|
@ -6,6 +6,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include <gint/display.h>
|
||||
#include <libprof.h>
|
||||
|
||||
struct camera
|
||||
{
|
||||
|
@ -102,4 +103,19 @@ void render_anim_frame(int x, int y, struct anim *anim, int frame);
|
|||
creates it automatically. */
|
||||
void render_effect_bpp(image_t **image, num t);
|
||||
|
||||
/* Render the death particles at time t. */
|
||||
void render_effect_dp(int x, int y, int count, num t, int color);
|
||||
|
||||
/* Blend from c1 to c2 (t in [0..32]). */
|
||||
uint16_t render_blend(uint16_t c1, uint16_t c2, int t);
|
||||
|
||||
/* Render full game.
|
||||
perf_comp: Performance counter for platform projection and commands. */
|
||||
void render_game(struct game &game, prof_t *perf_comp);
|
||||
|
||||
//======= Custom shaders =======//
|
||||
|
||||
/* Alpha blend an opaque color over the full screen (alpha = 0..31) */
|
||||
void shader_fsblend(uint16_t color, int alpha);
|
||||
|
||||
#endif /* __RENDERH__ */
|
||||
|
|
|
@ -29,10 +29,12 @@ using namespace libnum;
|
|||
/* Player height, for collisions with platform fronts. */
|
||||
#define PLAYER_HEIGHT num(0.8)
|
||||
|
||||
/* Duration of a rotation by one platform, in seconds. */
|
||||
/* Duration of a rotation by one platform (s). */
|
||||
#define TIME_ROTATION num(0.1)
|
||||
/* How long a jump must last for (s). */
|
||||
#define TIME_JUMP_THRUST_MIN num(0.25)
|
||||
/* Duration of the death animation (s). */
|
||||
#define TIME_DEATH_ANIMATION num(2.0)
|
||||
|
||||
/* Vertical FOV, in degrees */
|
||||
#define RENDER_FOV 120.0
|
||||
|
|
Loading…
Reference in New Issue