taisei/src/stages/stage1/draw.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
};