Premultiplied alpha (#133)
* WIP premultiplied alpha * WIP color API rework (doesn't build yet; lots of things left to convert) * convert everything remaining to new Color api except stage*_event.c files * convert the stages to new Color api. builds & runs now; still many rendering errors * fix the bullet shader for premultiplied alpha * fix masterspark, graphs and stage 1 fog clouds * fix marisa_b and most of spellcards * Add deprecation warnings for BLEND_ADD and PFLAG_DRAWADD * fix a segfault in stage 6 undo accidental earlier change * fix text_hud.frag.glsl * fix scuttle bg and remaining stage3 BLEND_ADDs * fix marisa laser opacity * hacky fix for myon The old implementation relied on alpha being stored inside p->color. In premul alpha this doesn’t work and functions like color_set_opacity can’t solve this i think. So I tried messing around with it until it looked somewhat similar. * fix marisa_b stars * remove color_set_opacity i overlooked * more plrmode blending changes * fixup additive blending in stage 1 * various premultiplied alpha fixups for bosses and enemies * stage 2 premul alpha fixups * stage 4 premul alpha fixups * stage 5 premul alpha fixups * stage 6 premul alpha fixups * make lasers also use the PMA blend mode * remove PFLAG_DRAWADD and PFLAG_DRAWSUB * fix remaining PMA issues in menus * lame extraspell bg workaround * fix item alpha * make marisaA lasers look somewhat like in master * fix marisaA bomb background fadeout * fixup various r_color4 calls * fix myon * remove dead code * fix use of BLEND_ADD in player death effect * fix myon shot trails (broken on master as well) * fix myon shot fade-in * extend the sprite shaders custom parameter to a vec4 * fix youmuB stuff and make it look somewhat better. the code looks even worse though.
This commit is contained in:
parent
1b82c80070
commit
8490576810
83 changed files with 1334 additions and 1275 deletions
|
@ -1,6 +1,7 @@
|
|||
#version 330 core
|
||||
|
||||
#include "interface/standard.glslh"
|
||||
#include "lib/util.glslh"
|
||||
|
||||
#define POINTS 120
|
||||
// #define INTERPOLATE
|
||||
|
@ -30,6 +31,7 @@ void main(void) {
|
|||
vec4 c = vec4(1.0);
|
||||
vec2 coord = vec2(texCoord.x, 1.0 - texCoord.y); // TODO: move to vertex shader
|
||||
float s = get_sample(coord.x);
|
||||
|
||||
c.a = float(coord.y <= s);
|
||||
c.a *= (0.2 + 0.8 * s);
|
||||
c.a = 0.05 + 0.95 * c.a;
|
||||
|
@ -43,5 +45,6 @@ void main(void) {
|
|||
}
|
||||
|
||||
c = mix(c, vec4(1.0), 0.5 * pow(1.0 - abs(coord.y - 0.5), 32.0));
|
||||
fragColor = c;
|
||||
|
||||
fragColor = color_mul_alpha(c);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ ATTRIBUTE(7) mat4 spriteTexTransform;
|
|||
ATTRIBUTE(11) vec4 spriteRGBA;
|
||||
ATTRIBUTE(12) vec4 spriteTexRegion;
|
||||
ATTRIBUTE(13) vec2 spriteDimensions;
|
||||
ATTRIBUTE(14) float spriteCustomParam;
|
||||
ATTRIBUTE(14) vec4 spriteCustomParams;
|
||||
#endif
|
||||
|
||||
#ifdef FRAG_STAGE
|
||||
|
@ -41,6 +41,6 @@ VARYING(2) vec2 texCoordOverlay;
|
|||
VARYING(3) vec4 texRegion;
|
||||
VARYING(4) vec4 color;
|
||||
VARYING(5) vec2 dimensions;
|
||||
VARYING(6) float customParam;
|
||||
VARYING(6) vec4 customParams;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -32,6 +32,6 @@ void main(void) {
|
|||
#endif
|
||||
|
||||
#ifdef SPRITE_OUT_CUSTOM
|
||||
customParam = spriteCustomParam;
|
||||
customParams = spriteCustomParams;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "defs.glslh"
|
||||
|
||||
const float pi = 3.141592653589793;
|
||||
const float pi = 3.141592653589793; // as supplied by google
|
||||
|
||||
vec2 dir(float a) {
|
||||
return vec2(cos(a), sin(a));
|
||||
|
@ -44,4 +44,8 @@ vec2 uv_to_region(vec4 region, vec2 uv) {
|
|||
);
|
||||
}
|
||||
|
||||
vec4 color_mul_alpha(vec4 c) {
|
||||
return vec4(c.rgb * c.a, c.a);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
|
||||
void main(void) {
|
||||
vec4 texel = texture(tex, texCoord);
|
||||
fragColor.rgb = mix(color.rgb, vec3(0.0), texel.b);
|
||||
fragColor.rgb = mix(color.rgb, vec3(0.0), texel.b)*texel.a;
|
||||
fragColor.a = texel.a * color.a;
|
||||
}
|
||||
|
|
|
@ -20,5 +20,5 @@ void main(void) {
|
|||
vec4 color = mix(color0, color1, 0.5 + 0.5 * sin(color_phase + color_freq * uv_scaled.x));
|
||||
|
||||
fragColor = texture(tex, texCoord);
|
||||
fragColor *= vec4(color.rgb, color.a * a);
|
||||
fragColor *= color * a;
|
||||
}
|
||||
|
|
|
@ -2,17 +2,25 @@
|
|||
|
||||
#include "lib/render_context.glslh"
|
||||
#include "interface/standard.glslh"
|
||||
#include "lib/util.glslh"
|
||||
|
||||
UNIFORM(1) float t;
|
||||
UNIFORM(2) vec2 plrpos;
|
||||
UNIFORM(3) float decay;
|
||||
|
||||
void main(void) {
|
||||
vec2 r = texCoordRaw-plrpos;
|
||||
r = vec2(atan(r.y,r.x)/3.1415*4.,length(r)-2.*t*(1.+decay*t));
|
||||
r.x+=(1.+r.y)*t*5.*decay;
|
||||
vec4 texel = texture(tex, r);
|
||||
texel.a = 0.7*min(1.,t)*(1.-t*decay)*4.;
|
||||
// This thing is supposed to produce a swirly texture centered
|
||||
// around plrpos. To do it we have to map to polar coordinates
|
||||
|
||||
fragColor = texel*r_color;
|
||||
vec2 x = texCoordRaw-plrpos;
|
||||
float r = length(x) - 2 * t * (1 + decay *t);
|
||||
float phi = atan(x.y, x.x) / pi * 4;
|
||||
|
||||
// give the angle some juicy r dependent offset to twist the swirl.
|
||||
phi += (1 + r) * t * 5 * decay;
|
||||
|
||||
vec4 texel = texture(tex, vec2(phi,r));
|
||||
|
||||
float fade = 0.7 * min(1,t) * (1 - t * decay) * 4;
|
||||
fragColor = texel * r_color * fade;
|
||||
}
|
||||
|
|
|
@ -6,9 +6,22 @@
|
|||
UNIFORM(1) float t;
|
||||
|
||||
void main(void) {
|
||||
vec2 r = vec2(texCoord.x-0.5,1.0-texCoord.y);
|
||||
float f = abs(r.x)-0.4*pow(r.y,0.3);
|
||||
vec3 clr = vec3(0.8+0.2*sin(r.x*2.),0.8+0.2*cos(r.y*2.-t*0.1),0.8+0.2*sin((r.x-r.y)*1.4+t*0.2));
|
||||
vec2 r = vec2(texCoord.x - 0.5, 1.0 - texCoord.y);
|
||||
|
||||
fragColor = vec4(clr,_smoothstep(-(f+0.01*sin(r.y*30.-t*0.7)*(1.-1./(t+1.))+0.05),0.05))+vec4(1.)*_smoothstep(-f-0.15-0.2/(1.+t*0.1),0.02);
|
||||
// this is > 0 in the basic core region of the spark. to make the shape more interesting, it is modulated with additional terms later.
|
||||
float coreRegion = 0.4 * pow(r.y, 0.3) - abs(r.x);
|
||||
|
||||
// random rainbow pattern moving with time
|
||||
vec3 clr = vec3(0.7+0.3*sin(r.x*2.),0.7+0.3*cos(r.y*2.-t*0.1),0.7+0.3*sin((r.x-r.y)*1.4+t*0.2));
|
||||
|
||||
|
||||
// _smoothstep(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);
|
||||
|
||||
fragColor = vec4(clr, 0) * _smoothstep(colorRegion, 0.05)
|
||||
+ vec4(1, 1, 1, 0) * _smoothstep(whiteRegion, 0.02);
|
||||
}
|
||||
|
|
9
resources/shader/max_to_alpha.frag.glsl
Normal file
9
resources/shader/max_to_alpha.frag.glsl
Normal file
|
@ -0,0 +1,9 @@
|
|||
#version 330 core
|
||||
|
||||
#include "lib/render_context.glslh"
|
||||
#include "interface/standard.glslh"
|
||||
|
||||
void main(void) {
|
||||
fragColor = texture(tex, texCoord);
|
||||
fragColor.a = max(fragColor.r, max(fragColor.g, fragColor.b));
|
||||
}
|
2
resources/shader/max_to_alpha.prog
Normal file
2
resources/shader/max_to_alpha.prog
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
glsl_objects = standard.vert max_to_alpha.frag
|
|
@ -15,14 +15,17 @@ glsl_files = files(
|
|||
'graph.frag.glsl',
|
||||
'ingame_menu.frag.glsl',
|
||||
'laser_generic.vert.glsl',
|
||||
'marisa_hakkero.frag.glsl',
|
||||
'marisa_laser.frag.glsl',
|
||||
'maristar_bombbg.frag.glsl',
|
||||
'masterspark.frag.glsl',
|
||||
'max_to_alpha.frag.glsl',
|
||||
'player_death.frag.glsl',
|
||||
'spellcard_intro.frag.glsl',
|
||||
'spellcard_outro.frag.glsl',
|
||||
'spellcard_walloftext.frag.glsl',
|
||||
'sprite_bullet.frag.glsl',
|
||||
'sprite_bullet.vert.glsl',
|
||||
'sprite_bullet_apple.frag.glsl',
|
||||
'sprite_circleclipped_indicator.frag.glsl',
|
||||
'sprite_circleclipped_indicator.vert.glsl',
|
||||
|
|
|
@ -10,8 +10,16 @@ void main(void) {
|
|||
pos.y *= ratio;
|
||||
float r = length(pos);
|
||||
float phi = atan(pos.y,pos.x)+t/10.0;
|
||||
float rmin = (1.+_smoothstep(t-.5)*sin(t*40.0+10.*phi))*step(0.,t)*t*1.5;
|
||||
fragColor = mix(clr, vec4(1.0) - vec4(clr.rgb,0.), step(rmin-.1*sqrt(r),r));
|
||||
|
||||
fragColor.a = float(r < rmin);
|
||||
// everything bigger than rmax is not drawn. The sine creates the
|
||||
// spinning wavy border.
|
||||
float rmax = 1 + _smoothstep(t - 0.5) * sin(t * 40 + 10 * phi);
|
||||
|
||||
// now make it grow with time.
|
||||
rmax *= step(0, t) * t * 1.5;
|
||||
|
||||
// at the rim, invert colors
|
||||
fragColor = mix(clr, vec4(1) - vec4(clr.rgb, 0), step(rmax-0.1*sqrt(r),r));
|
||||
|
||||
fragColor *= float(r < rmax);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,16 @@ void main(void) {
|
|||
pos.y *= ratio;
|
||||
float r = length(pos);
|
||||
float phi = atan(pos.y,pos.x)+t/10.0;
|
||||
float rmin = (1.+0.3*sin(t*20.0+10.*phi))*step(0.,t)*t*t*10.0;
|
||||
fragColor = mix(clr, vec4(1.0) - vec4(clr.rgb,0.), step(r,rmin+t*(.1+0.1*sin(10.*r))));
|
||||
|
||||
// everything lesser than rmin is not drawn. The sine creates the
|
||||
// spinning wavy border.
|
||||
float rmin = (1 + 0.3 * sin(t * 20 + 10 * phi));
|
||||
|
||||
fragColor.a = float(r > rmin);
|
||||
// now grow it
|
||||
rmin *= step(0.,t) * t * t * 10;
|
||||
|
||||
// at the rim, invert colors
|
||||
fragColor = mix(clr, vec4(1.0) - vec4(clr.rgb,0.), step(r, rmin + t * (0.1 + 0.1 * sin(10 * r))));
|
||||
|
||||
fragColor *= float(r > rmin);
|
||||
}
|
||||
|
|
|
@ -4,17 +4,34 @@
|
|||
#include "interface/spellcard.glslh"
|
||||
|
||||
void main(void) {
|
||||
float n = 10.;
|
||||
vec2 pos = (vec2(texCoordRaw)-origin*vec2(ratio,1.0))*n;
|
||||
pos.x /= ratio;
|
||||
pos = vec2(atan(pos.x,pos.y),length(pos)+n*t*0.1);
|
||||
pos.x *= h/w;
|
||||
pos.x += t*0.5*float(2*int(mod(pos.y,2.0))-1);
|
||||
pos = mod(pos,vec2(1.0+(0.01/w),1.0));
|
||||
pos *= vec2(w,h);
|
||||
vec4 clr = texture(tex, pos);
|
||||
clr.gb = clr.rr;
|
||||
clr.a *= float(pos.x < w && pos.y < h)*(2.*t-t*t);
|
||||
float n = 10; // rough number of rings of text
|
||||
|
||||
fragColor = clr;
|
||||
vec2 pos = (vec2(texCoordRaw) - origin * vec2(ratio, 1.0)) * n;
|
||||
pos.x /= ratio;
|
||||
|
||||
// Slightly distorted mapping to polar coordinates. I wrote this
|
||||
// shortly after hearing a general relativity lecture...
|
||||
|
||||
pos = vec2(atan(pos.x,pos.y), length(pos) + n * t * 0.1);
|
||||
|
||||
// The idea is (apart from the aspect factors) to map the
|
||||
// (phi, angle) coordinates to something that loops several
|
||||
// times and contains margins.
|
||||
// This is achieved using all those mods.
|
||||
//
|
||||
// Simplified example:
|
||||
// f(x) = x for 0 < x < 2
|
||||
// mod(f(x),1) then goes from 0 to 1 and from 0 to 1 again.
|
||||
//
|
||||
// If you use it as a texcoord you get two copies of the texture.
|
||||
//
|
||||
// The complicated example I don’t understand either:
|
||||
|
||||
pos.x *= h/w;
|
||||
pos.x += t * 0.5 * float(2 * int(mod(pos.y, 2)) - 1);
|
||||
pos = mod(pos, vec2(1 + 0.01 / w, 1));
|
||||
pos *= vec2(w,h);
|
||||
|
||||
vec4 texel = texture(tex, pos);
|
||||
fragColor = vec4(texel.r) * float(pos.x < w && pos.y < h)*t*(2-t);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,5 @@
|
|||
|
||||
void main(void) {
|
||||
vec4 texel = texture(tex, texCoord);
|
||||
fragColor.rgb = mix(color.rgb, vec3(1.0), texel.b);
|
||||
fragColor.a = texel.a * color.a;
|
||||
fragColor = (texel.g * color + vec4(texel.b)) * (1 - customParams.r);
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
glsl_objects = sprite_default.vert sprite_bullet.frag
|
||||
glsl_objects = sprite_bullet.vert sprite_bullet.frag
|
||||
|
|
7
resources/shader/sprite_bullet.vert.glsl
Normal file
7
resources/shader/sprite_bullet.vert.glsl
Normal file
|
@ -0,0 +1,7 @@
|
|||
#version 330 core
|
||||
|
||||
#define SPRITE_OUT_COLOR
|
||||
#define SPRITE_OUT_TEXCOORD
|
||||
#define SPRITE_OUT_CUSTOM
|
||||
|
||||
#include "lib/sprite_default.vert.glslh"
|
|
@ -6,7 +6,7 @@
|
|||
UNIFORM(1) vec4 back_color;
|
||||
|
||||
void main(void) {
|
||||
float fill = customParam;
|
||||
float fill = customParams.r;
|
||||
vec2 tc = texCoord;
|
||||
vec4 texel = texture(tex, tc);
|
||||
tc = texCoordRaw - vec2(0.5);
|
||||
|
|
|
@ -4,7 +4,5 @@
|
|||
|
||||
void main(void) {
|
||||
vec4 texel = texture(tex, texCoord);
|
||||
vec4 mixclr = 1.0-texel;
|
||||
mixclr.a = texel.a;
|
||||
fragColor = mixclr;
|
||||
fragColor = vec4((1.0 - texel.rgb / max(0.01, texel.a)) * texel.a, 0);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ void main(void) {
|
|||
vec2 uv = texCoordRaw;
|
||||
vec2 uv_orig = uv;
|
||||
vec4 texel;
|
||||
float deform = customParam;
|
||||
float deform = customParams.r;
|
||||
|
||||
const float limit = 1.0;
|
||||
const float step = 0.25;
|
||||
|
@ -30,7 +30,7 @@ void main(void) {
|
|||
uv = apply_deform(uv_orig, deform * i);
|
||||
texel = texture(tex, uv_to_region(texRegion, uv));
|
||||
float a = float(uv.x >= 0 && uv.x <= 1 && uv.y >= 0 && uv.y <= 1);
|
||||
fragColor += vec4(color.rgb, color.a * texel.a * a);
|
||||
fragColor += color * texel.a * a;
|
||||
}
|
||||
|
||||
fragColor /= num;
|
||||
|
|
|
@ -13,11 +13,12 @@ ported from:
|
|||
|
||||
void main(void) {
|
||||
vec4 texel = texture(tex, texCoord);
|
||||
float charge = customParam;
|
||||
float charge = customParams.r;
|
||||
|
||||
fragColor = vec4(0.0);
|
||||
fragColor.rgb += texel.r * mix(vec3(color.r, 0.0, 0.0), vec3(0.0, 0.0, color.b), 0.75 * sqrt(charge));
|
||||
fragColor.rgb += texel.r * mix(vec3(color.r, 0.0, 0.0), vec3(0.0, 0.0, color.b), sqrt(charge));
|
||||
fragColor.g += texel.g * color.g;
|
||||
fragColor.rgb += texel.b * mix(vec3(0.0, 0.0, color.b), vec3(2.0 * color.r, 0.0, 0.0), 0.75 * charge);
|
||||
fragColor.a = texel.a * color.a;
|
||||
fragColor *= customParams.g;
|
||||
}
|
||||
|
|
|
@ -9,4 +9,5 @@ void main(void) {
|
|||
fragColor.rgb += vec3(texel.r);
|
||||
fragColor.rgb += texel.b * vec3(color.r*color.r, color.g*color.g, color.b*color.b);
|
||||
fragColor.a = texel.a * color.a;
|
||||
fragColor *= (1 - customParams.r);
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
glsl_objects = sprite_default.vert sprite_youmu_myon_shot.frag
|
||||
glsl_objects = sprite_bullet.vert sprite_youmu_myon_shot.frag
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
#include "interface/sprite.glslh"
|
||||
|
||||
void main(void) {
|
||||
fragColor = color * vec4(1, 1, 1, texture(tex, texCoord).r);
|
||||
fragColor = color * vec4(texture(tex, texCoord).r);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ void main(void) {
|
|||
|
||||
// Transform local glyph texture coordinates.
|
||||
tc *= dimensions;
|
||||
tc.x += 5 * sin(3 * tc_overlay.y + customParam);
|
||||
tc.x += 5 * sin(3 * tc_overlay.y + customParams.r);
|
||||
tc /= dimensions;
|
||||
|
||||
// Compute alpha multiplier used to chop off unwanted texels from the atlas.
|
||||
|
@ -20,7 +20,7 @@ void main(void) {
|
|||
vec2 tc_atlas = uv_to_region(texRegion, tc);
|
||||
|
||||
// Display the glyph.
|
||||
fragColor = color * vec4(1, 1, 1, texture(tex, tc_atlas).r * a);
|
||||
fragColor = color * vec4(texture(tex, tc_atlas).r * a);
|
||||
|
||||
// Visualize global overlay coordinates. You could use them to span a texture across all glyphs.
|
||||
fragColor *= vec4(tc_overlay.x, tc_overlay.y, 0, 1);
|
||||
|
|
|
@ -27,8 +27,8 @@ void main(void) {
|
|||
// Fragment shader needs to know the sprite dimensions so that it can denormalize texCoord for processing.
|
||||
dimensions = spriteDimensions;
|
||||
|
||||
// Arbitrary custom parameter provided by the application. You can use it to pass e.g. times/frames.
|
||||
customParam = spriteCustomParam;
|
||||
// Arbitrary parameters provided by the application. You can use this to pass e.g. times/frames.
|
||||
customParams = spriteCustomParams;
|
||||
|
||||
// Should be obvious.
|
||||
color = spriteRGBA;
|
||||
|
|
|
@ -5,5 +5,7 @@
|
|||
|
||||
void main(void) {
|
||||
vec4 texel = texture(tex, texCoord);
|
||||
fragColor = vec4(color.rgb, color.a * texel.r) * mix(1.0, 0.8, texCoordOverlay.y);
|
||||
float gradient = mix(1.0, 0.8, texCoordOverlay.y);
|
||||
fragColor = color * texel.r * gradient;
|
||||
fragColor.rgb *= gradient;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ float tc_mask(vec2 tc) {
|
|||
}
|
||||
|
||||
void main(void) {
|
||||
float t = customParam;
|
||||
float t = customParams.r;
|
||||
vec2 tc = texCoord;
|
||||
vec2 tc_overlay = texCoordOverlay;
|
||||
vec2 f = tc_overlay - vec2(0.5);
|
||||
|
@ -22,18 +22,16 @@ void main(void) {
|
|||
|
||||
float a = tc_mask(tc);
|
||||
|
||||
vec4 texel = texture(tex, uv_to_region(texRegion, tc));
|
||||
vec4 textfrag = vec4(color.rgb, a * color.a * texel.r);
|
||||
vec4 textfrag = color * texture(tex, uv_to_region(texRegion, tc)).r * a;
|
||||
|
||||
tc -= vec2(1) / dimensions;
|
||||
a = tc_mask(tc);
|
||||
|
||||
texel = texture(tex, uv_to_region(texRegion, tc));
|
||||
vec4 shadowfrag = vec4(vec3(0), a * color.a * texel.r);
|
||||
vec4 shadowfrag = vec4(vec3(0), color.a) * texture(tex, uv_to_region(texRegion, tc)).r * a;
|
||||
|
||||
fragColor = textfrag;
|
||||
fragColor = mix(shadowfrag, textfrag, sqrt(textfrag.a));
|
||||
|
||||
tc_overlay = clamp(tc_overlay,0.01,0.99); // The overlay coordinates are outside of [0,1] in the padding region, so we make sure there are no wrap around artifacts when a bit of text is distorted to this region.
|
||||
fragColor.a *= clamp((texture(trans, tc_overlay).r + 0.5) * 2.5 * t-0.5, 0.0, 1.0);
|
||||
fragColor *= clamp((texture(trans, tc_overlay).r + 0.5) * 2.5 * t-0.5, 0.0, 1.0);
|
||||
}
|
||||
|
|
|
@ -2,26 +2,7 @@
|
|||
|
||||
#include "interface/standard.glslh"
|
||||
|
||||
vec4 replace_color(vec4 texel, vec4 neighbour) {
|
||||
return mix(texel, neighbour, float(neighbour.a > texel.a));
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec4 texel = textureLod(tex, texCoordRaw, 0);
|
||||
vec4 new_texel = texel;
|
||||
|
||||
/*
|
||||
* GL_LINEAR will sample even pixels with zero alpha.
|
||||
* Those usually don't have any meaningful RGB data.
|
||||
* This results in ugly dark borders around some sprites.
|
||||
* As a workaround, we change the RGB values of such pixels to those of the most opaque nearby one.
|
||||
*/
|
||||
|
||||
new_texel = replace_color(new_texel, textureLodOffset(tex, texCoordRaw, 0, ivec2( 0, 1)));
|
||||
new_texel = replace_color(new_texel, textureLodOffset(tex, texCoordRaw, 0, ivec2( 1, 0)));
|
||||
new_texel = replace_color(new_texel, textureLodOffset(tex, texCoordRaw, 0, ivec2( 0, -1)));
|
||||
new_texel = replace_color(new_texel, textureLodOffset(tex, texCoordRaw, 0, ivec2(-1, 0)));
|
||||
texel = mix(texel, vec4(new_texel.rgb, texel.a), float(texel.a <= 0.5));
|
||||
|
||||
fragColor = texel;
|
||||
fragColor = textureLod(tex, texCoordRaw, 0);
|
||||
fragColor.rgb *= fragColor.a;
|
||||
}
|
||||
|
|
109
src/boss.c
109
src/boss.c
|
@ -31,6 +31,7 @@ Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
|
|||
}
|
||||
|
||||
buf->birthtime = global.frames;
|
||||
buf->zoomcolor = *RGBA(0.1, 0.2, 0.3, 1.0);
|
||||
|
||||
buf->ent.draw_layer = LAYER_BOSS;
|
||||
buf->ent.draw_func = ent_draw_boss;
|
||||
|
@ -39,12 +40,12 @@ Boss* create_boss(char *name, char *ani, char *dialog, complex pos) {
|
|||
return buf;
|
||||
}
|
||||
|
||||
void draw_boss_text(Alignment align, float x, float y, const char *text, const char *fnt, Color clr) {
|
||||
void draw_boss_text(Alignment align, float x, float y, const char *text, const char *fnt, const Color *clr) {
|
||||
ShaderProgram *sh_prev = r_shader_current();
|
||||
r_shader("text_default");
|
||||
text_draw(text, &(TextParams) {
|
||||
.pos = { x + 1, y + 1 },
|
||||
.color = derive_color(rgb(0, 0, 0), CLRMASK_A, clr),
|
||||
.color = RGBA(0, 0, 0, clr->a),
|
||||
.font = fnt,
|
||||
.align = align,
|
||||
});
|
||||
|
@ -72,7 +73,7 @@ void spell_opening(Boss *b, int time) {
|
|||
float scale = f+1.*(1-f)*(1-f)*(1-f);
|
||||
r_mat_scale(scale,scale,1);
|
||||
r_mat_rotate_deg(360*f,1,1,0);
|
||||
draw_boss_text(ALIGN_RIGHT, strw/2*(1-f), 0, b->current->name, "standard", rgb(1, 1, 1));
|
||||
draw_boss_text(ALIGN_RIGHT, strw/2*(1-f), 0, b->current->name, "standard", RGB(1, 1, 1));
|
||||
r_mat_pop();
|
||||
|
||||
r_capability(RCAP_CULL_FACE, cullcap_saved);
|
||||
|
@ -80,27 +81,34 @@ void spell_opening(Boss *b, int time) {
|
|||
|
||||
void draw_extraspell_bg(Boss *boss, int time) {
|
||||
// overlay for all extra spells
|
||||
// FIXME: Please replace this with something that doesn't look like shit.
|
||||
|
||||
r_blend(BLEND_ADD);
|
||||
r_color4(0.2,0.1,0,0.7);
|
||||
float opacity = 0.7;
|
||||
r_color4(0.2 * opacity, 0.1 * opacity, 0, 0);
|
||||
fill_viewport(sin(time) * 0.015, time / 50.0, 1, "stage3/wspellclouds");
|
||||
r_color4(1,1,1,1);
|
||||
r_color4(2000, 2000, 2000, 0);
|
||||
r_blend(r_blend_compose(
|
||||
BLENDFACTOR_SRC_ALPHA, BLENDFACTOR_ONE, BLENDOP_MIN,
|
||||
BLENDFACTOR_SRC_COLOR, BLENDFACTOR_ONE, BLENDOP_MIN,
|
||||
BLENDFACTOR_ZERO, BLENDFACTOR_ONE, BLENDOP_MIN
|
||||
));
|
||||
fill_viewport(cos(time) * 0.015, time / 70.0, 1, "stage4/kurumibg2");
|
||||
fill_viewport(sin(time+2.1) * 0.015, time / 30.0, 1, "stage4/kurumibg2");
|
||||
r_blend(BLEND_ALPHA);
|
||||
fill_viewport(sin(time*1.1+2.1) * 0.015, time / 30.0, 1, "stage4/kurumibg2");
|
||||
r_blend(BLEND_PREMUL_ALPHA);
|
||||
r_color4(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
Color boss_healthbar_color(AttackType atype) {
|
||||
switch(atype) {
|
||||
default: case AT_Normal: return rgb(1.00, 1.00, 1.00);
|
||||
case AT_Spellcard: return rgb(1.00, 0.65, 0.65);
|
||||
case AT_SurvivalSpell: return rgb(0.50, 0.50, 1.00);
|
||||
case AT_ExtraSpell: return rgb(1.00, 0.30, 0.20);
|
||||
}
|
||||
const Color* boss_healthbar_color(AttackType atype) {
|
||||
static const Color colors[] = {
|
||||
[AT_Normal] = { 1.00, 1.00, 1.00, 1.00 },
|
||||
[AT_Move] = { 1.00, 1.00, 1.00, 1.00 },
|
||||
[AT_Spellcard] = { 1.00, 0.65, 0.65, 1.00 },
|
||||
[AT_SurvivalSpell] = { 0.50, 0.50, 1.00, 1.00 },
|
||||
[AT_ExtraSpell] = { 1.00, 0.30, 0.20, 1.00 },
|
||||
[AT_Immediate] = { 1.00, 1.00, 1.00, 1.00 },
|
||||
};
|
||||
|
||||
assert(atype >= 0 && atype < sizeof(colors)/sizeof(*colors));
|
||||
return colors + atype;
|
||||
}
|
||||
|
||||
static StageProgress* get_spellstage_progress(Attack *a, StageInfo **out_stginfo, bool write) {
|
||||
|
@ -175,14 +183,17 @@ static void BossGlow(Projectile *p, int t) {
|
|||
float s = 1.0+t/(double)p->timeout*0.5;
|
||||
float fade = 1 - (1.5 - s);
|
||||
float deform = 5 - 10 * fade * fade;
|
||||
Color c = p->color;
|
||||
|
||||
c.a = 0;
|
||||
color_mul_scalar(&c, 1.5 - s);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.pos = { creal(p->pos), cimag(p->pos) },
|
||||
.sprite_ptr = p->sprite,
|
||||
.scale.both = s,
|
||||
.blend = BLEND_ADD,
|
||||
.color = derive_color(p->color, CLRMASK_A, rgba(0, 0, 0, 1.5 - s)),
|
||||
.custom = deform,
|
||||
.color = &c,
|
||||
.shader_params = &(ShaderCustomParams){{ deform }},
|
||||
.shader = "sprite_silhouette",
|
||||
});
|
||||
}
|
||||
|
@ -195,7 +206,7 @@ static int boss_glow(Projectile *p, int t) {
|
|||
return linear(p, t);
|
||||
}
|
||||
|
||||
static Projectile* spawn_boss_glow(Boss *boss, Color clr, int timeout) {
|
||||
static Projectile* spawn_boss_glow(Boss *boss, const Color *clr, int timeout) {
|
||||
// XXX: memdup is required because the Sprite returned by animation_get_frame is only temporarily valid
|
||||
return PARTICLE(
|
||||
.sprite_ptr = memdup(aniplayer_get_frame(&boss->ani), sizeof(Sprite)),
|
||||
|
@ -211,8 +222,8 @@ static Projectile* spawn_boss_glow(Boss *boss, Color clr, int timeout) {
|
|||
}
|
||||
|
||||
static void spawn_particle_effects(Boss *boss) {
|
||||
Color glowcolor = boss->glowcolor;
|
||||
Color shadowcolor = boss->shadowcolor;
|
||||
Color *glowcolor = &boss->glowcolor;
|
||||
Color *shadowcolor = &boss->shadowcolor;
|
||||
|
||||
Attack *cur = boss->current;
|
||||
bool is_spell = cur && ATTACK_IS_SPELL(cur->type) && !cur->endtime;
|
||||
|
@ -222,28 +233,25 @@ static void spawn_particle_effects(Boss *boss) {
|
|||
PARTICLE(
|
||||
.sprite = "smoke",
|
||||
.pos = cexp(I*global.frames),
|
||||
.color = shadowcolor,
|
||||
.color = RGBA(shadowcolor->r, shadowcolor->g, shadowcolor->b, 0.0),
|
||||
.rule = enemy_flare,
|
||||
.timeout = 180,
|
||||
.draw_rule = Shrink,
|
||||
.args = { 0, add_ref(boss), },
|
||||
.angle = M_PI * 2 * frand(),
|
||||
.flags = PFLAG_DRAWADD,
|
||||
);
|
||||
}
|
||||
|
||||
if(!(global.frames % (2 + 2 * is_extra)) && (is_spell || boss_is_dying(boss))) {
|
||||
float glowstr = 0.5;
|
||||
float a = (1.0 - glowstr) + glowstr * pow(psin(global.frames/15.0), 1.0);
|
||||
spawn_boss_glow(boss, multiply_colors(glowcolor, rgb(a, a, a)), 24);
|
||||
float a = (1.0 - glowstr) + glowstr * psin(global.frames/15.0);
|
||||
spawn_boss_glow(boss, color_mul_scalar(COLOR_COPY(glowcolor), a), 24);
|
||||
}
|
||||
}
|
||||
|
||||
void draw_boss_background(Boss *boss) {
|
||||
r_mat_push();
|
||||
r_mat_translate(creal(boss->pos), cimag(boss->pos), 0);
|
||||
|
||||
r_blend(BLEND_ADD);
|
||||
r_mat_rotate_deg(global.frames*4.0, 0, 0, -1);
|
||||
|
||||
float f = 0.8+0.1*sin(global.frames/8.0);
|
||||
|
@ -253,9 +261,11 @@ void draw_boss_background(Boss *boss) {
|
|||
f -= t*(t-0.7)/max(0.01, 1-t);
|
||||
}
|
||||
|
||||
r_mat_scale(f,f,f);
|
||||
draw_sprite_batched(0, 0, "boss_circle");
|
||||
r_blend(BLEND_ALPHA);
|
||||
r_mat_scale(f, f, 1);
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "boss_circle",
|
||||
.color = RGBA(1, 1, 1, 0),
|
||||
});
|
||||
r_mat_pop();
|
||||
}
|
||||
|
||||
|
@ -275,14 +285,14 @@ static void ent_draw_boss(EntityInterface *ent) {
|
|||
boss_alpha = (1 - t) + 0.3;
|
||||
}
|
||||
|
||||
r_color4(1,1-red,1-red/2,boss_alpha);
|
||||
r_color(RGBA_MUL_ALPHA(1, 1-red, 1-red/2, boss_alpha));
|
||||
draw_sprite_batched_p(creal(boss->pos), cimag(boss->pos) + 6*sin(global.frames/25.0), aniplayer_get_frame(&boss->ani));
|
||||
r_color4(1,1,1,1);
|
||||
r_color4(1, 1, 1, 1);
|
||||
|
||||
if(boss->current->type == AT_Move && global.frames - boss->current->starttime > 0 && boss_attack_is_final(boss, boss->current))
|
||||
return;
|
||||
|
||||
draw_boss_text(ALIGN_LEFT, 10, 20, boss->name, "standard", rgb(1, 1, 1));
|
||||
draw_boss_text(ALIGN_LEFT, 10, 20, boss->name, "standard", RGB(1, 1, 1));
|
||||
|
||||
if(!boss->current)
|
||||
return;
|
||||
|
@ -296,15 +306,15 @@ static void ent_draw_boss(EntityInterface *ent) {
|
|||
Color textclr;
|
||||
|
||||
if(remaining < 6) {
|
||||
textclr = rgb(1.0, 0.2, 0.2);
|
||||
textclr = *RGB(1.0, 0.2, 0.2);
|
||||
} else if(remaining < 11) {
|
||||
textclr = rgb(1.0, 1.0, 0.2);
|
||||
textclr = *RGB(1.0, 1.0, 0.2);
|
||||
} else {
|
||||
textclr = rgb(1.0, 1.0, 1.0);
|
||||
textclr = *RGB(1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%.2f", remaining);
|
||||
draw_boss_text(ALIGN_CENTER, VIEWPORT_W - 24, 10, buf, "standard", textclr);
|
||||
draw_boss_text(ALIGN_CENTER, VIEWPORT_W - 24, 10, buf, "standard", &textclr);
|
||||
|
||||
StageProgress *p = get_spellstage_progress(boss->current, NULL, false);
|
||||
if(p) {
|
||||
|
@ -316,7 +326,7 @@ static void ent_draw_boss(EntityInterface *ent) {
|
|||
draw_boss_text(ALIGN_RIGHT,
|
||||
VIEWPORT_W + text_width(font, buf, 0) * pow(1 - a, 2),
|
||||
35 + text_height(font, buf, 0),
|
||||
buf, "small", rgba(1, 1, 1, a)
|
||||
buf, "small", RGBA(1, 1, 1, a)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -379,7 +389,7 @@ static void ent_draw_boss(EntityInterface *ent) {
|
|||
r_shader("sprite_default");
|
||||
|
||||
// remaining spells
|
||||
r_color4(1,1,1,0.7);
|
||||
r_color4(0.7, 0.7, 0.7, 0.7);
|
||||
Sprite *star = get_sprite("star");
|
||||
|
||||
for(int x = 0, i = boss->acount-1; i > nextspell; i--) {
|
||||
|
@ -420,15 +430,15 @@ void boss_rule_extra(Boss *boss, float alpha) {
|
|||
PARTICLE(
|
||||
.sprite = (frand() < v*0.3 || lt > 1) ? "stain" : "arc",
|
||||
.pos = boss->pos + dir * (100 + 50 * psin(alpha*global.frames/10.0+2*i)) * alpha,
|
||||
.color = rgb(
|
||||
.color = RGBA(
|
||||
1.0 - 0.5 * psina * v,
|
||||
0.5 + 0.2 * psina * (1-v),
|
||||
0.5 + 0.5 * psina * v
|
||||
0.5 + 0.5 * psina * v,
|
||||
0.0
|
||||
),
|
||||
.rule = linear,
|
||||
.timeout = 30*lt,
|
||||
.draw_rule = GrowFade,
|
||||
.flags = PFLAG_DRAWADD,
|
||||
.args = { vel * (1 - 2 * !(global.frames % 10)), 2.5 },
|
||||
);
|
||||
}
|
||||
|
@ -496,7 +506,7 @@ static void boss_give_spell_bonus(Boss *boss, Attack *a, Player *plr) {
|
|||
player_add_points(plr, total);
|
||||
|
||||
StageTextTable tbl;
|
||||
stagetext_begin_table(&tbl, title, rgb(1, 1, 1), rgb(1, 1, 1), VIEWPORT_W/2, 0,
|
||||
stagetext_begin_table(&tbl, title, RGB(1, 1, 1), RGB(1, 1, 1), VIEWPORT_W/2, 0,
|
||||
ATTACK_END_DELAY_SPELL * 2, ATTACK_END_DELAY_SPELL / 2, ATTACK_END_DELAY_SPELL);
|
||||
stagetext_table_add_numeric_nonzero(&tbl, "Clear bonus", clear_bonus);
|
||||
stagetext_table_add_numeric_nonzero(&tbl, "Time bonus", time_bonus);
|
||||
|
@ -691,19 +701,21 @@ void process_boss(Boss **pboss) {
|
|||
float t = (global.frames - boss->current->endtime)/(float)BOSS_DEATH_DELAY + 1;
|
||||
tsrand_fill(6);
|
||||
|
||||
Color *clr = RGBA_MUL_ALPHA(0.1 + sin(10*t), 0.1 + cos(10*t), 0.5, t);
|
||||
clr->a = 0;
|
||||
|
||||
PARTICLE(
|
||||
.sprite = "petal",
|
||||
.pos = boss->pos,
|
||||
.rule = asymptotic,
|
||||
.draw_rule = Petal,
|
||||
.color = rgba(0.1+sin(10*t),0.1+cos(10*t),0.5,t),
|
||||
.color = clr,
|
||||
.args = {
|
||||
sign(anfrand(5))*(3+t*5*afrand(0))*cexp(I*M_PI*8*t),
|
||||
5+I,
|
||||
afrand(2) + afrand(3)*I,
|
||||
afrand(4) + 360.0*I*afrand(1)
|
||||
},
|
||||
.flags = PFLAG_DRAWADD,
|
||||
);
|
||||
|
||||
if(!extra) {
|
||||
|
@ -716,7 +728,7 @@ void process_boss(Boss **pboss) {
|
|||
|
||||
if(t == 1) {
|
||||
for(int i = 0; i < 10; ++i) {
|
||||
spawn_boss_glow(boss, boss->glowcolor, 60 + 20 * i);
|
||||
spawn_boss_glow(boss, &boss->glowcolor, 60 + 20 * i);
|
||||
}
|
||||
|
||||
for(int i = 0; i < 256; i++) {
|
||||
|
@ -846,12 +858,11 @@ void boss_start_attack(Boss *b, Attack *a) {
|
|||
PARTICLE(
|
||||
.sprite = "stain",
|
||||
.pos = VIEWPORT_W/2 + VIEWPORT_W/4*anfrand(0)+I*VIEWPORT_H/2+I*anfrand(1)*30,
|
||||
.color = rgb(0.2,0.3,0.4),
|
||||
.color = RGBA(0.2, 0.3, 0.4, 0.0),
|
||||
.rule = linear,
|
||||
.timeout = 50,
|
||||
.draw_rule = GrowFade,
|
||||
.args = { sign(anfrand(2))*10*(1+afrand(3)) },
|
||||
.flags = PFLAG_DRAWADD,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
175
src/color.c
175
src/color.c
|
@ -11,95 +11,86 @@
|
|||
#include <stdio.h>
|
||||
#include "color.h"
|
||||
|
||||
static const float conv = 1.0f / CLR_ONEVALUE;
|
||||
#define COLOR_OP(c1, op, c2) do { \
|
||||
(c1)->r = (c1)->r op (c2)->r; \
|
||||
(c1)->g = (c1)->g op (c2)->g; \
|
||||
(c1)->b = (c1)->b op (c2)->b; \
|
||||
(c1)->a = (c1)->a op (c2)->a; \
|
||||
} while(0);
|
||||
|
||||
#ifndef COLOR_INLINE
|
||||
|
||||
Color rgba(float r, float g, float b, float a) {
|
||||
assert(!r || isnormal(r));
|
||||
assert(!g || isnormal(g));
|
||||
assert(!b || isnormal(b));
|
||||
assert(!a || isnormal(a));
|
||||
|
||||
return RGBA(r, g, b, a);
|
||||
Color* color_copy(Color *dst, const Color *src) {
|
||||
*dst = *src;
|
||||
return dst;
|
||||
}
|
||||
|
||||
Color rgb(float r, float g, float b) {
|
||||
assert(!r || isnormal(r));
|
||||
assert(!g || isnormal(g));
|
||||
assert(!b || isnormal(b));
|
||||
|
||||
return RGB(r, g, b);
|
||||
Color* color_add(Color *clr, const Color *clr2) {
|
||||
COLOR_OP(clr, +, clr2);
|
||||
return clr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void parse_color(Color clr, float *r, float *g, float *b, float *a) {
|
||||
*r = (ColorComponent)((clr >> CLR_R) & CLR_CMASK) * conv;
|
||||
*g = (ColorComponent)((clr >> CLR_G) & CLR_CMASK) * conv;
|
||||
*b = (ColorComponent)((clr >> CLR_B) & CLR_CMASK) * conv;
|
||||
*a = (ColorComponent)((clr >> CLR_A) & CLR_CMASK) * conv;
|
||||
Color* color_sub(Color *clr, const Color *clr2) {
|
||||
COLOR_OP(clr, -, clr2);
|
||||
return clr;
|
||||
}
|
||||
|
||||
void parse_color_array(Color clr, float array[4]) {
|
||||
parse_color(clr, array, array+1, array+2, array+3);
|
||||
Color* color_mul(Color *clr, const Color *clr2) {
|
||||
COLOR_OP(clr, *, clr2);
|
||||
return clr;
|
||||
}
|
||||
|
||||
Color derive_color(Color src, Color mask, Color mod) {
|
||||
return (src & ~mask) | (mod & mask);
|
||||
Color* color_mul_alpha(Color *clr) {
|
||||
clr->r *= clr->a;
|
||||
clr->g *= clr->a;
|
||||
clr->b *= clr->a;
|
||||
return clr;
|
||||
}
|
||||
|
||||
float color_component(Color clr, uint ofs) {
|
||||
return (ColorComponent)((clr >> ofs) & CLR_CMASK) * conv;
|
||||
Color* color_mul_scalar(Color *clr, float scalar) {
|
||||
clr->r *= scalar;
|
||||
clr->g *= scalar;
|
||||
clr->b *= scalar;
|
||||
clr->a *= scalar;
|
||||
return clr;
|
||||
}
|
||||
|
||||
Color multiply_colors(Color c1, Color c2) {
|
||||
float c1a[4], c2a[4];
|
||||
parse_color_array(c1, c1a);
|
||||
parse_color_array(c2, c2a);
|
||||
return rgba(c1a[0]*c2a[0], c1a[1]*c2a[1], c1a[2]*c2a[2], c1a[3]*c2a[3]);
|
||||
Color* color_div(Color *clr, const Color *clr2) {
|
||||
COLOR_OP(clr, /, clr2);
|
||||
return clr;
|
||||
}
|
||||
|
||||
Color add_colors(Color c1, Color c2) {
|
||||
float c1a[4], c2a[4];
|
||||
parse_color_array(c1, c1a);
|
||||
parse_color_array(c2, c2a);
|
||||
return rgba(c1a[0]+c2a[0], c1a[1]+c2a[1], c1a[2]+c2a[2], c1a[3]+c2a[3]);
|
||||
Color* color_div_alpha(Color *clr) {
|
||||
if(clr->a != 0) {
|
||||
clr->r /= clr->a;
|
||||
clr->g /= clr->a;
|
||||
clr->b /= clr->a;
|
||||
}
|
||||
|
||||
return clr;
|
||||
}
|
||||
|
||||
Color subtract_colors(Color c1, Color c2) {
|
||||
float c1a[4], c2a[4];
|
||||
parse_color_array(c1, c1a);
|
||||
parse_color_array(c2, c2a);
|
||||
return rgba(c1a[0]-c2a[0], c1a[1]-c2a[1], c1a[2]-c2a[2], c1a[3]-c2a[3]);
|
||||
Color* color_div_scalar(Color *clr, float scalar) {
|
||||
clr->r /= scalar;
|
||||
clr->g /= scalar;
|
||||
clr->b /= scalar;
|
||||
clr->a /= scalar;
|
||||
return clr;
|
||||
}
|
||||
|
||||
Color divide_colors(Color c1, Color c2) {
|
||||
float c1a[4], c2a[4];
|
||||
parse_color_array(c1, c1a);
|
||||
parse_color_array(c2, c2a);
|
||||
return rgba(c1a[0]/c2a[0], c1a[1]/c2a[1], c1a[2]/c2a[2], c1a[3]/c2a[3]);
|
||||
Color* color_lerp(Color *clr, const Color *clr2, float a) {
|
||||
float ia = 1 - a;
|
||||
clr->r = clr->r * ia + clr2->r * a;
|
||||
clr->g = clr->g * ia + clr2->g * a;
|
||||
clr->b = clr->b * ia + clr2->b * a;
|
||||
clr->a = clr->a * ia + clr2->a * a;
|
||||
return clr;
|
||||
}
|
||||
|
||||
Color mix_colors(Color c1, Color c2, double a) {
|
||||
float c1a[4], c2a[4];
|
||||
double f1 = a;
|
||||
double f2 = 1 - f1;
|
||||
parse_color_array(c1, c1a);
|
||||
parse_color_array(c2, c2a);
|
||||
return rgba(f1*c1a[0]+f2*c2a[0], f1*c1a[1]+f2*c2a[1], f1*c1a[2]+f2*c2a[2], f1*c1a[3]+f2*c2a[3]);
|
||||
}
|
||||
|
||||
Color approach_color(Color src, Color dst, double delta) {
|
||||
float c1a[4], c2a[4];
|
||||
parse_color_array(src, c1a);
|
||||
parse_color_array(dst, c2a);
|
||||
return rgba(
|
||||
c1a[0] + (c2a[0] - c1a[0]) * delta,
|
||||
c1a[1] + (c2a[1] - c1a[1]) * delta,
|
||||
c1a[2] + (c2a[2] - c1a[2]) * delta,
|
||||
c1a[3] + (c2a[3] - c1a[3]) * delta
|
||||
);
|
||||
Color* color_approach(Color *clr, const Color *clr2, float delta) {
|
||||
clr->r += (clr2->r - clr->r) * delta;
|
||||
clr->g += (clr2->g - clr->g) * delta;
|
||||
clr->b += (clr2->b - clr->b) * delta;
|
||||
clr->a += (clr2->a - clr->a) * delta;
|
||||
return clr;
|
||||
}
|
||||
|
||||
static float hue_to_rgb(float v1, float v2, float vH) {
|
||||
|
@ -126,11 +117,9 @@ static float hue_to_rgb(float v1, float v2, float vH) {
|
|||
return v1;
|
||||
}
|
||||
|
||||
Color hsla(float h, float s, float l, float a) {
|
||||
float r, g, b;
|
||||
|
||||
Color* color_hsla(Color *clr, float h, float s, float l, float a) {
|
||||
if(s == 0) {
|
||||
r = g = b = l;
|
||||
clr->r = clr->g = clr->b = l;
|
||||
} else {
|
||||
float v1, v2;
|
||||
h = fmod(h, 1.0);
|
||||
|
@ -143,20 +132,42 @@ Color hsla(float h, float s, float l, float a) {
|
|||
|
||||
v1 = 2.0 * l - v2;
|
||||
|
||||
r = hue_to_rgb(v1, v2, h + (1.0/3.0));
|
||||
g = hue_to_rgb(v1, v2, h);
|
||||
b = hue_to_rgb(v1, v2, h - (1.0/3.0));
|
||||
clr->r = hue_to_rgb(v1, v2, h + (1.0/3.0));
|
||||
clr->g = hue_to_rgb(v1, v2, h);
|
||||
clr->b = hue_to_rgb(v1, v2, h - (1.0/3.0));
|
||||
}
|
||||
|
||||
return rgba(r, g, b, a);
|
||||
clr->a = a;
|
||||
return clr;
|
||||
}
|
||||
|
||||
Color hsl(float h, float s, float l) {
|
||||
return hsla(h, s, l, 1.0);
|
||||
Color* color_set_opacity(Color *clr, float opacity) {
|
||||
// FIXME: is this correct?
|
||||
|
||||
if(clr->a != 0) {
|
||||
opacity /= clr->a;
|
||||
}
|
||||
|
||||
color_mul_scalar(clr, opacity);
|
||||
return clr;
|
||||
}
|
||||
|
||||
char* color_str(Color c) {
|
||||
float r, g, b, a;
|
||||
parse_color(c, &r, &g, &b, &a);
|
||||
return strfmt("rgba(%f, %f, %f, %f) 0x%016"PRIxMAX, r, g, b, a, (uintmax_t)c);
|
||||
bool color_equals(const Color *clr, const Color *clr2) {
|
||||
return (
|
||||
clr->r == clr2->r &&
|
||||
clr->g == clr2->g &&
|
||||
clr->b == clr2->b &&
|
||||
clr->a == clr2->a
|
||||
);
|
||||
}
|
||||
|
||||
char* color_str(const Color *clr) {
|
||||
return strfmt(
|
||||
"RGBA(%f, %f, %f, %f) at %p",
|
||||
clr->r,
|
||||
clr->g,
|
||||
clr->b,
|
||||
clr->a,
|
||||
(void*)clr
|
||||
);
|
||||
}
|
||||
|
|
112
src/color.h
112
src/color.h
|
@ -11,66 +11,74 @@
|
|||
|
||||
#include "util.h"
|
||||
|
||||
#define CLR_R ((Color)48)
|
||||
#define CLR_G ((Color)32)
|
||||
#define CLR_B ((Color)16)
|
||||
#define CLR_A ((Color)0)
|
||||
typedef struct Color {
|
||||
float r, g, b, a;
|
||||
} Color;
|
||||
|
||||
#define CLR_CMASK ((Color)0xffff)
|
||||
#define CLR_ONEVALUE ((Color)0xff)
|
||||
/*
|
||||
* These macros return a pointer to a new Color instance, which is *block-scoped*,
|
||||
* and has automatic storage-class.
|
||||
*/
|
||||
|
||||
#define CLRMASK_R (CLR_CMASK << CLR_R)
|
||||
#define CLRMASK_G (CLR_CMASK << CLR_G)
|
||||
#define CLRMASK_B (CLR_CMASK << CLR_B)
|
||||
#define CLRMASK_A (CLR_CMASK << CLR_A)
|
||||
#define RGBA(r, g, b, a) (&(Color) { (r), (g), (b), (a) })
|
||||
#define RGBA_MUL_ALPHA(r, g, b, a) color_mul_alpha(RGBA((r), (g), (b), (a)))
|
||||
#define RGB(r, g, b) RGBA((r), (g), (b), 1)
|
||||
|
||||
typedef uint64_t Color;
|
||||
typedef int16_t ColorComponent;
|
||||
#define HSLA(h, s, l, a) color_hsla((&(Color) { 0 }), (h), (s), (l), (a))
|
||||
#define HSLA_MUL_ALPHA(h, s, l, a) color_mul_alpha(HSLA((h), (s), (l), (a)))
|
||||
#define HSL(h, s, l) HSLA((h), (s), (l), 1)
|
||||
|
||||
#ifndef COLOR_INLINE
|
||||
#ifdef NDEBUG
|
||||
#define COLOR_INLINE
|
||||
#endif
|
||||
#endif
|
||||
#define COLOR_COPY(c) color_copy((&(Color) { 0 }), (c))
|
||||
|
||||
#ifdef COLOR_INLINE
|
||||
#define rgba(r,g,b,a) RGBA(r,g,b,a)
|
||||
#define rgb(r,g,b) RGB(r,g,b)
|
||||
#else
|
||||
Color rgba(float r, float g, float b, float a) attr_const;
|
||||
Color rgb(float r, float g, float b) attr_const;
|
||||
#endif
|
||||
/*
|
||||
* All of these modify the first argument in-place, and return it for convenience.
|
||||
*/
|
||||
|
||||
Color hsla(float h, float s, float l, float a) attr_const;
|
||||
Color hsl(float h, float s, float l) attr_const;
|
||||
Color* color_copy(Color *dst, const Color *src)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
void parse_color(Color clr, float *r, float *g, float *b, float *a) attr_nonnull(2, 3, 4, 5);
|
||||
void parse_color_array(Color clr, float array[4]) attr_nonnull(2);
|
||||
Color derive_color(Color src, Color mask, Color mod) attr_const;
|
||||
Color multiply_colors(Color c1, Color c2) attr_const;
|
||||
Color add_colors(Color c1, Color c2) attr_const;
|
||||
Color subtract_colors(Color c1, Color c2) attr_const;
|
||||
Color mix_colors(Color c1, Color c2, double a) attr_const;
|
||||
Color approach_color(Color src, Color dst, double delta) attr_const;
|
||||
float color_component(Color clr, uint ofs) attr_const;
|
||||
char* color_str(Color c) attr_returns_nonnull;
|
||||
Color* color_hsla(Color *clr, float h, float s, float l, float a)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
#ifdef RGBA
|
||||
#undef RGBA
|
||||
#endif
|
||||
Color* color_add(Color *clr, const Color *clr2)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
#ifdef RGB
|
||||
#undef RGB
|
||||
#endif
|
||||
Color* color_sub(Color *clr, const Color *clr2)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
#define RGBA(r,g,b,a) \
|
||||
((((Color)(ColorComponent)(CLR_ONEVALUE * (r)) & CLR_CMASK) << CLR_R) + \
|
||||
(((Color)(ColorComponent)(CLR_ONEVALUE * (g)) & CLR_CMASK) << CLR_G) + \
|
||||
(((Color)(ColorComponent)(CLR_ONEVALUE * (b)) & CLR_CMASK) << CLR_B) + \
|
||||
(((Color)(ColorComponent)(CLR_ONEVALUE * (a)) & CLR_CMASK) << CLR_A))
|
||||
Color* color_mul(Color *clr, const Color *clr2)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
#define RGB(r,g,b) \
|
||||
((((Color)(ColorComponent)(CLR_ONEVALUE * (r)) & CLR_CMASK) << CLR_R) + \
|
||||
(((Color)(ColorComponent)(CLR_ONEVALUE * (g)) & CLR_CMASK) << CLR_G) + \
|
||||
(((Color)(ColorComponent)(CLR_ONEVALUE * (b)) & CLR_CMASK) << CLR_B) + \
|
||||
(CLR_ONEVALUE << CLR_A))
|
||||
Color* color_mul_alpha(Color *clr)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
Color* color_mul_scalar(Color *clr, float scalar)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
Color* color_div(Color *clr, const Color *clr2)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
Color* color_div_alpha(Color *clr)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
Color* color_div_scalar(Color *clr, float scalar)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
Color* color_lerp(Color *clr, const Color *clr2, float a)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
Color* color_approach(Color *clr, const Color *clr2, float delta)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
Color* color_set_opacity(Color *clr, float opacity)
|
||||
attr_nonnull(1) attr_returns_nonnull;
|
||||
|
||||
/*
|
||||
* End of color manipulation routines.
|
||||
*/
|
||||
|
||||
bool color_equals(const Color *clr, const Color *clr2)
|
||||
attr_nonnull(1, 2);
|
||||
|
||||
char* color_str(const Color *clr)
|
||||
attr_nonnull(1) attr_returns_nonnull attr_nodiscard;
|
||||
|
|
|
@ -307,7 +307,7 @@ static void credits_draw_entry(CreditsEntry *e) {
|
|||
render_pop();
|
||||
*/
|
||||
|
||||
r_color4(1, 1, 1, fadein * fadeout);
|
||||
r_color(RGBA_MUL_ALPHA(1, 1, 1, fadein * fadeout));
|
||||
|
||||
if(yukkuri) {
|
||||
r_mat_translate(0, (-h_body) * 0.5, 0);
|
||||
|
|
|
@ -11,37 +11,46 @@
|
|||
#include "difficulty.h"
|
||||
#include "resource/resource.h"
|
||||
|
||||
const char* difficulty_name(Difficulty diff) {
|
||||
switch(diff) {
|
||||
case D_Easy: return "Easy";
|
||||
case D_Normal: return "Normal";
|
||||
case D_Hard: return "Hard";
|
||||
case D_Lunatic: return "Lunatic";
|
||||
case D_Extra: return "Extra";
|
||||
default: return "Unknown";
|
||||
typedef struct DiffDef {
|
||||
const char *name;
|
||||
const char *spr_name;
|
||||
Color color;
|
||||
} DiffDef;
|
||||
|
||||
static DiffDef diffs[] = {
|
||||
{ "Easy", "difficulty/easy", { 0.5, 1.0, 0.5, 1.0 } },
|
||||
{ "Normal", "difficulty/normal", { 0.5, 0.5, 1.0, 1.0 } },
|
||||
{ "Hard", "difficulty/hard", { 1.0, 0.5, 0.5, 1.0 } },
|
||||
{ "Lunatic", "difficulty/lunatic", { 1.0, 0.5, 1.0, 1.0 } },
|
||||
|
||||
// TODO: sprite for this
|
||||
{ "Extra", "difficulty/lunatic", { 0.5, 1.0, 1.0, 1.0 } },
|
||||
};
|
||||
|
||||
static inline DiffDef* get_diff_def(Difficulty diff) {
|
||||
uint idx = diff - D_Easy;
|
||||
|
||||
if(idx < sizeof(diffs)/sizeof(*diffs)) {
|
||||
return diffs + idx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* difficulty_name(Difficulty diff) {
|
||||
DiffDef *d = get_diff_def(diff);
|
||||
return d ? d->name : "Unknown";
|
||||
}
|
||||
|
||||
const char* difficulty_sprite_name(Difficulty diff) {
|
||||
switch(diff) {
|
||||
case D_Easy: return "difficulty/easy";
|
||||
case D_Normal: return "difficulty/normal";
|
||||
case D_Hard: return "difficulty/hard";
|
||||
case D_Lunatic: return "difficulty/lunatic";
|
||||
case D_Extra: return "difficulty/lunatic";
|
||||
default: return "difficulty/unknown"; // This sprite is not supposed to exist.
|
||||
}
|
||||
DiffDef *d = get_diff_def(diff);
|
||||
return d ? d->spr_name : "difficulty/unknown";
|
||||
}
|
||||
|
||||
Color difficulty_color(Difficulty diff) {
|
||||
switch(diff) {
|
||||
case D_Easy: return rgb(0.5, 1.0, 0.5);
|
||||
case D_Normal: return rgb(0.5, 0.5, 1.0);
|
||||
case D_Hard: return rgb(1.0, 0.5, 0.5);
|
||||
case D_Lunatic: return rgb(1.0, 0.5, 1.0);
|
||||
case D_Extra: return rgb(0.5, 1.0, 1.0);
|
||||
default: return rgb(0.5, 0.5, 0.5);
|
||||
}
|
||||
const Color* difficulty_color(Difficulty diff) {
|
||||
static Color unknown_clr = { 0.5, 0.5, 0.5, 1.0 };
|
||||
DiffDef *d = get_diff_def(diff);
|
||||
return d ? &d->color : &unknown_clr;
|
||||
}
|
||||
|
||||
void difficulty_preload(void) {
|
||||
|
|
|
@ -23,12 +23,12 @@ typedef enum {
|
|||
#define NUM_SELECTABLE_DIFFICULTIES D_Lunatic
|
||||
|
||||
const char* difficulty_name(Difficulty diff)
|
||||
attr_const attr_returns_nonnull;
|
||||
attr_pure attr_returns_nonnull;
|
||||
|
||||
const char* difficulty_sprite_name(Difficulty diff)
|
||||
attr_const attr_returns_nonnull;
|
||||
attr_pure attr_returns_nonnull;
|
||||
|
||||
Color difficulty_color(Difficulty diff)
|
||||
attr_const;
|
||||
const |