// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2023 by Ronald "Eidolon" Kinard // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- #include "pass_postprocess.hpp" #include #include #include #include #include "../f_finale.h" #include "../w_wad.h" using namespace srb2; using namespace srb2::hwr2; using namespace srb2::rhi; namespace { struct PostprocessVertex { float x; float y; float z; float u; float v; }; static const PostprocessVertex kPostprocessVerts[] = {{-.5f, -.5f, 0.f, 0.f, 0.f}, {.5f, -.5f, 0.f, 1.f, 0.f}, {-.5f, .5f, 0.f, 0.f, 1.f}, {.5f, .5f, 0.f, 1.f, 1.f}}; static const uint16_t kPostprocessIndices[] = {0, 1, 2, 1, 3, 2}; } // namespace static const PipelineDesc kWipePipelineDesc = { PipelineProgram::kPostprocessWipe, {{{sizeof(PostprocessVertex)}}, { {VertexAttributeName::kPosition, 0, 0}, {VertexAttributeName::kTexCoord0, 0, 12}, }}, {{{{UniformName::kProjection, UniformName::kWipeColorizeMode, UniformName::kWipeEncoreSwizzle}}}}, {{SamplerName::kSampler0, SamplerName::kSampler1, SamplerName::kSampler2}}, std::nullopt, {PixelFormat::kRGBA8, std::nullopt, {true, true, true, true}}, PrimitiveType::kTriangles, CullMode::kNone, FaceWinding::kCounterClockwise, {0.f, 0.f, 0.f, 1.f}}; PostprocessWipePass::PostprocessWipePass() { } PostprocessWipePass::~PostprocessWipePass() = default; void PostprocessWipePass::prepass(Rhi& rhi) { if (!render_pass_) { render_pass_ = rhi.create_render_pass( { false, AttachmentLoadOp::kLoad, AttachmentStoreOp::kStore, AttachmentLoadOp::kDontCare, AttachmentStoreOp::kDontCare, AttachmentLoadOp::kDontCare, AttachmentStoreOp::kDontCare } ); } if (!pipeline_) { pipeline_ = rhi.create_pipeline(kWipePipelineDesc); } if (!vbo_) { vbo_ = rhi.create_buffer({sizeof(PostprocessVertex) * 4, BufferType::kVertexBuffer, BufferUsage::kImmutable}); upload_vbo_ = true; } if (!ibo_) { ibo_ = rhi.create_buffer({2 * 6, BufferType::kIndexBuffer, BufferUsage::kImmutable}); upload_ibo_ = true; } uint32_t wipe_mode = g_wipemode; uint32_t wipe_type = g_wipetype; uint32_t wipe_frame = g_wipeframe; bool wipe_reverse = g_wipereverse; wipe_color_mode_ = 0; // TODO 0 = modulate, 1 = invert, 2 = MD to black, 3 = MD to white if (F_WipeIsToBlack(wipe_mode)) { wipe_color_mode_ = 2; } else if (F_WipeIsToWhite(wipe_mode)) { wipe_color_mode_ = 3; } else if (F_WipeIsToInvert(wipe_mode)) { wipe_color_mode_ = 1; } else if (F_WipeIsCrossfade(wipe_mode)) { wipe_color_mode_ = 0; } wipe_swizzle_ = g_wipeencorewiggle; if (wipe_type >= 100 || wipe_frame >= 100) { return; } std::string lumpname = fmt::format(FMT_STRING("FADE{:02d}{:02d}"), wipe_type, wipe_frame); lumpnum_t mask_lump = W_CheckNumForName(lumpname.c_str()); if (mask_lump == LUMPERROR) { return; } std::size_t mask_lump_size = W_LumpLength(mask_lump); switch (mask_lump_size) { case 256000: mask_w_ = 640; mask_h_ = 400; break; case 64000: mask_w_ = 320; mask_h_ = 200; break; case 16000: mask_w_ = 160; mask_h_ = 100; break; case 4000: mask_w_ = 80; mask_h_ = 50; break; default: return; } mask_data_.clear(); mask_data_.resize(mask_lump_size, 0); W_ReadLump(mask_lump, mask_data_.data()); if (wipe_reverse) { for (auto& b : mask_data_) { b = 32 - b; } } wipe_tex_ = rhi.create_texture({TextureFormat::kLuminance, mask_w_, mask_h_}); } void PostprocessWipePass::transfer(Rhi& rhi, Handle ctx) { if (wipe_tex_ == kNullHandle) { return; } if (start_ == kNullHandle || end_ == kNullHandle) { return; } if (upload_vbo_) { rhi.update_buffer(ctx, vbo_, 0, tcb::as_bytes(tcb::span(kPostprocessVerts))); upload_vbo_ = false; } if (upload_ibo_) { rhi.update_buffer(ctx, ibo_, 0, tcb::as_bytes(tcb::span(kPostprocessIndices))); upload_ibo_ = false; } tcb::span data = tcb::as_bytes(tcb::span(mask_data_)); rhi.update_texture(ctx, wipe_tex_, {0, 0, mask_w_, mask_h_}, PixelFormat::kR8, data); UniformVariant uniforms[] = { glm::scale(glm::identity(), glm::vec3(2.f, 2.f, 1.f)), static_cast(wipe_color_mode_), static_cast(wipe_swizzle_) }; us_ = rhi.create_uniform_set(ctx, {tcb::span(uniforms)}); VertexAttributeBufferBinding vbos[] = {{0, vbo_}}; TextureBinding tx[] = { {SamplerName::kSampler0, start_}, {SamplerName::kSampler1, end_}, {SamplerName::kSampler2, wipe_tex_}}; bs_ = rhi.create_binding_set(ctx, pipeline_, {vbos, tx}); } void PostprocessWipePass::graphics(Rhi& rhi, Handle ctx) { if (wipe_tex_ == kNullHandle) { return; } if (target_) { rhi.begin_render_pass(ctx, {render_pass_, target_, std::nullopt, {0, 0, 0, 1}}); } else { rhi.begin_default_render_pass(ctx, false); } rhi.bind_pipeline(ctx, pipeline_); if (target_) { rhi.set_viewport(ctx, {0, 0, target_w_, target_h_}); } rhi.bind_uniform_set(ctx, 0, us_); rhi.bind_binding_set(ctx, bs_); rhi.bind_index_buffer(ctx, ibo_); rhi.draw_indexed(ctx, 6, 0); rhi.end_render_pass(ctx); } void PostprocessWipePass::postpass(Rhi& rhi) { if (wipe_tex_) { rhi.destroy_texture(wipe_tex_); wipe_tex_ = kNullHandle; } mask_data_.clear(); }