My take on the Marisa bombs. Also blur shaders, minor fixes, etc.

This commit is contained in:
Andrei Alexeyev 2018-08-27 10:08:14 +03:00
parent c06a333726
commit 4e9ec77c91
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
24 changed files with 243 additions and 63 deletions

View file

@ -0,0 +1,4 @@
#version 330 core
#define BLUR_KERNEL_SIZE 13
#include "lib/blur.frag.glslh"

View file

@ -0,0 +1 @@
glsl_objects = blur13.frag standardnotex.vert

View file

@ -0,0 +1,4 @@
#version 330 core
#define BLUR_KERNEL_SIZE 25
#include "lib/blur.frag.glslh"

View file

@ -0,0 +1 @@
glsl_objects = blur25.frag standardnotex.vert

View file

@ -0,0 +1,4 @@
#version 330 core
#define BLUR_KERNEL_SIZE 5
#include "lib/blur.frag.glslh"

View file

@ -0,0 +1 @@
glsl_objects = blur5.frag standardnotex.vert

View file

@ -0,0 +1,4 @@
#version 330 core
#define BLUR_KERNEL_SIZE 9
#include "lib/blur.frag.glslh"

View file

@ -0,0 +1 @@
glsl_objects = blur9.frag standardnotex.vert

View file

@ -0,0 +1,24 @@
#ifndef BLUR_KERNEL_SIZE
#error BLUR_KERNEL_SIZE not defined
#endif
#include "../interface/standard.glslh"
UNIFORM(1) vec2 blur_resolution;
UNIFORM(2) vec2 blur_direction;
// must have the highest location index
UNIFORM(3) float blur_kernel[BLUR_KERNEL_SIZE];
void main(void) {
const int kernel_half_size = (BLUR_KERNEL_SIZE - 1) / 2;
vec4 color = vec4(0.0);
vec2 dir = blur_direction / blur_resolution;
for(int i = -kernel_half_size; i <= kernel_half_size; ++i) {
color += blur_kernel[kernel_half_size + i] * texture(tex, texCoord + dir * float(i));
}
fragColor = color;
}

View file

