// 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 "imgui_renderer.hpp" #include #include #include "../v_video.h" using namespace srb2; using namespace srb2::hwr2; using namespace srb2::rhi; ImguiRenderer::ImguiRenderer() { } ImguiRenderer::~ImguiRenderer() = default; void ImguiRenderer::render(Rhi& rhi) { if (!program_) { const char* defines[2] = { "ENABLE_VA_TEXCOORD0", "ENABLE_VA_COLOR" }; ProgramDesc desc; desc.name = "unshaded"; desc.defines = tcb::make_span(defines); program_ = rhi.create_program(desc); } ImGuiIO& io = ImGui::GetIO(); if (!font_atlas_) { unsigned char* pixels; int width; int height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); uint32_t uwidth = static_cast(width); uint32_t uheight = static_cast(height); font_atlas_ = rhi.create_texture({ TextureFormat::kRGBA, uwidth, uheight, TextureWrapMode::kRepeat, TextureWrapMode::kRepeat }); io.Fonts->SetTexID(font_atlas_); } if (!default_tex_) { uint32_t pixel = 0xFFFFFFFF; default_tex_ = rhi.create_texture({ TextureFormat::kRGBA, 1, 1, TextureWrapMode::kRepeat, TextureWrapMode::kRepeat }); rhi.update_texture(default_tex_, {0, 0, 1, 1}, rhi::PixelFormat::kRGBA8, tcb::as_bytes(tcb::span(&pixel, 1))); } ImGui::Render(); ImDrawData* data = ImGui::GetDrawData(); ImVec2 clip_off(data->DisplayPos); ImVec2 clip_scale(data->FramebufferScale); tcb::span draw_lists = tcb::span(data->CmdLists, data->CmdListsCount); for (auto list : draw_lists) { Handle vbo = rhi.create_buffer( {static_cast(list->VtxBuffer.size_in_bytes()), BufferType::kVertexBuffer, BufferUsage::kImmutable} ); Handle ibo = rhi.create_buffer( {static_cast(list->IdxBuffer.size_in_bytes()), BufferType::kIndexBuffer, BufferUsage::kImmutable} ); DrawList hwr2_list; hwr2_list.list = list; hwr2_list.vbo = vbo; hwr2_list.ibo = ibo; for (auto& cmd : list->CmdBuffer) { if (cmd.UserCallback) { cmd.UserCallback(list, &cmd); continue; } ImVec2 clip_min((cmd.ClipRect.x - clip_off.x) * clip_scale.x, (cmd.ClipRect.y - clip_off.y) * clip_scale.y); ImVec2 clip_max((cmd.ClipRect.z - clip_off.x) * clip_scale.x, (cmd.ClipRect.w - clip_off.y) * clip_scale.y); if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) { continue; } DrawCmd draw_cmd; ImTextureID tex_id = cmd.GetTexID(); if (tex_id == 0) { draw_cmd.tex = default_tex_; } else { draw_cmd.tex = tex_id; } draw_cmd.v_offset = cmd.VtxOffset; draw_cmd.i_offset = cmd.IdxOffset; draw_cmd.elems = cmd.ElemCount; draw_cmd.clip = { static_cast(clip_min.x), static_cast((data->DisplaySize.y * data->FramebufferScale.y) - clip_max.y), static_cast(clip_max.x - clip_min.x), static_cast(clip_max.y - clip_min.y)}; hwr2_list.cmds.push_back(std::move(draw_cmd)); } draw_lists_.push_back(std::move(hwr2_list)); } { unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); rhi.update_texture( font_atlas_, {0, 0, static_cast(width), static_cast(height)}, rhi::PixelFormat::kRGBA8, tcb::as_bytes(tcb::span(pixels, static_cast(width * height * 4))) ); } for (auto& draw_list : draw_lists_) { Handle vbo = draw_list.vbo; Handle ibo = draw_list.ibo; ImDrawList* im_list = static_cast(draw_list.list); for (auto& vtx : im_list->VtxBuffer) { vtx.pos.z = 0.f; vtx.colf[0] = ((vtx.col & 0xFF) >> 0) / 255.f; vtx.colf[1] = ((vtx.col & 0xFF00) >> 8) / 255.f; vtx.colf[2] = ((vtx.col & 0xFF0000) >> 16) / 255.f; vtx.colf[3] = ((vtx.col & 0xFF000000) >> 24) / 255.f; } tcb::span vert_span = tcb::span(im_list->VtxBuffer.Data, im_list->VtxBuffer.size()); rhi.update_buffer(vbo, 0, tcb::as_bytes(vert_span)); tcb::span index_span = tcb::span(im_list->IdxBuffer.Data, im_list->IdxBuffer.size()); rhi.update_buffer(ibo, 0, tcb::as_bytes(index_span)); for (auto& draw_cmd : draw_list.cmds) { draw_cmd.vbo = vbo; draw_cmd.ibo = ibo; draw_cmd.tex = draw_cmd.tex; } } rhi.bind_program(program_); RasterizerStateDesc rs; rs.cull = CullMode::kNone; rs.winding = FaceWinding::kCounterClockwise; rs.depth_test = true; rs.depth_func = CompareFunc::kAlways; rs.blend_enabled = true; rs.blend_source_factor_color = BlendFactor::kSourceAlpha; rs.blend_dest_factor_color = BlendFactor::kOneMinusSourceAlpha; rs.blend_color_function = BlendFunction::kAdd; rs.blend_source_factor_alpha = BlendFactor::kOne; rs.blend_dest_factor_alpha = BlendFactor::kOneMinusSourceAlpha; rs.blend_alpha_function = BlendFunction::kAdd; for (auto& draw_list : draw_lists_) { glm::mat4 projection = glm::mat4( glm::vec4(2.f / vid.realwidth, 0.f, 0.f, 0.f), glm::vec4(0.f, 2.f / vid.realheight, 0.f, 0.f), glm::vec4(0.f, 0.f, 1.f, 0.f), glm::vec4(-1.f, 1.f, 0.f, 1.f) ); glm::mat4 modelview = glm::mat4( glm::vec4(1.f, 0.f, 0.f, 0.f), glm::vec4(0.f, -1.f, 0.f, 0.f), glm::vec4(0.f, 0.f, 1.f, 0.f), glm::vec4(0.f, 0, 0.f, 1.f) ); glm::mat3 texcoord0_transform = glm::mat3( glm::vec3(1.f, 0.f, 0.f), glm::vec3(0.f, 1.f, 0.f), glm::vec3(0.f, 0.f, 1.f) ); rhi.set_uniform("u_projection", projection); rhi.set_uniform("u_modelview", modelview); rhi.set_uniform("u_texcoord0_transform", texcoord0_transform); for (auto& cmd : draw_list.cmds) { rs.scissor_test = true; rs.scissor = cmd.clip; rhi.set_rasterizer_state(rs); rhi.bind_vertex_attrib("a_position", cmd.vbo, VertexAttributeFormat::kFloat3, offsetof(ImDrawVert, pos), sizeof(ImDrawVert)); rhi.bind_vertex_attrib("a_texcoord0", cmd.vbo, VertexAttributeFormat::kFloat2, offsetof(ImDrawVert, uv), sizeof(ImDrawVert)); rhi.bind_vertex_attrib("a_color", cmd.vbo, VertexAttributeFormat::kFloat4, offsetof(ImDrawVert, colf), sizeof(ImDrawVert)); rhi.set_sampler("s_sampler0", 0, cmd.tex); rhi.bind_index_buffer(draw_list.ibo); rhi.draw_indexed(cmd.elems, cmd.i_offset); } } for (auto& list : draw_lists_) { rhi.destroy_buffer(list.vbo); rhi.destroy_buffer(list.ibo); } draw_lists_.clear(); }