mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-04-07 02:36:42 +00:00
247 lines
6.1 KiB
C++
247 lines
6.1 KiB
C++
// DR. ROBOTNIK'S RING RACERS
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
|
|
// Copyright (C) 2025 by Kart Krew
|
|
//
|
|
// 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 "blit_rect.hpp"
|
|
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <tcb/span.hpp>
|
|
|
|
#include "../cxxutil.hpp"
|
|
|
|
using namespace srb2;
|
|
using namespace srb2::hwr2;
|
|
using namespace srb2::rhi;
|
|
|
|
namespace
|
|
{
|
|
struct BlitVertex
|
|
{
|
|
float x = 0.f;
|
|
float y = 0.f;
|
|
float z = 0.f;
|
|
float u = 0.f;
|
|
float v = 0.f;
|
|
};
|
|
} // namespace
|
|
|
|
static const BlitVertex kVerts[] =
|
|
{{-.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 kIndices[] = {0, 1, 2, 1, 3, 2};
|
|
|
|
BlitRectPass::BlitRectPass() : BlitRectPass(BlitRectPass::BlitMode::kNearest) {}
|
|
BlitRectPass::BlitRectPass(BlitRectPass::BlitMode blit_mode) : blit_mode_(blit_mode) {}
|
|
BlitRectPass::~BlitRectPass() = default;
|
|
|
|
void BlitRectPass::draw(Rhi& rhi)
|
|
{
|
|
prepass(rhi);
|
|
transfer(rhi);
|
|
graphics(rhi);
|
|
}
|
|
|
|
void BlitRectPass::prepass(Rhi& rhi)
|
|
{
|
|
if (!program_)
|
|
{
|
|
ProgramDesc desc {};
|
|
const char* defines[1] = {"ENABLE_VA_TEXCOORD0"};
|
|
desc.defines = tcb::make_span(defines);
|
|
switch (blit_mode_)
|
|
{
|
|
case BlitRectPass::BlitMode::kNearest:
|
|
desc.name = "unshaded";
|
|
break;
|
|
case BlitRectPass::BlitMode::kSharpBilinear:
|
|
desc.name = "sharpbilinear";
|
|
break;
|
|
case BlitRectPass::BlitMode::kCrt:
|
|
desc.name = "crt";
|
|
break;
|
|
case BlitRectPass::BlitMode::kCrtSharp:
|
|
desc.name = "crtsharp";
|
|
break;
|
|
default:
|
|
std::terminate();
|
|
}
|
|
program_ = rhi.create_program(desc);
|
|
}
|
|
|
|
if (!quad_vbo_)
|
|
{
|
|
quad_vbo_ = rhi.create_buffer({sizeof(kVerts), BufferType::kVertexBuffer, BufferUsage::kImmutable});
|
|
quad_vbo_needs_upload_ = true;
|
|
}
|
|
|
|
if (!quad_ibo_)
|
|
{
|
|
quad_ibo_ = rhi.create_buffer({sizeof(kIndices), BufferType::kIndexBuffer, BufferUsage::kImmutable});
|
|
quad_ibo_needs_upload_ = true;
|
|
}
|
|
|
|
if ((blit_mode_ == BlitRectPass::BlitMode::kCrt || blit_mode_ == BlitRectPass::BlitMode::kCrtSharp) && !dot_pattern_)
|
|
{
|
|
dot_pattern_ = rhi.create_texture({
|
|
rhi::TextureFormat::kRGBA,
|
|
12,
|
|
4,
|
|
rhi::TextureWrapMode::kRepeat,
|
|
rhi::TextureWrapMode::kRepeat,
|
|
rhi::TextureFilterMode::kLinear,
|
|
rhi::TextureFilterMode::kLinear
|
|
});
|
|
dot_pattern_needs_upload_ = true;
|
|
}
|
|
}
|
|
|
|
void BlitRectPass::transfer(Rhi& rhi)
|
|
{
|
|
if (quad_vbo_needs_upload_ && quad_vbo_)
|
|
{
|
|
rhi.update_buffer(quad_vbo_, 0, tcb::as_bytes(tcb::span(kVerts)));
|
|
quad_vbo_needs_upload_ = false;
|
|
}
|
|
|
|
if (quad_ibo_needs_upload_ && quad_ibo_)
|
|
{
|
|
rhi.update_buffer(quad_ibo_, 0, tcb::as_bytes(tcb::span(kIndices)));
|
|
quad_ibo_needs_upload_ = false;
|
|
}
|
|
|
|
if (dot_pattern_needs_upload_ && dot_pattern_)
|
|
{
|
|
// Listen. I'm a *very* particular kind of lazy.
|
|
// If I'm being honest, I just don't want to have to embed a .png in the pk3s and deal with that.
|
|
static const uint8_t kDotPattern[] = {
|
|
255, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 255, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 255, 255,
|
|
0, 0, 0, 255,
|
|
255, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 255, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 255, 255,
|
|
0, 0, 0, 255,
|
|
|
|
255, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 255, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 255, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
|
|
255, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 255, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 255, 255,
|
|
0, 0, 0, 255,
|
|
255, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 255, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 255, 255,
|
|
0, 0, 0, 255,
|
|
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
255, 0, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 255, 0, 255,
|
|
0, 0, 0, 255,
|
|
0, 0, 255, 255,
|
|
0, 0, 0, 255,
|
|
};
|
|
rhi.update_texture(dot_pattern_, {0, 0, 12, 4}, PixelFormat::kRGBA8, tcb::as_bytes(tcb::span(kDotPattern)));
|
|
dot_pattern_needs_upload_ = false;
|
|
}
|
|
}
|
|
|
|
void BlitRectPass::graphics(Rhi& rhi)
|
|
{
|
|
rhi.bind_program(program_);
|
|
|
|
RasterizerStateDesc rs {};
|
|
rs.cull = CullMode::kNone;
|
|
|
|
rhi.set_rasterizer_state(rs);
|
|
rhi.bind_vertex_attrib("a_position", quad_vbo_, VertexAttributeFormat::kFloat3, offsetof(BlitVertex, x), sizeof(BlitVertex));
|
|
rhi.bind_vertex_attrib("a_texcoord0", quad_vbo_, VertexAttributeFormat::kFloat2, offsetof(BlitVertex, u), sizeof(BlitVertex));
|
|
|
|
float aspect = 1.0;
|
|
float output_aspect = 1.0;
|
|
if (output_correct_aspect_)
|
|
{
|
|
aspect = static_cast<float>(texture_width_) / static_cast<float>(texture_height_);
|
|
output_aspect = static_cast<float>(output_position_.w) / static_cast<float>(output_position_.h);
|
|
}
|
|
bool taller = aspect > output_aspect;
|
|
|
|
rhi::TextureDetails texture_details = rhi.get_texture_details(texture_);
|
|
|
|
glm::mat4 projection = glm::scale(
|
|
glm::identity<glm::mat4>(),
|
|
glm::vec3(taller ? 1.f : 1.f / output_aspect, taller ? -1.f / (1.f / output_aspect) : -1.f, 1.f)
|
|
);
|
|
glm::mat4 modelview = glm::scale(
|
|
glm::identity<glm::mat4>(),
|
|
glm::vec3(taller ? 2.f : 2.f * aspect, taller ? 2.f * (1.f / aspect) : 2.f, 1.f)
|
|
);;
|
|
glm::mat3 texcoord0_transform = glm::mat3(
|
|
glm::vec3(1.f, 0.f, 0.f),
|
|
glm::vec3(0.f, output_flip_ ? -1.f : 1.f, 0.f),
|
|
glm::vec3(0.f, output_flip_ ? 1.f : 0.f, 1.f)
|
|
);
|
|
glm::vec2 sampler0_size = glm::vec2(texture_details.width, texture_details.height);
|
|
|
|
rhi.set_uniform("u_projection", projection);
|
|
rhi.set_uniform("u_modelview", modelview);
|
|
rhi.set_uniform("u_texcoord0_transform", texcoord0_transform);
|
|
|
|
switch (blit_mode_)
|
|
{
|
|
case BlitRectPass::BlitMode::kNearest:
|
|
break;
|
|
default:
|
|
rhi.set_uniform("u_sampler0_size", sampler0_size);
|
|
break;
|
|
}
|
|
rhi.set_sampler("s_sampler0", 0, texture_);
|
|
switch (blit_mode_)
|
|
{
|
|
case BlitRectPass::BlitMode::kCrt:
|
|
rhi.set_sampler("s_sampler1", 1, dot_pattern_);
|
|
break;
|
|
case BlitRectPass::BlitMode::kCrtSharp:
|
|
rhi.set_sampler("s_sampler1", 1, dot_pattern_);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
rhi.set_viewport(output_position_);
|
|
rhi.bind_index_buffer(quad_ibo_);
|
|
rhi.draw_indexed(6, 0);
|
|
}
|