diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 54799604d..19365d2ca 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -69,6 +69,9 @@ list(APPEND CLDLL_SOURCES # render source files file(GLOB RENDER_SOURCES "render/*.cpp") +# render visualizer source files +file(GLOB RENDER_VISUALIZER_SOURCES "render/visualizer/*.cpp") + # entity wrappers source files file(GLOB ENTITIES_SOURCES "entities/*.cpp") @@ -93,6 +96,7 @@ if(NOT ENABLE_VGUI_COMPATIBILITY) endif() list(APPEND CLDLL_SOURCES ${RENDER_SOURCES}) +list(APPEND CLDLL_SOURCES ${RENDER_VISUALIZER_SOURCES}) list(APPEND CLDLL_SOURCES ${ENTITIES_SOURCES}) list(APPEND CLDLL_SOURCES ${GAME_EVENTS_SOURCES}) list(APPEND CLDLL_SOURCES ${WEAPONS_SHARED_SOURCES}) diff --git a/client/render/gl_local.h b/client/render/gl_local.h index 6acd17150..f50de46a9 100644 --- a/client/render/gl_local.h +++ b/client/render/gl_local.h @@ -36,6 +36,7 @@ GNU General Public License for more details. #include "vector.h" #include #include "material.h" +#include "debug_visualizer.h" #define ACTUAL_GL_VERSION 30.0f @@ -627,6 +628,7 @@ typedef struct // cull info Vector modelorg; // relative to viewpoint + CDebugVisualizer debugVisualizer; } ref_globals_t; typedef struct diff --git a/client/render/gl_rmisc.cpp b/client/render/gl_rmisc.cpp index 536b4089d..671e6a8fd 100644 --- a/client/render/gl_rmisc.cpp +++ b/client/render/gl_rmisc.cpp @@ -28,6 +28,7 @@ GNU General Public License for more details. #include "gl_debug.h" #include "gl_unit_cube.h" #include "r_weather.h" +#include "visualizer/debug_visualizer.h" #define DEFAULT_SMOOTHNESS 0.0f #define FILTER_SIZE 2 @@ -915,6 +916,7 @@ void R_VidInit( void ) glState.height = RENDER_GET_PARM( PARM_SCREEN_HEIGHT, 0 ); COpenGLUnitCube::GetInstance().Initialize(); + CDebugVisualizer::GetInstance().Initialize(); R_InitCommonTextures(); R_InitCubemapShaders(); diff --git a/client/render/visualizer/debug_visualizer.cpp b/client/render/visualizer/debug_visualizer.cpp new file mode 100644 index 000000000..b434c4d2f --- /dev/null +++ b/client/render/visualizer/debug_visualizer.cpp @@ -0,0 +1,166 @@ +#include "gl_local.h" +#include "debug_visualizer.h" + +CDebugVisualizer g_DebugVisualizer; + +CDebugVisualizer& CDebugVisualizer::GetInstance() +{ + return g_DebugVisualizer; +} + +void CDebugVisualizer::Initialize() +{ + if (m_bInitialized) + return; + + // reserve memory for line buffers + m_noDepthLines.reserve(1024); + m_depthLines.reserve(1024); + + // load debug shader + word shaderHandle = GL_FindShader("common/debug_draw", "common/debug_draw", "common/debug_draw"); + m_DebugShader.SetShader(shaderHandle); + + // create VAO and VBO + pglGenVertexArrays(1, &m_iVAO); + pglGenBuffersARB(1, &m_iVBO); + + // fill buffer + pglBindBufferARB(GL_ARRAY_BUFFER_ARB, m_iVBO); + pglBufferDataARB(GL_ARRAY_BUFFER_ARB, MAX_DEBUG_VBO_SIZE * sizeof(SLineVertex), nullptr, GL_DYNAMIC_DRAW_ARB); + + // link vertex attributes + pglBindVertexArray(m_iVAO); + + pglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION); + pglVertexAttribPointerARB(ATTR_INDEX_POSITION, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0); + + pglEnableVertexAttribArrayARB(/*ATTR_INDEX_TANGENT*/ATTR_INDEX_LIGHT_COLOR); + pglVertexAttribPointerARB(/*ATTR_INDEX_TANGENT*/ATTR_INDEX_LIGHT_COLOR, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float))); + + pglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + pglBindVertexArray(0); + + m_bInitialized = true; +} + +void CDebugVisualizer::Shutdown() +{ + // Release ? TODO... +} + +void CDebugVisualizer::Render() +{ + GL_BindShader(m_DebugShader.GetShader()); + + // Render lines with depth buffer + RenderLines(m_depthLines, true); + + // ... and without + RenderLines(m_noDepthLines, false); +} + +void CDebugVisualizer::RenderLines(const std::vector& lines, bool isDepthEnabled) +{ + if (lines.empty()) + return; + + if (isDepthEnabled) + pglEnable(GL_DEPTH_TEST); + else + pglDisable(GL_DEPTH_TEST); + + // bind vertex buffer + pglBindVertexArray(m_iVAO); + pglBindBufferARB(GL_ARRAY_BUFFER_ARB, m_iVBO); + + size_t offset = 0; + while (offset < lines.size()) + { + const size_t kLineVertexLimit = MAX_DEBUG_VBO_SIZE; + + size_t drawCount = std::min(kLineVertexLimit, lines.size() - offset); + + // Update vertex buffer + //pglBufferDataARB(GL_ARRAY_BUFFER_ARB, drawCount * sizeof(SLineVertex), lines.data() + offset, GL_DYNAMIC_DRAW_ARB); + pglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, drawCount * sizeof(SLineVertex), lines.data() + offset); + pglDrawArrays(GL_LINES, 0, lines.size()); + + offset += drawCount; + } + + // TODO: return depth back +} + +void CDebugVisualizer::DrawLine(const Vector& start, const Vector& end, Vector color, float lifespan, bool depthTest) +{ + SLineVertex vertexA = { start, color }; + SLineVertex vertexB = { end, color }; + + std::vector& lines = depthTest ? m_depthLines : m_noDepthLines; + lines.push_back(vertexA); + lines.push_back(vertexB); +} + +void CDebugVisualizer::DrawAABB(const Vector &mins, const Vector &maxs, Vector color, float lifespan, bool depthTest) +{ + Vector from = Vector(mins.x, mins.y, mins.z); + Vector to = Vector(maxs.x, maxs.y, maxs.z); + + Vector halfExtents = (to - from) * 0.5f; + Vector center = (to + from) * 0.5f; + + Vector edgecoord(1.f, 1.f, 1.f), pa, pb; + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 3; j++) + { + pa = Vector(edgecoord[0] * halfExtents[0], edgecoord[1] * halfExtents[1], + edgecoord[2] * halfExtents[2]); + pa += center; + + int othercoord = j % 3; + edgecoord[othercoord] *= -1.f; + pb = Vector(edgecoord[0] * halfExtents[0], edgecoord[1] * halfExtents[1], + edgecoord[2] * halfExtents[2]); + pb += center; + + DrawLine(pa, pb, color, lifespan, depthTest); + } + + edgecoord = Vector(-1.f, -1.f, -1.f); + if (i < 3) + edgecoord[i] *= -1.f; + } +} + +void CDebugVisualizer::DrawSphere(const Vector& center, float radius, Vector color, float lifespan, bool depthTest) +{ + std::vector& lines = depthTest ? m_depthLines : m_noDepthLines; + + const uint latitudeDivisions = 8; + const uint longitudeDivisions = 8; + uint voffset = lines.size(); + uint count = (latitudeDivisions + 1) * (longitudeDivisions + 1); + for (uint lat = 0; lat <= latitudeDivisions; ++lat) { + float theta = (float)lat * M_PI / (float)latitudeDivisions; + for (uint lon = 0; lon <= longitudeDivisions; ++lon) { + float phi = (float)lon * M_PI2 / (float)longitudeDivisions; + lines.push_back({ { + center.x + radius * sin(theta) * cos(phi), + center.y + radius * sin(theta) * sin(phi), + center.z + radius * cos(theta) }, + color }); + } + } +} + +void CDebugVisualizer::DrawVector(const Vector& position, const Vector& direction, Vector color, float lifespan, bool depthTest) +{ + DrawLine(position, (position + direction) * position.Length(), color, lifespan, depthTest); +} + +void CDebugVisualizer::DrawFrustum(const CFrustum& frustum, Vector color, float lifespan, bool depthTest) +{ + // TODO +} diff --git a/client/render/visualizer/debug_visualizer.h b/client/render/visualizer/debug_visualizer.h new file mode 100644 index 000000000..54e8141cc --- /dev/null +++ b/client/render/visualizer/debug_visualizer.h @@ -0,0 +1,47 @@ +#pragma once +#include "vector.h" +#include "gl_frustum.h" +#include "shader.h" + +#define MAX_DEBUG_VBO_SIZE 1024 * 1024 + +struct SLineVertex +{ + Vector position; + Vector color; +}; + +class CDebugVisualizer +{ +public: + static CDebugVisualizer& GetInstance(); + +public: + void Initialize(); + void Shutdown(); + + void Render(); + + void DrawLine(const Vector &start, const Vector& end, Vector color, float lifespan, bool depthTest); + void DrawAABB(const Vector &mins, const Vector &maxs, Vector color, float lifespan, bool depthTest); + void DrawSphere(const Vector ¢er, float radius, Vector color, float lifespan, bool depthTest); + void DrawVector(const Vector &position, const Vector& direction, Vector color, float lifespan, bool depthTest); + void DrawFrustum(const CFrustum &frustum, Vector color, float lifespan, bool depthTest); + // and so on... + +private: + void RenderLines(const std::vector& lines, bool isDepthEnabled); + +private: + std::vector m_noDepthLines; + std::vector m_depthLines; + + shader_t m_DebugShader; + + GLuint m_iVAO = 0; + GLuint m_iVBO = 0; + + bool m_bInitialized = false; +}; + +extern CDebugVisualizer g_DebugVisualizer; diff --git a/game_dir/glsl/common/debug_draw_fp.glsl b/game_dir/glsl/common/debug_draw_fp.glsl new file mode 100644 index 000000000..c0366efef --- /dev/null +++ b/game_dir/glsl/common/debug_draw_fp.glsl @@ -0,0 +1,21 @@ +/* +debug_draw_vp.glsl - debug draw +Copyright (C) 2025 ugo_zapad + +This program 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 3 of the License, or +(at your option) any later version. + +This program 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. +*/ + +varying vec3 var_Color; + +void main() +{ + gl_FragColor = vec4(var_Color, 1.0); +} diff --git a/game_dir/glsl/common/debug_draw_vp.glsl b/game_dir/glsl/common/debug_draw_vp.glsl new file mode 100644 index 000000000..cf0fba83e --- /dev/null +++ b/game_dir/glsl/common/debug_draw_vp.glsl @@ -0,0 +1,28 @@ +/* +debug_draw_vp.glsl - debug draw +Copyright (C) 2025 ugo_zapad + +This program 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 3 of the License, or +(at your option) any later version. + +This program 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. +*/ + +attribute vec3 attr_Position; +attribute vec3 attr_Normal; // Color + +uniform mat4 u_ModelMatrix; // model-view matrix +uniform mat4 u_ProjectionMatrix; // projection matrix + +varying vec3 var_Color; + +void main() +{ + var_Color = attr_Normal; + gl_Position = u_ProjectionMatrix * u_ModelMatrix * vec4(attr_Position, 1.0); +}