@ -4,7 +4,6 @@
#include "interface/standard.glslh"
UNIFORM(1) float t;
UNIFORM(2) vec3 tint;
float smootherstep(float x, float width) {
// step function with a very slow decay towards zero
@ -15,21 +14,25 @@ float smootherstep(float x, float width) {
void main(void) {
vec2 r = vec2(texCoord.x - 0.5, 1.0 - texCoord.y);
// globally distort the coordinate system a little bit for a more interesting/dynamic shape
r.x -= 0.005 * sin(r.y * 13.32 - t * 0.53);
// this is > 0 in the basic core region of the spark. to make the shape more interesting, it is modulated with additional terms later.
//
//
// The terms after abs(r.x) diverge to -∞ (= invisible later) assuring that
// there are no hard edges. This is necessary because I use this debil
// smootherstep function which never actually is zero for finite x. But it looks
// better, mate. Glow!
float coreRegion = 0.4 * pow(r.y, 0.3) - abs(r.x) - pow(0.1/(0.5 - abs(r.x))+0.1*abs(r.x)/r.y, 2);
float coreRegion = 0.4 * pow(r.y, 0.25) - pow(abs(r.x), 1.0)*1.0 - pow(0.1/(0.5 - abs(r.x))+0.1*pow(abs(r.x), 1.5)/r.y, 2);
// smootherstep(coreRegion, 0) would be a mask for the bare cone shape of the spark. By adding extra terms it is distorted.
// sin adds a wavy edge, the fraction with t takes care of fading in/out
float colorRegion = coreRegion - 0.01 * sin(r.y * 30 - t * 0.7) * (1-1/(t+1)) - 0.05;
float whiteRegion = coreRegion - 0.15 - 0.2 / (1 + t * 0.1);
float whiteRegion = coreRegion - 0.15 * pow(r.y, 0.5) - 0.2 / (1 + t * 0.1);
fragColor = vec4(tint, 0) * smootherstep(colorRegion, 0.05)
+ vec4(1, 1, 1, 0) * smootherstep(whiteRegion, 0.02);
vec3 tint = hsv2rgb(vec3(t / 100.0 - pow(r.y, 0.25), 1.0, 1.0));
fragColor = mix(vec4(tint, 0) * smootherstep(colorRegion, 0.05), vec4(vec3(1), 0), 0.5 * smootherstep(whiteRegion, 0.02));
}

View file

@ -10,6 +10,10 @@ glsl_files = files(
# @begin glsl
'alpha_depth.frag.glsl',
'bloom.frag.glsl',
'blur13.frag.glsl',
'blur25.frag.glsl',
'blur5.frag.glsl',
'blur9.frag.glsl',
'boss_zoom.frag.glsl',
'copy_depth.frag.glsl',
'fxaa.frag.glsl',

View file

@ -101,7 +101,7 @@ Enemy *create_enemy_p(EnemyList *enemies, complex pos, float hp, EnemyVisualRule
void* _delete_enemy(ListAnchor *enemies, List* enemy, void *arg) {
Enemy *e = (Enemy*)enemy;
if(e->hp <= 0 && e->hp != ENEMY_IMMUNE) {
if(e->hp <= 0 && e->hp != ENEMY_IMMUNE && e->hp != ENEMY_BOMB) {
play_sound("enemydeath");
for(int i = 0; i < 10; i++) {

View file

@ -11,6 +11,7 @@
#include "global.h"
#include "plrmodes.h"
#include "marisa.h"
#include "stagedraw.h"
PlayerCharacter character_marisa = {
.id = PLR_CHAR_MARISA,
@ -83,11 +84,76 @@ void marisa_common_slave_visual(Enemy *e, int t, bool render) {
});
}
void marisa_common_masterspark_draw(int t, Color *tint) {
ShaderProgram *prog_saved = r_shader_current();
r_shader("masterspark");
r_uniform_vec3("tint", tint->r, tint->g, tint->b);
r_uniform_float("t", t);
r_draw_quad();
r_shader_ptr(prog_saved);
static void draw_masterspark_ring(int t, float width) {
float sy = sqrt(t / 500.0) * 6;
float sx = sy * width / 800;
if(sx == 0 || sy == 0) {
return;
}
r_draw_sprite(&(SpriteParams) {
.sprite = "masterspark_ring",
.shader = "sprite_default",
.pos = { 0, -t*t*0.4 + 2 },
.color = RGBA(0.5, 0.5, 0.5, 0.0),
.scale = { .x = sx, .y = sy * sy * 1.5 },
});
}
static void draw_masterspark_beam(complex origin, complex size, float angle, int t, float alpha) {
r_mat_push();
r_mat_translate(creal(origin), cimag(origin), 0);
r_mat_rotate(angle, 0, 0, 1);
r_shader("masterspark");
r_uniform_float("t", t);
r_mat_push();
r_mat_translate(0, cimag(size) * -0.5, 0);
r_mat_scale(alpha * creal(size), cimag(size), 1);
r_draw_quad();
r_mat_pop();
for(int i = 0; i < 4; i++) {
draw_masterspark_ring(t % 20 + 10 * i, alpha * creal(size));
}
r_mat_pop();
}
void marisa_common_masterspark_draw(complex origin, complex size, float angle, int t, float alpha) {
r_state_push();
float blur = 1.0 - alpha;
if(blur == 0) {
draw_masterspark_beam(origin, size, angle, t, alpha);
} else {
Framebuffer *main_fb = r_framebuffer_current();
FBPair *aux = stage_get_fbpair(FBPAIR_FG_AUX);
r_framebuffer(aux->back);
r_clear_color4(0, 0, 0, 0);
r_clear(CLEAR_COLOR);
draw_masterspark_beam(origin, size, angle, t, alpha);
r_shader("blur25");
r_uniform_vec2("blur_resolution", VIEWPORT_W, VIEWPORT_H);
r_uniform_vec2("blur_direction", blur, 0);
fbpair_swap(aux);
r_framebuffer(aux->back);
r_clear(CLEAR_COLOR);
draw_framebuffer_tex(aux->front, VIEWPORT_W, VIEWPORT_H);
r_uniform_vec2("blur_direction", 0, blur);
fbpair_swap(aux);
r_framebuffer(main_fb);
draw_framebuffer_tex(aux->front, VIEWPORT_W, VIEWPORT_H);
}
r_state_pop();
}

View file

@ -20,4 +20,4 @@ extern PlayerCharacter character_marisa;
double marisa_common_property(Player *plr, PlrProperty prop);
void marisa_common_shot(Player *plr, float dmg);
void marisa_common_slave_visual(Enemy *e, int t, bool render);
void marisa_common_masterspark_draw(int t, Color *tint);
void marisa_common_masterspark_draw(complex origin, complex size, float angle, int t, float alpha);

View file

@ -401,27 +401,18 @@ static void masterspark_visual(Enemy *e, int t2, bool render) {
float t = player_get_bomb_progress(&global.plr, NULL);
float fade = 1;
complex diroffset = e->args[0];
if(t < 1./6) {
fade = t*6;
fade = sqrt(fade);
fade = pow(fade, 1.0/3.0);
}
if(t > 3./4) {
fade = 1-t*4 + 3;
fade *= fade;
if(t > 4./5) {
fade = 1-t*5 + 4;
fade = pow(fade, 5);
}
r_mat_push();
r_mat_translate(creal(global.plr.pos), cimag(global.plr.pos), 0);
r_mat_rotate(carg(diroffset), 0, 0, 1);
r_mat_translate(0, - 40 - VIEWPORT_H/2, 0);
Color color = *HSL(t2/100., 1, 0.5);
r_mat_scale(fade * 800, VIEWPORT_H, 1);
marisa_common_masterspark_draw(t * global.plr.bombtotaltime, &color);
r_mat_pop();
marisa_common_masterspark_draw(global.plr.pos - 30 * I, 800 + I * VIEWPORT_H * 1.25, carg(e->args[0]), t2, fade);
}
static int masterspark_star(Projectile *p, int t) {
@ -434,30 +425,37 @@ static int masterspark_star(Projectile *p, int t) {
}
static int masterspark(Enemy *e, int t2) {
// FIXME: This may interact badly with other view shake effects...
// We need a proper system for this stuff.
if(t2 == EVENT_BIRTH) {
global.shake_view = 8;
return 1;
} else if(t2 == EVENT_DEATH) {
global.shake_view=0;
global.shake_view = 0;
return 1;
}
if(t2 < 0)
return 1;
e->args[0] *= cexp(I*0.005*creal(global.plr.velocity));
e->args[0] *= cexp(I*(0.005*creal(global.plr.velocity) + nfrand() * 0.005));
complex diroffset = e->args[0];
float t = player_get_bomb_progress(&global.plr, NULL);
if(t2%2==0 && t < 3./4) {
complex dir = -cexp(1.2*I*nfrand())*I;
Color *c = HSLA(-t,1,0.5,0);
if(t >= 3.0/4.0) {
global.shake_view = 8 * (1 - t * 4 + 3);
} else if(t2 % 2 == 0) {
complex dir = -cexp(1.5*I*sin(t2*M_PI*1.12))*I;
Color *c = HSLA(-t*5.321,1,0.5,0.5*frand());
PARTICLE(
.sprite = "maristar_orbit",
.pos = global.plr.pos+40*dir,
.color = c,
.rule = masterspark_star,
.timeout = 50,
.args= { (10 * dir - 10*I)*diroffset, 6 },
.args= { (10 * dir - 10*I)*diroffset, 4 },
.angle = nfrand(),
.draw_rule = GrowFade
);
@ -468,7 +466,7 @@ static int masterspark(Enemy *e, int t2) {
.color = c,
.rule = masterspark_star,
.timeout = 50,
.args = { (10 * dir - 10*I)*diroffset, 6 },
.args = { (10 * dir - 10*I)*diroffset, 4 },
.angle = nfrand(),
.draw_rule = GrowFade
);
@ -512,7 +510,8 @@ static void marisa_laser_bombbg(Player *plr) {
static void marisa_laser_bomb(Player *plr) {
play_sound("bomb_marisa_a");
create_enemy_p(&plr->slaves, 40.0*I, ENEMY_BOMB, masterspark_visual, masterspark, 1,0,0,0);
Enemy *e = create_enemy_p(&plr->slaves, 0.0*I, ENEMY_BOMB, masterspark_visual, masterspark, 1,0,0,0);
e->ent.draw_layer = LAYER_PLAYER_FOCUS - 1;
}
static Enemy* marisa_laser_spawn_slave(Player *plr, complex pos, complex a0, complex a1, complex a2, complex a3) {
@ -609,6 +608,7 @@ static void marisa_laser_preload(void) {
"proj/marisa",
"part/maristar_orbit",
"hakkero",
"masterspark_ring",
NULL);
preload_resources(RES_TEXTURE, flags,

View file

@ -201,16 +201,10 @@ static void marisa_star_orbit_visual(Enemy *e, int t, bool render) {
color_mul_scalar(&color, fade);
marisa_common_masterspark_draw(e->pos, 250*fade + VIEWPORT_H*1.5*I, carg(e->pos - global.plr.pos) + M_PI/2, global.plr.bombtotaltime * tb, fade);
r_mat_push();
r_mat_translate(creal(e->pos),cimag(e->pos),0);
r_mat_push();
r_mat_rotate_deg(carg(e->pos-global.plr.pos)*180/M_PI+90,0,0,1);
r_mat_scale(250*fade,VIEWPORT_H*1.5,1);
r_mat_translate(0,-0.5,0);
marisa_common_masterspark_draw(global.plr.bombtotaltime*tb, &color);
r_mat_pop();
color.a = 0;
r_color(&color);
r_mat_rotate_deg(t*10,0,0,1);
@ -226,7 +220,8 @@ static void marisa_star_bomb(Player *plr) {
int count = 5; // might as well be hard coded. We are talking marisa here.
for(int i = 0; i < 5; i++) {
complex dir = cexp(2*I*M_PI/count*i);
create_enemy2c(plr->pos,ENEMY_IMMUNE,marisa_star_orbit_visual,marisa_star_orbit,i,dir);
Enemy *e = create_enemy2c(plr->pos, ENEMY_BOMB, marisa_star_orbit_visual, marisa_star_orbit, i ,dir);
e->ent.draw_layer = LAYER_PLAYER_FOCUS - 1;
}
}
@ -322,6 +317,7 @@ static void marisa_star_preload(void) {
"proj/maristar",
"part/maristar_orbit",
"hakkero",
"masterspark_ring",
NULL);
preload_resources(RES_TEXTURE, flags,

View file

@ -15,6 +15,7 @@
#include "common/models.h"
#include "common/state.h"
#include "util/glm.h"
#include "util/graphics.h"
#define B _r_backend.funcs
@ -29,6 +30,12 @@ void r_init(void) {
_r_backend_init();
}
typedef struct BlurInfo {
const char *progname;
int kernel_size;
float sigma;
} BlurInfo;
void r_post_init(void) {
_r_state_init();
B.post_init();
@ -43,6 +50,21 @@ void r_post_init(void) {
"standardnotex",
NULL);
BlurInfo blurs[] = {
// { "blur5", 5, 1.25, },
// { "blur9", 9, 1.85, },
// { "blur13", 13, 2.45, },
{ "blur25", 25, 4.25, },
};
for(int i = 0; i < sizeof(blurs)/sizeof(*blurs); ++i) {
preload_resource(RES_SHADER_PROGRAM, blurs[i].progname, RESF_PERMANENT);
}
for(int i = 0; i < sizeof(blurs)/sizeof(*blurs); ++i) {
init_blur_shader(r_shader_get(blurs[i].progname), blurs[i].kernel_size, blurs[i].sigma);
}
R.progs.standard = r_shader_get("standard");
R.progs.standardnotex = r_shader_get("standardnotex");

View file

@ -67,7 +67,17 @@ static bool stage1_draw_predicate(EntityInterface *ent) {
return false;
}
if(ent->type == ENT_BOSS || ent->type == ENT_ENEMY) {
if(ent->type == ENT_BOSS) {
return true;
}
if(ent->type == ENT_ENEMY) {
Enemy *e = ENT_CAST(ent, Enemy);
if(e->hp == ENEMY_BOMB) {
return false;
}
return true;
}

View file

@ -30,41 +30,43 @@ static void fbpair_resize_fb(Framebuffer *fb, FramebufferAttachment attachment,
void fbpair_create(FBPair *pair, uint num_attachments, FBAttachmentConfig attachments[num_attachments]) {
assert(num_attachments > 0 && num_attachments <= FRAMEBUFFER_MAX_ATTACHMENTS);
memset(pair, 0, sizeof(*pair));
fbpair_create_fb(*(void**)&pair->front = pair->framebuffers + 0, num_attachments, attachments);
fbpair_create_fb(*(void**)&pair->back = pair->framebuffers + 1, num_attachments, attachments);
fbpair_create_fb(pair->front = calloc(1, sizeof(Framebuffer)), num_attachments, attachments);
fbpair_create_fb(pair->back = calloc(1, sizeof(Framebuffer)), num_attachments, attachments);
}
void fbpair_destroy(FBPair *pair) {
fbpair_destroy_fb(pair->framebuffers + 0);
fbpair_destroy_fb(pair->framebuffers + 1);
fbpair_destroy_fb(pair->front);
fbpair_destroy_fb(pair->back);
free(pair->front);
free(pair->back);
}
void fbpair_swap(FBPair *pair) {
void *tmp = pair->front;
*(void**)&pair->front = pair->back;
*(void**)&pair->back = tmp;
pair->front = pair->back;
pair->back = tmp;
}
static void fbpair_clear(FBPair *pair) {
r_state_push();
r_clear_color4(0, 0, 0, 0);
r_framebuffer(pair->framebuffers + 0);
r_framebuffer(pair->front);
r_clear(CLEAR_ALL);
r_framebuffer(pair->framebuffers + 1);
r_framebuffer(pair->back);
r_clear(CLEAR_ALL);
r_state_pop();
}
void fbpair_resize(FBPair *pair, FramebufferAttachment attachment, uint width, uint height) {
fbpair_resize_fb(pair->framebuffers + 0, attachment, width, height);
fbpair_resize_fb(pair->framebuffers + 1, attachment, width, height);
fbpair_resize_fb(pair->front, attachment, width, height);
fbpair_resize_fb(pair->back, attachment, width, height);
fbpair_clear(pair);
}
void fbpair_resize_all(FBPair *pair, uint width, uint height) {
for(uint i = 0; i < FRAMEBUFFER_MAX_ATTACHMENTS; ++i) {
fbpair_resize_fb(pair->framebuffers + 0, i, width, height);
fbpair_resize_fb(pair->framebuffers + 1, i, width, height);
fbpair_resize_fb(pair->front, i, width, height);
fbpair_resize_fb(pair->back, i, width, height);
}
fbpair_clear(pair);

View file

@ -20,10 +20,8 @@ typedef struct FBPair {
* 4. Rinse, repeat.
*/
Framebuffer *const front;
Framebuffer *const back;
Framebuffer framebuffers[2];
Framebuffer *front;
Framebuffer *back;
} FBPair;
typedef struct FBAttachmentConfig {

View file

@ -157,3 +157,9 @@ void fbutil_resize_attachment(Framebuffer *fb, FramebufferAttachment attachment,
r_texture_create(tex, &params);
r_framebuffer_attach(fb, tex, 0, attachment);
}
void init_blur_shader(ShaderProgram *prog, size_t kernel_size, float sigma) {
float kernel[kernel_size];
gaussian_kernel_1d(kernel_size, sigma, kernel);
r_uniform_ptr(r_shader_uniform(prog, "blur_kernel[0]"), kernel_size, kernel);
}

View file

@ -21,3 +21,5 @@ void draw_framebuffer_attachment(Framebuffer *fb, double width, double height, F
void fbutil_create_attachments(Framebuffer *fb, uint num_attachments, FBAttachmentConfig attachments[num_attachments]);
void fbutil_destroy_attachments(Framebuffer *fb);
void fbutil_resize_attachment(Framebuffer *fb, FramebufferAttachment attachment, uint width, uint height);
void init_blur_shader(ShaderProgram *prog, size_t kernel_size, float sigma);

View file

@ -106,3 +106,28 @@ float smoothreclamp(float x, float old_min, float old_max, float new_min, float
float sanitize_scale(float scale) {
return max(0.1, scale);
}
float normpdf(float x, float sigma) {
return 0.39894 * exp(-0.5 * pow(x, 2) / pow(sigma, 2)) / sigma;
}
void gaussian_kernel_1d(size_t size, float sigma, float kernel[size]) {
assert(size & 1);
double sum = 0.0;
size_t halfsize = size / 2;
kernel[halfsize] = normpdf(0, sigma);
sum += kernel[halfsize];
for(size_t i = 1; i <= halfsize; ++i) {
float k = normpdf(i, sigma);
kernel[halfsize + i] = kernel[halfsize - i] = k;
sum += k * 2;
}
for(size_t i = 0; i < size; ++i) {
kernel[i] /= sum;
}
}

View file

@ -25,6 +25,8 @@ float ftopow2(float x) attr_const;
float smooth(float x) attr_const;
float smoothreclamp(float x, float old_min, float old_max, float new_min, float new_max) attr_const;
float sanitize_scale(float scale) attr_const;
float normpdf(float x, float sigma) attr_const;
void gaussian_kernel_1d(size_t size, float sigma, float kernel[size]) attr_nonnull(3);
#define topow2(x) (_Generic((x), \
uint32_t: topow2_u32, \