diff --git a/Editor/OptionsWindow.cpp b/Editor/OptionsWindow.cpp index 5669dbd5b..2eb8dc9c1 100644 --- a/Editor/OptionsWindow.cpp +++ b/Editor/OptionsWindow.cpp @@ -613,6 +613,25 @@ void OptionsWindow::Create(EditorComponent* _editor) paintToolWnd.brushTextureButton.SetColor(wi::Color::White(), wi::gui::IDLE); paintToolWnd.revealTextureButton.SetColor(wi::Color::White(), wi::gui::IDLE); editor->aboutLabel.sprites[wi::gui::FOCUS] = editor->aboutLabel.sprites[wi::gui::IDLE]; + for (int i = 0; i < arraysize(sprites); ++i) + { + sprites[i].params.enableCornerRounding(); + sprites[i].params.corners_rounding[1].radius = 10; + resizeDragger_UpperRight.sprites[i].params.enableCornerRounding(); + resizeDragger_UpperRight.sprites[i].params.corners_rounding[1].radius = 10; + } + for (int i = 0; i < arraysize(editor->componentsWnd.sprites); ++i) + { + editor->componentsWnd.sprites[i].params.enableCornerRounding(); + editor->componentsWnd.sprites[i].params.corners_rounding[0].radius = 10; + editor->componentsWnd.resizeDragger_UpperLeft.sprites[i].params.enableCornerRounding(); + editor->componentsWnd.resizeDragger_UpperLeft.sprites[i].params.corners_rounding[0].radius = 10; + } + for (int i = 0; i < arraysize(editor->saveButton.sprites); ++i) + { + editor->saveButton.sprites[i].params.enableCornerRounding(); + editor->saveButton.sprites[i].params.corners_rounding[2].radius = 10; + } }); AddWidget(&themeCombo); diff --git a/WickedEngine/shaders/ShaderInterop_Image.h b/WickedEngine/shaders/ShaderInterop_Image.h index 3f41d52dc..779663dfd 100644 --- a/WickedEngine/shaders/ShaderInterop_Image.h +++ b/WickedEngine/shaders/ShaderInterop_Image.h @@ -2,32 +2,37 @@ #define WI_SHADERINTEROP_IMAGE_H #include "ShaderInterop.h" -static const uint IMAGE_FLAG_EXTRACT_NORMALMAP = 1 << 0; -static const uint IMAGE_FLAG_OUTPUT_COLOR_SPACE_HDR10_ST2084 = 1 << 1; -static const uint IMAGE_FLAG_OUTPUT_COLOR_SPACE_LINEAR = 1 << 2; -static const uint IMAGE_FLAG_FULLSCREEN = 1 << 3; +static const uint IMAGE_FLAG_EXTRACT_NORMALMAP = 1u << 0u; +static const uint IMAGE_FLAG_OUTPUT_COLOR_SPACE_HDR10_ST2084 = 1u << 1u; +static const uint IMAGE_FLAG_OUTPUT_COLOR_SPACE_LINEAR = 1u << 2u; +static const uint IMAGE_FLAG_FULLSCREEN = 1u << 3u; +static const uint IMAGE_FLAG_MIRROR = 1u << 4u; +static const uint IMAGE_FLAG_CORNER_ROUNDING = 1u << 5u; struct ImageConstants { - float4 corners0; - float4 corners1; - float4 corners2; - float4 corners3; + uint flags; + float hdr_scaling; + uint2 packed_color; // packed half4 uint2 texMulAdd; // packed half4 uint2 texMulAdd2; // packed half4 - uint2 output_resolution; - float2 output_resolution_rcp; - - uint2 packed_color; // packed half4 - uint flags; - float hdr_scaling; - + int buffer_index; + uint buffer_offset; int sampler_index; int texture_base_index; + int texture_mask_index; int texture_background_index; + float border_soften; + int padding0; + + // parameters for inverse bilinear interpolation: + uint b0; // packed half2 + uint b1; // packed half2 + uint b2; // packed half2 + uint b3; // packed half2 }; CONSTANTBUFFER(image, ImageConstants, CBSLOT_IMAGE); diff --git a/WickedEngine/shaders/imageHF.hlsli b/WickedEngine/shaders/imageHF.hlsli index 9c2baeff7..6c6918bfb 100644 --- a/WickedEngine/shaders/imageHF.hlsli +++ b/WickedEngine/shaders/imageHF.hlsli @@ -11,14 +11,13 @@ float Wedge2D(float2 v, float2 w) struct VertextoPixel { float4 pos : SV_POSITION; - float2 q : TEXCOORD3; - float2 b1 : TEXCOORD4; - float2 b2 : TEXCOORD5; - float2 b3 : TEXCOORD6; + float4 screen : TEXCOORD0; + float2 q : TEXCOORD1; + float2 edge : TEXCOORD2; float2 uv_screen() { - return pos.xy * image.output_resolution_rcp; + return clipspace_to_uv(screen.xy / screen.w); } float4 compute_uvs() { @@ -34,6 +33,9 @@ struct VertextoPixel else { // Quad interpolation: http://reedbeta.com/blog/quadrilateral-interpolation-part-2/ + float2 b1 = unpack_half2(image.b1); + float2 b2 = unpack_half2(image.b2); + float2 b3 = unpack_half2(image.b3); // Set up quadratic formula float A = Wedge2D(b2, b3); @@ -68,6 +70,12 @@ struct VertextoPixel uv1 = mad(uv, texMulAdd2.xy, texMulAdd2.zw); } + if (image.flags & IMAGE_FLAG_MIRROR) + { + uv0.x = 1 - uv0.x; + uv1.x = 1 - uv1.x; + } + return float4(uv0, uv1); } }; diff --git a/WickedEngine/shaders/imagePS.hlsl b/WickedEngine/shaders/imagePS.hlsl index 7935e1d75..041131b2f 100644 --- a/WickedEngine/shaders/imagePS.hlsl +++ b/WickedEngine/shaders/imagePS.hlsl @@ -53,5 +53,11 @@ float4 main(VertextoPixel input) : SV_TARGET color.rgb *= image.hdr_scaling; } + if (image.border_soften > 0) + { + float edge = max(abs(input.edge.x), abs(input.edge.y)); + color.a *= smoothstep(0, image.border_soften, 1 - edge); + } + return color; } diff --git a/WickedEngine/shaders/imageVS.hlsl b/WickedEngine/shaders/imageVS.hlsl index 8a2b89ab0..3770cb1a8 100644 --- a/WickedEngine/shaders/imageVS.hlsl +++ b/WickedEngine/shaders/imageVS.hlsl @@ -1,9 +1,17 @@ #include "globals.hlsli" #include "imageHF.hlsli" +static const float2 QUAD_EDGE[] = { + float2(-1, 1), + float2(1, 1), + float2(-1, -1), + float2(1, -1), +}; + VertextoPixel main(uint vI : SV_VertexID) { VertextoPixel Out; + Out.edge = 0; [branch] if (image.flags & IMAGE_FLAG_FULLSCREEN) @@ -12,38 +20,24 @@ VertextoPixel main(uint vI : SV_VertexID) } else { - // This vertex shader generates a trianglestrip like this: - // 1--2 - // / - // / - // 3--4 - - // If the corners are push constants, they cannot be indexed dynamically - // (This was only a problem on AMD in practice) - switch (vI) - { - default: - case 0: - Out.pos = image.corners0; - break; - case 1: - Out.pos = image.corners1; - break; - case 2: - Out.pos = image.corners2; - break; - case 3: - Out.pos = image.corners3; - break; - } + Out.pos = bindless_buffers[image.buffer_index].Load(image.buffer_offset + vI * sizeof(float4)); // Set up inverse bilinear interpolation - Out.q = Out.pos.xy - image.corners0.xy; - Out.b1 = image.corners1.xy - image.corners0.xy; - Out.b2 = image.corners2.xy - image.corners0.xy; - Out.b3 = image.corners0.xy - image.corners1.xy - image.corners2.xy + image.corners3.xy; + Out.q = Out.pos.xy - unpack_half2(image.b0); + + if (image.flags & IMAGE_FLAG_CORNER_ROUNDING) + { + // triangle fan, complex shape; center vertex is not edge, rest are edge: + Out.edge = vI == 0 ? 0 : 1; + } + else + { + // simple rectange shape, edge weight is based on uvs: + Out.edge = QUAD_EDGE[vI]; + } } + Out.screen = Out.pos; return Out; } diff --git a/WickedEngine/wiGUI.cpp b/WickedEngine/wiGUI.cpp index 7bca1423c..c2cf708ea 100644 --- a/WickedEngine/wiGUI.cpp +++ b/WickedEngine/wiGUI.cpp @@ -816,7 +816,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx(translation.x - shadow, translation.y - shadow, scale.x + shadow * 2, scale.y + shadow * 2, shadow_color); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2; + fx.siz.y += shadow * 2; + fx.color = shadow_color; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); } @@ -1147,7 +1152,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx(translation.x - shadow, translation.y - shadow, scale.x + shadow * 2, scale.y + shadow * 2, shadow_color); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2; + fx.siz.y += shadow * 2; + fx.color = shadow_color; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); } @@ -1392,7 +1402,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx(translation.x - shadow, translation.y - shadow, scale.x + shadow * 2, scale.y + shadow * 2, shadow_color); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2; + fx.siz.y += shadow * 2; + fx.color = shadow_color; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); } @@ -1724,7 +1739,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx(translation.x - shadow, translation.y - shadow, scale.x + 1 + valueInputField.GetSize().x + shadow * 2, scale.y + shadow * 2, shadow_color); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2 + 1 + valueInputField.GetSize().x; + fx.siz.y += shadow * 2; + fx.color = shadow_color; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); } @@ -1879,13 +1899,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx( - translation.x - shadow, - translation.y - shadow, - scale.x + shadow * 2, - scale.y + shadow * 2, - shadow_color - ); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2; + fx.siz.y += shadow * 2; + fx.color = shadow_color; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); } @@ -2157,7 +2176,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx(translation.x - shadow, translation.y - shadow, scale.x + 1 + scale.y + shadow * 2, scale.y + shadow * 2, shadow_color); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2 + 1 + scale.y; + fx.siz.y += shadow * 2; + fx.color = shadow_color; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); } @@ -2862,7 +2886,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx(translation.x - shadow, translation.y - shadow, scale.x + shadow * 2, scale.y + shadow * 2, shadow_color); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2; + fx.siz.y += shadow * 2; + fx.color = shadow_color; if (IsMinimized()) { fx.siz.y = control_size + shadow * 2; @@ -3177,6 +3206,10 @@ namespace wi::gui label_size.x -= control_size; label_pos.x += control_size; } + if (resizeDragger_UpperRight.parent != nullptr) + { + label_size.x -= control_size; + } if (closeButton.parent != nullptr) { label_size.x -= control_size; @@ -4190,7 +4223,12 @@ namespace wi::gui // shadow: if (shadow > 0) { - wi::image::Params fx(translation.x - shadow, translation.y - shadow, scale.x + shadow * 2, scale.y + shadow * 2, shadow_color); + wi::image::Params fx = sprites[state].params; + fx.pos.x -= shadow; + fx.pos.y -= shadow; + fx.siz.x += shadow * 2; + fx.siz.y = scale.y + shadow * 2; + fx.color = shadow_color; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); } diff --git a/WickedEngine/wiGUI.h b/WickedEngine/wiGUI.h index acf778675..2298e9f2a 100644 --- a/WickedEngine/wiGUI.h +++ b/WickedEngine/wiGUI.h @@ -101,6 +101,8 @@ namespace wi::gui wi::image::SAMPLEMODE sampleFlag = wi::image::Params().sampleFlag; wi::image::QUALITY quality = wi::image::Params().quality; bool background = wi::image::Params().isBackgroundEnabled(); + bool corner_rounding = wi::image::Params().isCornerRoundingEnabled(); + wi::image::Params::Rounding corners_rounding[arraysize(wi::image::Params().corners_rounding)]; void Apply(wi::image::Params& params) const { @@ -116,6 +118,15 @@ namespace wi::gui { params.disableBackground(); } + if (corner_rounding) + { + params.enableCornerRounding(); + } + else + { + params.disableCornerRounding(); + } + std::memcpy(params.corners_rounding, corners_rounding, sizeof(corners_rounding)); } void CopyFrom(const wi::image::Params& params) { @@ -131,6 +142,15 @@ namespace wi::gui { background = false; } + if (params.isCornerRoundingEnabled()) + { + corner_rounding = true; + } + else + { + corner_rounding = false; + } + std::memcpy(corners_rounding, params.corners_rounding, sizeof(corners_rounding)); } } image; diff --git a/WickedEngine/wiGraphicsDevice.h b/WickedEngine/wiGraphicsDevice.h index 3ac8c6c64..225b06956 100644 --- a/WickedEngine/wiGraphicsDevice.h +++ b/WickedEngine/wiGraphicsDevice.h @@ -204,8 +204,6 @@ namespace wi::graphics virtual void EventEnd(CommandList cmd) = 0; virtual void SetMarker(const char* name, CommandList cmd) = 0; - virtual const RenderPass* GetCurrentRenderPass(CommandList cmd) const = 0; - // Some useful helpers: diff --git a/WickedEngine/wiGraphicsDevice_DX12.h b/WickedEngine/wiGraphicsDevice_DX12.h index c2500bee9..70ea1b37c 100644 --- a/WickedEngine/wiGraphicsDevice_DX12.h +++ b/WickedEngine/wiGraphicsDevice_DX12.h @@ -312,11 +312,6 @@ namespace wi::graphics void EventEnd(CommandList cmd) override; void SetMarker(const char* name, CommandList cmd) override; - const RenderPass* GetCurrentRenderPass(CommandList cmd) const override - { - const CommandList_DX12& commandlist = GetCommandList(cmd); - return commandlist.active_renderpass; - } GPULinearAllocator& GetFrameAllocator(CommandList cmd) override { return GetCommandList(cmd).frame_allocators[GetBufferIndex()]; diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.h b/WickedEngine/wiGraphicsDevice_Vulkan.h index 7a0f1b9a7..425ce78e0 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.h +++ b/WickedEngine/wiGraphicsDevice_Vulkan.h @@ -412,11 +412,6 @@ namespace wi::graphics void EventEnd(CommandList cmd) override; void SetMarker(const char* name, CommandList cmd) override; - const RenderPass* GetCurrentRenderPass(CommandList cmd) const override - { - const CommandList_Vulkan& commandlist = GetCommandList(cmd); - return commandlist.active_renderpass; - } GPULinearAllocator& GetFrameAllocator(CommandList cmd) override { return GetCommandList(cmd).frame_allocators[GetBufferIndex()]; diff --git a/WickedEngine/wiImage.cpp b/WickedEngine/wiImage.cpp index dfa15b15f..9322bff71 100644 --- a/WickedEngine/wiImage.cpp +++ b/WickedEngine/wiImage.cpp @@ -18,7 +18,13 @@ namespace wi::image static BlendState blendStates[BLENDMODE_COUNT]; static RasterizerState rasterizerState; static DepthStencilState depthStencilStates[STENCILMODE_COUNT][STENCILREFMODE_COUNT]; - static PipelineState imagePSO[BLENDMODE_COUNT][STENCILMODE_COUNT][STENCILREFMODE_COUNT]; + enum STRIP_MODE + { + STRIP_OFF, + STRIP_ON, + STRIP_MODE_COUNT, + }; + static PipelineState imagePSO[BLENDMODE_COUNT][STENCILMODE_COUNT][STENCILREFMODE_COUNT][STRIP_MODE_COUNT]; static thread_local Texture backgroundTexture; static thread_local wi::Canvas canvas; @@ -67,6 +73,8 @@ namespace wi::image } ImageConstants image = {}; + image.buffer_index = -1; + image.buffer_offset = 0; image.texture_base_index = device->GetDescriptorIndex(texture, SubresourceType::SRV); image.texture_mask_index = device->GetDescriptorIndex(params.maskMap, SubresourceType::SRV); if (params.isBackgroundEnabled()) @@ -81,15 +89,6 @@ namespace wi::image if (image.sampler_index < 0) return; - const RenderPass* renderpass = device->GetCurrentRenderPass(cmd); - assert(renderpass != nullptr); // image renderer must draw inside render pass! - assert(!renderpass->GetDesc().attachments.empty()); - assert(renderpass->GetDesc().attachments.front().texture != nullptr); - image.output_resolution.x = renderpass->GetDesc().attachments.front().texture->GetDesc().width; - image.output_resolution.y = renderpass->GetDesc().attachments.front().texture->GetDesc().height; - image.output_resolution_rcp.x = 1.0f / image.output_resolution.x; - image.output_resolution_rcp.y = 1.0f / image.output_resolution.y; - XMFLOAT4 color = params.color; const float darken = 1 - params.fade; color.x *= darken; @@ -125,21 +124,35 @@ namespace wi::image image.flags |= IMAGE_FLAG_FULLSCREEN; } - XMMATRIX M = XMMatrixScaling(params.scale.x * params.siz.x, params.scale.y * params.siz.y, 1); - M = M * XMMatrixRotationZ(params.rotation); + image.border_soften = params.border_soften; - if (params.customRotation != nullptr) + size_t vertex_size = sizeof(float4); + + STRIP_MODE strip_mode = STRIP_ON; + uint32_t index_count = 0; + + if (params.isFullScreenEnabled()) { - M = M * (*params.customRotation); + // full screen triangle, no vertex buffer: + image.buffer_index = -1; + image.buffer_offset = 0; } - - M = M * XMMatrixTranslation(params.pos.x, params.pos.y, params.pos.z); - - if (!params.isFullScreenEnabled()) + else { + // vertex buffer: + XMMATRIX S = XMMatrixScaling(params.scale.x * params.siz.x, params.scale.y * params.siz.y, 1); + XMMATRIX M = XMMatrixRotationZ(params.rotation); + + if (params.customRotation != nullptr) + { + M = M * (*params.customRotation); + } + + M = M * XMMatrixTranslation(params.pos.x, params.pos.y, params.pos.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" + S = XMMatrixScaling(1, -1, 1) * S; // 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 @@ -151,28 +164,119 @@ namespace wi::image assert(canvas.dpi > 0); M = M * canvas.GetProjection(); } + + XMVECTOR V[4]; + float4 corners[4]; + for (int i = 0; i < arraysize(params.corners); ++i) + { + V[i] = XMVectorSet(params.corners[i].x - params.pivot.x, params.corners[i].y - params.pivot.y, 0, 1); + V[i] = XMVector2Transform(V[i], S); + XMStoreFloat4(corners + i, XMVector2Transform(V[i], M)); // division by w will happen on GPU + } + + image.b0 = XMHALF2(corners[0].x, corners[0].y).v; + image.b1 = XMHALF2(corners[1].x - corners[0].x, corners[1].y - corners[0].y).v; + image.b2 = XMHALF2(corners[2].x - corners[0].x, corners[2].y - corners[0].y).v; + image.b3 = XMHALF2(corners[0].x - corners[1].x - corners[2].x + corners[3].x, corners[0].y - corners[1].y - corners[2].y + corners[3].y).v; + + if (params.isCornerRoundingEnabled()) + { + // The rounded corner mode will use a triangle fan structure (implemrnted by indexed triangle list): + strip_mode = STRIP_OFF; + image.flags |= IMAGE_FLAG_CORNER_ROUNDING; + size_t vertex_count = 1; // start with center vertex + const int min_segment_count = 2; + for (int i = 0; i < arraysize(params.corners_rounding); ++i) + { + int segments = std::max(min_segment_count, params.corners_rounding[i].segments); + vertex_count += segments; + index_count += segments * 3; + } + index_count += 3; // closing triangle + const size_t vb_size = sizeof(float4) * vertex_count; + const size_t ib_size = sizeof(uint16_t) * index_count; + GraphicsDevice::GPUAllocation mem = device->AllocateGPU(vb_size + ib_size, cmd); + image.buffer_index = device->GetDescriptorIndex(&mem.buffer, SubresourceType::SRV); + image.buffer_offset = (uint)mem.offset; + device->BindIndexBuffer(&mem.buffer, IndexBufferFormat::UINT16, mem.offset + vb_size, cmd); + float4* vertices = (float4*)mem.data; + uint16_t* indices = (uint16_t*)((uint8_t*)mem.data + vb_size); + uint32_t vi = 0; + uint32_t ii = 0; + XMStoreFloat4(vertices + vi, XMVector2Transform((V[0] + V[1] + V[2] + V[3]) * 0.25f, M)); // center vertex + vi++; + for (int i = 0; i < arraysize(params.corners_rounding); ++i) + { + Params::Rounding rounding; + XMVECTOR A; + XMVECTOR B; + XMVECTOR C; + switch (i) + { + default: + case 0: + rounding = params.corners_rounding[0]; + A = V[2]; + B = V[0]; + C = V[1]; + break; + case 1: + rounding = params.corners_rounding[1]; + A = V[0]; + B = V[1]; + C = V[3]; + break; + case 2: + rounding = params.corners_rounding[3]; + A = V[1]; + B = V[3]; + C = V[2]; + break; + case 3: + rounding = params.corners_rounding[2]; + A = V[3]; + B = V[2]; + C = V[0]; + break; + } + rounding.segments = std::max(min_segment_count, rounding.segments); + const XMVECTOR BA = A - B; + const XMVECTOR BC = C - B; + const XMVECTOR BA_length = XMVector2Length(BA); + const XMVECTOR BC_length = XMVector2Length(BC); + const XMVECTOR radius = XMVectorReplicate(rounding.radius); + const XMVECTOR start = B + BA / BA_length * XMVectorMin(radius, BA_length * 0.5f); + const XMVECTOR end = B + BC / BC_length * XMVectorMin(radius, BC_length * 0.5f); + + for (int j = 0; j < rounding.segments; ++j) + { + float t = float(j) / float(rounding.segments - 1); + XMVECTOR bezier = wi::math::GetQuadraticBezierPos(start, B, end, t); + XMStoreFloat4(vertices + vi, XMVector2Transform(bezier, M)); + indices[ii++] = 0; + indices[ii++] = vi - 1; + indices[ii++] = vi; + vi++; + } + } + // closing the triangle fan: + indices[ii++] = 0; + indices[ii++] = vi - 1; + indices[ii++] = 1; + } + else + { + // Non rounded image will simply use a 4 vertex triangle strip (simple quad) + GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(float4) * 4, cmd); + image.buffer_index = device->GetDescriptorIndex(&mem.buffer, SubresourceType::SRV); + image.buffer_offset = (uint)mem.offset; + std::memcpy(mem.data, corners, sizeof(corners)); + } } - XMVECTOR V = XMVectorSet(params.corners[0].x - params.pivot.x, params.corners[0].y - params.pivot.y, 0, 1); - V = XMVector2Transform(V, M); // division by w will happen on GPU - XMStoreFloat4(&image.corners0, V); - - V = XMVectorSet(params.corners[1].x - params.pivot.x, params.corners[1].y - params.pivot.y, 0, 1); - V = XMVector2Transform(V, M); // division by w will happen on GPU - XMStoreFloat4(&image.corners1, V); - - V = XMVectorSet(params.corners[2].x - params.pivot.x, params.corners[2].y - params.pivot.y, 0, 1); - V = XMVector2Transform(V, M); // division by w will happen on GPU - XMStoreFloat4(&image.corners2, V); - - V = XMVectorSet(params.corners[3].x - params.pivot.x, params.corners[3].y - params.pivot.y, 0, 1); - V = XMVector2Transform(V, M); // division by w will happen on GPU - XMStoreFloat4(&image.corners3, V); - if (params.isMirrorEnabled()) { - std::swap(image.corners0, image.corners1); - std::swap(image.corners2, image.corners3); + image.flags |= IMAGE_FLAG_MIRROR; } const TextureDesc& desc = texture->GetDesc(); @@ -232,17 +336,26 @@ namespace wi::image } device->BindStencilRef(stencilRef, cmd); - device->BindPipelineState(&imagePSO[params.blendFlag][params.stencilComp][params.stencilRefMode], cmd); + device->BindPipelineState(&imagePSO[params.blendFlag][params.stencilComp][params.stencilRefMode][strip_mode], cmd); device->BindDynamicConstantBuffer(image, CBSLOT_IMAGE, cmd); if (params.isFullScreenEnabled()) { - device->Draw(3, 0, cmd); + device->Draw(3, 0, cmd); // full screen triangle } else { - device->Draw(4, 0, cmd); + switch (strip_mode) + { + case wi::image::STRIP_OFF: + device->DrawIndexed(index_count, 0, 0, cmd); // corner rounding with indexed geometry + break; + case wi::image::STRIP_ON: + default: + device->Draw(4, 0, cmd); // simple quad + break; + } } device->EventEnd(cmd); @@ -260,7 +373,6 @@ namespace wi::image desc.vs = &vertexShader; desc.ps = &pixelShader; desc.rs = &rasterizerState; - desc.pt = PrimitiveTopology::TRIANGLESTRIP; for (int j = 0; j < BLENDMODE_COUNT; ++j) { @@ -271,7 +383,20 @@ namespace wi::image { desc.dss = &depthStencilStates[k][m]; - device->CreatePipelineState(&desc, &imagePSO[j][k][m]); + for (int n = 0; n < STRIP_MODE_COUNT; ++n) + { + switch (n) + { + default: + case STRIP_ON: + desc.pt = PrimitiveTopology::TRIANGLESTRIP; + break; + case STRIP_OFF: + desc.pt = PrimitiveTopology::TRIANGLELIST; + break; + } + device->CreatePipelineState(&desc, &imagePSO[j][k][m][n]); + } } } diff --git a/WickedEngine/wiImage.h b/WickedEngine/wiImage.h index 37f1e4a91..4743b87b3 100644 --- a/WickedEngine/wiImage.h +++ b/WickedEngine/wiImage.h @@ -55,6 +55,7 @@ namespace wi::image BACKGROUND = 1 << 5, OUTPUT_COLOR_SPACE_HDR10_ST2084 = 1 << 6, OUTPUT_COLOR_SPACE_LINEAR = 1 << 7, + CORNER_ROUNDING = 1 << 8, }; uint32_t _flags = EMPTY; @@ -80,6 +81,14 @@ namespace wi::image const XMMATRIX* customRotation = nullptr; const XMMATRIX* customProjection = nullptr; + struct Rounding + { + float radius = 0; // the radius of corner (in logical pixel units) + int segments = 18; // how many segments to add to smoothing curve + } corners_rounding[4]; // specify rounding corners (0: top left, 1: top right, 2: bottom left, 3: bottom right) + + float border_soften = 0; // how much alpha softening to apply to image border in range [0, 1] (0: disable) + uint8_t stencilRef = 0; STENCILMODE stencilComp = STENCILMODE_DISABLED; STENCILREFMODE stencilRefMode = STENCILREFMODE_ALL; @@ -100,6 +109,7 @@ namespace wi::image constexpr bool isBackgroundEnabled() const { return _flags & BACKGROUND; } constexpr bool isHDR10OutputMappingEnabled() const { return _flags & OUTPUT_COLOR_SPACE_HDR10_ST2084; } constexpr bool isLinearOutputMappingEnabled() const { return _flags & OUTPUT_COLOR_SPACE_LINEAR; } + constexpr bool isCornerRoundingEnabled() const { return _flags & CORNER_ROUNDING; } // enables draw rectangle for base texture (cutout texture outside draw rectangle) constexpr void enableDrawRect(const XMFLOAT4& rect) { _flags |= DRAWRECT; drawRect = rect; } @@ -118,6 +128,7 @@ namespace wi::image 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 enableCornerRounding(float scaling = 1.0f) { _flags |= CORNER_ROUNDING; hdr_scaling = scaling; } // disable draw rectangle for base texture (whole texture will be drawn, no cutout) constexpr void disableDrawRect() { _flags &= ~DRAWRECT; } @@ -129,6 +140,7 @@ namespace wi::image constexpr void disableBackground() { _flags &= ~BACKGROUND; } constexpr void disableHDR10OutputMapping() { _flags &= ~OUTPUT_COLOR_SPACE_HDR10_ST2084; } constexpr void disableLinearOutputMapping() { _flags &= ~OUTPUT_COLOR_SPACE_LINEAR; } + constexpr void disableCornerRounding() { _flags &= ~CORNER_ROUNDING; } Params() = default; diff --git a/WickedEngine/wiMath.cpp b/WickedEngine/wiMath.cpp index 48dc754d0..daf719a6f 100644 --- a/WickedEngine/wiMath.cpp +++ b/WickedEngine/wiMath.cpp @@ -23,17 +23,23 @@ namespace wi::math } - XMFLOAT3 getCubicHermiteSplinePos(const XMFLOAT3& startPos, const XMFLOAT3& endPos - , const XMFLOAT3& startTangent, const XMFLOAT3& endTangent - , float atInterval){ - float x, y, z, t; float r1 = 1.0f, r4 = 1.0f; + XMFLOAT3 GetCubicHermiteSplinePos( + const XMFLOAT3& startPos, + const XMFLOAT3& endPos, + const XMFLOAT3& startTangent, + const XMFLOAT3& endTangent, + float atInterval + ) + { + float x, y, z, t; + float r1 = 1.0f, r4 = 1.0f; t = atInterval; x = (2 * t*t*t - 3 * t*t + 1)*startPos.x + (-2 * t*t*t + 3 * t*t)*endPos.x + (t*t*t - 2 * t*t + t)*startTangent.x + (t*t*t - t*t)*endTangent.x; y = (2 * t*t*t - 3 * t*t + 1)*startPos.y + (-2 * t*t*t + 3 * t*t)*endPos.y + (t*t*t - 2 * t*t + 1)*startTangent.y + (t*t*t - t*t)*endTangent.y; z = (2 * t*t*t - 3 * t*t + 1)*startPos.z + (-2 * t*t*t + 3 * t*t)*endPos.z + (t*t*t - 2 * t*t + 1)*startTangent.z + (t*t*t - t*t)*endTangent.z; return XMFLOAT3(x, y, z); } - XMFLOAT3 getQuadraticBezierPos(const XMFLOAT3& a, const XMFLOAT3&b, const XMFLOAT3& c, float t){ + XMFLOAT3 GetQuadraticBezierPos(const XMFLOAT3& a, const XMFLOAT3&b, const XMFLOAT3& c, float t){ float param0, param1, param2; param0 = powf(1 - t, 2); param1 = 2 * (1 - t)*t; @@ -43,8 +49,8 @@ namespace wi::math float z = param0*a.z + param1*b.z + param2*c.z; return XMFLOAT3(x, y, z); } - XMFLOAT3 getQuadraticBezierPos(const XMFLOAT4& a, const XMFLOAT4&b, const XMFLOAT4& c, float t){ - return getQuadraticBezierPos(XMFLOAT3(a.x, a.y, a.z), XMFLOAT3(b.x, b.y, b.z), XMFLOAT3(c.x, c.y, c.z), t); + XMFLOAT3 GetQuadraticBezierPos(const XMFLOAT4& a, const XMFLOAT4&b, const XMFLOAT4& c, float t){ + return GetQuadraticBezierPos(XMFLOAT3(a.x, a.y, a.z), XMFLOAT3(b.x, b.y, b.z), XMFLOAT3(c.x, c.y, c.z), t); } XMFLOAT3 QuaternionToRollPitchYaw(const XMFLOAT4& quaternion) diff --git a/WickedEngine/wiMath.h b/WickedEngine/wiMath.h index 9551940b8..82f5e5324 100644 --- a/WickedEngine/wiMath.h +++ b/WickedEngine/wiMath.h @@ -228,11 +228,25 @@ namespace wi::math // a, b, c: trangle side lengths float TriangleArea(float a, float b, float c); - XMFLOAT3 getCubicHermiteSplinePos(const XMFLOAT3& startPos, const XMFLOAT3& endPos - , const XMFLOAT3& startTangent, const XMFLOAT3& endTangent - , float atInterval); - XMFLOAT3 getQuadraticBezierPos(const XMFLOAT3& a,const XMFLOAT3&b, const XMFLOAT3& c, float t); - XMFLOAT3 getQuadraticBezierPos(const XMFLOAT4& a,const XMFLOAT4&b, const XMFLOAT4& c, float t); + XMFLOAT3 GetCubicHermiteSplinePos( + const XMFLOAT3& startPos, + const XMFLOAT3& endPos, + const XMFLOAT3& startTangent, + const XMFLOAT3& endTangent, + float atInterval + ); + XMFLOAT3 GetQuadraticBezierPos(const XMFLOAT3& a,const XMFLOAT3&b, const XMFLOAT3& c, float t); + XMFLOAT3 GetQuadraticBezierPos(const XMFLOAT4& a, const XMFLOAT4& b, const XMFLOAT4& c, float t); + inline XMVECTOR GetQuadraticBezierPos(const XMVECTOR& a, const XMVECTOR& b, const XMVECTOR& c, float t) + { + // XMVECTOR optimized version + const float param0 = std::pow(1 - t, 2.0f); + const float param1 = 2 * (1 - t) * t; + const float param2 = std::pow(t, 2.0f); + const XMVECTOR param = XMVectorSet(param0, param1, param2, 1); + const XMMATRIX M = XMMATRIX(a, b, c, XMVectorSet(0, 0, 0, 1)); + return XMVector3TransformNormal(param, M); + } XMFLOAT3 QuaternionToRollPitchYaw(const XMFLOAT4& quaternion); @@ -296,7 +310,6 @@ namespace wi::math - //----------------------------------------------------------------------------- // Compute the intersection of a ray (Origin, Direction) with a triangle // (V0, V1, V2). Return true if there is an intersection and also set *pDist diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index c22d6239f..d5c991738 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 = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 0; + const int revision = 1; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);