cnq3/code/renderer/srp_im3d.cpp

373 lines
11 KiB
C++

/*
===========================================================================
Copyright (C) 2024 Gian 'myT' Schellenbaum
This file is part of Challenge Quake 3 (CNQ3).
Challenge Quake 3 is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Challenge Quake 3 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
===========================================================================
*/
// Shared Rendering Pipeline - Im3d integration
#include "srp_local.h"
#include "../im3d/im3d.h"
#include "../im3d/im3d_math.h"
#include "../client/cl_imgui.h"
#define MAX_VERTEX_COUNT 65536 // we use 16-bit indices
#define MAX_INDEX_COUNT ((MAX_VERTEX_COUNT / 4) * 6)
#pragma pack(push, 4)
struct Im3DVertexRC_CRP
{
vec2_t viewport;
uint32_t vertexBufferIndex;
uint32_t vertexOffset;
};
struct Im3DVertexRC_GRP
{
matrix4x4_t viewMatrix;
matrix4x4_t projectionMatrix;
vec2_t viewport;
uint32_t vertexOffset;
};
#pragma pack(pop)
static void SetupPipeline(
Im3DVertexRC_CRP& crp, Im3DVertexRC_GRP& grp, bool ddhi, HPipeline pipeline,
HRootSignature rootSignature, HDescriptorTable descriptorTable, uint32_t vertexOffset)
{
CmdBindPipeline(pipeline);
if(ddhi)
{
crp.vertexOffset = vertexOffset;
CmdSetGraphicsRootConstants(0, sizeof(crp), &crp);
}
else
{
grp.vertexOffset = vertexOffset;
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &grp);
}
if(!ddhi)
{
CmdBindDescriptorTable(rootSignature, descriptorTable);
}
}
void Im3D::Init(bool ddhi_, const ShaderByteCode shaders[Im3D::Shader::Count], TextureFormat::Id rtFormat)
{
ddhi = ddhi_;
if(ddhi)
{
rootSignature = RHI_MAKE_NULL_HANDLE();
}
else
{
RootSignatureDesc desc("Im3d");
desc.shortLifeTime = true;
desc.AddRange(DescriptorType::Buffer, 0, 1);
desc.constants[ShaderStage::Vertex].byteCount = sizeof(Im3DVertexRC_GRP);
rootSignature = CreateRootSignature(desc);
}
if(ddhi)
{
for(uint32_t i = 0; i < ARRAY_LEN(descriptorTables); i++)
{
descriptorTables[i] = RHI_MAKE_NULL_HANDLE();
}
}
else
{
for(uint32_t i = 0; i < ARRAY_LEN(descriptorTables); i++)
{
DescriptorTableDesc desc("Im3d", rootSignature);
desc.shortLifeTime = true;
descriptorTables[i] = CreateDescriptorTable(desc);
}
}
{
GraphicsPipelineDesc desc("Im3d point", rootSignature);
desc.shortLifeTime = true;
desc.vertexShader = shaders[Shader::PointVS];
desc.pixelShader = shaders[Shader::PointPS];
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.depthStencil.DisableDepth();
desc.AddRenderTarget(GLS_BLEND_STD_ALPHA, rtFormat);
pointPipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("Im3d line", rootSignature);
desc.shortLifeTime = true;
desc.vertexShader = shaders[Shader::LineVS];
desc.pixelShader = shaders[Shader::LinePS];
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.depthStencil.DisableDepth();
desc.AddRenderTarget(GLS_BLEND_STD_ALPHA, rtFormat);
linePipeline = CreateGraphicsPipeline(desc);
}
{
GraphicsPipelineDesc desc("Im3d triangle", rootSignature);
desc.shortLifeTime = true;
desc.vertexShader = shaders[Shader::TriangleVS];
desc.pixelShader = shaders[Shader::TrianglePS];
desc.rasterizer.cullMode = CT_TWO_SIDED;
desc.depthStencil.DisableDepth();
desc.AddRenderTarget(GLS_BLEND_STD_ALPHA, rtFormat);
trianglePipeline = CreateGraphicsPipeline(desc);
}
{
uint32_t byteCount = sizeof(Im3d::VertexData) * MAX_VERTEX_COUNT;
BufferDesc desc("", byteCount, ResourceStates::VertexShaderAccessBit);
desc.shortLifeTime = true;
desc.structureByteCount = sizeof(Im3d::VertexData);
desc.memoryUsage = MemoryUsage::Upload;
desc.name = "Im3d vertex #1";
vertexBuffers[0] = CreateBuffer(desc);
desc.name = "Im3d vertex #2";
vertexBuffers[1] = CreateBuffer(desc);
}
{
uint32_t byteCount = sizeof(uint16_t) * MAX_INDEX_COUNT;
BufferDesc desc("Im3d index", byteCount, ResourceStates::IndexBufferBit);
desc.shortLifeTime = true;
indexBuffer = CreateBuffer(desc);
uint16_t* indices = (uint16_t*)BeginBufferUpload(indexBuffer);
for(uint32_t i = 0, v = 0; i < MAX_INDEX_COUNT; i += 6, v += 4)
{
indices[i + 0] = v + 0;
indices[i + 1] = v + 1;
indices[i + 2] = v + 2;
indices[i + 3] = v + 2;
indices[i + 4] = v + 3;
indices[i + 5] = v + 0;
}
EndBufferUpload(indexBuffer);
}
if(!ddhi)
{
for(uint32_t i = 0; i < ARRAY_LEN(descriptorTables); i++)
{
DescriptorTableUpdate update;
update.SetBuffers(1, &vertexBuffers[i]);
UpdateDescriptorTable(descriptorTables[i], update);
}
}
}
void Im3D::BeginFrame()
{
const int currTime = Sys_Milliseconds();
const int timeDelta = prevTime == INT_MIN ? 0 : currTime - prevTime;
prevTime = currTime;
int x, y;
Sys_GetCursorPosition(&x, &y);
R_ComputeCursorPosition(&x, &y);
Im3d::AppData& ad = Im3d::GetAppData();
ad.m_deltaTime = (float)timeDelta / 1000.0f;
ad.m_viewportSize = Im3d::Vec2((float)glConfig.vidWidth, (float)glConfig.vidHeight);
ad.m_viewOrigin = Im3d::Vec3(tr.rtRefdef.vieworg[0], tr.rtRefdef.vieworg[1], tr.rtRefdef.vieworg[2]);
ad.m_viewDirection = Im3d::Vec3(tr.rtRefdef.viewaxis[0][0], tr.rtRefdef.viewaxis[0][1], tr.rtRefdef.viewaxis[0][2]);
ad.m_worldUp = Im3d::Vec3(0.0f, 0.0f, 1.0f);
ad.m_projScaleY = 2.0f * tanf(DEG2RAD(tr.rtRefdef.fov_y) * 0.5f);
Im3d::Mat4 camWorld = LookAt(ad.m_viewOrigin, ad.m_viewOrigin + ad.m_viewDirection, ad.m_worldUp);
Im3d::Vec2 cursorNDC;
cursorNDC.x = ((float)x / (float)glConfig.vidWidth ) * -2.0f + 1.0f;
cursorNDC.y = ((float)y / (float)glConfig.vidHeight) * -2.0f + 1.0f;
Im3d::Vec3 rayOrigin, rayDirection;
rayOrigin = ad.m_viewOrigin;
rayDirection.x = tanf(0.5f * DEG2RAD(tr.rtRefdef.fov_x)) * cursorNDC.x;
rayDirection.y = tanf(0.5f * DEG2RAD(tr.rtRefdef.fov_y)) * cursorNDC.y;
rayDirection.z = 1.0f;
rayDirection = camWorld * Im3d::Vec4(Im3d::Normalize(rayDirection), 0.0f);
ad.m_cursorRayOrigin = rayOrigin;
ad.m_cursorRayDirection = rayDirection;
if(r_debugInput->integer == 0 || ImGui::GetIO().WantCaptureMouse)
{
ad.m_cursorRayOrigin = Im3d::Vec3(0.0f, 0.0f, 0.0f);
ad.m_cursorRayDirection = Im3d::Vec3(0.0f, 0.0f, 0.0f);
}
Im3d::NewFrame();
}
void Im3D::Draw(const drawSceneViewCommand_t& cmd, HTexture colorTarget, HTexture depthTarget)
{
if(cmd.viewParms.isPortal || !IsViewportFullscreen(cmd.viewParms))
{
return;
}
#if 0 // cpm3 test/demo code
{
static Im3d::Vec3 translation(700, 50, -50);
static Im3d::Mat3 rotation(1.0f);
static Im3d::Vec3 scale(1.0f);
Im3d::PushMatrix(Im3d::Mat4(translation, rotation, scale));
if(ShouldDrawGizmos())
{
Im3d::Gizmo("Test", translation, rotation, scale);
}
Im3d::DrawConeFilled(Im3d::Vec3(0, 0, 0), Im3d::Vec3(0, 0, 1), 32.0f, 8.0f, 16);
Im3d::PopMatrix();
Im3d::DrawPoint(Im3d::Vec3(700, 0, 0), 16.0f, Im3d::Color(1, 0, 0, 1));
Im3d::DrawPoint(Im3d::Vec3(725, 0, 0), 16.0f, Im3d::Color(0, 1, 0, 1));
Im3d::DrawPoint(Im3d::Vec3(750, 0, 0), 16.0f, Im3d::Color(0, 0, 1, 1));
Im3d::DrawLine(Im3d::Vec3(700, 0, -100), Im3d::Vec3(750, 0, -100), 4.0f, Im3d::Color(1, 0, 0, 1));
Im3d::DrawLine(Im3d::Vec3(700, 0, -110), Im3d::Vec3(750, 0, -110), 4.0f, Im3d::Color(0, 1, 0, 1));
Im3d::DrawLine(Im3d::Vec3(700, 0, -120), Im3d::Vec3(750, 0, -120), 4.0f, Im3d::Color(0, 0, 1, 1));
Im3d::SetColor(0, 1, 0, 1);
Im3d::SetSize(4.0f);
Im3d::DrawArrow(Im3d::Vec3(700, 0, -150), Im3d::Vec3(750, 0, -150), 10.0f, 10.0f);
Im3d::DrawConeFilled(Im3d::Vec3(725, 0, -200), Im3d::Vec3(0, 0, 1), 32.0f, 8.0f, 16);
}
#endif
Im3d::EndFrame();
if(!ShouldDrawShapes())
{
return;
}
const Im3d::U32 drawListCount = Im3d::GetDrawListCount();
if(drawListCount == 0)
{
return;
}
SCOPED_DEBUG_LABEL("Im3d", 1.0f, 1.0f, 1.0f);
CmdSetViewportAndScissor(0, 0, glConfig.vidWidth, glConfig.vidHeight);
CmdBeginBarrier();
CmdTextureBarrier(colorTarget, ResourceStates::RenderTargetBit);
CmdEndBarrier();
CmdBindRenderTargets(1, &colorTarget, NULL);
CmdBindIndexBuffer(indexBuffer, IndexType::UInt16, 0);
const uint32_t frameIndex = GetFrameIndex();
const HBuffer vertexBuffer = vertexBuffers[frameIndex];
Im3DVertexRC_CRP vertexRC_CRP;
Im3DVertexRC_GRP vertexRC_GRP;
if(ddhi)
{
vertexRC_CRP = {};
vertexRC_CRP.vertexBufferIndex = GetBufferIndexSRV(vertexBuffer);
vertexRC_CRP.viewport[0] = (float)glConfig.vidWidth;
vertexRC_CRP.viewport[1] = (float)glConfig.vidHeight;
CmdSetGraphicsRootConstants(0, sizeof(vertexRC_CRP), &vertexRC_CRP);
}
else
{
vertexRC_GRP = {};
vertexRC_GRP.viewport[0] = (float)glConfig.vidWidth;
vertexRC_GRP.viewport[1] = (float)glConfig.vidHeight;
memcpy(vertexRC_GRP.viewMatrix, cmd.viewParms.world.modelMatrix, sizeof(vertexRC_GRP.viewMatrix));
memcpy(vertexRC_GRP.projectionMatrix, cmd.viewParms.projectionMatrix, sizeof(vertexRC_GRP.projectionMatrix));
CmdSetRootConstants(rootSignature, ShaderStage::Vertex, &vertexRC_GRP);
}
Im3d::VertexData* vertices = (Im3d::VertexData*)MapBuffer(vertexBuffer);
uint32_t vertexOffset = 0;
for(Im3d::U32 i = 0; i < drawListCount; i++)
{
const Im3d::DrawList& drawList = Im3d::GetDrawLists()[i];
switch(drawList.m_primType)
{
case Im3d::DrawPrimitive_Triangles:
{
const uint32_t pointCount = (uint32_t)drawList.m_vertexCount;
const uint32_t indexCount = pointCount;
Q_assert(pointCount % 3 == 0);
memcpy(vertices, drawList.m_vertexData, (size_t)pointCount * sizeof(Im3d::VertexData));
SetupPipeline(vertexRC_CRP, vertexRC_GRP, ddhi, trianglePipeline, rootSignature, descriptorTables[frameIndex], vertexOffset);
CmdDraw(indexCount, 0);
vertexOffset += pointCount;
vertices += pointCount;
break;
}
case Im3d::DrawPrimitive_Lines:
{
const uint32_t pointCount = (uint32_t)drawList.m_vertexCount;
const uint32_t indexCount = pointCount * 3;
Q_assert(pointCount % 2 == 0);
memcpy(vertices, drawList.m_vertexData, (size_t)pointCount * sizeof(Im3d::VertexData));
SetupPipeline(vertexRC_CRP, vertexRC_GRP, ddhi, linePipeline, rootSignature, descriptorTables[frameIndex], vertexOffset);
CmdDrawIndexed(indexCount, 0, 0);
vertexOffset += pointCount;
vertices += pointCount;
break;
}
case Im3d::DrawPrimitive_Points:
{
const uint32_t pointCount = (uint32_t)drawList.m_vertexCount;
const uint32_t indexCount = pointCount * 6;
memcpy(vertices, drawList.m_vertexData, (size_t)pointCount * sizeof(Im3d::VertexData));
SetupPipeline(vertexRC_CRP, vertexRC_GRP, ddhi, pointPipeline, rootSignature, descriptorTables[frameIndex], vertexOffset);
CmdDrawIndexed(indexCount, 0, 0);
vertexOffset += pointCount;
vertices += pointCount;
break;
}
default:
Q_assert(!"Invalid primitive type");
break;
}
}
UnmapBuffer(vertexBuffer);
}
void Im3D::DrawGUI()
{
if(ImGui::IsKeyDown(ImGuiMod_Shift) && ImGui::IsKeyPressed(ImGuiKey_G, false))
{
drawGizmos = !drawGizmos;
}
GUI_AddMainMenuItem(GUI_MainMenu::Im3D, "Enable", "", &drawIm3d);
GUI_AddMainMenuItem(GUI_MainMenu::Im3D, "Draw Gizmos", "Shift+G", &drawGizmos);
}
bool Im3D::ShouldDrawShapes()
{
return drawIm3d;
}
bool Im3D::ShouldDrawGizmos()
{
return drawIm3d && drawGizmos && r_debugInput->integer != 0;
}