358 lines
9.2 KiB
C
358 lines
9.2 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#include "draw.h"
|
|
|
|
#include "global.h"
|
|
#include "stagedraw.h"
|
|
#include "stageutils.h"
|
|
#include "util/glm.h"
|
|
#include "util/graphics.h"
|
|
|
|
static Stage1DrawData *stage1_draw_data;
|
|
|
|
Stage1DrawData *stage1_get_draw_data(void) {
|
|
return NOT_NULL(stage1_draw_data);
|
|
}
|
|
|
|
void stage1_drawsys_init(void) {
|
|
stage1_draw_data = ALLOC(typeof(*stage1_draw_data));
|
|
stage3d_init(&stage_3d_context, 64);
|
|
|
|
FBAttachmentConfig cfg = { 0 };
|
|
cfg.attachment = FRAMEBUFFER_ATTACH_COLOR0;
|
|
cfg.tex_params.filter.min = TEX_FILTER_LINEAR;
|
|
cfg.tex_params.filter.mag = TEX_FILTER_LINEAR;
|
|
cfg.tex_params.type = TEX_TYPE_RGBA;
|
|
cfg.tex_params.wrap.s = TEX_WRAP_CLAMP;
|
|
cfg.tex_params.wrap.t = TEX_WRAP_CLAMP;
|
|
|
|
stage1_draw_data->water_shader = res_shader("stage1_water");
|
|
stage1_draw_data->water_fbpair.front = stage_add_background_framebuffer(
|
|
"Stage 1 water FB 1", 0.5, 0.5, 1, &cfg);
|
|
stage1_draw_data->water_fbpair.back = stage_add_background_framebuffer(
|
|
"Stage 1 water FB 2", 0.5, 0.5, 1, &cfg);
|
|
}
|
|
|
|
void stage1_drawsys_shutdown(void) {
|
|
stage3d_shutdown(&stage_3d_context);
|
|
mem_free(stage1_draw_data);
|
|
stage1_draw_data = NULL;
|
|
}
|
|
|
|
static bool reflect_particle_filter(Projectile *part) {
|
|
return !(part->flags & PFLAG_NOREFLECT) && stage_should_draw_particle(part);
|
|
}
|
|
|
|
static bool reflect_draw_predicate(EntityInterface *ent) {
|
|
switch(ent->draw_layer & ~LAYER_LOW_MASK) {
|
|
case LAYER_PLAYER_SLAVE:
|
|
case LAYER_PLAYER_FOCUS:
|
|
return true;
|
|
case LAYER_PLAYER_SHOT:
|
|
case LAYER_PLAYER_SHOT_HIGH:
|
|
return false;
|
|
default: break;
|
|
}
|
|
|
|
switch(ent->type) {
|
|
case ENT_TYPE_ID(Player):
|
|
case ENT_TYPE_ID(Boss):
|
|
case ENT_TYPE_ID(Enemy):
|
|
return true;
|
|
|
|
case ENT_TYPE_ID(Projectile): {
|
|
Projectile *p = ENT_CAST(ent, Projectile);
|
|
|
|
if(p->type == PROJ_PARTICLE) {
|
|
return reflect_particle_filter(p);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
default: return false;
|
|
}
|
|
|
|
UNREACHABLE;
|
|
}
|
|
|
|
|
|
static void stage1_horizon_draw(vec3 pos) {
|
|
r_state_push();
|
|
r_mat_mv_push();
|
|
r_mat_mv_translate_v(pos);
|
|
r_mat_mv_rotate(1.5 * M_PI, 1, 0, 0);
|
|
r_mat_mv_scale(3640 * 1.4, 1456 * 1.4, 1);
|
|
r_mat_mv_translate(0, -0.5, 0);
|
|
r_shader_standard();
|
|
r_uniform_sampler("tex", "stage1/horizon");
|
|
r_draw_quad();
|
|
r_mat_mv_pop();
|
|
r_state_pop();
|
|
}
|
|
|
|
static uint stage1_horizon_pos(Stage3D *s3d, vec3 cam, float maxrange) {
|
|
vec3 o;
|
|
glm_vec3_copy(cam, o);
|
|
o[2] = 0;
|
|
o[1] += 9700;
|
|
return stage3d_pos_single(s3d, cam, o, maxrange);
|
|
}
|
|
|
|
static const Color water_color = { 0, 0.08, 0.08, 1 };
|
|
|
|
static void stage1_water_render_reflections(void) {
|
|
r_disable(RCAP_DEPTH_TEST);
|
|
r_shader("sprite_default");
|
|
r_blend(BLEND_PREMUL_ALPHA);
|
|
r_mat_proj_push_ortho(VIEWPORT_W, VIEWPORT_H);
|
|
ent_draw(reflect_draw_predicate);
|
|
r_mat_proj_pop();
|
|
}
|
|
|
|
static void stage1_water_render_waves(float pos) {
|
|
r_shader_ptr(stage1_draw_data->water_shader);
|
|
r_uniform_float("time", 0.5f * global.frames / (float)FPS);
|
|
r_uniform_vec4_rgba("water_color", &water_color);
|
|
r_uniform_vec2("wave_offset", 0, pos / 2400.0f);
|
|
r_uniform_sampler("water_noisetex", "fractal_noise");
|
|
r_mat_mv_push();
|
|
r_mat_mv_scale(VIEWPORT_W, VIEWPORT_H, 1);
|
|
r_mat_mv_scale(0.01, 0.01, 1);
|
|
r_mat_mv_translate(-VIEWPORT_W/2, -VIEWPORT_H/2, -0.1);
|
|
draw_framebuffer_tex(stage1_draw_data->water_fbpair.front, VIEWPORT_W, VIEWPORT_H);
|
|
r_mat_mv_pop();
|
|
}
|
|
|
|
static void stage1_water_draw(vec3 pos) {
|
|
r_state_push();
|
|
r_disable(RCAP_CULL_FACE);
|
|
|
|
r_mat_mv_push();
|
|
r_mat_mv_translate(pos[0], pos[1], 0);
|
|
|
|
r_state_push();
|
|
r_enable(RCAP_DEPTH_TEST);
|
|
r_depth_func(DEPTH_LESS);
|
|
r_shader_standard_notex();
|
|
r_mat_mv_push();
|
|
r_mat_mv_scale(10000, 900000, 1);
|
|
r_color(&water_color);
|
|
r_draw_quad();
|
|
r_mat_mv_pop();
|
|
r_state_pop();
|
|
|
|
r_disable(RCAP_DEPTH_TEST);
|
|
|
|
switch(config_get_int(CONFIG_POSTPROCESS)) {
|
|
case 0: break;
|
|
case 1:
|
|
// NOTE: we used to render water into a half-res framebuffer here,
|
|
// but it's no longer beneficial. In fact it's slightly slower.
|
|
default: {
|
|
r_mat_mv_translate(0, 0, pos[2]);
|
|
stage1_water_render_waves(pos[1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
r_mat_mv_pop();
|
|
r_state_pop();
|
|
}
|
|
|
|
static uint stage1_water_pos(Stage3D *s3d, vec3 cam, float maxrange) {
|
|
cmplx sp = VIEWPORT_W*0.5 + VIEWPORT_H*0.6*I;
|
|
vec3 ray;
|
|
camera3d_unprojected_ray(&s3d->cam, sp, ray);
|
|
vec3 o = { 0, 0, 0 };
|
|
glm_vec3_scale(ray, 3000, ray);
|
|
glm_vec3_add(cam, ray, o);
|
|
return stage3d_pos_single(s3d, cam, o, maxrange);
|
|
}
|
|
|
|
static void stage1_smoke_draw(vec3 pos) {
|
|
float d = fabsf(pos[1] - stage_3d_context.cam.pos[1]);
|
|
|
|
float o = ((d-500)*(d-500))/1.5e7;
|
|
o *= 5 * pow((5000 - d) / 5000, 3);
|
|
|
|
if(o < 1.0f/255.0f) {
|
|
return;
|
|
}
|
|
|
|
float spin = 0.01 * sin(pos[1]*993.11);
|
|
|
|
r_state_push();
|
|
r_shader("sprite_default");
|
|
r_disable(RCAP_DEPTH_TEST);
|
|
r_cull(CULL_BACK);
|
|
r_mat_mv_push();
|
|
r_mat_mv_translate(pos[0]+600*sin(pos[1]), pos[1], pos[2]+600*sin(pos[1]/25.0));
|
|
r_mat_mv_rotate(M_PI/2, -1, 0, 0);
|
|
r_mat_mv_scale(3.5*2, 2*1.5, 1);
|
|
r_mat_mv_rotate(global.frames * spin + M_PI * 2 * sin(pos[1]*321.23), 0, 0, 1);
|
|
r_draw_sprite(&(SpriteParams) {
|
|
.sprite_ptr = res_sprite("stage1/fog"),
|
|
.color = RGBA(0.6 * o, 0.7 * o, 0.8 * o, o * 0.5),
|
|
});
|
|
r_mat_mv_pop();
|
|
r_state_pop();
|
|
}
|
|
|
|
static uint stage1_smoke_pos(Stage3D *s3d, vec3 cam, float maxrange) {
|
|
vec3 origin = {0,0,800};
|
|
vec3 step = {0,200,0};
|
|
return stage3d_pos_ray_farfirst(s3d, cam, origin, step, maxrange * 0.5f, 0);
|
|
}
|
|
|
|
static bool stage1_fog(Framebuffer *fb) {
|
|
Stage1DrawData *draw_data = stage1_get_draw_data();
|
|
|
|
r_shader("zbuf_fog");
|
|
r_uniform_sampler("depth", r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
|
|
r_uniform_vec4("fog_color", 0.78, 0.8, 0.85, 1.0);
|
|
r_uniform_float("start", draw_data->fog.near);
|
|
r_uniform_float("end", draw_data->fog.far);
|
|
r_uniform_float("exponent", 1.0);
|
|
r_uniform_float("curvature", 0.2);
|
|
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
|
|
r_shader_standard();
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline uint32_t floathash(float f) {
|
|
uint32_t bits = float_to_bits(f);
|
|
return splitmix32(&bits);
|
|
}
|
|
|
|
static void stage1_waterplants_draw(vec3 pos) {
|
|
r_state_push();
|
|
|
|
// stateless pseudo-random fuckery
|
|
int tile = floathash(pos[1] * 311.4312) & 1;
|
|
float offs = 600 * sin(2*M_PI*remainder(3214.322211333 * floathash(pos[1]), M_E));
|
|
float d = -55+50*sin(pos[1]/25.0);
|
|
r_mat_mv_push();
|
|
r_mat_mv_translate(pos[0]+offs, pos[1], d);
|
|
r_mat_mv_rotate(2 * M_PI * sin(32.234*pos[1]) + global.frames / 800.0, 0, 0, 1);
|
|
|
|
Sprite spr = { 0 };
|
|
spr.w = spr.h = 1;
|
|
spr.tex = res_texture("stage1/waterplants");
|
|
spr.tex_area.w = 0.5f;
|
|
spr.tex_area.h = 1.0f;
|
|
spr.tex_area.x = 0.5f * tile;
|
|
|
|
float a = 0.8;
|
|
float s = 160 * (1 + 2 * psin(13.12993*pos[1]));
|
|
r_mat_mv_scale(s, s, 1);
|
|
r_disable(RCAP_DEPTH_TEST);
|
|
r_cull(CULL_FRONT);
|
|
r_mat_tex_push();
|
|
r_mat_tex_scale(0.5, 1, 1);
|
|
r_mat_tex_translate(tile, 0, 0);
|
|
r_draw_sprite(&(SpriteParams) {
|
|
.sprite_ptr = &spr,
|
|
.shader_ptr = res_shader("sprite_default"),
|
|
.flip.x = floathash(pos[1] * 231544.213) & 1,
|
|
.flip.y = floathash(pos[1] * 941233.513) & 1,
|
|
.color = RGBA(0.5*a, 0.4*a, 0.5*a, 0.5*a),
|
|
});
|
|
r_mat_tex_pop();
|
|
r_mat_mv_pop();
|
|
r_state_pop();
|
|
}
|
|
|
|
static uint stage1_waterplants_pos(Stage3D *s3d, vec3 cam, float maxrange) {
|
|
vec3 origin = {0,0,0};
|
|
vec3 step = {0,150,0};
|
|
return stage3d_pos_ray_farfirst(s3d, cam, origin, step, maxrange * 0.5f, 0.0f);
|
|
}
|
|
|
|
static void stage1_snow_draw(vec3 pos) {
|
|
Stage1DrawData *draw_data = stage1_get_draw_data();
|
|
float o = draw_data->snow.opacity;
|
|
|
|
if(o < 1.0f/256.0f) {
|
|
return;
|
|
}
|
|
|
|
float d = fabsf(pos[1] - stage_3d_context.cam.pos[1]);
|
|
|
|
if(fabsf(d) < 500) {
|
|
return;
|
|
}
|
|
|
|
float x = ((d+500)*(d+500))/(5000*5000);
|
|
|
|
float h0 = floathash(pos[1]+0) / (double)UINT32_MAX;
|
|
float h1 = floathash(pos[1]+1) / (double)UINT32_MAX;
|
|
float h2 = floathash(pos[1]+2) / (double)UINT32_MAX;
|
|
|
|
float height = 1 + sawtooth(h0 + global.frames/280.0);
|
|
|
|
o *= pow(1 - 1.5 * clamp(height - 1, 0, 1), 5) * x;
|
|
|
|
if(o < 1.0f/256.0f) {
|
|
return;
|
|
}
|
|
|
|
if(o > 1) {
|
|
o = 1;
|
|
}
|
|
|
|
r_state_push();
|
|
r_shader("sprite_default");
|
|
r_disable(RCAP_DEPTH_TEST);
|
|
r_cull(CULL_BACK);
|
|
r_mat_mv_push();
|
|
r_mat_mv_translate(pos[0] + 2200 * sawtooth(h1), pos[1] + 10 * sawtooth(h2), 1200 - 1200 * height);
|
|
r_mat_mv_rotate(M_PI/2, -1, 0, 0);
|
|
r_draw_sprite(&(SpriteParams) {
|
|
.sprite_ptr = res_sprite("part/smoothdot"),
|
|
.color = RGBA(o, o, o, 0),
|
|
});
|
|
r_mat_mv_pop();
|
|
r_state_pop();
|
|
}
|
|
|
|
static uint stage1_snow_pos(Stage3D *s3d, vec3 cam, float maxrange) {
|
|
vec3 origin = {0,0,0};
|
|
vec3 step = {0,15,0};
|
|
return stage3d_pos_ray_farfirst(s3d, cam, origin, step, maxrange, 0);
|
|
}
|
|
|
|
void stage1_draw(void) {
|
|
int ppq = config_get_int(CONFIG_POSTPROCESS);
|
|
|
|
if(ppq > 0) {
|
|
r_state_push();
|
|
r_framebuffer(stage1_draw_data->water_fbpair.back);
|
|
r_clear(BUFFER_COLOR, RGBA(0, 0, 0, 0), 1);
|
|
stage1_water_render_reflections();
|
|
r_state_pop();
|
|
fbpair_swap(&stage1_draw_data->water_fbpair);
|
|
}
|
|
|
|
Stage3DSegment segs[] = {
|
|
{ stage1_horizon_draw, stage1_horizon_pos },
|
|
{ stage1_water_draw, stage1_water_pos },
|
|
{ stage1_waterplants_draw, stage1_waterplants_pos },
|
|
{ stage1_snow_draw, stage1_snow_pos },
|
|
{ stage1_smoke_draw, stage1_smoke_pos },
|
|
};
|
|
|
|
stage3d_draw(&stage_3d_context, 10000, ARRAY_SIZE(segs), segs);
|
|
}
|
|
|
|
ShaderRule stage1_bg_effects[] = {
|
|
stage1_fog,
|
|
NULL
|
|
};
|