From b944465263fa0c7ab1cfaa349256dc2b56ed0e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tur=C3=A1nszki=20J=C3=A1nos?= Date: Tue, 5 Jul 2022 15:20:23 +0200 Subject: [PATCH] Font renderer updates (#483) * font renderer updates, text debug drawer; editor: name visualizer; github CI: vulkan sdk not required; * cmake: vulkan sdk not required * refactors * lua binding for DrawDebugText() * comment * transparency sorting for debug texts; font alphablend fix; * softer debug text --- .github/workflows/build-nightly.yml | 3 - .github/workflows/build-pr.yml | 3 - .github/workflows/build.yml | 3 - .../ScriptingAPI-Documentation.md | 5 + Content/scripts/debug_draw.lua | 8 +- Editor/Editor.cpp | 61 +++++++++- Editor/RendererWindow.cpp | 24 ++-- Editor/RendererWindow.h | 3 +- WickedEngine/CMakeLists.txt | 2 - WickedEngine/shaders/fontPS.hlsl | 3 +- WickedEngine/wiFont.cpp | 105 ++++++++++++------ WickedEngine/wiFont.h | 18 ++- WickedEngine/wiPhysics_Bullet.cpp | 27 ++++- WickedEngine/wiRenderer.cpp | 74 ++++++++++++ WickedEngine/wiRenderer.h | 21 ++++ WickedEngine/wiRenderer_BindLua.cpp | 53 +++++++++ WickedEngine/wiVersion.cpp | 2 +- 17 files changed, 350 insertions(+), 65 deletions(-) diff --git a/.github/workflows/build-nightly.yml b/.github/workflows/build-nightly.yml index b9338f239..98dcbaa7c 100644 --- a/.github/workflows/build-nightly.yml +++ b/.github/workflows/build-nightly.yml @@ -76,10 +76,7 @@ jobs: - name: Install dependencies run: | - wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.170-focal.list https://packages.lunarg.com/vulkan/1.2.170/lunarg-vulkan-1.2.170-focal.list sudo apt update - sudo apt install vulkan-sdk sudo apt install libsdl2-dev - name: Build diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 6dfd34768..0ff2d9327 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -75,10 +75,7 @@ jobs: - name: Install dependencies run: | - wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.170-focal.list https://packages.lunarg.com/vulkan/1.2.170/lunarg-vulkan-1.2.170-focal.list sudo apt update - sudo apt install vulkan-sdk sudo apt install libsdl2-dev - name: Build diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1387eabcf..48404e62b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -76,10 +76,7 @@ jobs: - name: Install dependencies run: | - wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-1.2.170-focal.list https://packages.lunarg.com/vulkan/1.2.170/lunarg-vulkan-1.2.170-focal.list sudo apt update - sudo apt install vulkan-sdk sudo apt install libsdl2-dev - name: Build diff --git a/Content/Documentation/ScriptingAPI-Documentation.md b/Content/Documentation/ScriptingAPI-Documentation.md index c34a38fae..e549def25 100644 --- a/Content/Documentation/ScriptingAPI-Documentation.md +++ b/Content/Documentation/ScriptingAPI-Documentation.md @@ -125,6 +125,11 @@ You can use the Renderer with the following functions, all of which are in the g - DrawBox(Matrix boxMatrix, opt Vector color) - DrawSphere(Sphere sphere, opt Vector color) - DrawCapsule(Capsule capsule, opt Vector color) +- DrawDebugText(string text, opt Vector position, opt Vector color, opt float scaling, opt int flags) + DrawDebugText flags, these can be combined with binary OR operator: + [outer]DEBUG_TEXT_DEPTH_TEST -- text can be occluded by geometry + [outer]DEBUG_TEXT_CAMERA_FACING -- text will be rotated to face the camera + [outer]DEBUG_TEXT_CAMERA_SCALING -- text will be always the same size, independent of distance to camera - PutWaterRipple(String imagename, Vector position) - PutDecal(Decal decal) - PutEnvProbe(Vector pos) diff --git a/Content/scripts/debug_draw.lua b/Content/scripts/debug_draw.lua index d26ac6015..aca46580d 100644 --- a/Content/scripts/debug_draw.lua +++ b/Content/scripts/debug_draw.lua @@ -1,4 +1,4 @@ --- This script will draw some debug primitives in the world such as line, point, box +-- This script will draw some debug primitives in the world such as line, point, box, capsule, text killProcesses() -- stops all running lua coroutine processes backlog_post("---> START SCRIPT: debug_draw.lua") @@ -13,6 +13,12 @@ runProcess(function() local T = matrix.Translation(Vector(0,2,3)) local M = S:Multiply(R):Multiply(T) DrawBox(M, Vector(0,1,1,1)) + + local capsule = Capsule(Vector(1,1,1), Vector(30,30,30), 1.5) + DrawCapsule(capsule, Vector(1,0,0,1)) + + DrawDebugText("Debug text", Vector(-5,4,2), Vector(0,1,0,1), 2, DEBUG_TEXT_CAMERA_FACING | DEBUG_TEXT_CAMERA_SCALING) + DrawDebugText("Debug text behind", Vector(-5,4,4), Vector(0,0,1,1), 2, DEBUG_TEXT_CAMERA_FACING | DEBUG_TEXT_CAMERA_SCALING) render() end diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index e39134826..1c930485e 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -2126,12 +2126,12 @@ void EditorComponent::Compose(CommandList cmd) const { return; } + GraphicsDevice* device = wi::graphics::GetDevice(); // Draw selection outline to the screen: const float selectionColorIntensity = std::sin(selectionOutlineTimer * XM_2PI * 0.8f) * 0.5f + 0.5f; if (renderPath->GetDepthStencil() != nullptr && !translator.selected.empty()) { - GraphicsDevice* device = wi::graphics::GetDevice(); device->EventBegin("Editor - Selection Outline", cmd); wi::renderer::BindCommonResources(cmd); float opacity = wi::math::Lerp(0.4f, 1.0f, selectionColorIntensity); @@ -2442,6 +2442,65 @@ void EditorComponent::Compose(CommandList cmd) const } } + if (rendererWnd.nameDebugCheckBox.GetCheck()) + { + device->EventBegin("Debug Names", cmd); + struct DebugNameEntitySorter + { + size_t name_index; + float distance; + XMFLOAT3 position; + }; + static wi::vector debugNameEntitiesSorted; + debugNameEntitiesSorted.clear(); + for (size_t i = 0; i < scene.names.GetCount(); ++i) + { + Entity entity = scene.names.GetEntity(i); + const TransformComponent* transform = scene.transforms.GetComponent(entity); + if (transform != nullptr) + { + auto& x = debugNameEntitiesSorted.emplace_back(); + x.name_index = i; + x.position = transform->GetPosition(); + const ObjectComponent* object = scene.objects.GetComponent(entity); + if (object != nullptr) + { + x.position = object->center; + } + x.distance = wi::math::Distance(x.position, camera.Eye); + } + } + std::sort(debugNameEntitiesSorted.begin(), debugNameEntitiesSorted.end(), [](const DebugNameEntitySorter& a, const DebugNameEntitySorter& b) + { + return a.distance > b.distance; + }); + for (auto& x : debugNameEntitiesSorted) + { + Entity entity = scene.names.GetEntity(x.name_index); + wi::font::Params params; + params.position = x.position; + params.size = wi::font::WIFONTSIZE_DEFAULT; + params.scaling = 1.0f / params.size * x.distance * 0.03f; + params.color = wi::Color::White(); + for (auto& picked : translator.selected) + { + if (picked.entity == entity) + { + params.color = selectedEntityColor; + break; + } + } + params.h_align = wi::font::WIFALIGN_CENTER; + params.v_align = wi::font::WIFALIGN_CENTER; + params.softness = 0.1f; + params.shadowColor = wi::Color::Black(); + params.shadow_softness = 0.5f; + params.customProjection = &VP; + params.customRotation = &R; + wi::font::Draw(scene.names[x.name_index].name, params, cmd); + } + device->EventEnd(cmd); + } if (translator.enabled) { diff --git a/Editor/RendererWindow.cpp b/Editor/RendererWindow.cpp index 3512683d6..9f20c5aa5 100644 --- a/Editor/RendererWindow.cpp +++ b/Editor/RendererWindow.cpp @@ -528,9 +528,15 @@ void RendererWindow::Create(EditorComponent* editor) // Visualizer toggles: x = 540, y = 0; + nameDebugCheckBox.Create("Name visualizer: "); + nameDebugCheckBox.SetTooltip("Visualize the entity names in the scene"); + nameDebugCheckBox.SetPos(XMFLOAT2(x, y)); + nameDebugCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); + AddWidget(&nameDebugCheckBox); + physicsDebugCheckBox.Create("Physics visualizer: "); physicsDebugCheckBox.SetTooltip("Visualize the physics world"); - physicsDebugCheckBox.SetPos(XMFLOAT2(x, y)); + physicsDebugCheckBox.SetPos(XMFLOAT2(x, y += step)); physicsDebugCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); physicsDebugCheckBox.OnClick([](wi::gui::EventArgs args) { wi::physics::SetDebugDrawEnabled(args.bValue); @@ -538,16 +544,16 @@ void RendererWindow::Create(EditorComponent* editor) physicsDebugCheckBox.SetCheck(wi::physics::IsDebugDrawEnabled()); AddWidget(&physicsDebugCheckBox); - partitionBoxesCheckBox.Create("SPTree visualizer: "); - partitionBoxesCheckBox.SetTooltip("Visualize the scene bounding boxes"); - partitionBoxesCheckBox.SetScriptTip("SetDebugPartitionTreeEnabled(bool enabled)"); - partitionBoxesCheckBox.SetPos(XMFLOAT2(x, y += step)); - partitionBoxesCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); - partitionBoxesCheckBox.OnClick([](wi::gui::EventArgs args) { + aabbDebugCheckBox.Create("AABB visualizer: "); + aabbDebugCheckBox.SetTooltip("Visualize the scene bounding boxes"); + aabbDebugCheckBox.SetScriptTip("SetDebugPartitionTreeEnabled(bool enabled)"); + aabbDebugCheckBox.SetPos(XMFLOAT2(x, y += step)); + aabbDebugCheckBox.SetSize(XMFLOAT2(itemheight, itemheight)); + aabbDebugCheckBox.OnClick([](wi::gui::EventArgs args) { wi::renderer::SetToDrawDebugPartitionTree(args.bValue); }); - partitionBoxesCheckBox.SetCheck(wi::renderer::GetToDrawDebugPartitionTree()); - AddWidget(&partitionBoxesCheckBox); + aabbDebugCheckBox.SetCheck(wi::renderer::GetToDrawDebugPartitionTree()); + AddWidget(&aabbDebugCheckBox); boneLinesCheckBox.Create("Bone line visualizer: "); boneLinesCheckBox.SetTooltip("Visualize bones of armatures"); diff --git a/Editor/RendererWindow.h b/Editor/RendererWindow.h index 52a7736a5..59fb8f169 100644 --- a/Editor/RendererWindow.h +++ b/Editor/RendererWindow.h @@ -42,8 +42,9 @@ public: wi::gui::Slider voxelRadianceConeTracingSlider; wi::gui::Slider voxelRadianceRayStepSizeSlider; wi::gui::Slider voxelRadianceMaxDistanceSlider; + wi::gui::CheckBox nameDebugCheckBox; wi::gui::CheckBox physicsDebugCheckBox; - wi::gui::CheckBox partitionBoxesCheckBox; + wi::gui::CheckBox aabbDebugCheckBox; wi::gui::CheckBox boneLinesCheckBox; wi::gui::CheckBox debugEmittersCheckBox; wi::gui::CheckBox debugForceFieldsCheckBox; diff --git a/WickedEngine/CMakeLists.txt b/WickedEngine/CMakeLists.txt index d3f3630f8..8b50b1d71 100644 --- a/WickedEngine/CMakeLists.txt +++ b/WickedEngine/CMakeLists.txt @@ -9,7 +9,6 @@ if (WIN32) set(TARGET_NAME WickedEngine_Windows) else () set(TARGET_NAME WickedEngine_Linux) - find_package(Vulkan REQUIRED) find_package(SDL2 REQUIRED) find_package(OpenImageDenoise QUIET) find_package(Threads REQUIRED) @@ -221,7 +220,6 @@ if (WIN32) else () target_link_libraries(${TARGET_NAME} PUBLIC Threads::Threads - Vulkan::Vulkan SDL2::SDL2 $<$:OpenImageDenoise> # links OpenImageDenoise only if it's found ) diff --git a/WickedEngine/shaders/fontPS.hlsl b/WickedEngine/shaders/fontPS.hlsl index 5c3feafa1..b315bf123 100644 --- a/WickedEngine/shaders/fontPS.hlsl +++ b/WickedEngine/shaders/fontPS.hlsl @@ -10,7 +10,8 @@ struct VertextoPixel float4 main(VertextoPixel input) : SV_TARGET { float dist = bindless_textures[font.texture_index].SampleLevel(sampler_linear_clamp, input.uv, 0).r; - float4 color = smoothstep(font.sdf_threshold_bottom, font.sdf_threshold_top, dist) * unpack_rgba(font.color); + float4 color = unpack_rgba(font.color); + color.a *= smoothstep(font.sdf_threshold_bottom, font.sdf_threshold_top, dist); // sdf [branch] if (font.flags & FONT_FLAG_OUTPUT_COLOR_SPACE_HDR10_ST2084) diff --git a/WickedEngine/wiFont.cpp b/WickedEngine/wiFont.cpp index da6500e12..746de0b81 100644 --- a/WickedEngine/wiFont.cpp +++ b/WickedEngine/wiFont.cpp @@ -24,19 +24,21 @@ using namespace wi::graphics; namespace wi::font { -#define WHITESPACE_SIZE ((float(params.size) + params.spacingX) * params.scaling * 0.25f) +#define WHITESPACE_SIZE ((float(params.size) + params.spacingX) * 0.25f) #define TAB_SIZE (WHITESPACE_SIZE * 4) -#define LINEBREAK_SIZE ((float(params.size) + params.spacingY) * params.scaling) +#define LINEBREAK_SIZE ((float(params.size) + params.spacingY)) namespace font_internal { static BlendState blendState; static RasterizerState rasterizerState; static DepthStencilState depthStencilState; + static DepthStencilState depthStencilState_depth_test; static Shader vertexShader; static Shader pixelShader; static PipelineState PSO; + static PipelineState PSO_depth_test; static thread_local wi::Canvas canvas; @@ -180,10 +182,10 @@ namespace wi::font else { const Glyph& glyph = glyph_lookup.at(hash); - const float glyphWidth = glyph.width * params.scaling; - const float glyphHeight = glyph.height * params.scaling; - const float glyphOffsetX = glyph.x * params.scaling; - const float glyphOffsetY = glyph.y * params.scaling; + const float glyphWidth = glyph.width; + const float glyphHeight = glyph.height; + const float glyphOffsetX = glyph.x; + const float glyphOffsetY = glyph.y; const size_t vertexID = size_t(status.quadCount) * 4; vertexList.resize(vertexID + 4); @@ -212,7 +214,7 @@ namespace wi::font int advance, lsb; stbtt_GetCodepointHMetrics(&fontStyle.fontInfo, code, &advance, &lsb); - status.cursor.position.x += advance * fontScale * params.scaling; + status.cursor.position.x += advance * fontScale; status.cursor.position.x += params.spacingX; @@ -253,6 +255,9 @@ namespace wi::font desc.rs = &rasterizerState; desc.pt = PrimitiveTopology::TRIANGLESTRIP; wi::graphics::GetDevice()->CreatePipelineState(&desc, &PSO); + + desc.dss = &depthStencilState_depth_test; + wi::graphics::GetDevice()->CreatePipelineState(&desc, &PSO_depth_test); } void Initialize() { @@ -268,7 +273,7 @@ namespace wi::font RasterizerState rs; rs.fill_mode = FillMode::SOLID; - rs.cull_mode = CullMode::FRONT; + rs.cull_mode = CullMode::NONE; rs.front_counter_clockwise = true; rs.depth_bias = 0; rs.depth_bias_clamp = 0; @@ -280,10 +285,10 @@ namespace wi::font BlendState bd; bd.render_target[0].blend_enable = true; - bd.render_target[0].src_blend = Blend::ONE; + bd.render_target[0].src_blend = Blend::SRC_ALPHA; bd.render_target[0].dest_blend = Blend::INV_SRC_ALPHA; bd.render_target[0].blend_op = BlendOp::ADD; - bd.render_target[0].src_blend_alpha = Blend::ONE; + bd.render_target[0].src_blend_alpha = Blend::SRC_ALPHA; bd.render_target[0].dest_blend_alpha = Blend::INV_SRC_ALPHA; bd.render_target[0].blend_op_alpha = BlendOp::ADD; bd.render_target[0].render_target_write_mask = ColorWrite::ENABLE_ALL; @@ -295,6 +300,11 @@ namespace wi::font dsd.stencil_enable = false; depthStencilState = dsd; + dsd.depth_enable = true; + dsd.depth_write_mask = DepthWriteMask::ZERO; + dsd.depth_func = ComparisonFunc::GREATER; + depthStencilState_depth_test = dsd; + static wi::eventhandler::Handle handle1 = wi::eventhandler::Subscribe(wi::eventhandler::EVENT_RELOAD_SHADERS, [](uint64_t userdata) { LoadShaders(); }); LoadShaders(); @@ -436,23 +446,13 @@ namespace wi::font } template - Cursor Draw_internal(const T* text, size_t text_length, const Params& params_in, CommandList cmd) + Cursor Draw_internal(const T* text, size_t text_length, const Params& params, CommandList cmd) { if (text_length <= 0) { return Cursor(); } - ParseStatus status = ParseText(text, text_length, params_in); - - Params params = params_in; - if (params.h_align == WIFALIGN_CENTER) - params.posX -= status.cursor.size.x / 2; - else if (params.h_align == WIFALIGN_RIGHT) - params.posX -= status.cursor.size.x; - if (params.v_align == WIFALIGN_CENTER) - params.posY -= status.cursor.size.y / 2; - else if (params.v_align == WIFALIGN_BOTTOM) - params.posY -= status.cursor.size.y; + ParseStatus status = ParseText(text, text_length, params); if (status.quadCount > 0) { @@ -475,7 +475,14 @@ namespace wi::font device->EventBegin("Font", cmd); - device->BindPipelineState(&PSO, cmd); + if (params.isDepthTestEnabled()) + { + device->BindPipelineState(&PSO_depth_test, cmd); + } + else + { + device->BindPipelineState(&PSO, cmd); + } font.flags = 0; if (params.isHDR10OutputMappingEnabled()) @@ -488,20 +495,47 @@ namespace wi::font font.hdr_scaling = params.hdr_scaling; } - // Asserts will check that a proper canvas was set for this cmd with wi::image::SetCanvas() - // The canvas must be set to have dpi aware rendering - assert(canvas.width > 0); - assert(canvas.height > 0); - assert(canvas.dpi > 0); - const XMMATRIX Projection = canvas.GetProjection(); + XMFLOAT3 offset = XMFLOAT3(0, 0, 0); + float vertical_flip = params.customProjection == nullptr ? 1.0f : -1.0f; + if (params.h_align == WIFALIGN_CENTER) + offset.x -= status.cursor.size.x / 2; + else if (params.h_align == WIFALIGN_RIGHT) + offset.x -= status.cursor.size.x; + if (params.v_align == WIFALIGN_CENTER) + offset.y -= status.cursor.size.y / 2 * vertical_flip; + else if (params.v_align == WIFALIGN_BOTTOM) + offset.y -= status.cursor.size.y * vertical_flip; + + XMMATRIX M = XMMatrixTranslation(offset.x, offset.y, offset.z); + M = M * XMMatrixScaling(params.scaling, params.scaling, params.scaling); + M = M * XMMatrixRotationZ(params.rotation); + + if (params.customRotation != nullptr) + { + M = M * (*params.customRotation); + } + + M = M * XMMatrixTranslation(params.position.x, params.position.y, params.position.z); + + if (params.customProjection != nullptr) + { + M = XMMatrixScaling(1, -1, 1) * M; // reason: screen projection is Y down (like UV-space) and that is the common case for image rendering. But custom projections will use the "world space" + M = M * (*params.customProjection); + } + else + { + // Asserts will check that a proper canvas was set for this cmd with wi::image::SetCanvas() + // The canvas must be set to have dpi aware rendering + assert(canvas.width > 0); + assert(canvas.height > 0); + assert(canvas.dpi > 0); + M = M * canvas.GetProjection(); + } if (params.shadowColor.getA() > 0) { // font shadow render: - XMStoreFloat4x4(&font.transform, - XMMatrixTranslation((float)params.posX + params.shadow_offset_x, (float)params.posY + params.shadow_offset_y, 0) - * Projection - ); + XMStoreFloat4x4(&font.transform, XMMatrixTranslation(params.shadow_offset_x, params.shadow_offset_y, 0) * M); font.color = params.shadowColor.rgba; font.sdf_threshold_top = wi::math::Lerp(float(SDF::onedge_value) / 255.0f, 0, std::max(0.0f, params.shadow_bolden)); font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, params.shadow_softness)); @@ -511,10 +545,7 @@ namespace wi::font } // font base render: - XMStoreFloat4x4(&font.transform, - XMMatrixTranslation((float)params.posX, (float)params.posY, 0) - * Projection - ); + XMStoreFloat4x4(&font.transform, M); font.color = params.color.rgba; font.sdf_threshold_top = wi::math::Lerp(float(SDF::onedge_value) / 255.0f, 0, std::max(0.0f, params.bolden)); font.sdf_threshold_bottom = wi::math::Lerp(font.sdf_threshold_top, 0, std::max(0.0f, params.softness)); diff --git a/WickedEngine/wiFont.h b/WickedEngine/wiFont.h index 1602b9f94..338e4c2c5 100644 --- a/WickedEngine/wiFont.h +++ b/WickedEngine/wiFont.h @@ -29,10 +29,18 @@ namespace wi::font struct Params { - float posX = 0; // position in horizontal direction (logical canvas units) - float posY = 0; // position in vertical direction (logical canvas units) + union + { + XMFLOAT3 position = {}; // position in logical canvas units + struct // back-compat aliasing + { + float posX; // position in horizontal direction (logical canvas units) + float posY; // position in vertical direction (logical canvas units) + }; + }; int size = WIFONTSIZE_DEFAULT; // line height (logical canvas units) float scaling = 1; // this will apply upscaling to the text while keeping the same resolution (size) of the font + float rotation = 0; // rotation around alignment anchor (in radians) float spacingX = 0, spacingY = 0; // minimum spacing between characters (logical canvas units) Alignment h_align = WIFALIGN_LEFT; // horizontal alignment Alignment v_align = WIFALIGN_TOP; // vertical alignment @@ -48,25 +56,31 @@ namespace wi::font float shadow_offset_y = 0; // offset for shadow under the text in logical canvas coordinates Cursor cursor; // cursor can be used to continue text drawing by taking the Draw's return value (optional) float hdr_scaling = 1.0f; // a scaling value for use by linear output mapping + const XMMATRIX* customProjection = nullptr; + const XMMATRIX* customRotation = nullptr; enum FLAGS { EMPTY = 0, OUTPUT_COLOR_SPACE_HDR10_ST2084 = 1 << 1, OUTPUT_COLOR_SPACE_LINEAR = 1 << 2, + DEPTH_TEST = 1 << 3, }; uint32_t _flags = EMPTY; constexpr bool isHDR10OutputMappingEnabled() const { return _flags & OUTPUT_COLOR_SPACE_HDR10_ST2084; } constexpr bool isLinearOutputMappingEnabled() const { return _flags & OUTPUT_COLOR_SPACE_LINEAR; } + constexpr bool isDepthTestEnabled() const { return _flags & DEPTH_TEST; } // enable HDR10 output mapping, if this image can be interpreted in linear space and converted to HDR10 display format constexpr void enableHDR10OutputMapping() { _flags |= OUTPUT_COLOR_SPACE_HDR10_ST2084; } // enable linear output mapping, which means removing gamma curve and outputting in linear space (useful for blending in HDR space) constexpr void enableLinearOutputMapping(float scaling = 1.0f) { _flags |= OUTPUT_COLOR_SPACE_LINEAR; hdr_scaling = scaling; } + constexpr void enableDepthTest() { _flags |= DEPTH_TEST; } constexpr void disableHDR10OutputMapping() { _flags &= ~OUTPUT_COLOR_SPACE_HDR10_ST2084; } constexpr void disableLinearOutputMapping() { _flags &= ~OUTPUT_COLOR_SPACE_LINEAR; } + constexpr void disableDepthTest() { _flags &= ~DEPTH_TEST; } Params( float posX = 0, diff --git a/WickedEngine/wiPhysics_Bullet.cpp b/WickedEngine/wiPhysics_Bullet.cpp index 3040257c7..dab1f1c73 100644 --- a/WickedEngine/wiPhysics_Bullet.cpp +++ b/WickedEngine/wiPhysics_Bullet.cpp @@ -53,13 +53,24 @@ namespace wi::physics } void draw3dText(const btVector3& location, const char* textString) override { + wi::renderer::DebugTextParams params; + params.position.x = location.x(); + params.position.y = location.y(); + params.position.z = location.z(); + params.scaling = 0.6f; + params.flags |= wi::renderer::DebugTextParams::CAMERA_FACING; + params.flags |= wi::renderer::DebugTextParams::CAMERA_SCALING; + wi::renderer::DrawDebugText(textString, params); } void setDebugMode(int debugMode) override { } int getDebugMode() const override { - return DBG_DrawWireframe; + int retval = 0; + retval |= DBG_DrawWireframe; + retval |= DBG_DrawText; + return retval; } }; DebugDraw debugDraw; @@ -540,6 +551,20 @@ namespace wi::physics continue; } + // If you need it, you can enable soft body node debug strings here: +#if 0 + if (IsDebugDrawEnabled()) + { + btSoftBodyHelpers::DrawInfos( + softbody, + &debugDraw, + false, // masses + true, // areas + false // stress + ); + } +#endif + MeshComponent& mesh = *scene.meshes.GetComponent(entity); // System mesh aabb will be queried from physics engine soft body: diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index d14447c88..c8fe7de04 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -20,6 +20,7 @@ #include "wiShaderCompiler.h" #include "wiTimer.h" #include "wiUnorderedMap.h" // leave it here for shader dump! +#include "wiFont.h" #include "shaders/ShaderInterop_Postprocess.h" #include "shaders/ShaderInterop_Raytracing.h" @@ -132,6 +133,7 @@ wi::vector renderableLines2D; wi::vector renderablePoints; wi::vector renderableTriangles_solid; wi::vector renderableTriangles_wireframe; +wi::vector debugTextStorage; // A stream of DebugText struct + text characters wi::vector paintrads; wi::SpinLock deferredMIPGenLock; @@ -6092,6 +6094,66 @@ void DrawDebugWorld( } } + if (!debugTextStorage.empty()) + { + device->EventBegin("DebugTexts", cmd); + const XMMATRIX VP = camera.GetViewProjection(); + const XMMATRIX R = XMLoadFloat3x3(&camera.rotationMatrix); + struct DebugTextSorter + { + const char* text; + size_t text_len; + DebugTextParams params; + float distance; + }; + static thread_local wi::vector sorted_texts; + sorted_texts.clear(); + size_t offset = 0; + while(offset < debugTextStorage.size()) + { + auto& x = sorted_texts.emplace_back(); + x.params = *(const DebugTextParams*)(debugTextStorage.data() + offset); + offset += sizeof(DebugTextParams); + x.text = (const char*)(debugTextStorage.data() + offset); + x.text_len = strlen(x.text); + offset += x.text_len + 1; + x.distance = wi::math::Distance(x.params.position, camera.Eye); + + } + std::sort(sorted_texts.begin(), sorted_texts.end(), [](const DebugTextSorter& a, const DebugTextSorter& b) { + return a.distance > b.distance; + }); + for (auto& x : sorted_texts) + { + wi::font::Params params; + params.position = x.params.position; + params.size = x.params.pixel_height; + params.scaling = 1.0f / params.size * x.params.scaling; + params.color = wi::Color::fromFloat4(x.params.color); + params.h_align = wi::font::WIFALIGN_CENTER; + params.v_align = wi::font::WIFALIGN_CENTER; + params.softness = 0.1f; + params.shadowColor = wi::Color::Black(); + params.shadow_softness = 0.8f; + params.customProjection = &VP; + if (x.params.flags & DebugTextParams::DEPTH_TEST) + { + params.enableDepthTest(); + } + if (x.params.flags & DebugTextParams::CAMERA_FACING) + { + params.customRotation = &R; + } + if (x.params.flags & DebugTextParams::CAMERA_SCALING) + { + params.scaling *= x.distance * 0.05f; + } + wi::font::Draw(x.text, x.text_len, params, cmd); + } + debugTextStorage.clear(); + device->EventEnd(cmd); + } + device->EventEnd(cmd); } @@ -12847,6 +12909,18 @@ void DrawTriangle(const RenderableTriangle& triangle, bool wireframe) renderableTriangles_solid.push_back(triangle); } } +void DrawDebugText(const char* text, const DebugTextParams& params) +{ + for (size_t i = 0; i < sizeof(DebugTextParams); ++i) + { + debugTextStorage.push_back(((uint8_t*)(¶ms))[i]); + } + size_t len = strlen(text) + 1; + for (size_t i = 0; i < len; ++i) + { + debugTextStorage.push_back(uint8_t(text[i])); + } +} void DrawPaintRadius(const PaintRadius& paintrad) { paintrads.push_back(paintrad); diff --git a/WickedEngine/wiRenderer.h b/WickedEngine/wiRenderer.h index f8c87f970..b871d5c41 100644 --- a/WickedEngine/wiRenderer.h +++ b/WickedEngine/wiRenderer.h @@ -880,6 +880,7 @@ namespace wi::renderer float size = 1.0f; XMFLOAT4 color = XMFLOAT4(1, 1, 1, 1); }; + // Add point to render in the next frame. It will be rendered in DrawDebugWorld() as an X void DrawPoint(const RenderablePoint& point); struct RenderableTriangle @@ -891,8 +892,28 @@ namespace wi::renderer XMFLOAT3 positionC = XMFLOAT3(0, 0, 0); XMFLOAT4 colorC = XMFLOAT4(1, 1, 1, 1); }; + // Add triangle to render in the next frame. It will be rendered in DrawDebugWorld() void DrawTriangle(const RenderableTriangle& triangle, bool wireframe = false); + struct DebugTextParams + { + XMFLOAT3 position = XMFLOAT3(0, 0, 0); + int pixel_height = 32; + float scaling = 1; + XMFLOAT4 color = XMFLOAT4(1, 1, 1, 1); + enum FLAGS // do not change values, it's bound to lua manually! + { + NONE = 0, + DEPTH_TEST = 1 << 0, // text can be occluded by geometry + CAMERA_FACING = 1 << 1, // text will be rotated to face the camera + CAMERA_SCALING = 1 << 2, // text will be always the same size, independent of distance to camera + }; + uint32_t flags = NONE; + }; + // Add text to render in the next frame. It will be rendered in DrawDebugWorld() + // The memory to text doesn't need to be retained by the caller, as it will be copied internally + void DrawDebugText(const char* text, const DebugTextParams& params); + struct PaintRadius { wi::ecs::Entity objectEntity = wi::ecs::INVALID_ENTITY; diff --git a/WickedEngine/wiRenderer_BindLua.cpp b/WickedEngine/wiRenderer_BindLua.cpp index b74f7b62e..0c51bd9a3 100644 --- a/WickedEngine/wiRenderer_BindLua.cpp +++ b/WickedEngine/wiRenderer_BindLua.cpp @@ -301,6 +301,54 @@ namespace wi::lua::renderer return 0; } + int DrawDebugText(lua_State* L) + { + int argc = wi::lua::SGetArgCount(L); + if (argc > 0) + { + std::string text = wi::lua::SGetString(L, 1); + wi::renderer::DebugTextParams params; + if (argc > 1) + { + Vector_BindLua* position = Luna::lightcheck(L, 2); + if (position != nullptr) + { + params.position.x = position->x; + params.position.y = position->y; + params.position.z = position->z; + + if (argc > 2) + { + Vector_BindLua* color = Luna::lightcheck(L, 3); + if (color != nullptr) + { + params.color = *color; + + if (argc > 3) + { + params.scaling = wi::lua::SGetFloat(L, 4); + + if (argc > 4) + { + params.flags = wi::lua::SGetInt(L, 5); + } + } + } + else + wi::lua::SError(L, "DrawDebugText(string text, opt Vector position, opt Vector color, opt float scaling, opt int flags) third argument was not a Vector!"); + } + } + else + wi::lua::SError(L, "DrawDebugText(string text, opt Vector position, opt Vector color, opt float scaling, opt int flags) second argument was not a Vector!"); + + } + wi::renderer::DrawDebugText(text.c_str(), params); + } + else + wi::lua::SError(L, "DrawDebugText(string text, opt Vector position, opt Vector color, opt float scaling, opt int flags) not enough arguments!"); + + return 0; + } int PutWaterRipple(lua_State* L) { int argc = wi::lua::SGetArgCount(L); @@ -372,6 +420,7 @@ namespace wi::lua::renderer wi::lua::RegisterFunc("DrawBox", DrawBox); wi::lua::RegisterFunc("DrawSphere", DrawSphere); wi::lua::RegisterFunc("DrawCapsule", DrawCapsule); + wi::lua::RegisterFunc("DrawDebugText", DrawDebugText); wi::lua::RegisterFunc("PutWaterRipple", PutWaterRipple); @@ -380,6 +429,10 @@ namespace wi::lua::renderer wi::lua::RunText("PICK_TRANSPARENT = 2"); wi::lua::RunText("PICK_WATER = 4"); + wi::lua::RunText("DEBUG_TEXT_DEPTH_TEST = 1"); + wi::lua::RunText("DEBUG_TEXT_CAMERA_FACING = 2"); + wi::lua::RunText("DEBUG_TEXT_CAMERA_SCALING = 4"); + wi::lua::RegisterFunc("ClearWorld", ClearWorld); wi::lua::RegisterFunc("ReloadShaders", ReloadShaders); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 136a16092..95659312a 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 70; // minor bug fixes, alterations, refactors, updates - const int revision = 6; + const int revision = 7; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);