2017-04-07 14:20:45 +02:00
|
|
|
/*
|
2019-08-03 19:43:48 +02:00
|
|
|
* This software is licensed under the terms of the MIT License.
|
2017-04-07 14:20:45 +02:00
|
|
|
* See COPYING for further information.
|
|
|
|
* ---
|
2024-05-16 23:30:41 +02:00
|
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
2017-04-07 14:20:45 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "stagedraw.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
#include "entity.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
#include "events.h"
|
|
|
|
#include "global.h"
|
2021-05-30 03:26:21 +02:00
|
|
|
#include "replay/struct.h"
|
2024-05-17 04:41:28 +02:00
|
|
|
#include "resource/postprocess.h"
|
|
|
|
#include "stageobjects.h"
|
|
|
|
#include "stagetext.h"
|
|
|
|
#include "util/env.h"
|
|
|
|
#include "util/fbmgr.h"
|
|
|
|
#include "util/graphics.h"
|
|
|
|
#include "video.h"
|
2017-04-07 14:20:45 +02:00
|
|
|
|
2017-12-11 19:56:46 +01:00
|
|
|
#ifdef DEBUG
|
|
|
|
#define GRAPHS_DEFAULT 1
|
2019-02-22 00:56:03 +01:00
|
|
|
#define OBJPOOLSTATS_DEFAULT 0
|
2017-12-11 19:56:46 +01:00
|
|
|
#else
|
|
|
|
#define GRAPHS_DEFAULT 0
|
2017-12-13 20:05:12 +01:00
|
|
|
#define OBJPOOLSTATS_DEFAULT 0
|
2017-12-11 19:56:46 +01:00
|
|
|
#endif
|
|
|
|
|
2021-05-14 05:28:16 +02:00
|
|
|
#define SPELL_INTRO_DURATION 120
|
|
|
|
#define SPELL_INTRO_TIME_FACTOR 0.8
|
|
|
|
|
2017-11-23 03:25:53 +01:00
|
|
|
static struct {
|
|
|
|
struct {
|
2018-04-12 16:08:48 +02:00
|
|
|
ShaderProgram *shader;
|
2018-06-29 23:36:51 +02:00
|
|
|
Font *font;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
Color active;
|
|
|
|
Color inactive;
|
2019-02-22 00:56:03 +01:00
|
|
|
Color dark;
|
2018-06-29 23:36:51 +02:00
|
|
|
Color label;
|
2019-01-04 23:59:39 +01:00
|
|
|
Color label_power;
|
|
|
|
Color label_value;
|
|
|
|
Color label_graze;
|
2019-02-22 00:56:03 +01:00
|
|
|
Color label_voltage;
|
2018-06-29 23:36:51 +02:00
|
|
|
} color;
|
2017-11-23 03:25:53 +01:00
|
|
|
} hud_text;
|
2018-06-29 23:36:51 +02:00
|
|
|
|
2018-08-16 21:52:41 +02:00
|
|
|
struct {
|
|
|
|
ShaderProgram *fxaa;
|
|
|
|
} shaders;
|
|
|
|
|
2018-07-04 10:36:16 +02:00
|
|
|
PostprocessShader *viewport_pp;
|
|
|
|
FBPair fb_pairs[NUM_FBPAIRS];
|
2019-02-22 00:56:03 +01:00
|
|
|
FBPair powersurge_fbpair;
|
2020-04-13 13:26:14 +02:00
|
|
|
FBPair *current_postprocess_fbpair;
|
2018-07-04 10:36:16 +02:00
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
ManagedFramebufferGroup *mfb_group;
|
2020-03-25 07:52:18 +01:00
|
|
|
StageDrawEvents events;
|
|
|
|
|
2020-05-16 22:42:22 +02:00
|
|
|
struct {
|
|
|
|
float alpha;
|
|
|
|
float target_alpha;
|
|
|
|
} clear_screen;
|
|
|
|
|
|
|
|
bool framerate_graphs;
|
|
|
|
bool objpool_stats;
|
|
|
|
|
2018-05-08 15:02:08 +02:00
|
|
|
#ifdef DEBUG
|
|
|
|
Sprite dummy;
|
|
|
|
#endif
|
2018-06-29 23:36:51 +02:00
|
|
|
} stagedraw = {
|
|
|
|
.hud_text.color = {
|
2018-07-23 19:07:59 +02:00
|
|
|
// NOTE: premultiplied alpha
|
2019-02-22 00:56:03 +01:00
|
|
|
.active = { 1.00, 1.00, 1.00, 1.00 },
|
|
|
|
.inactive = { 0.50, 0.50, 0.50, 0.70 },
|
|
|
|
.dark = { 0.30, 0.30, 0.30, 0.70 },
|
|
|
|
.label = { 1.00, 1.00, 1.00, 1.00 },
|
|
|
|
.label_power = { 1.00, 0.50, 0.50, 1.00 },
|
|
|
|
.label_value = { 0.30, 0.60, 1.00, 1.00 },
|
|
|
|
.label_graze = { 0.50, 1.00, 0.50, 1.00 },
|
|
|
|
.label_voltage = { 0.80, 0.50, 1.00, 1.00 },
|
2018-06-29 23:36:51 +02:00
|
|
|
}
|
|
|
|
};
|
2017-11-23 03:25:53 +01:00
|
|
|
|
2024-04-29 00:42:33 +02:00
|
|
|
bool stage_draw_is_initialized(void) {
|
|
|
|
return stagedraw.mfb_group;
|
|
|
|
}
|
|
|
|
|
2018-07-04 10:36:16 +02:00
|
|
|
static double fb_scale(void) {
|
2019-04-04 01:43:03 +02:00
|
|
|
float vp_width, vp_height;
|
2018-07-04 10:36:16 +02:00
|
|
|
video_get_viewport_size(&vp_width, &vp_height);
|
|
|
|
return (double)vp_height / SCREEN_H;
|
|
|
|
}
|
|
|
|
|
2019-01-04 23:59:39 +01:00
|
|
|
static void set_fb_size(StageFBPair fb_id, int *w, int *h, float scale_worst, float scale_best) {
|
2018-07-04 10:36:16 +02:00
|
|
|
double scale = fb_scale();
|
|
|
|
|
2019-02-22 00:56:03 +01:00
|
|
|
if(fb_id == FBPAIR_FG_AUX || fb_id == FBPAIR_BG_AUX) {
|
2019-01-04 23:59:39 +01:00
|
|
|
scale_worst *= 0.5;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pp_qual = config_get_int(CONFIG_POSTPROCESS);
|
|
|
|
|
|
|
|
// We might lerp between these in the future
|
|
|
|
if(pp_qual < 2) {
|
|
|
|
scale *= scale_worst;
|
|
|
|
} else {
|
|
|
|
scale *= scale_best;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_debug("%u %f %f %f %i", fb_id, scale, scale_worst, scale_best, pp_qual);
|
|
|
|
|
2018-07-04 10:36:16 +02:00
|
|
|
switch(fb_id) {
|
|
|
|
case FBPAIR_BG:
|
|
|
|
scale *= config_get_float(CONFIG_BG_QUALITY);
|
2019-01-04 23:59:39 +01:00
|
|
|
// fallthrough
|
2018-07-04 10:36:16 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
scale *= config_get_float(CONFIG_FG_QUALITY);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-09-23 21:56:34 +02:00
|
|
|
scale = max(0.1, scale);
|
2018-07-04 10:36:16 +02:00
|
|
|
*w = round(VIEWPORT_W * scale);
|
|
|
|
*h = round(VIEWPORT_H * scale);
|
|
|
|
}
|
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
typedef struct StageFramebufferResizeParams {
|
|
|
|
struct { float worst, best; } scale;
|
|
|
|
StageFBPair scaling_base;
|
|
|
|
int refs;
|
|
|
|
} StageFramebufferResizeParams;
|
2018-08-11 21:13:48 +02:00
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
static void stage_framebuffer_resize_strategy(void *userdata, IntExtent *out_dimensions, FloatRect *out_viewport) {
|
|
|
|
StageFramebufferResizeParams *rp = userdata;
|
|
|
|
set_fb_size(rp->scaling_base, &out_dimensions->w, &out_dimensions->h, rp->scale.worst, rp->scale.best);
|
|
|
|
*out_viewport = (FloatRect) { 0, 0, out_dimensions->w, out_dimensions->h };
|
|
|
|
}
|
2018-08-11 21:13:48 +02:00
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
static void stage_framebuffer_resize_strategy_cleanup(void *userdata) {
|
|
|
|
StageFramebufferResizeParams *rp = userdata;
|
|
|
|
if(--rp->refs <= 0) {
|
2023-01-09 04:19:31 +01:00
|
|
|
mem_free(rp);
|
2018-08-11 21:13:48 +02:00
|
|
|
}
|
2018-07-04 10:36:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool stage_draw_event(SDL_Event *e, void *arg) {
|
|
|
|
if(!IS_TAISEI_EVENT(e->type)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(TAISEI_EVENT(e->type)) {
|
2019-04-11 11:23:24 +02:00
|
|
|
case TE_FRAME: {
|
|
|
|
fapproach_p(&stagedraw.clear_screen.alpha, stagedraw.clear_screen.target_alpha, 0.01);
|
|
|
|
break;
|
|
|
|
}
|
2018-07-04 10:36:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
static void stage_draw_fbpair_create(
|
|
|
|
FBPair *pair,
|
|
|
|
int num_attachments,
|
|
|
|
FBAttachmentConfig *attachments,
|
|
|
|
const StageFramebufferResizeParams *resize_params,
|
|
|
|
const char *name
|
|
|
|
) {
|
|
|
|
StageFramebufferResizeParams *rp = memdup(resize_params, sizeof(*resize_params));
|
|
|
|
rp->refs = 2;
|
|
|
|
|
|
|
|
FramebufferConfig fbconf = { 0 };
|
|
|
|
fbconf.attachments = attachments;
|
|
|
|
fbconf.num_attachments = num_attachments;
|
|
|
|
fbconf.resize_strategy.resize_func = stage_framebuffer_resize_strategy;
|
|
|
|
fbconf.resize_strategy.userdata = rp;
|
|
|
|
fbconf.resize_strategy.cleanup_func = stage_framebuffer_resize_strategy_cleanup;
|
|
|
|
fbmgr_group_fbpair_create(stagedraw.mfb_group, name, &fbconf, pair);
|
|
|
|
}
|
|
|
|
|
2018-07-04 10:36:16 +02:00
|
|
|
static void stage_draw_setup_framebuffers(void) {
|
|
|
|
FBAttachmentConfig a[2], *a_color, *a_depth;
|
|
|
|
memset(a, 0, sizeof(a));
|
|
|
|
|
|
|
|
a_color = &a[0];
|
|
|
|
a_depth = &a[1];
|
|
|
|
|
|
|
|
a_color->attachment = FRAMEBUFFER_ATTACH_COLOR0;
|
|
|
|
a_depth->attachment = FRAMEBUFFER_ATTACH_DEPTH;
|
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
StageFramebufferResizeParams rp_fg = { .scaling_base = FBPAIR_FG, .scale.best = 1, .scale.worst = 1 };
|
|
|
|
StageFramebufferResizeParams rp_fg_aux = { .scaling_base = FBPAIR_FG_AUX, .scale.best = 1, .scale.worst = 1 };
|
|
|
|
StageFramebufferResizeParams rp_bg = { .scaling_base = FBPAIR_BG, .scale.best = 1, .scale.worst = 1 };
|
|
|
|
StageFramebufferResizeParams rp_bg_aux = { .scaling_base = FBPAIR_BG_AUX, .scale.best = 1, .scale.worst = 1 };
|
2018-07-04 10:36:16 +02:00
|
|
|
|
|
|
|
// Set up some parameters shared by all attachments
|
|
|
|
TextureParams tex_common = {
|
2018-07-10 11:14:29 +02:00
|
|
|
.filter.min = TEX_FILTER_LINEAR,
|
|
|
|
.filter.mag = TEX_FILTER_LINEAR,
|
2018-07-04 10:36:16 +02:00
|
|
|
.wrap.s = TEX_WRAP_MIRROR,
|
|
|
|
.wrap.t = TEX_WRAP_MIRROR,
|
|
|
|
};
|
|
|
|
|
|
|
|
memcpy(&a_color->tex_params, &tex_common, sizeof(tex_common));
|
|
|
|
memcpy(&a_depth->tex_params, &tex_common, sizeof(tex_common));
|
|
|
|
|
2020-08-15 13:51:12 +02:00
|
|
|
a_depth->tex_params.type = TEX_TYPE_DEPTH;
|
2019-08-25 01:28:46 +02:00
|
|
|
|
2018-07-04 10:36:16 +02:00
|
|
|
// Foreground: 1 RGB texture per FB
|
2019-01-04 23:59:39 +01:00
|
|
|
a_color->tex_params.type = TEX_TYPE_RGB_16;
|
2019-08-25 01:28:46 +02:00
|
|
|
stage_draw_fbpair_create(stagedraw.fb_pairs + FBPAIR_FG, 1, a, &rp_fg, "Stage FG");
|
2018-07-04 10:36:16 +02:00
|
|
|
|
|
|
|
// Foreground auxiliary: 1 RGBA texture per FB
|
2019-08-25 01:28:46 +02:00
|
|
|
a_color->tex_params.type = TEX_TYPE_RGBA_8;
|
|
|
|
stage_draw_fbpair_create(stagedraw.fb_pairs + FBPAIR_FG_AUX, 1, a, &rp_fg_aux, "Stage FG AUX");
|
2018-07-04 10:36:16 +02:00
|
|
|
|
2021-10-20 22:53:24 +02:00
|
|
|
// Background: 1 HDR RGBA texture + depth per FB
|
|
|
|
a_color->tex_params.type = TEX_TYPE_RGBA_16_FLOAT;
|
2019-08-25 01:28:46 +02:00
|
|
|
stage_draw_fbpair_create(stagedraw.fb_pairs + FBPAIR_BG, 2, a, &rp_bg, "Stage BG");
|
2019-02-22 00:56:03 +01:00
|
|
|
|
|
|
|
// Background auxiliary: 1 RGBA texture per FB
|
|
|
|
a_color->tex_params.type = TEX_TYPE_RGBA_8;
|
2019-08-25 01:28:46 +02:00
|
|
|
stage_draw_fbpair_create(stagedraw.fb_pairs + FBPAIR_BG_AUX, 1, a, &rp_bg_aux, "Stage BG AUX");
|
2019-02-22 00:56:03 +01:00
|
|
|
|
|
|
|
// CAUTION: should be at least 16-bit, lest the feedback shader do an oopsie!
|
|
|
|
a_color->tex_params.type = TEX_TYPE_RGBA_16;
|
|
|
|
stagedraw.powersurge_fbpair.front = stage_add_background_framebuffer("Powersurge effect FB 1", 0.125, 0.25, 1, a);
|
|
|
|
stagedraw.powersurge_fbpair.back = stage_add_background_framebuffer("Powersurge effect FB 2", 0.125, 0.25, 1, a);
|
2018-07-04 10:36:16 +02:00
|
|
|
}
|
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
static Framebuffer *add_custom_framebuffer(
|
|
|
|
const char *label,
|
|
|
|
StageFBPair fbtype,
|
|
|
|
float scale_worst,
|
|
|
|
float scale_best,
|
|
|
|
uint num_attachments,
|
|
|
|
FBAttachmentConfig attachments[num_attachments]
|
|
|
|
) {
|
|
|
|
StageFramebufferResizeParams rp = { 0 };
|
|
|
|
rp.scaling_base = fbtype;
|
|
|
|
rp.scale.worst = scale_worst;
|
|
|
|
rp.scale.best = scale_best;
|
|
|
|
|
|
|
|
FramebufferConfig fbconf = { 0 };
|
|
|
|
fbconf.attachments = attachments;
|
|
|
|
fbconf.num_attachments = num_attachments;
|
|
|
|
fbconf.resize_strategy.resize_func = stage_framebuffer_resize_strategy;
|
|
|
|
fbconf.resize_strategy.userdata = memdup(&rp, sizeof(rp));
|
|
|
|
fbconf.resize_strategy.cleanup_func = stage_framebuffer_resize_strategy_cleanup;
|
|
|
|
return fbmgr_group_framebuffer_create(stagedraw.mfb_group, label, &fbconf);
|
2018-08-11 21:13:48 +02:00
|
|
|
}
|
|
|
|
|
2020-04-24 00:10:20 +02:00
|
|
|
Framebuffer *stage_add_foreground_framebuffer(const char *label, float scale_worst, float scale_best, uint num_attachments, FBAttachmentConfig attachments[num_attachments]) {
|
2019-01-04 23:59:39 +01:00
|
|
|
return add_custom_framebuffer(label, FBPAIR_FG, scale_worst, scale_best, num_attachments, attachments);
|
2018-08-11 21:13:48 +02:00
|
|
|
}
|
|
|
|
|
2020-04-24 00:10:20 +02:00
|
|
|
Framebuffer *stage_add_background_framebuffer(const char *label, float scale_worst, float scale_best, uint num_attachments, FBAttachmentConfig attachments[num_attachments]) {
|
2019-01-04 23:59:39 +01:00
|
|
|
return add_custom_framebuffer(label, FBPAIR_BG, scale_worst, scale_best, num_attachments, attachments);
|
2018-08-11 21:13:48 +02:00
|
|
|
}
|
|
|
|
|
2020-04-24 00:10:20 +02:00
|
|
|
Framebuffer *stage_add_static_framebuffer(const char *label, uint num_attachments, FBAttachmentConfig attachments[num_attachments]) {
|
|
|
|
FramebufferConfig fbconf = { 0 };
|
|
|
|
fbconf.attachments = attachments;
|
|
|
|
fbconf.num_attachments = num_attachments;
|
|
|
|
return fbmgr_group_framebuffer_create(stagedraw.mfb_group, label, &fbconf);
|
|
|
|
}
|
|
|
|
|
2018-08-11 21:13:48 +02:00
|
|
|
static void stage_draw_destroy_framebuffers(void) {
|
2019-08-25 01:28:46 +02:00
|
|
|
fbmgr_group_destroy(stagedraw.mfb_group);
|
|
|
|
stagedraw.mfb_group = NULL;
|
2018-08-11 21:13:48 +02:00
|
|
|
}
|
|
|
|
|
2023-03-22 23:41:32 +01:00
|
|
|
void stage_draw_preload(ResourceGroup *rg) {
|
|
|
|
res_group_preload(rg, RES_POSTPROCESS, RESF_OPTIONAL,
|
2018-04-12 16:08:48 +02:00
|
|
|
"viewport",
|
|
|
|
NULL);
|
|
|
|
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_SPRITE, RESF_DEFAULT,
|
2019-01-04 23:59:39 +01:00
|
|
|
"hud/heart",
|
|
|
|
"hud/star",
|
2017-11-23 03:25:53 +01:00
|
|
|
"star",
|
2018-02-06 07:19:25 +01:00
|
|
|
NULL);
|
|
|
|
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_TEXTURE, RESF_DEFAULT,
|
2019-02-22 00:56:03 +01:00
|
|
|
"powersurge_flow",
|
2017-11-23 03:25:53 +01:00
|
|
|
"titletransition",
|
2019-01-23 15:02:51 +01:00
|
|
|
"hud",
|
|
|
|
NULL);
|
|
|
|
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_MODEL, RESF_DEFAULT,
|
2019-01-23 15:02:51 +01:00
|
|
|
"hud",
|
2017-11-23 03:25:53 +01:00
|
|
|
NULL);
|
|
|
|
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_SHADER_PROGRAM, RESF_DEFAULT,
|
2017-11-23 03:25:53 +01:00
|
|
|
"ingame_menu",
|
2019-02-22 00:56:03 +01:00
|
|
|
"powersurge_effect",
|
|
|
|
"powersurge_feedback",
|
2018-04-12 16:08:48 +02:00
|
|
|
"sprite_circleclipped_indicator",
|
2019-02-22 00:56:03 +01:00
|
|
|
"text_hud",
|
|
|
|
"text_stagetext",
|
2018-08-16 21:52:41 +02:00
|
|
|
|
|
|
|
// TODO: Maybe don't preload this if FXAA is disabled.
|
|
|
|
// But then we also have to handle the edge case of it being enabled later mid-game.
|
|
|
|
"fxaa",
|
|
|
|
|
2018-05-08 15:02:08 +02:00
|
|
|
#ifdef DEBUG
|
|
|
|
"sprite_filled_circle",
|
|
|
|
#endif
|
2017-11-23 03:25:53 +01:00
|
|
|
NULL);
|
|
|
|
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_FONT, RESF_DEFAULT,
|
2018-06-29 23:36:51 +02:00
|
|
|
"mono",
|
|
|
|
"small",
|
|
|
|
"monosmall",
|
|
|
|
NULL);
|
2017-11-23 03:25:53 +01:00
|
|
|
|
2023-04-07 07:57:49 +02:00
|
|
|
if(stage_is_demo_mode()) {
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_SHADER_PROGRAM, RESF_DEFAULT,
|
2023-04-07 07:57:49 +02:00
|
|
|
"text_demo",
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2018-04-18 00:34:41 +02:00
|
|
|
stagedraw.framerate_graphs = env_get("TAISEI_FRAMERATE_GRAPHS", GRAPHS_DEFAULT);
|
|
|
|
stagedraw.objpool_stats = env_get("TAISEI_OBJPOOL_STATS", OBJPOOLSTATS_DEFAULT);
|
2017-12-14 16:07:24 +01:00
|
|
|
|
|
|
|
if(stagedraw.framerate_graphs) {
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_SHADER_PROGRAM, RESF_DEFAULT,
|
2017-12-14 16:07:24 +01:00
|
|
|
"graph",
|
|
|
|
NULL);
|
|
|
|
}
|
2018-04-12 16:08:48 +02:00
|
|
|
|
2018-06-29 23:36:51 +02:00
|
|
|
if(stagedraw.objpool_stats) {
|
2023-03-22 23:41:32 +01:00
|
|
|
res_group_preload(rg, RES_FONT, RESF_DEFAULT,
|
2018-06-29 23:36:51 +02:00
|
|
|
"monotiny",
|
|
|
|
NULL);
|
|
|
|
}
|
2019-08-25 01:28:46 +02:00
|
|
|
}
|
2018-06-29 23:36:51 +02:00
|
|
|
|
2019-08-25 01:28:46 +02:00
|
|
|
void stage_draw_init(void) {
|
2023-03-22 23:41:32 +01:00
|
|
|
stagedraw.mfb_group = fbmgr_group_create();
|
|
|
|
|
2023-03-22 23:14:10 +01:00
|
|
|
stagedraw.viewport_pp = res_get_data(RES_POSTPROCESS, "viewport", RESF_OPTIONAL);
|
2020-06-09 03:33:22 +02:00
|
|
|
stagedraw.hud_text.shader = res_shader("text_hud");
|
|
|
|
stagedraw.hud_text.font = res_font("standard");
|
|
|
|
stagedraw.shaders.fxaa = res_shader("fxaa");
|
2018-06-29 23:36:51 +02:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_shader_standard();
|
2018-05-08 15:02:08 +02:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2020-06-09 03:33:22 +02:00
|
|
|
stagedraw.dummy.tex = res_sprite("star")->tex;
|
2018-05-08 15:02:08 +02:00
|
|
|
stagedraw.dummy.w = 1;
|
|
|
|
stagedraw.dummy.h = 1;
|
|
|
|
#endif
|
2018-07-04 10:36:16 +02:00
|
|
|
|
|
|
|
stage_draw_setup_framebuffers();
|
|
|
|
|
2019-02-22 00:56:03 +01:00
|
|
|
stagedraw.clear_screen.alpha = 0;
|
|
|
|
stagedraw.clear_screen.target_alpha = 0;
|
|
|
|
|
2018-07-04 10:36:16 +02:00
|
|
|
events_register_handler(&(EventHandler) {
|
|
|
|
stage_draw_event, NULL, EPRIO_SYSTEM,
|
|
|
|
});
|
2020-03-25 07:52:18 +01:00
|
|
|
|
|
|
|
COEVENT_INIT_ARRAY(stagedraw.events);
|
2018-07-04 10:36:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void stage_draw_shutdown(void) {
|
2020-03-25 07:52:18 +01:00
|
|
|
COEVENT_CANCEL_ARRAY(stagedraw.events);
|
2018-07-04 10:36:16 +02:00
|
|
|
events_unregister_handler(stage_draw_event);
|
2018-08-11 21:13:48 +02:00
|
|
|
stage_draw_destroy_framebuffers();
|
2018-07-04 10:36:16 +02:00
|
|
|
}
|
|
|
|
|
2020-04-13 13:26:14 +02:00
|
|
|
FBPair *stage_get_fbpair(StageFBPair id) {
|
2018-07-04 10:36:16 +02:00
|
|
|
assert(id >= 0 && id < NUM_FBPAIRS);
|
|
|
|
return stagedraw.fb_pairs + id;
|
2018-05-08 15:02:08 +02:00
|
|
|
}
|
|
|
|
|
2020-04-13 13:26:14 +02:00
|
|
|
FBPair *stage_get_postprocess_fbpair(void) {
|
|
|
|
return NOT_NULL(stagedraw.current_postprocess_fbpair);
|
|
|
|
}
|
|
|
|
|
2018-05-08 15:02:08 +02:00
|
|
|
static void stage_draw_collision_areas(void) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
static bool enabled, keystate_saved;
|
|
|
|
bool keystate = gamekeypressed(KEY_HITAREAS);
|
|
|
|
|
|
|
|
if(keystate ^ keystate_saved) {
|
|
|
|
if(keystate) {
|
|
|
|
enabled = !enabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
keystate_saved = keystate;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
r_shader("sprite_filled_circle");
|
|
|
|
r_uniform_vec4("color_inner", 0, 0, 0, 1);
|
2018-05-13 15:08:58 +02:00
|
|
|
r_uniform_vec4("color_outer", 1, 1, 1, 0.1);
|
2018-05-08 15:02:08 +02:00
|
|
|
|
2018-06-01 20:40:18 +02:00
|
|
|
for(Projectile *p = global.projs.first; p; p = p->next) {
|
2019-11-22 04:37:11 +01:00
|
|
|
cmplx gsize = projectile_graze_size(p);
|
2018-05-13 15:08:58 +02:00
|
|
|
|
2023-09-20 04:14:15 +02:00
|
|
|
if(re(gsize)) {
|
2018-05-13 15:08:58 +02:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
2018-07-23 19:07:59 +02:00
|
|
|
.color = RGB(0, 0.5, 0.5),
|
2018-05-13 15:08:58 +02:00
|
|
|
.sprite_ptr = &stagedraw.dummy,
|
2023-09-20 04:14:15 +02:00
|
|
|
.pos = { re(p->pos), im(p->pos) },
|
2018-05-13 15:08:58 +02:00
|
|
|
.rotation.angle = p->angle + M_PI/2,
|
2023-09-20 04:14:15 +02:00
|
|
|
.scale = { .x = re(gsize), .y = im(gsize) },
|
2018-05-13 15:08:58 +02:00
|
|
|
.blend = BLEND_SUB,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r_flush_sprites();
|
|
|
|
r_uniform_vec4("color_inner", 0.0, 1.0, 0.0, 0.75);
|
|
|
|
r_uniform_vec4("color_outer", 0.0, 0.5, 0.5, 0.75);
|
2018-05-08 15:02:08 +02:00
|
|
|
|
2018-06-01 20:40:18 +02:00
|
|
|
for(Projectile *p = global.projs.first; p; p = p->next) {
|
2018-05-08 15:02:08 +02:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
|
|
|
.sprite_ptr = &stagedraw.dummy,
|
2023-09-20 04:14:15 +02:00
|
|
|
.pos = { re(p->pos), im(p->pos) },
|
2018-05-08 15:02:08 +02:00
|
|
|
.rotation.angle = p->angle + M_PI/2,
|
2023-09-20 04:14:15 +02:00
|
|
|
.scale = { .x = re(p->collision_size), .y = im(p->collision_size) },
|
2018-05-13 15:08:58 +02:00
|
|
|
.blend = BLEND_ALPHA,
|
2018-05-08 15:02:08 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-03-01 17:10:49 +01:00
|
|
|
for(Enemy *e = global.enemies.first; e; e = e->next) {
|
2020-07-09 14:07:40 +02:00
|
|
|
float hurt_radius = enemy_get_hurt_radius(e);
|
|
|
|
|
|
|
|
if(hurt_radius > 0) {
|
2019-03-01 17:10:49 +01:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
|
|
|
.sprite_ptr = &stagedraw.dummy,
|
2023-09-20 04:14:15 +02:00
|
|
|
.pos = { re(e->pos), im(e->pos) },
|
2020-07-09 14:07:40 +02:00
|
|
|
.scale = { .x = hurt_radius * 2, .y = hurt_radius * 2 },
|
2019-03-01 17:10:49 +01:00
|
|
|
.blend = BLEND_ALPHA,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 13:01:00 +02:00
|
|
|
if(global.boss && boss_is_player_collision_active(global.boss)) {
|
2019-03-01 17:10:49 +01:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
|
|
|
.sprite_ptr = &stagedraw.dummy,
|
2023-09-20 04:14:15 +02:00
|
|
|
.pos = { re(global.boss->pos), im(global.boss->pos) },
|
2019-03-01 17:10:49 +01:00
|
|
|
.scale = { .x = BOSS_HURT_RADIUS * 2, .y = BOSS_HURT_RADIUS * 2 },
|
|
|
|
.blend = BLEND_ALPHA,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-05-13 15:08:58 +02:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
|
|
|
.sprite_ptr = &stagedraw.dummy,
|
2023-09-20 04:14:15 +02:00
|
|
|
.pos = { re(global.plr.pos), im(global.plr.pos) },
|
2018-05-13 15:08:58 +02:00
|
|
|
.scale.both = 2, // NOTE: actual player is a singular point
|
|
|
|
});
|
|
|
|
|
2019-03-01 17:10:49 +01:00
|
|
|
// TODO: perhaps handle lasers somehow
|
2018-05-08 15:02:08 +02:00
|
|
|
|
|
|
|
r_flush_sprites();
|
|
|
|
#endif
|
2017-11-23 03:25:53 +01:00
|
|
|
}
|
|
|
|
|
2018-07-04 10:36:16 +02:00
|
|
|
static void apply_shader_rules(ShaderRule *shaderrules, FBPair *fbos) {
|
2017-10-01 10:58:45 +02:00
|
|
|
if(!shaderrules) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-07 14:20:45 +02:00
|
|
|
for(ShaderRule *rule = shaderrules; *rule; ++rule) {
|
2018-07-04 10:36:16 +02:00
|
|
|
r_framebuffer(fbos->back);
|
2019-02-22 00:56:03 +01:00
|
|
|
|
|
|
|
if((*rule)(fbos->front)) {
|
|
|
|
fbpair_swap(fbos);
|
|
|
|
}
|
2017-04-07 14:20:45 +02:00
|
|
|
}
|
|
|
|
|
2017-10-01 10:58:45 +02:00
|
|
|
return;
|
2017-04-07 14:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_wall_of_text(float f, const char *txt) {
|
2018-02-06 07:19:25 +01:00
|
|
|
Sprite spr;
|
2023-05-09 02:50:48 +02:00
|
|
|
TextBBox bbox;
|
2018-06-29 23:36:51 +02:00
|
|
|
|
2018-08-28 10:25:54 +02:00
|
|
|
char buf[strlen(txt) + 4];
|
|
|
|
memcpy(buf, txt, sizeof(buf) - 4);
|
|
|
|
memcpy(buf + sizeof(buf) - 4, " ~ ", 4);
|
|
|
|
|
2020-06-09 03:33:22 +02:00
|
|
|
text_render(buf, res_font("standard"), &spr, &bbox);
|
2018-06-29 23:36:51 +02:00
|
|
|
|
|
|
|
// FIXME: The shader currently assumes that the sprite takes up the entire texture.
|
2019-02-22 00:56:03 +01:00
|
|
|
// If it could handle any arbitrary sprite, then text_render wouldn't have to resize
|
|
|
|
// the texture per every new string of text.
|
2017-04-07 14:20:45 +02:00
|
|
|
|
2017-04-14 10:42:09 +02:00
|
|
|
float w = VIEWPORT_W;
|
|
|
|
float h = VIEWPORT_H;
|
2017-04-07 14:20:45 +02:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_push();
|
|
|
|
r_mat_mv_translate(w/2, h/2, 0);
|
|
|
|
r_mat_mv_scale(w, h, 1.0);
|
2018-04-12 16:08:48 +02:00
|
|
|
|
2018-09-14 09:37:20 +02:00
|
|
|
uint tw, th;
|
|
|
|
r_texture_get_size(spr.tex, 0, &tw, &th);
|
|
|
|
|
|
|
|
r_shader("spellcard_walloftext");
|
2020-04-25 04:35:22 +02:00
|
|
|
r_uniform_float("w", spr.tex_area.w);
|
|
|
|
r_uniform_float("h", spr.tex_area.h);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_uniform_float("ratio", h/w);
|
2023-09-20 04:14:15 +02:00
|
|
|
r_uniform_vec2("origin", re(global.boss->pos)/h, im(global.boss->pos)/w); // what the fuck?
|
2018-04-12 16:08:48 +02:00
|
|
|
r_uniform_float("t", f);
|
2018-09-14 09:37:20 +02:00
|
|
|
r_uniform_sampler("tex", spr.tex);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_draw_quad();
|
|
|
|
r_shader_standard();
|
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_mat_mv_pop();
|
2017-04-07 14:20:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_spellbg(int t) {
|
|
|
|
Boss *b = global.boss;
|
2019-02-22 00:56:03 +01:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_state_push();
|
2017-04-07 14:20:45 +02:00
|
|
|
b->current->draw_rule(b, t);
|
2019-10-12 14:02:15 +02:00
|
|
|
r_state_pop();
|
2017-04-07 14:20:45 +02:00
|
|
|
|
2019-01-24 21:21:08 +01:00
|
|
|
if(b->current->type == AT_ExtraSpell) {
|
2017-04-07 14:20:45 +02:00
|
|
|
draw_extraspell_bg(b, t);
|
2019-01-24 21:21:08 +01:00
|
|
|
}
|
2017-04-07 14:20:45 +02:00
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
float scale = 1;
|
2017-04-07 14:20:45 +02:00
|
|
|
|
|
|
|
if(t < 0) {
|
2021-05-14 05:28:16 +02:00
|
|
|
scale = 1.0 - t/attacktype_start_delay(b->current->type);
|
2017-04-07 14:20:45 +02:00
|
|
|
}
|
|
|
|
|
2019-10-12 14:02:15 +02:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
2024-09-22 21:06:57 +02:00
|
|
|
.sprite_ptr = res_sprite("boss_spellcircle0"),
|
|
|
|
.shader_ptr = res_shader("sprite_default"),
|
2023-09-20 04:14:15 +02:00
|
|
|
.pos = { re(b->pos), im(b->pos) },
|
2019-10-12 14:02:15 +02:00
|
|
|
.rotation.angle = global.frames * 7.0 * DEG2RAD,
|
|
|
|
.rotation.vector = { 0, 0, -1 },
|
|
|
|
.scale.both = scale,
|
|
|
|
});
|
2019-02-22 00:56:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_spellbg_overlay(int t) {
|
|
|
|
Boss *b = global.boss;
|
|
|
|
|
2021-05-14 05:28:16 +02:00
|
|
|
float delay = attacktype_start_delay(b->current->type);
|
2019-07-03 19:50:43 +02:00
|
|
|
float f = (ATTACK_START_DELAY - t) / (delay + ATTACK_START_DELAY);
|
2019-02-22 00:56:03 +01:00
|
|
|
|
2019-07-03 19:50:43 +02:00
|
|
|
if |