engine: sync editor rendering and ui changes
This commit is contained in:
@@ -1,5 +0,0 @@
|
|||||||
fileFormatVersion: 1
|
|
||||||
guid: 60a2f7fbbea9b0ef86cefe4d4ea75578
|
|
||||||
folderAsset: true
|
|
||||||
importer: FolderImporter
|
|
||||||
importerVersion: 5
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
fileFormatVersion: 1
|
|
||||||
guid: 6dd3f8fecfde5a3f1d52cb3a9e18ed53
|
|
||||||
folderAsset: true
|
|
||||||
importer: FolderImporter
|
|
||||||
importerVersion: 5
|
|
||||||
@@ -16,7 +16,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
option(XCENGINE_ENABLE_MONO_SCRIPTING "Build the Mono-based C# scripting runtime" ON)
|
option(XCENGINE_ENABLE_MONO_SCRIPTING "Build the Mono-based C# scripting runtime" ON)
|
||||||
option(XCENGINE_BUILD_NEW_EDITOR "Build the XCUI new_editor shell app" ON)
|
option(XCENGINE_BUILD_XCUI_EDITOR_APP "Build the XCUI editor shell app" ON)
|
||||||
set(
|
set(
|
||||||
XCENGINE_MONO_ROOT_DIR
|
XCENGINE_MONO_ROOT_DIR
|
||||||
"${CMAKE_SOURCE_DIR}/参考/Fermion/Fermion/external/mono"
|
"${CMAKE_SOURCE_DIR}/参考/Fermion/Fermion/external/mono"
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"cmakeMinimumRequired": {
|
|
||||||
"major": 3,
|
|
||||||
"minor": 21,
|
|
||||||
"patch": 0
|
|
||||||
},
|
|
||||||
"configurePresets": [
|
|
||||||
{
|
|
||||||
"name": "vs2022-x64",
|
|
||||||
"displayName": "Visual Studio 2022 x64",
|
|
||||||
"generator": "Visual Studio 17 2022",
|
|
||||||
"binaryDir": "${sourceDir}/build"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"buildPresets": [
|
|
||||||
{
|
|
||||||
"name": "debug",
|
|
||||||
"displayName": "Build Debug",
|
|
||||||
"configurePreset": "vs2022-x64",
|
|
||||||
"configuration": "Debug"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"testPresets": [
|
|
||||||
{
|
|
||||||
"name": "debug",
|
|
||||||
"displayName": "Test Debug",
|
|
||||||
"configurePreset": "vs2022-x64",
|
|
||||||
"configuration": "Debug",
|
|
||||||
"output": {
|
|
||||||
"outputOnFailure": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.12)
|
|||||||
project(Doom)
|
project(Doom)
|
||||||
|
|
||||||
#本工程支持的C++版本
|
#本工程支持的C++版本
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
file(GLOB resources "../res")
|
file(GLOB resources "../res")
|
||||||
file(COPY ${resources} DESTINATION ${CMAKE_BINARY_DIR})
|
file(COPY ${resources} DESTINATION ${CMAKE_BINARY_DIR})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.12)
|
|||||||
|
|
||||||
project("XCRender")
|
project("XCRender")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
include_directories(./package/include/)
|
include_directories(./package/include/)
|
||||||
include_directories(./package/glm/)
|
include_directories(./package/glm/)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ project(XCVolumeRenderer VERSION 1.0 LANGUAGES CXX)
|
|||||||
set(VCPKG_ROOT "D:/vcpkg")
|
set(VCPKG_ROOT "D:/vcpkg")
|
||||||
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "")
|
set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" CACHE STRING "")
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
project(XCVolumeRendererUI2 VERSION 1.0 LANGUAGES CXX)
|
project(XCVolumeRendererUI2 VERSION 1.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
add_definitions(-DUNICODE -D_UNICODE)
|
add_definitions(-DUNICODE -D_UNICODE)
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ add_executable(${PROJECT_NAME} WIN32
|
|||||||
src/panels/MenuBar.cpp
|
src/panels/MenuBar.cpp
|
||||||
src/panels/HierarchyPanel.cpp
|
src/panels/HierarchyPanel.cpp
|
||||||
src/panels/SceneViewPanel.cpp
|
src/panels/SceneViewPanel.cpp
|
||||||
|
src/panels/MaterialInspectorMaterialStateIO.cpp
|
||||||
src/Viewport/SceneViewportPicker.cpp
|
src/Viewport/SceneViewportPicker.cpp
|
||||||
src/Viewport/SceneViewportMoveGizmo.cpp
|
src/Viewport/SceneViewportMoveGizmo.cpp
|
||||||
src/Viewport/SceneViewportRotateGizmo.cpp
|
src/Viewport/SceneViewportRotateGizmo.cpp
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
editor/resources/Icons/point_light_gizmo.png
Normal file
BIN
editor/resources/Icons/point_light_gizmo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 875 B |
BIN
editor/resources/Icons/spot_light_gizmo.png
Normal file
BIN
editor/resources/Icons/spot_light_gizmo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 519 B |
@@ -1,132 +0,0 @@
|
|||||||
// XC_EDITOR_SCENE_VIEW_INFINITE_GRID_D3D12_PS
|
|
||||||
cbuffer GridConstants : register(b0) {
|
|
||||||
float4x4 gViewProjectionMatrix;
|
|
||||||
float4 gCameraPositionAndScale;
|
|
||||||
float4 gCameraRightAndFade;
|
|
||||||
float4 gCameraUpAndTanHalfFov;
|
|
||||||
float4 gCameraForwardAndAspect;
|
|
||||||
float4 gViewportNearFar;
|
|
||||||
float4 gGridTransition;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VSOutput {
|
|
||||||
float4 position : SV_POSITION;
|
|
||||||
};
|
|
||||||
|
|
||||||
float PristineGridLine(float2 uv) {
|
|
||||||
float2 deriv = max(fwidth(uv), float2(1e-6, 1e-6));
|
|
||||||
float2 uvMod = frac(uv);
|
|
||||||
float2 uvDist = min(uvMod, 1.0 - uvMod);
|
|
||||||
float2 distInPixels = uvDist / deriv;
|
|
||||||
float2 lineAlpha = 1.0 - smoothstep(0.0, 1.0, distInPixels);
|
|
||||||
float density = max(deriv.x, deriv.y);
|
|
||||||
float densityFade = 1.0 - smoothstep(0.5, 1.0, density);
|
|
||||||
return max(lineAlpha.x, lineAlpha.y) * densityFade;
|
|
||||||
}
|
|
||||||
|
|
||||||
float AxisLineAA(float coord, float deriv) {
|
|
||||||
float distInPixels = abs(coord) / max(deriv, 1e-6);
|
|
||||||
return 1.0 - smoothstep(0.0, 1.5, distInPixels);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GridLayer {
|
|
||||||
float minor;
|
|
||||||
float major;
|
|
||||||
};
|
|
||||||
|
|
||||||
GridLayer SampleGridLayer(float2 worldPos2D, float baseScale) {
|
|
||||||
GridLayer layer;
|
|
||||||
const float2 gridCoord1 = worldPos2D / baseScale;
|
|
||||||
const float2 gridCoord10 = worldPos2D / (baseScale * 10.0);
|
|
||||||
const float grid1 = PristineGridLine(gridCoord1);
|
|
||||||
const float grid10 = PristineGridLine(gridCoord10);
|
|
||||||
const float2 deriv1 = fwidth(gridCoord1);
|
|
||||||
const float lodFactor = smoothstep(0.3, 0.6, max(deriv1.x, deriv1.y));
|
|
||||||
|
|
||||||
layer.major = max(grid10, grid1 * 0.35);
|
|
||||||
layer.minor = grid1 * (1.0 - lodFactor);
|
|
||||||
return layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PSOutput {
|
|
||||||
float4 color : SV_TARGET0;
|
|
||||||
float depth : SV_Depth;
|
|
||||||
};
|
|
||||||
|
|
||||||
PSOutput MainPS(VSOutput input) {
|
|
||||||
const float2 viewportSize = max(gViewportNearFar.xy, float2(1.0, 1.0));
|
|
||||||
const float scale = max(gCameraPositionAndScale.w, 1e-4);
|
|
||||||
const float fadeDistance = max(gCameraRightAndFade.w, scale * 10.0);
|
|
||||||
const float tanHalfFov = max(gCameraUpAndTanHalfFov.w, 1e-4);
|
|
||||||
const float aspect = max(gCameraForwardAndAspect.w, 1e-4);
|
|
||||||
const float transitionBlend = saturate(gGridTransition.x);
|
|
||||||
const float nearClip = gViewportNearFar.z;
|
|
||||||
const float sceneFarClip = gViewportNearFar.w;
|
|
||||||
|
|
||||||
const float2 ndc = float2(
|
|
||||||
(input.position.x / viewportSize.x) * 2.0 - 1.0,
|
|
||||||
1.0 - (input.position.y / viewportSize.y) * 2.0);
|
|
||||||
|
|
||||||
const float3 cameraPosition = gCameraPositionAndScale.xyz;
|
|
||||||
const float3 rayDirection = normalize(
|
|
||||||
gCameraForwardAndAspect.xyz +
|
|
||||||
ndc.x * aspect * tanHalfFov * gCameraRightAndFade.xyz +
|
|
||||||
ndc.y * tanHalfFov * gCameraUpAndTanHalfFov.xyz);
|
|
||||||
|
|
||||||
if (abs(rayDirection.y) < 1e-5) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float t = -cameraPosition.y / rayDirection.y;
|
|
||||||
if (t <= nearClip) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float3 worldPosition = cameraPosition + rayDirection * t;
|
|
||||||
float depth = 0.999999;
|
|
||||||
if (t < sceneFarClip) {
|
|
||||||
const float4 clipPosition = mul(gViewProjectionMatrix, float4(worldPosition, 1.0));
|
|
||||||
if (clipPosition.w <= 1e-6) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
depth = clipPosition.z / clipPosition.w;
|
|
||||||
if (depth <= 0.0 || depth >= 1.0) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const float radialFade =
|
|
||||||
1.0 - smoothstep(fadeDistance * 0.3, fadeDistance, length(worldPosition - cameraPosition));
|
|
||||||
const float normalFade = smoothstep(0.0, 0.15, abs(rayDirection.y));
|
|
||||||
const float fadeFactor = radialFade * normalFade;
|
|
||||||
if (fadeFactor < 1e-3) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float2 worldPos2D = worldPosition.xz;
|
|
||||||
const GridLayer baseLayer = SampleGridLayer(worldPos2D, scale);
|
|
||||||
const GridLayer nextLayer = SampleGridLayer(worldPos2D, scale * 10.0);
|
|
||||||
const float minorGridIntensity = lerp(baseLayer.minor, nextLayer.minor, transitionBlend);
|
|
||||||
const float majorGridIntensity = lerp(baseLayer.major, nextLayer.major, transitionBlend);
|
|
||||||
float3 finalColor = float3(0.56, 0.56, 0.56);
|
|
||||||
float finalAlpha = max(
|
|
||||||
0.13 * minorGridIntensity * fadeFactor,
|
|
||||||
0.28 * majorGridIntensity * fadeFactor);
|
|
||||||
|
|
||||||
const float2 worldDeriv = max(fwidth(worldPos2D), float2(1e-6, 1e-6));
|
|
||||||
const float xAxisAlpha = AxisLineAA(worldPos2D.y, worldDeriv.y) * fadeFactor;
|
|
||||||
const float zAxisAlpha = AxisLineAA(worldPos2D.x, worldDeriv.x) * fadeFactor;
|
|
||||||
|
|
||||||
const float axisAlpha = max(xAxisAlpha, zAxisAlpha);
|
|
||||||
finalAlpha = max(finalAlpha, 0.34 * saturate(axisAlpha));
|
|
||||||
|
|
||||||
if (finalAlpha < 1e-3) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
PSOutput output;
|
|
||||||
output.color = float4(finalColor, finalAlpha);
|
|
||||||
output.depth = depth;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
@@ -1,29 +1,177 @@
|
|||||||
|
Shader "Scene View Infinite Grid"
|
||||||
{
|
{
|
||||||
"name": "Scene View Infinite Grid",
|
HLSLINCLUDE
|
||||||
"passes": [
|
// XC_EDITOR_SCENE_VIEW_INFINITE_GRID_D3D12_SHARED
|
||||||
|
cbuffer GridConstants
|
||||||
{
|
{
|
||||||
"name": "InfiniteGrid",
|
float4x4 gViewProjectionMatrix;
|
||||||
"tags": {
|
float4 gCameraPositionAndScale;
|
||||||
"LightMode": "InfiniteGrid"
|
float4 gCameraRightAndFade;
|
||||||
},
|
float4 gCameraUpAndTanHalfFov;
|
||||||
"variants": [
|
float4 gCameraForwardAndAspect;
|
||||||
{
|
float4 gViewportNearFar;
|
||||||
"stage": "Vertex",
|
float4 gGridTransition;
|
||||||
"backend": "D3D12",
|
};
|
||||||
"language": "HLSL",
|
|
||||||
"source": "infinite-grid.vs.hlsl",
|
struct VSOutput
|
||||||
"entryPoint": "MainVS",
|
{
|
||||||
"profile": "vs_5_0"
|
float4 position : SV_POSITION;
|
||||||
},
|
};
|
||||||
{
|
|
||||||
"stage": "Fragment",
|
VSOutput MainVS(uint vertexId : SV_VertexID)
|
||||||
"backend": "D3D12",
|
{
|
||||||
"language": "HLSL",
|
// XC_EDITOR_SCENE_VIEW_INFINITE_GRID_D3D12_VS
|
||||||
"source": "infinite-grid.ps.hlsl",
|
static const float2 positions[3] = {
|
||||||
"entryPoint": "MainPS",
|
float2(-1.0, -1.0),
|
||||||
"profile": "ps_5_0"
|
float2(-1.0, 3.0),
|
||||||
}
|
float2( 3.0, -1.0)
|
||||||
]
|
};
|
||||||
|
|
||||||
|
VSOutput output;
|
||||||
|
output.position = float4(positions[vertexId], 0.0, 1.0);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
float PristineGridLine(float2 uv)
|
||||||
|
{
|
||||||
|
float2 deriv = max(fwidth(uv), float2(1e-6, 1e-6));
|
||||||
|
float2 uvMod = frac(uv);
|
||||||
|
float2 uvDist = min(uvMod, 1.0 - uvMod);
|
||||||
|
float2 distInPixels = uvDist / deriv;
|
||||||
|
float2 lineAlpha = 1.0 - smoothstep(0.0, 1.0, distInPixels);
|
||||||
|
float density = max(deriv.x, deriv.y);
|
||||||
|
float densityFade = 1.0 - smoothstep(0.5, 1.0, density);
|
||||||
|
return max(lineAlpha.x, lineAlpha.y) * densityFade;
|
||||||
|
}
|
||||||
|
|
||||||
|
float AxisLineAA(float coord, float deriv)
|
||||||
|
{
|
||||||
|
float distInPixels = abs(coord) / max(deriv, 1e-6);
|
||||||
|
return 1.0 - smoothstep(0.0, 1.5, distInPixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GridLayer
|
||||||
|
{
|
||||||
|
float minor;
|
||||||
|
float major;
|
||||||
|
};
|
||||||
|
|
||||||
|
GridLayer SampleGridLayer(float2 worldPos2D, float baseScale)
|
||||||
|
{
|
||||||
|
GridLayer layer;
|
||||||
|
const float2 gridCoord1 = worldPos2D / baseScale;
|
||||||
|
const float2 gridCoord10 = worldPos2D / (baseScale * 10.0);
|
||||||
|
const float grid1 = PristineGridLine(gridCoord1);
|
||||||
|
const float grid10 = PristineGridLine(gridCoord10);
|
||||||
|
const float2 deriv1 = fwidth(gridCoord1);
|
||||||
|
const float lodFactor = smoothstep(0.3, 0.6, max(deriv1.x, deriv1.y));
|
||||||
|
|
||||||
|
layer.major = max(grid10, grid1 * 0.35);
|
||||||
|
layer.minor = grid1 * (1.0 - lodFactor);
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PSOutput
|
||||||
|
{
|
||||||
|
float4 color : SV_TARGET0;
|
||||||
|
float depth : SV_Depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
PSOutput MainPS(VSOutput input)
|
||||||
|
{
|
||||||
|
// XC_EDITOR_SCENE_VIEW_INFINITE_GRID_D3D12_PS
|
||||||
|
const float2 viewportSize = max(gViewportNearFar.xy, float2(1.0, 1.0));
|
||||||
|
const float scale = max(gCameraPositionAndScale.w, 1e-4);
|
||||||
|
const float fadeDistance = max(gCameraRightAndFade.w, scale * 10.0);
|
||||||
|
const float tanHalfFov = max(gCameraUpAndTanHalfFov.w, 1e-4);
|
||||||
|
const float aspect = max(gCameraForwardAndAspect.w, 1e-4);
|
||||||
|
const float transitionBlend = saturate(gGridTransition.x);
|
||||||
|
const float nearClip = gViewportNearFar.z;
|
||||||
|
const float sceneFarClip = gViewportNearFar.w;
|
||||||
|
|
||||||
|
const float2 ndc = float2(
|
||||||
|
(input.position.x / viewportSize.x) * 2.0 - 1.0,
|
||||||
|
1.0 - (input.position.y / viewportSize.y) * 2.0);
|
||||||
|
|
||||||
|
const float3 cameraPosition = gCameraPositionAndScale.xyz;
|
||||||
|
const float3 rayDirection = normalize(
|
||||||
|
gCameraForwardAndAspect.xyz +
|
||||||
|
ndc.x * aspect * tanHalfFov * gCameraRightAndFade.xyz +
|
||||||
|
ndc.y * tanHalfFov * gCameraUpAndTanHalfFov.xyz);
|
||||||
|
|
||||||
|
if (abs(rayDirection.y) < 1e-5) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float t = -cameraPosition.y / rayDirection.y;
|
||||||
|
if (t <= nearClip) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float3 worldPosition = cameraPosition + rayDirection * t;
|
||||||
|
float depth = 0.999999;
|
||||||
|
if (t < sceneFarClip) {
|
||||||
|
const float4 clipPosition = mul(gViewProjectionMatrix, float4(worldPosition, 1.0));
|
||||||
|
if (clipPosition.w <= 1e-6) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
depth = clipPosition.z / clipPosition.w;
|
||||||
|
if (depth <= 0.0 || depth >= 1.0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const float radialFade =
|
||||||
|
1.0 - smoothstep(fadeDistance * 0.3, fadeDistance, length(worldPosition - cameraPosition));
|
||||||
|
const float normalFade = smoothstep(0.0, 0.15, abs(rayDirection.y));
|
||||||
|
const float fadeFactor = radialFade * normalFade;
|
||||||
|
if (fadeFactor < 1e-3) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float2 worldPos2D = worldPosition.xz;
|
||||||
|
const GridLayer baseLayer = SampleGridLayer(worldPos2D, scale);
|
||||||
|
const GridLayer nextLayer = SampleGridLayer(worldPos2D, scale * 10.0);
|
||||||
|
const float minorGridIntensity = lerp(baseLayer.minor, nextLayer.minor, transitionBlend);
|
||||||
|
const float majorGridIntensity = lerp(baseLayer.major, nextLayer.major, transitionBlend);
|
||||||
|
float3 finalColor = float3(0.56, 0.56, 0.56);
|
||||||
|
float finalAlpha = max(
|
||||||
|
0.13 * minorGridIntensity * fadeFactor,
|
||||||
|
0.28 * majorGridIntensity * fadeFactor);
|
||||||
|
|
||||||
|
const float2 worldDeriv = max(fwidth(worldPos2D), float2(1e-6, 1e-6));
|
||||||
|
const float xAxisAlpha = AxisLineAA(worldPos2D.y, worldDeriv.y) * fadeFactor;
|
||||||
|
const float zAxisAlpha = AxisLineAA(worldPos2D.x, worldDeriv.x) * fadeFactor;
|
||||||
|
|
||||||
|
const float axisAlpha = max(xAxisAlpha, zAxisAlpha);
|
||||||
|
finalAlpha = max(finalAlpha, 0.34 * saturate(axisAlpha));
|
||||||
|
|
||||||
|
if (finalAlpha < 1e-3) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
PSOutput output;
|
||||||
|
output.color = float4(finalColor, finalAlpha);
|
||||||
|
output.depth = depth;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
ENDHLSL
|
||||||
|
SubShader
|
||||||
|
{
|
||||||
|
Pass
|
||||||
|
{
|
||||||
|
Name "InfiniteGrid"
|
||||||
|
Tags { "LightMode" = "InfiniteGrid" }
|
||||||
|
Cull Off
|
||||||
|
ZWrite Off
|
||||||
|
ZTest LEqual
|
||||||
|
Blend SrcAlpha OneMinusSrcAlpha, One OneMinusSrcAlpha
|
||||||
|
HLSLPROGRAM
|
||||||
|
#pragma target 4.5
|
||||||
|
#pragma vertex MainVS
|
||||||
|
#pragma fragment MainPS
|
||||||
|
ENDHLSL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
// XC_EDITOR_SCENE_VIEW_INFINITE_GRID_D3D12_VS
|
|
||||||
cbuffer GridConstants : register(b0) {
|
|
||||||
float4x4 gViewProjectionMatrix;
|
|
||||||
float4 gCameraPositionAndScale;
|
|
||||||
float4 gCameraRightAndFade;
|
|
||||||
float4 gCameraUpAndTanHalfFov;
|
|
||||||
float4 gCameraForwardAndAspect;
|
|
||||||
float4 gViewportNearFar;
|
|
||||||
float4 gGridTransition;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VSOutput {
|
|
||||||
float4 position : SV_POSITION;
|
|
||||||
};
|
|
||||||
|
|
||||||
VSOutput MainVS(uint vertexId : SV_VertexID) {
|
|
||||||
static const float2 positions[3] = {
|
|
||||||
float2(-1.0, -1.0),
|
|
||||||
float2(-1.0, 3.0),
|
|
||||||
float2( 3.0, -1.0)
|
|
||||||
};
|
|
||||||
|
|
||||||
VSOutput output;
|
|
||||||
output.position = float4(positions[vertexId], 0.0, 1.0);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
// XC_EDITOR_SCENE_VIEW_OBJECT_ID_OUTLINE_D3D12_PS
|
|
||||||
cbuffer OutlineConstants : register(b0) {
|
|
||||||
float4 gViewportSizeAndTexelSize;
|
|
||||||
float4 gOutlineColor;
|
|
||||||
float4 gSelectedInfo;
|
|
||||||
float4 gSelectedObjectColors[256];
|
|
||||||
};
|
|
||||||
|
|
||||||
Texture2D gObjectIdTexture : register(t0);
|
|
||||||
|
|
||||||
struct VSOutput {
|
|
||||||
float4 position : SV_POSITION;
|
|
||||||
};
|
|
||||||
|
|
||||||
int2 ClampPixelCoord(int2 pixelCoord) {
|
|
||||||
const int2 maxCoord = int2(
|
|
||||||
max((int)gViewportSizeAndTexelSize.x - 1, 0),
|
|
||||||
max((int)gViewportSizeAndTexelSize.y - 1, 0));
|
|
||||||
return clamp(pixelCoord, int2(0, 0), maxCoord);
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 LoadObjectId(int2 pixelCoord) {
|
|
||||||
return gObjectIdTexture.Load(int3(ClampPixelCoord(pixelCoord), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsSelectedObject(float4 objectIdColor) {
|
|
||||||
if (objectIdColor.a <= 0.0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int selectedCount = min((int)gSelectedInfo.x, 256);
|
|
||||||
[loop]
|
|
||||||
for (int i = 0; i < selectedCount; ++i) {
|
|
||||||
const float4 selectedColor = gSelectedObjectColors[i];
|
|
||||||
if (all(abs(objectIdColor - selectedColor) <= float4(
|
|
||||||
0.0025,
|
|
||||||
0.0025,
|
|
||||||
0.0025,
|
|
||||||
0.0025))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 MainPS(VSOutput input) : SV_TARGET {
|
|
||||||
const int2 pixelCoord = int2(input.position.xy);
|
|
||||||
const bool debugSelectionMask = gSelectedInfo.y > 0.5;
|
|
||||||
const bool centerSelected = IsSelectedObject(LoadObjectId(pixelCoord));
|
|
||||||
|
|
||||||
if (debugSelectionMask) {
|
|
||||||
return centerSelected ? float4(1.0, 1.0, 1.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (centerSelected) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int outlineWidth = max((int)gSelectedInfo.z, 1);
|
|
||||||
float outline = 0.0;
|
|
||||||
[loop]
|
|
||||||
for (int y = -2; y <= 2; ++y) {
|
|
||||||
[loop]
|
|
||||||
for (int x = -2; x <= 2; ++x) {
|
|
||||||
if (x == 0 && y == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float distancePixels = length(float2((float)x, (float)y));
|
|
||||||
if (distancePixels > outlineWidth) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsSelectedObject(LoadObjectId(pixelCoord + int2(x, y)))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float weight = saturate(1.0 - ((distancePixels - 1.0) / max((float)outlineWidth, 1.0)));
|
|
||||||
outline = max(outline, weight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outline <= 0.001) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
|
|
||||||
return float4(gOutlineColor.rgb, gOutlineColor.a * outline);
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Scene View Object Id Outline",
|
|
||||||
"passes": [
|
|
||||||
{
|
|
||||||
"name": "ObjectIdOutline",
|
|
||||||
"tags": {
|
|
||||||
"LightMode": "ObjectIdOutline"
|
|
||||||
},
|
|
||||||
"variants": [
|
|
||||||
{
|
|
||||||
"stage": "Vertex",
|
|
||||||
"backend": "D3D12",
|
|
||||||
"language": "HLSL",
|
|
||||||
"source": "object-id-outline.vs.hlsl",
|
|
||||||
"entryPoint": "MainVS",
|
|
||||||
"profile": "vs_5_0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"stage": "Fragment",
|
|
||||||
"backend": "D3D12",
|
|
||||||
"language": "HLSL",
|
|
||||||
"source": "object-id-outline.ps.hlsl",
|
|
||||||
"entryPoint": "MainPS",
|
|
||||||
"profile": "ps_5_0"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// XC_EDITOR_SCENE_VIEW_OBJECT_ID_OUTLINE_D3D12_VS
|
|
||||||
cbuffer OutlineConstants : register(b0) {
|
|
||||||
float4 gViewportSizeAndTexelSize;
|
|
||||||
float4 gOutlineColor;
|
|
||||||
float4 gSelectedInfo;
|
|
||||||
float4 gSelectedObjectColors[256];
|
|
||||||
};
|
|
||||||
|
|
||||||
Texture2D gObjectIdTexture : register(t0);
|
|
||||||
|
|
||||||
struct VSOutput {
|
|
||||||
float4 position : SV_POSITION;
|
|
||||||
};
|
|
||||||
|
|
||||||
VSOutput MainVS(uint vertexId : SV_VertexID) {
|
|
||||||
static const float2 positions[3] = {
|
|
||||||
float2(-1.0, -1.0),
|
|
||||||
float2(-1.0, 3.0),
|
|
||||||
float2( 3.0, -1.0)
|
|
||||||
};
|
|
||||||
|
|
||||||
VSOutput output;
|
|
||||||
output.position = float4(positions[vertexId], 0.0, 1.0);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
@@ -72,17 +72,60 @@ inline ::XCEngine::Components::GameObject* CreateLightEntity(
|
|||||||
IEditorContext& context,
|
IEditorContext& context,
|
||||||
::XCEngine::Components::GameObject* parent = nullptr,
|
::XCEngine::Components::GameObject* parent = nullptr,
|
||||||
const std::string& commandLabel = "Create Light",
|
const std::string& commandLabel = "Create Light",
|
||||||
const std::string& entityName = "Light") {
|
const std::string& entityName = "Light",
|
||||||
|
::XCEngine::Components::LightType lightType = ::XCEngine::Components::LightType::Directional) {
|
||||||
return CreateEntity(
|
return CreateEntity(
|
||||||
context,
|
context,
|
||||||
commandLabel,
|
commandLabel,
|
||||||
entityName,
|
entityName,
|
||||||
parent,
|
parent,
|
||||||
[](::XCEngine::Components::GameObject& entity, ISceneManager&) {
|
[lightType](::XCEngine::Components::GameObject& entity, ISceneManager&) {
|
||||||
entity.AddComponent<::XCEngine::Components::LightComponent>();
|
auto* light = entity.AddComponent<::XCEngine::Components::LightComponent>();
|
||||||
|
if (light != nullptr) {
|
||||||
|
light->SetLightType(lightType);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::Components::GameObject* CreateDirectionalLightEntity(
|
||||||
|
IEditorContext& context,
|
||||||
|
::XCEngine::Components::GameObject* parent = nullptr,
|
||||||
|
const std::string& commandLabel = "Create Directional Light",
|
||||||
|
const std::string& entityName = "Directional Light") {
|
||||||
|
return CreateLightEntity(
|
||||||
|
context,
|
||||||
|
parent,
|
||||||
|
commandLabel,
|
||||||
|
entityName,
|
||||||
|
::XCEngine::Components::LightType::Directional);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::Components::GameObject* CreatePointLightEntity(
|
||||||
|
IEditorContext& context,
|
||||||
|
::XCEngine::Components::GameObject* parent = nullptr,
|
||||||
|
const std::string& commandLabel = "Create Point Light",
|
||||||
|
const std::string& entityName = "Point Light") {
|
||||||
|
return CreateLightEntity(
|
||||||
|
context,
|
||||||
|
parent,
|
||||||
|
commandLabel,
|
||||||
|
entityName,
|
||||||
|
::XCEngine::Components::LightType::Point);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ::XCEngine::Components::GameObject* CreateSpotLightEntity(
|
||||||
|
IEditorContext& context,
|
||||||
|
::XCEngine::Components::GameObject* parent = nullptr,
|
||||||
|
const std::string& commandLabel = "Create Spot Light",
|
||||||
|
const std::string& entityName = "Spot Light") {
|
||||||
|
return CreateLightEntity(
|
||||||
|
context,
|
||||||
|
parent,
|
||||||
|
commandLabel,
|
||||||
|
entityName,
|
||||||
|
::XCEngine::Components::LightType::Spot);
|
||||||
|
}
|
||||||
|
|
||||||
inline ::XCEngine::Components::GameObject* CreatePrimitiveEntity(
|
inline ::XCEngine::Components::GameObject* CreatePrimitiveEntity(
|
||||||
IEditorContext& context,
|
IEditorContext& context,
|
||||||
::XCEngine::Resources::BuiltinPrimitiveType primitiveType,
|
::XCEngine::Resources::BuiltinPrimitiveType primitiveType,
|
||||||
|
|||||||
@@ -99,7 +99,9 @@ struct BuiltInIconState {
|
|||||||
BuiltInTexture gameObject;
|
BuiltInTexture gameObject;
|
||||||
BuiltInTexture scene;
|
BuiltInTexture scene;
|
||||||
BuiltInTexture cameraGizmo;
|
BuiltInTexture cameraGizmo;
|
||||||
BuiltInTexture mainLightGizmo;
|
BuiltInTexture directionalLightGizmo;
|
||||||
|
BuiltInTexture pointLightGizmo;
|
||||||
|
BuiltInTexture spotLightGizmo;
|
||||||
struct CachedAssetPreview {
|
struct CachedAssetPreview {
|
||||||
BuiltInTexture texture;
|
BuiltInTexture texture;
|
||||||
std::unique_ptr<LoadedTexturePixels> decodedPixels;
|
std::unique_ptr<LoadedTexturePixels> decodedPixels;
|
||||||
@@ -157,9 +159,19 @@ std::filesystem::path ResolveCameraGizmoIconPath() {
|
|||||||
return (exeDir / ".." / ".." / "resources" / "Icons" / "camera_gizmo.png").lexically_normal();
|
return (exeDir / ".." / ".." / "resources" / "Icons" / "camera_gizmo.png").lexically_normal();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::filesystem::path ResolveMainLightGizmoIconPath() {
|
std::filesystem::path ResolveDirectionalLightGizmoIconPath() {
|
||||||
const std::filesystem::path exeDir(Platform::GetExecutableDirectoryUtf8());
|
const std::filesystem::path exeDir(Platform::GetExecutableDirectoryUtf8());
|
||||||
return (exeDir / ".." / ".." / "resources" / "Icons" / "main_light_gizmo.png").lexically_normal();
|
return (exeDir / ".." / ".." / "resources" / "Icons" / "directional_light_gizmo.png").lexically_normal();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path ResolvePointLightGizmoIconPath() {
|
||||||
|
const std::filesystem::path exeDir(Platform::GetExecutableDirectoryUtf8());
|
||||||
|
return (exeDir / ".." / ".." / "resources" / "Icons" / "point_light_gizmo.png").lexically_normal();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path ResolveSpotLightGizmoIconPath() {
|
||||||
|
const std::filesystem::path exeDir(Platform::GetExecutableDirectoryUtf8());
|
||||||
|
return (exeDir / ".." / ".." / "resources" / "Icons" / "spot_light_gizmo.png").lexically_normal();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string NormalizePathKey(const std::filesystem::path& path) {
|
std::string NormalizePathKey(const std::filesystem::path& path) {
|
||||||
@@ -975,8 +987,12 @@ BuiltInTexture* ResolveEditorTexture(EditorTextureIconKind kind) {
|
|||||||
switch (kind) {
|
switch (kind) {
|
||||||
case EditorTextureIconKind::CameraGizmo:
|
case EditorTextureIconKind::CameraGizmo:
|
||||||
return &g_icons.cameraGizmo;
|
return &g_icons.cameraGizmo;
|
||||||
case EditorTextureIconKind::MainLightGizmo:
|
case EditorTextureIconKind::DirectionalLightGizmo:
|
||||||
return &g_icons.mainLightGizmo;
|
return &g_icons.directionalLightGizmo;
|
||||||
|
case EditorTextureIconKind::PointLightGizmo:
|
||||||
|
return &g_icons.pointLightGizmo;
|
||||||
|
case EditorTextureIconKind::SpotLightGizmo:
|
||||||
|
return &g_icons.spotLightGizmo;
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -1025,8 +1041,30 @@ void InitializeBuiltInIcons(
|
|||||||
backend,
|
backend,
|
||||||
device,
|
device,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
ResolveMainLightGizmoIconPath(),
|
ResolveDirectionalLightGizmoIconPath(),
|
||||||
g_icons.mainLightGizmo,
|
g_icons.directionalLightGizmo,
|
||||||
|
pendingUpload)) {
|
||||||
|
g_icons.pendingIconUploads.push_back(std::move(pendingUpload));
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingUpload.reset();
|
||||||
|
if (LoadTextureFromFile(
|
||||||
|
backend,
|
||||||
|
device,
|
||||||
|
commandQueue,
|
||||||
|
ResolvePointLightGizmoIconPath(),
|
||||||
|
g_icons.pointLightGizmo,
|
||||||
|
pendingUpload)) {
|
||||||
|
g_icons.pendingIconUploads.push_back(std::move(pendingUpload));
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingUpload.reset();
|
||||||
|
if (LoadTextureFromFile(
|
||||||
|
backend,
|
||||||
|
device,
|
||||||
|
commandQueue,
|
||||||
|
ResolveSpotLightGizmoIconPath(),
|
||||||
|
g_icons.spotLightGizmo,
|
||||||
pendingUpload)) {
|
pendingUpload)) {
|
||||||
g_icons.pendingIconUploads.push_back(std::move(pendingUpload));
|
g_icons.pendingIconUploads.push_back(std::move(pendingUpload));
|
||||||
}
|
}
|
||||||
@@ -1043,7 +1081,9 @@ void ShutdownBuiltInIcons() {
|
|||||||
ResetTexture(g_icons.gameObject);
|
ResetTexture(g_icons.gameObject);
|
||||||
ResetTexture(g_icons.scene);
|
ResetTexture(g_icons.scene);
|
||||||
ResetTexture(g_icons.cameraGizmo);
|
ResetTexture(g_icons.cameraGizmo);
|
||||||
ResetTexture(g_icons.mainLightGizmo);
|
ResetTexture(g_icons.directionalLightGizmo);
|
||||||
|
ResetTexture(g_icons.pointLightGizmo);
|
||||||
|
ResetTexture(g_icons.spotLightGizmo);
|
||||||
g_icons.backend = nullptr;
|
g_icons.backend = nullptr;
|
||||||
g_icons.device = nullptr;
|
g_icons.device = nullptr;
|
||||||
g_icons.commandQueue = nullptr;
|
g_icons.commandQueue = nullptr;
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ enum class AssetIconKind {
|
|||||||
|
|
||||||
enum class EditorTextureIconKind {
|
enum class EditorTextureIconKind {
|
||||||
CameraGizmo,
|
CameraGizmo,
|
||||||
MainLightGizmo
|
DirectionalLightGizmo,
|
||||||
|
PointLightGizmo,
|
||||||
|
SpotLightGizmo,
|
||||||
|
MainLightGizmo = DirectionalLightGizmo
|
||||||
};
|
};
|
||||||
|
|
||||||
void InitializeBuiltInIcons(
|
void InitializeBuiltInIcons(
|
||||||
|
|||||||
@@ -14,8 +14,17 @@ namespace XCEngine {
|
|||||||
namespace Editor {
|
namespace Editor {
|
||||||
namespace UI {
|
namespace UI {
|
||||||
|
|
||||||
|
inline bool ShouldTracePopupSubmenuLabel(const char* label) {
|
||||||
|
if (!label) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string text(label);
|
||||||
|
return text == "Create" || text == "3D Object";
|
||||||
|
}
|
||||||
|
|
||||||
inline void TracePopupSubmenuIfNeeded(const char* label, const std::string& message) {
|
inline void TracePopupSubmenuIfNeeded(const char* label, const std::string& message) {
|
||||||
if (!label || std::string(label) != "Create") {
|
if (!ShouldTracePopupSubmenuLabel(label)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +101,7 @@ inline bool DrawMenuScope(const char* label, DrawContentFn&& drawContent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename DrawContentFn>
|
template <typename DrawContentFn>
|
||||||
inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent) {
|
inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent, bool enabled = true) {
|
||||||
if (!label || label[0] == '\0') {
|
if (!label || label[0] == '\0') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -103,18 +112,24 @@ inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent
|
|||||||
const ImVec2 rowPos = ImGui::GetCursorScreenPos();
|
const ImVec2 rowPos = ImGui::GetCursorScreenPos();
|
||||||
const float rowHeight = labelSize.y;
|
const float rowHeight = labelSize.y;
|
||||||
const float rowWidth = ImMax(ImGui::GetContentRegionAvail().x, 1.0f);
|
const float rowWidth = ImMax(ImGui::GetContentRegionAvail().x, 1.0f);
|
||||||
const bool popupOpen = ImGui::IsPopupOpen(popupId);
|
const bool popupOpen = enabled && ImGui::IsPopupOpen(popupId);
|
||||||
|
ImGuiSelectableFlags selectableFlags = ImGuiSelectableFlags_NoAutoClosePopups;
|
||||||
|
if (!enabled) {
|
||||||
|
selectableFlags |= ImGuiSelectableFlags_Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui::Selectable(
|
if (ImGui::Selectable(
|
||||||
"##PopupSubmenuRow",
|
"##PopupSubmenuRow",
|
||||||
popupOpen,
|
popupOpen,
|
||||||
ImGuiSelectableFlags_NoAutoClosePopups,
|
selectableFlags,
|
||||||
ImVec2(rowWidth, rowHeight))) {
|
ImVec2(rowWidth, rowHeight))) {
|
||||||
TracePopupSubmenuIfNeeded(label, "Hierarchy create submenu selectable clicked -> OpenPopup");
|
if (enabled) {
|
||||||
ImGui::OpenPopup(popupId);
|
TracePopupSubmenuIfNeeded(label, "Hierarchy create submenu selectable clicked -> OpenPopup");
|
||||||
|
ImGui::OpenPopup(popupId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool hovered = ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
|
const bool hovered = enabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup);
|
||||||
if (hovered && !popupOpen) {
|
if (hovered && !popupOpen) {
|
||||||
TracePopupSubmenuIfNeeded(label, "Hierarchy create submenu hovered -> OpenPopup");
|
TracePopupSubmenuIfNeeded(label, "Hierarchy create submenu hovered -> OpenPopup");
|
||||||
ImGui::OpenPopup(popupId);
|
ImGui::OpenPopup(popupId);
|
||||||
@@ -127,9 +142,10 @@ inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent
|
|||||||
const float parentWindowRight = parentWindowPos.x + parentWindowSize.x;
|
const float parentWindowRight = parentWindowPos.x + parentWindowSize.x;
|
||||||
const float itemHeight = itemMax.y - itemMin.y;
|
const float itemHeight = itemMax.y - itemMin.y;
|
||||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||||
|
const ImU32 textColor = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled);
|
||||||
drawList->AddText(
|
drawList->AddText(
|
||||||
ImVec2(itemMin.x + ImGui::GetStyle().FramePadding.x, itemMin.y + (itemHeight - labelSize.y) * 0.5f),
|
ImVec2(itemMin.x + ImGui::GetStyle().FramePadding.x, itemMin.y + (itemHeight - labelSize.y) * 0.5f),
|
||||||
ImGui::GetColorU32(ImGuiCol_Text),
|
textColor,
|
||||||
label);
|
label);
|
||||||
|
|
||||||
const float arrowExtent = PopupSubmenuArrowExtent();
|
const float arrowExtent = PopupSubmenuArrowExtent();
|
||||||
@@ -139,7 +155,12 @@ inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent
|
|||||||
ImVec2(arrowCenterX - arrowExtent * 0.30f, arrowCenterY - arrowExtent * 0.50f),
|
ImVec2(arrowCenterX - arrowExtent * 0.30f, arrowCenterY - arrowExtent * 0.50f),
|
||||||
ImVec2(arrowCenterX - arrowExtent * 0.30f, arrowCenterY + arrowExtent * 0.50f),
|
ImVec2(arrowCenterX - arrowExtent * 0.30f, arrowCenterY + arrowExtent * 0.50f),
|
||||||
ImVec2(arrowCenterX + arrowExtent * 0.50f, arrowCenterY),
|
ImVec2(arrowCenterX + arrowExtent * 0.50f, arrowCenterY),
|
||||||
ImGui::GetColorU32(ImGuiCol_Text));
|
textColor);
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
ImGui::PopID();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(
|
ImGui::SetNextWindowPos(
|
||||||
ImVec2(parentWindowRight + PopupSubmenuOpenOffsetX(), rowPos.y - PopupWindowPadding().y),
|
ImVec2(parentWindowRight + PopupSubmenuOpenOffsetX(), rowPos.y - PopupWindowPadding().y),
|
||||||
@@ -149,7 +170,7 @@ inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent
|
|||||||
ImGuiWindowFlags_NoMove |
|
ImGuiWindowFlags_NoMove |
|
||||||
ImGuiWindowFlags_NoResize |
|
ImGuiWindowFlags_NoResize |
|
||||||
ImGuiWindowFlags_NoSavedSettings);
|
ImGuiWindowFlags_NoSavedSettings);
|
||||||
if (std::string(label) == "Create") {
|
if (ShouldTracePopupSubmenuLabel(label)) {
|
||||||
static bool s_lastCreateOpen = false;
|
static bool s_lastCreateOpen = false;
|
||||||
if (open != s_lastCreateOpen) {
|
if (open != s_lastCreateOpen) {
|
||||||
TracePopupSubmenuIfNeeded(
|
TracePopupSubmenuIfNeeded(
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
|
||||||
|
|
||||||
#include "Passes/SceneViewportSelectionOutlinePass.h"
|
#include "Passes/SceneViewportSelectionOutlinePass.h"
|
||||||
|
|
||||||
#include "Viewport/SceneViewportShaderPaths.h"
|
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
@@ -45,7 +41,7 @@ private:
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
SceneViewportSelectionOutlinePassRenderer::SceneViewportSelectionOutlinePassRenderer()
|
SceneViewportSelectionOutlinePassRenderer::SceneViewportSelectionOutlinePassRenderer()
|
||||||
: m_outlinePass(GetSceneViewportObjectIdOutlineShaderPath()) {
|
: m_outlinePass() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneViewportSelectionOutlinePassRenderer::Shutdown() {
|
void SceneViewportSelectionOutlinePassRenderer::Shutdown() {
|
||||||
|
|||||||
@@ -20,7 +20,10 @@ enum class SceneViewportOverlayDepthMode : uint8_t {
|
|||||||
|
|
||||||
enum class SceneViewportOverlaySpriteTextureKind : uint8_t {
|
enum class SceneViewportOverlaySpriteTextureKind : uint8_t {
|
||||||
Camera = 0,
|
Camera = 0,
|
||||||
Light = 1
|
DirectionalLight = 1,
|
||||||
|
PointLight = 2,
|
||||||
|
SpotLight = 3,
|
||||||
|
Light = DirectionalLight
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SceneViewportOverlayHandleKind : uint8_t {
|
enum class SceneViewportOverlayHandleKind : uint8_t {
|
||||||
|
|||||||
@@ -29,12 +29,24 @@ bool DrawSceneViewportSpriteIcon(
|
|||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
UI::EditorTextureIconKind::CameraGizmo);
|
UI::EditorTextureIconKind::CameraGizmo);
|
||||||
case SceneViewportOverlaySpriteTextureKind::Light:
|
case SceneViewportOverlaySpriteTextureKind::DirectionalLight:
|
||||||
return UI::DrawEditorTextureIcon(
|
return UI::DrawEditorTextureIcon(
|
||||||
drawList,
|
drawList,
|
||||||
min,
|
min,
|
||||||
max,
|
max,
|
||||||
UI::EditorTextureIconKind::MainLightGizmo);
|
UI::EditorTextureIconKind::DirectionalLightGizmo);
|
||||||
|
case SceneViewportOverlaySpriteTextureKind::PointLight:
|
||||||
|
return UI::DrawEditorTextureIcon(
|
||||||
|
drawList,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
UI::EditorTextureIconKind::PointLightGizmo);
|
||||||
|
case SceneViewportOverlaySpriteTextureKind::SpotLight:
|
||||||
|
return UI::DrawEditorTextureIcon(
|
||||||
|
drawList,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
UI::EditorTextureIconKind::SpotLightGizmo);
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,16 @@ inline bool ShouldBeginSceneViewportNavigationDrag(
|
|||||||
return hasInteractiveViewport && hovered && !activeDrag && !otherDrag && !gizmoActive && clicked;
|
return hasInteractiveViewport && hovered && !activeDrag && !otherDrag && !gizmoActive && clicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool ShouldBeginSceneViewportHeldNavigationDrag(
|
||||||
|
bool hasInteractiveViewport,
|
||||||
|
bool hovered,
|
||||||
|
bool activeDrag,
|
||||||
|
bool otherDrag,
|
||||||
|
bool gizmoActive,
|
||||||
|
bool buttonDown) {
|
||||||
|
return hasInteractiveViewport && hovered && !activeDrag && !otherDrag && !gizmoActive && buttonDown;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool IsSceneViewportPanDragButtonDown(
|
inline bool IsSceneViewportPanDragButtonDown(
|
||||||
const SceneViewportNavigationRequest& request,
|
const SceneViewportNavigationRequest& request,
|
||||||
int button) {
|
int button) {
|
||||||
@@ -139,13 +149,21 @@ inline SceneViewportNavigationUpdate UpdateSceneViewportNavigationState(
|
|||||||
request.state.panDragging,
|
request.state.panDragging,
|
||||||
request.gizmoActive,
|
request.gizmoActive,
|
||||||
request.clickedRight);
|
request.clickedRight);
|
||||||
update.beginMiddlePanDrag = ShouldBeginSceneViewportNavigationDrag(
|
update.beginMiddlePanDrag =
|
||||||
request.hasInteractiveViewport,
|
ShouldBeginSceneViewportNavigationDrag(
|
||||||
request.viewportHovered,
|
request.hasInteractiveViewport,
|
||||||
request.state.panDragging,
|
request.viewportHovered,
|
||||||
request.state.lookDragging,
|
request.state.panDragging,
|
||||||
request.gizmoActive,
|
request.state.lookDragging,
|
||||||
request.clickedMiddle);
|
request.gizmoActive,
|
||||||
|
request.clickedMiddle) ||
|
||||||
|
ShouldBeginSceneViewportHeldNavigationDrag(
|
||||||
|
request.hasInteractiveViewport,
|
||||||
|
request.viewportHovered,
|
||||||
|
request.state.panDragging,
|
||||||
|
request.state.lookDragging,
|
||||||
|
request.gizmoActive,
|
||||||
|
request.middleMouseDown);
|
||||||
update.beginPanDrag = update.beginLeftPanDrag || update.beginMiddlePanDrag;
|
update.beginPanDrag = update.beginLeftPanDrag || update.beginMiddlePanDrag;
|
||||||
|
|
||||||
if (update.beginLookDrag) {
|
if (update.beginLookDrag) {
|
||||||
|
|||||||
@@ -73,6 +73,42 @@ void AppendWorldLine(
|
|||||||
line.depthMode = depthMode;
|
line.depthMode = depthMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr Math::Color kSelectedLightHelperColor(1.0f, 0.92f, 0.24f, 1.0f);
|
||||||
|
constexpr float kSelectedLightHelperLineThickness = 1.8f;
|
||||||
|
constexpr size_t kSelectedLightHelperSegmentCount = 32u;
|
||||||
|
|
||||||
|
void AppendWireCircle(
|
||||||
|
SceneViewportOverlayFrameData& frameData,
|
||||||
|
const Math::Vector3& center,
|
||||||
|
const Math::Vector3& basisA,
|
||||||
|
const Math::Vector3& basisB,
|
||||||
|
float radius,
|
||||||
|
const Math::Color& color,
|
||||||
|
float thicknessPixels,
|
||||||
|
SceneViewportOverlayDepthMode depthMode) {
|
||||||
|
const Math::Vector3 axisA = basisA.Normalized();
|
||||||
|
const Math::Vector3 axisB = basisB.Normalized();
|
||||||
|
if (radius <= Math::EPSILON ||
|
||||||
|
axisA.SqrMagnitude() <= Math::EPSILON ||
|
||||||
|
axisB.SqrMagnitude() <= Math::EPSILON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t segmentIndex = 0; segmentIndex < kSelectedLightHelperSegmentCount; ++segmentIndex) {
|
||||||
|
const float angle0 =
|
||||||
|
static_cast<float>(segmentIndex) / static_cast<float>(kSelectedLightHelperSegmentCount) *
|
||||||
|
Math::PI * 2.0f;
|
||||||
|
const float angle1 =
|
||||||
|
static_cast<float>(segmentIndex + 1u) / static_cast<float>(kSelectedLightHelperSegmentCount) *
|
||||||
|
Math::PI * 2.0f;
|
||||||
|
const Math::Vector3 p0 =
|
||||||
|
center + axisA * std::cos(angle0) * radius + axisB * std::sin(angle0) * radius;
|
||||||
|
const Math::Vector3 p1 =
|
||||||
|
center + axisA * std::cos(angle1) * radius + axisB * std::sin(angle1) * radius;
|
||||||
|
AppendWorldLine(frameData, p0, p1, color, thicknessPixels, depthMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AppendWorldSprite(
|
void AppendWorldSprite(
|
||||||
SceneViewportOverlayFrameData& frameData,
|
SceneViewportOverlayFrameData& frameData,
|
||||||
const Math::Vector3& worldPosition,
|
const Math::Vector3& worldPosition,
|
||||||
@@ -164,6 +200,20 @@ void AppendSceneIconOverlay(
|
|||||||
projectedPoint.ndcDepth);
|
projectedPoint.ndcDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SceneViewportOverlaySpriteTextureKind ResolveLightSceneIconTextureKind(
|
||||||
|
const Components::LightComponent& light) {
|
||||||
|
switch (light.GetLightType()) {
|
||||||
|
case Components::LightType::Directional:
|
||||||
|
return SceneViewportOverlaySpriteTextureKind::DirectionalLight;
|
||||||
|
case Components::LightType::Point:
|
||||||
|
return SceneViewportOverlaySpriteTextureKind::PointLight;
|
||||||
|
case Components::LightType::Spot:
|
||||||
|
return SceneViewportOverlaySpriteTextureKind::SpotLight;
|
||||||
|
default:
|
||||||
|
return SceneViewportOverlaySpriteTextureKind::DirectionalLight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AppendCameraFrustumOverlay(
|
void AppendCameraFrustumOverlay(
|
||||||
SceneViewportOverlayFrameData& frameData,
|
SceneViewportOverlayFrameData& frameData,
|
||||||
const Components::CameraComponent& camera,
|
const Components::CameraComponent& camera,
|
||||||
@@ -251,10 +301,10 @@ void AppendDirectionalLightOverlay(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Math::Vector3 position = transform->GetPosition();
|
const Math::Vector3 position = transform->GetPosition();
|
||||||
const Math::Vector3 lightDirection = (transform->GetForward() * -1.0f).Normalized();
|
const Math::Vector3 lightRayDirection = transform->GetForward().Normalized();
|
||||||
const Math::Vector3 right = transform->GetRight().Normalized();
|
const Math::Vector3 right = transform->GetRight().Normalized();
|
||||||
const Math::Vector3 up = transform->GetUp().Normalized();
|
const Math::Vector3 up = transform->GetUp().Normalized();
|
||||||
if (lightDirection.SqrMagnitude() <= Math::EPSILON ||
|
if (lightRayDirection.SqrMagnitude() <= Math::EPSILON ||
|
||||||
right.SqrMagnitude() <= Math::EPSILON ||
|
right.SqrMagnitude() <= Math::EPSILON ||
|
||||||
up.SqrMagnitude() <= Math::EPSILON) {
|
up.SqrMagnitude() <= Math::EPSILON) {
|
||||||
return;
|
return;
|
||||||
@@ -265,8 +315,6 @@ void AppendDirectionalLightOverlay(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr Math::Color kDirectionalLightColor(1.0f, 0.92f, 0.24f, 1.0f);
|
|
||||||
constexpr float kLineThickness = 1.8f;
|
|
||||||
constexpr size_t kRingSegmentCount = 32u;
|
constexpr size_t kRingSegmentCount = 32u;
|
||||||
constexpr std::array<float, 6> kRayAngles = {{
|
constexpr std::array<float, 6> kRayAngles = {{
|
||||||
0.0f,
|
0.0f,
|
||||||
@@ -281,7 +329,7 @@ void AppendDirectionalLightOverlay(
|
|||||||
const float ringOffset = worldUnitsPerPixel * 54.0f;
|
const float ringOffset = worldUnitsPerPixel * 54.0f;
|
||||||
const float innerRayRadius = ringRadius * 0.52f;
|
const float innerRayRadius = ringRadius * 0.52f;
|
||||||
const float rayLength = worldUnitsPerPixel * 96.0f;
|
const float rayLength = worldUnitsPerPixel * 96.0f;
|
||||||
const Math::Vector3 ringCenter = position + lightDirection * ringOffset;
|
const Math::Vector3 ringCenter = position + lightRayDirection * ringOffset;
|
||||||
|
|
||||||
for (size_t segmentIndex = 0; segmentIndex < kRingSegmentCount; ++segmentIndex) {
|
for (size_t segmentIndex = 0; segmentIndex < kRingSegmentCount; ++segmentIndex) {
|
||||||
const float angle0 =
|
const float angle0 =
|
||||||
@@ -296,8 +344,8 @@ void AppendDirectionalLightOverlay(
|
|||||||
frameData,
|
frameData,
|
||||||
p0,
|
p0,
|
||||||
p1,
|
p1,
|
||||||
kDirectionalLightColor,
|
kSelectedLightHelperColor,
|
||||||
kLineThickness,
|
kSelectedLightHelperLineThickness,
|
||||||
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,15 +353,15 @@ void AppendDirectionalLightOverlay(
|
|||||||
frameData,
|
frameData,
|
||||||
position,
|
position,
|
||||||
ringCenter,
|
ringCenter,
|
||||||
kDirectionalLightColor,
|
kSelectedLightHelperColor,
|
||||||
kLineThickness,
|
kSelectedLightHelperLineThickness,
|
||||||
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
AppendWorldLine(
|
AppendWorldLine(
|
||||||
frameData,
|
frameData,
|
||||||
ringCenter,
|
ringCenter,
|
||||||
ringCenter + lightDirection * rayLength,
|
ringCenter + lightRayDirection * rayLength,
|
||||||
kDirectionalLightColor,
|
kSelectedLightHelperColor,
|
||||||
kLineThickness,
|
kSelectedLightHelperLineThickness,
|
||||||
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
|
|
||||||
for (float angle : kRayAngles) {
|
for (float angle : kRayAngles) {
|
||||||
@@ -322,9 +370,119 @@ void AppendDirectionalLightOverlay(
|
|||||||
AppendWorldLine(
|
AppendWorldLine(
|
||||||
frameData,
|
frameData,
|
||||||
rayStart,
|
rayStart,
|
||||||
rayStart + lightDirection * rayLength,
|
rayStart + lightRayDirection * rayLength,
|
||||||
kDirectionalLightColor,
|
kSelectedLightHelperColor,
|
||||||
kLineThickness,
|
kSelectedLightHelperLineThickness,
|
||||||
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendPointLightOverlay(
|
||||||
|
SceneViewportOverlayFrameData& frameData,
|
||||||
|
const Components::GameObject& gameObject,
|
||||||
|
const Components::LightComponent& light) {
|
||||||
|
const Components::TransformComponent* transform = gameObject.GetTransform();
|
||||||
|
if (transform == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Math::Vector3 position = transform->GetPosition();
|
||||||
|
const Math::Vector3 right = transform->GetRight().Normalized();
|
||||||
|
const Math::Vector3 up = transform->GetUp().Normalized();
|
||||||
|
const Math::Vector3 forward = transform->GetForward().Normalized();
|
||||||
|
if (right.SqrMagnitude() <= Math::EPSILON ||
|
||||||
|
up.SqrMagnitude() <= Math::EPSILON ||
|
||||||
|
forward.SqrMagnitude() <= Math::EPSILON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float range = (std::max)(light.GetRange(), 0.001f);
|
||||||
|
AppendWireCircle(
|
||||||
|
frameData,
|
||||||
|
position,
|
||||||
|
right,
|
||||||
|
up,
|
||||||
|
range,
|
||||||
|
kSelectedLightHelperColor,
|
||||||
|
kSelectedLightHelperLineThickness,
|
||||||
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
|
AppendWireCircle(
|
||||||
|
frameData,
|
||||||
|
position,
|
||||||
|
right,
|
||||||
|
forward,
|
||||||
|
range,
|
||||||
|
kSelectedLightHelperColor,
|
||||||
|
kSelectedLightHelperLineThickness,
|
||||||
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
|
AppendWireCircle(
|
||||||
|
frameData,
|
||||||
|
position,
|
||||||
|
up,
|
||||||
|
forward,
|
||||||
|
range,
|
||||||
|
kSelectedLightHelperColor,
|
||||||
|
kSelectedLightHelperLineThickness,
|
||||||
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendSpotLightOverlay(
|
||||||
|
SceneViewportOverlayFrameData& frameData,
|
||||||
|
const Components::GameObject& gameObject,
|
||||||
|
const Components::LightComponent& light) {
|
||||||
|
const Components::TransformComponent* transform = gameObject.GetTransform();
|
||||||
|
if (transform == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Math::Vector3 position = transform->GetPosition();
|
||||||
|
const Math::Vector3 forward = transform->GetForward().Normalized();
|
||||||
|
const Math::Vector3 right = transform->GetRight().Normalized();
|
||||||
|
const Math::Vector3 up = transform->GetUp().Normalized();
|
||||||
|
if (forward.SqrMagnitude() <= Math::EPSILON ||
|
||||||
|
right.SqrMagnitude() <= Math::EPSILON ||
|
||||||
|
up.SqrMagnitude() <= Math::EPSILON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float range = (std::max)(light.GetRange(), 0.001f);
|
||||||
|
const float halfAngleRadians =
|
||||||
|
std::clamp(light.GetSpotAngle(), 1.0f, 179.0f) * Math::DEG_TO_RAD * 0.5f;
|
||||||
|
const float coneRadius = std::tan(halfAngleRadians) * range;
|
||||||
|
const Math::Vector3 coneBaseCenter = position + forward * range;
|
||||||
|
|
||||||
|
AppendWorldLine(
|
||||||
|
frameData,
|
||||||
|
position,
|
||||||
|
coneBaseCenter,
|
||||||
|
kSelectedLightHelperColor,
|
||||||
|
kSelectedLightHelperLineThickness,
|
||||||
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
|
AppendWireCircle(
|
||||||
|
frameData,
|
||||||
|
coneBaseCenter,
|
||||||
|
right,
|
||||||
|
up,
|
||||||
|
coneRadius,
|
||||||
|
kSelectedLightHelperColor,
|
||||||
|
kSelectedLightHelperLineThickness,
|
||||||
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
|
|
||||||
|
static constexpr std::array<float, 4> kConeEdgeAngles = {{
|
||||||
|
0.0f,
|
||||||
|
Math::PI * 0.5f,
|
||||||
|
Math::PI,
|
||||||
|
Math::PI * 1.5f
|
||||||
|
}};
|
||||||
|
for (float angle : kConeEdgeAngles) {
|
||||||
|
const Math::Vector3 coneEdgePoint =
|
||||||
|
coneBaseCenter + right * std::cos(angle) * coneRadius + up * std::sin(angle) * coneRadius;
|
||||||
|
AppendWorldLine(
|
||||||
|
frameData,
|
||||||
|
position,
|
||||||
|
coneEdgePoint,
|
||||||
|
kSelectedLightHelperColor,
|
||||||
|
kSelectedLightHelperLineThickness,
|
||||||
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -421,7 +579,7 @@ public:
|
|||||||
context.viewportHeight,
|
context.viewportHeight,
|
||||||
*gameObject,
|
*gameObject,
|
||||||
kLightIconSize,
|
kLightIconSize,
|
||||||
SceneViewportOverlaySpriteTextureKind::Light);
|
ResolveLightSceneIconTextureKind(*light));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint64_t entityId : *context.selectedObjectIds) {
|
for (uint64_t entityId : *context.selectedObjectIds) {
|
||||||
@@ -435,17 +593,25 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Components::LightComponent* light = gameObject->GetComponent<Components::LightComponent>();
|
Components::LightComponent* light = gameObject->GetComponent<Components::LightComponent>();
|
||||||
if (light == nullptr ||
|
if (light == nullptr || !light->IsEnabled()) {
|
||||||
!light->IsEnabled() ||
|
|
||||||
light->GetLightType() != Components::LightType::Directional) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendDirectionalLightOverlay(
|
switch (light->GetLightType()) {
|
||||||
frameData,
|
case Components::LightType::Directional:
|
||||||
*gameObject,
|
AppendDirectionalLightOverlay(
|
||||||
*context.overlay,
|
frameData,
|
||||||
context.viewportHeight);
|
*gameObject,
|
||||||
|
*context.overlay,
|
||||||
|
context.viewportHeight);
|
||||||
|
break;
|
||||||
|
case Components::LightType::Point:
|
||||||
|
AppendPointLightOverlay(frameData, *gameObject, *light);
|
||||||
|
break;
|
||||||
|
case Components::LightType::Spot:
|
||||||
|
AppendSpotLightOverlay(frameData, *gameObject, *light);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ class RHITexture;
|
|||||||
|
|
||||||
namespace Editor {
|
namespace Editor {
|
||||||
|
|
||||||
inline constexpr std::array<SceneViewportOverlaySpriteTextureKind, 2> kSceneViewportOverlaySpriteTextureKinds = {
|
inline constexpr std::array<SceneViewportOverlaySpriteTextureKind, 4> kSceneViewportOverlaySpriteTextureKinds = {
|
||||||
SceneViewportOverlaySpriteTextureKind::Camera,
|
SceneViewportOverlaySpriteTextureKind::Camera,
|
||||||
SceneViewportOverlaySpriteTextureKind::Light
|
SceneViewportOverlaySpriteTextureKind::DirectionalLight,
|
||||||
|
SceneViewportOverlaySpriteTextureKind::PointLight,
|
||||||
|
SceneViewportOverlaySpriteTextureKind::SpotLight
|
||||||
};
|
};
|
||||||
|
|
||||||
inline constexpr size_t kSceneViewportOverlaySpriteResourceCount =
|
inline constexpr size_t kSceneViewportOverlaySpriteResourceCount =
|
||||||
@@ -35,8 +37,12 @@ inline size_t GetSceneViewportOverlaySpriteResourceIndex(
|
|||||||
switch (textureKind) {
|
switch (textureKind) {
|
||||||
case SceneViewportOverlaySpriteTextureKind::Camera:
|
case SceneViewportOverlaySpriteTextureKind::Camera:
|
||||||
return 0u;
|
return 0u;
|
||||||
case SceneViewportOverlaySpriteTextureKind::Light:
|
case SceneViewportOverlaySpriteTextureKind::DirectionalLight:
|
||||||
return 1u;
|
return 1u;
|
||||||
|
case SceneViewportOverlaySpriteTextureKind::PointLight:
|
||||||
|
return 2u;
|
||||||
|
case SceneViewportOverlaySpriteTextureKind::SpotLight:
|
||||||
|
return 3u;
|
||||||
default:
|
default:
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
@@ -63,8 +69,14 @@ inline SceneViewportOverlaySpriteAssetSpec GetSceneViewportOverlaySpriteAssetSpe
|
|||||||
case SceneViewportOverlaySpriteTextureKind::Camera:
|
case SceneViewportOverlaySpriteTextureKind::Camera:
|
||||||
spec.resourcePath = GetSceneViewportCameraGizmoIconPath();
|
spec.resourcePath = GetSceneViewportCameraGizmoIconPath();
|
||||||
break;
|
break;
|
||||||
case SceneViewportOverlaySpriteTextureKind::Light:
|
case SceneViewportOverlaySpriteTextureKind::DirectionalLight:
|
||||||
spec.resourcePath = GetSceneViewportMainLightGizmoIconPath();
|
spec.resourcePath = GetSceneViewportDirectionalLightGizmoIconPath();
|
||||||
|
break;
|
||||||
|
case SceneViewportOverlaySpriteTextureKind::PointLight:
|
||||||
|
spec.resourcePath = GetSceneViewportPointLightGizmoIconPath();
|
||||||
|
break;
|
||||||
|
case SceneViewportOverlaySpriteTextureKind::SpotLight:
|
||||||
|
spec.resourcePath = GetSceneViewportSpotLightGizmoIconPath();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -39,22 +39,28 @@ inline Containers::String GetSceneViewportInfiniteGridShaderPath() {
|
|||||||
"infinite-grid.shader");
|
"infinite-grid.shader");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Containers::String GetSceneViewportObjectIdOutlineShaderPath() {
|
|
||||||
return Detail::BuildSceneViewportEditorResourcePath(
|
|
||||||
std::filesystem::path("shaders") /
|
|
||||||
"scene-viewport" /
|
|
||||||
"object-id-outline" /
|
|
||||||
"object-id-outline.shader");
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Containers::String GetSceneViewportCameraGizmoIconPath() {
|
inline Containers::String GetSceneViewportCameraGizmoIconPath() {
|
||||||
return Detail::BuildSceneViewportEditorResourcePath(
|
return Detail::BuildSceneViewportEditorResourcePath(
|
||||||
std::filesystem::path("Icons") / "camera_gizmo.png");
|
std::filesystem::path("Icons") / "camera_gizmo.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Containers::String GetSceneViewportMainLightGizmoIconPath() {
|
inline Containers::String GetSceneViewportDirectionalLightGizmoIconPath() {
|
||||||
return Detail::BuildSceneViewportEditorResourcePath(
|
return Detail::BuildSceneViewportEditorResourcePath(
|
||||||
std::filesystem::path("Icons") / "main_light_gizmo.png");
|
std::filesystem::path("Icons") / "directional_light_gizmo.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Containers::String GetSceneViewportPointLightGizmoIconPath() {
|
||||||
|
return Detail::BuildSceneViewportEditorResourcePath(
|
||||||
|
std::filesystem::path("Icons") / "point_light_gizmo.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Containers::String GetSceneViewportSpotLightGizmoIconPath() {
|
||||||
|
return Detail::BuildSceneViewportEditorResourcePath(
|
||||||
|
std::filesystem::path("Icons") / "spot_light_gizmo.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Containers::String GetSceneViewportMainLightGizmoIconPath() {
|
||||||
|
return GetSceneViewportDirectionalLightGizmoIconPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Editor
|
} // namespace Editor
|
||||||
|
|||||||
@@ -77,10 +77,15 @@ inline void RenderViewportInteractionSurface(
|
|||||||
|
|
||||||
result.itemMin = ImGui::GetItemRectMin();
|
result.itemMin = ImGui::GetItemRectMin();
|
||||||
result.itemMax = ImGui::GetItemRectMax();
|
result.itemMax = ImGui::GetItemRectMax();
|
||||||
result.hovered = ImGui::IsItemHovered();
|
// Use explicit viewport-rect hit testing instead of ImGui item hover state. Dear ImGui ties
|
||||||
result.clickedLeft = result.hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left, false);
|
// hovered items to click ownership, so a middle-button press that arrives before the panel
|
||||||
result.clickedRight = result.hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right, false);
|
// has established hover can fail to start panning in the live docked editor even though the
|
||||||
result.clickedMiddle = result.hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Middle, false);
|
// cursor is inside the viewport.
|
||||||
|
const bool hoveredForInput = ImGui::IsMouseHoveringRect(result.itemMin, result.itemMax, false);
|
||||||
|
result.hovered = hoveredForInput;
|
||||||
|
result.clickedLeft = hoveredForInput && ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
|
result.clickedRight = hoveredForInput && ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
||||||
|
result.clickedMiddle = hoveredForInput && ImGui::IsMouseClicked(ImGuiMouseButton_Middle);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ViewportPanelContentResult RenderViewportPanelContent(
|
inline ViewportPanelContentResult RenderViewportPanelContent(
|
||||||
|
|||||||
@@ -394,8 +394,6 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderRuntimeBuildUtils.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderRuntimeBuildUtils.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderManifestLoader.h
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderManifestLoader.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringInternal.h
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringInternal.h
|
||||||
@@ -569,12 +567,14 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIKeyboardNavigationModel.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIKeyboardNavigationModel.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIPopupOverlayModel.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIPopupOverlayModel.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIPropertyEditModel.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIPropertyEditModel.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UIScrollModel.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UISelectionModel.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UISelectionModel.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UITabStripModel.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Widgets/UITabStripModel.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIExpansionModel.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIExpansionModel.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIKeyboardNavigationModel.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIKeyboardNavigationModel.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIPopupOverlayModel.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIPopupOverlayModel.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIPropertyEditModel.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIPropertyEditModel.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UIScrollModel.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UISelectionModel.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Widgets/UISelectionModel.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenTypes.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenTypes.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenDocumentHost.h
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Runtime/UIScreenDocumentHost.h
|
||||||
|
|||||||
@@ -46,7 +46,14 @@ Shader "Builtin Object Id Outline"
|
|||||||
|
|
||||||
bool IsSelectedObject(float4 objectIdColor)
|
bool IsSelectedObject(float4 objectIdColor)
|
||||||
{
|
{
|
||||||
if (objectIdColor.a <= 0.0) {
|
// Object-id surfaces encode the low 32 bits across RGBA, so low-valued
|
||||||
|
// runtime ids legitimately have zero alpha. Only the all-zero clear
|
||||||
|
// color should be treated as "no object".
|
||||||
|
if (all(abs(objectIdColor) <= float4(
|
||||||
|
0.0025,
|
||||||
|
0.0025,
|
||||||
|
0.0025,
|
||||||
|
0.0025))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Math.h"
|
#include "Math.h"
|
||||||
|
#include "Vector3.h"
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace Math {
|
namespace Math {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ enum class KeyCode : Core::uint8 {
|
|||||||
Up = 126, Down = 125, Left = 123, Right = 124,
|
Up = 126, Down = 125, Left = 123, Right = 124,
|
||||||
|
|
||||||
Home = 115, End = 119, PageUp = 116, PageDown = 121,
|
Home = 115, End = 119, PageUp = 116, PageDown = 121,
|
||||||
Delete = 51, Backspace = 51,
|
Delete = 117, Backspace = 51,
|
||||||
|
|
||||||
Zero = 39, One = 30, Two = 31, Three = 32,
|
Zero = 39, One = 30, Two = 31, Three = 32,
|
||||||
Four = 33, Five = 34, Six = 35, Seven = 37,
|
Four = 33, Five = 34, Six = 35, Seven = 37,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public:
|
|||||||
bool IsValid() const override;
|
bool IsValid() const override;
|
||||||
|
|
||||||
void SetViewType(ResourceViewType type) { m_viewType = type; }
|
void SetViewType(ResourceViewType type) { m_viewType = type; }
|
||||||
|
void SetFormat(Format format) { m_format = format; }
|
||||||
ResourceViewType GetViewType() const override { return m_viewType; }
|
ResourceViewType GetViewType() const override { return m_viewType; }
|
||||||
ResourceViewDimension GetDimension() const override { return m_dimension; }
|
ResourceViewDimension GetDimension() const override { return m_dimension; }
|
||||||
Format GetFormat() const override { return m_format; }
|
Format GetFormat() const override { return m_format; }
|
||||||
|
|||||||
@@ -19,10 +19,14 @@ struct OpenGLDepthStencilState {
|
|||||||
uint8_t stencilReadMask = 0xFF;
|
uint8_t stencilReadMask = 0xFF;
|
||||||
uint8_t stencilWriteMask = 0xFF;
|
uint8_t stencilWriteMask = 0xFF;
|
||||||
int stencilRef = 0;
|
int stencilRef = 0;
|
||||||
ComparisonFunc stencilFunc = ComparisonFunc::Always;
|
ComparisonFunc frontStencilFunc = ComparisonFunc::Always;
|
||||||
StencilOp stencilFailOp = StencilOp::Keep;
|
StencilOp frontStencilFailOp = StencilOp::Keep;
|
||||||
StencilOp stencilDepthFailOp = StencilOp::Keep;
|
StencilOp frontStencilDepthFailOp = StencilOp::Keep;
|
||||||
StencilOp stencilDepthPassOp = StencilOp::Keep;
|
StencilOp frontStencilDepthPassOp = StencilOp::Keep;
|
||||||
|
ComparisonFunc backStencilFunc = ComparisonFunc::Always;
|
||||||
|
StencilOp backStencilFailOp = StencilOp::Keep;
|
||||||
|
StencilOp backStencilDepthFailOp = StencilOp::Keep;
|
||||||
|
StencilOp backStencilDepthPassOp = StencilOp::Keep;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OpenGLBlendState {
|
struct OpenGLBlendState {
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ private:
|
|||||||
bool m_renderPassActive = false;
|
bool m_renderPassActive = false;
|
||||||
bool m_hasViewport = false;
|
bool m_hasViewport = false;
|
||||||
bool m_hasScissor = false;
|
bool m_hasScissor = false;
|
||||||
|
uint32_t m_stencilRef = 0;
|
||||||
VkViewport m_viewport = {};
|
VkViewport m_viewport = {};
|
||||||
VkRect2D m_scissor = {};
|
VkRect2D m_scissor = {};
|
||||||
std::vector<VkFramebuffer> m_transientFramebuffers;
|
std::vector<VkFramebuffer> m_transientFramebuffers;
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ enum class BuiltinMaterialPass : Core::uint32 {
|
|||||||
DepthOnly,
|
DepthOnly,
|
||||||
ShadowCaster,
|
ShadowCaster,
|
||||||
ObjectId,
|
ObjectId,
|
||||||
|
Skybox,
|
||||||
|
PostProcess,
|
||||||
|
FinalColor,
|
||||||
Forward = ForwardLit
|
Forward = ForwardLit
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,7 +39,12 @@ enum class BuiltinPassResourceSemantic : Core::uint8 {
|
|||||||
Material,
|
Material,
|
||||||
Lighting,
|
Lighting,
|
||||||
ShadowReceiver,
|
ShadowReceiver,
|
||||||
|
Environment,
|
||||||
|
PassConstants,
|
||||||
BaseColorTexture,
|
BaseColorTexture,
|
||||||
|
SourceColorTexture,
|
||||||
|
SkyboxPanoramicTexture,
|
||||||
|
SkyboxTexture,
|
||||||
ShadowMapTexture,
|
ShadowMapTexture,
|
||||||
LinearClampSampler,
|
LinearClampSampler,
|
||||||
ShadowMapSampler
|
ShadowMapSampler
|
||||||
@@ -60,7 +68,12 @@ struct BuiltinPassResourceBindingPlan {
|
|||||||
PassResourceBindingLocation material = {};
|
PassResourceBindingLocation material = {};
|
||||||
PassResourceBindingLocation lighting = {};
|
PassResourceBindingLocation lighting = {};
|
||||||
PassResourceBindingLocation shadowReceiver = {};
|
PassResourceBindingLocation shadowReceiver = {};
|
||||||
|
PassResourceBindingLocation environment = {};
|
||||||
|
PassResourceBindingLocation passConstants = {};
|
||||||
PassResourceBindingLocation baseColorTexture = {};
|
PassResourceBindingLocation baseColorTexture = {};
|
||||||
|
PassResourceBindingLocation sourceColorTexture = {};
|
||||||
|
PassResourceBindingLocation skyboxPanoramicTexture = {};
|
||||||
|
PassResourceBindingLocation skyboxTexture = {};
|
||||||
PassResourceBindingLocation linearClampSampler = {};
|
PassResourceBindingLocation linearClampSampler = {};
|
||||||
PassResourceBindingLocation shadowMapTexture = {};
|
PassResourceBindingLocation shadowMapTexture = {};
|
||||||
PassResourceBindingLocation shadowMapSampler = {};
|
PassResourceBindingLocation shadowMapSampler = {};
|
||||||
@@ -85,8 +98,13 @@ struct BuiltinPassSetLayoutMetadata {
|
|||||||
bool usesMaterial = false;
|
bool usesMaterial = false;
|
||||||
bool usesLighting = false;
|
bool usesLighting = false;
|
||||||
bool usesShadowReceiver = false;
|
bool usesShadowReceiver = false;
|
||||||
|
bool usesEnvironment = false;
|
||||||
|
bool usesPassConstants = false;
|
||||||
bool usesTexture = false;
|
bool usesTexture = false;
|
||||||
bool usesBaseColorTexture = false;
|
bool usesBaseColorTexture = false;
|
||||||
|
bool usesSourceColorTexture = false;
|
||||||
|
bool usesSkyboxPanoramicTexture = false;
|
||||||
|
bool usesSkyboxTexture = false;
|
||||||
bool usesShadowMapTexture = false;
|
bool usesShadowMapTexture = false;
|
||||||
bool usesSampler = false;
|
bool usesSampler = false;
|
||||||
bool usesLinearClampSampler = false;
|
bool usesLinearClampSampler = false;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <XCEngine/RHI/RHICommandList.h>
|
||||||
#include <XCEngine/RHI/RHITypes.h>
|
#include <XCEngine/RHI/RHITypes.h>
|
||||||
#include <XCEngine/Resources/Material/Material.h>
|
#include <XCEngine/Resources/Material/Material.h>
|
||||||
#include <XCEngine/Resources/Shader/Shader.h>
|
#include <XCEngine/Resources/Shader/Shader.h>
|
||||||
@@ -97,6 +100,28 @@ inline RHI::BlendOp ToRHIBlendOp(Resources::MaterialBlendOp op) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline RHI::StencilOp ToRHIStencilOp(Resources::MaterialStencilOp op) {
|
||||||
|
switch (op) {
|
||||||
|
case Resources::MaterialStencilOp::Zero:
|
||||||
|
return RHI::StencilOp::Zero;
|
||||||
|
case Resources::MaterialStencilOp::Replace:
|
||||||
|
return RHI::StencilOp::Replace;
|
||||||
|
case Resources::MaterialStencilOp::IncrSat:
|
||||||
|
return RHI::StencilOp::IncrSat;
|
||||||
|
case Resources::MaterialStencilOp::DecrSat:
|
||||||
|
return RHI::StencilOp::DecrSat;
|
||||||
|
case Resources::MaterialStencilOp::Invert:
|
||||||
|
return RHI::StencilOp::Invert;
|
||||||
|
case Resources::MaterialStencilOp::IncrWrap:
|
||||||
|
return RHI::StencilOp::Incr;
|
||||||
|
case Resources::MaterialStencilOp::DecrWrap:
|
||||||
|
return RHI::StencilOp::Decr;
|
||||||
|
case Resources::MaterialStencilOp::Keep:
|
||||||
|
default:
|
||||||
|
return RHI::StencilOp::Keep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline Resources::MaterialRenderState ResolveEffectiveRenderState(
|
inline Resources::MaterialRenderState ResolveEffectiveRenderState(
|
||||||
const Resources::ShaderPass* shaderPass,
|
const Resources::ShaderPass* shaderPass,
|
||||||
const Resources::Material* material) {
|
const Resources::Material* material) {
|
||||||
@@ -122,6 +147,8 @@ inline RHI::RasterizerDesc BuildRasterizerState(const Resources::MaterialRenderS
|
|||||||
desc.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
|
desc.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
|
||||||
desc.depthClipEnable = true;
|
desc.depthClipEnable = true;
|
||||||
desc.cullMode = static_cast<uint32_t>(ToRHICullMode(renderState.cullMode));
|
desc.cullMode = static_cast<uint32_t>(ToRHICullMode(renderState.cullMode));
|
||||||
|
desc.depthBias = renderState.depthBiasUnits;
|
||||||
|
desc.slopeScaledDepthBias = renderState.depthBiasFactor;
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
@@ -145,7 +172,17 @@ inline RHI::DepthStencilStateDesc BuildDepthStencilState(const Resources::Materi
|
|||||||
desc.depthTestEnable = renderState.depthTestEnable;
|
desc.depthTestEnable = renderState.depthTestEnable;
|
||||||
desc.depthWriteEnable = renderState.depthWriteEnable;
|
desc.depthWriteEnable = renderState.depthWriteEnable;
|
||||||
desc.depthFunc = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.depthFunc));
|
desc.depthFunc = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.depthFunc));
|
||||||
desc.stencilEnable = false;
|
desc.stencilEnable = renderState.stencil.enabled;
|
||||||
|
desc.stencilReadMask = renderState.stencil.readMask;
|
||||||
|
desc.stencilWriteMask = renderState.stencil.writeMask;
|
||||||
|
desc.front.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.failOp));
|
||||||
|
desc.front.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.passOp));
|
||||||
|
desc.front.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.depthFailOp));
|
||||||
|
desc.front.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.front.func));
|
||||||
|
desc.back.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.failOp));
|
||||||
|
desc.back.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.passOp));
|
||||||
|
desc.back.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.depthFailOp));
|
||||||
|
desc.back.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.back.func));
|
||||||
|
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
@@ -167,12 +204,32 @@ inline void ApplyResolvedRenderState(
|
|||||||
ApplyRenderState(ResolveEffectiveRenderState(shaderPass, material), pipelineDesc);
|
ApplyRenderState(ResolveEffectiveRenderState(shaderPass, material), pipelineDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void ApplyDynamicRenderState(
|
||||||
|
const Resources::MaterialRenderState& renderState,
|
||||||
|
RHI::RHICommandList& commandList) {
|
||||||
|
if (renderState.stencil.enabled) {
|
||||||
|
commandList.SetStencilRef(renderState.stencil.reference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Resources::MaterialRenderState BuildStaticPipelineRenderStateKey(
|
||||||
|
const Resources::MaterialRenderState& renderState) {
|
||||||
|
Resources::MaterialRenderState keyState = renderState;
|
||||||
|
keyState.stencil.reference = 0;
|
||||||
|
return keyState;
|
||||||
|
}
|
||||||
|
|
||||||
struct MaterialRenderStateHash {
|
struct MaterialRenderStateHash {
|
||||||
size_t operator()(const Resources::MaterialRenderState& state) const noexcept {
|
size_t operator()(const Resources::MaterialRenderState& state) const noexcept {
|
||||||
size_t hash = 2166136261u;
|
size_t hash = 2166136261u;
|
||||||
auto combine = [&hash](size_t value) {
|
auto combine = [&hash](size_t value) {
|
||||||
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||||
};
|
};
|
||||||
|
auto combineFloat = [&combine](float value) {
|
||||||
|
Core::uint32 bits = 0;
|
||||||
|
std::memcpy(&bits, &value, sizeof(bits));
|
||||||
|
combine(static_cast<size_t>(bits));
|
||||||
|
};
|
||||||
|
|
||||||
combine(static_cast<size_t>(state.blendEnable));
|
combine(static_cast<size_t>(state.blendEnable));
|
||||||
combine(static_cast<size_t>(state.srcBlend));
|
combine(static_cast<size_t>(state.srcBlend));
|
||||||
@@ -186,6 +243,20 @@ struct MaterialRenderStateHash {
|
|||||||
combine(static_cast<size_t>(state.depthWriteEnable));
|
combine(static_cast<size_t>(state.depthWriteEnable));
|
||||||
combine(static_cast<size_t>(state.depthFunc));
|
combine(static_cast<size_t>(state.depthFunc));
|
||||||
combine(static_cast<size_t>(state.cullMode));
|
combine(static_cast<size_t>(state.cullMode));
|
||||||
|
combineFloat(state.depthBiasFactor);
|
||||||
|
combine(static_cast<size_t>(state.depthBiasUnits));
|
||||||
|
combine(static_cast<size_t>(state.stencil.enabled));
|
||||||
|
combine(static_cast<size_t>(state.stencil.readMask));
|
||||||
|
combine(static_cast<size_t>(state.stencil.writeMask));
|
||||||
|
combine(static_cast<size_t>(state.stencil.reference));
|
||||||
|
combine(static_cast<size_t>(state.stencil.front.failOp));
|
||||||
|
combine(static_cast<size_t>(state.stencil.front.passOp));
|
||||||
|
combine(static_cast<size_t>(state.stencil.front.depthFailOp));
|
||||||
|
combine(static_cast<size_t>(state.stencil.front.func));
|
||||||
|
combine(static_cast<size_t>(state.stencil.back.failOp));
|
||||||
|
combine(static_cast<size_t>(state.stencil.back.passOp));
|
||||||
|
combine(static_cast<size_t>(state.stencil.back.depthFailOp));
|
||||||
|
combine(static_cast<size_t>(state.stencil.back.func));
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -102,16 +102,6 @@ public:
|
|||||||
bool HasRenderStateOverride() const { return m_hasRenderStateOverride; }
|
bool HasRenderStateOverride() const { return m_hasRenderStateOverride; }
|
||||||
void SetRenderStateOverrideEnabled(bool enabled);
|
void SetRenderStateOverrideEnabled(bool enabled);
|
||||||
|
|
||||||
// Legacy-only compatibility hint for older material assets that still
|
|
||||||
// select a builtin pass by serialized name.
|
|
||||||
void SetLegacyShaderPassHint(const Containers::String& shaderPass);
|
|
||||||
const Containers::String& GetLegacyShaderPassHint() const { return m_legacyShaderPassHint; }
|
|
||||||
bool HasLegacyShaderPassHint() const { return !m_legacyShaderPassHint.Empty(); }
|
|
||||||
void ClearLegacyShaderPassHint();
|
|
||||||
|
|
||||||
void SetShaderPass(const Containers::String& shaderPass);
|
|
||||||
const Containers::String& GetShaderPass() const { return GetLegacyShaderPassHint(); }
|
|
||||||
|
|
||||||
void SetTag(const Containers::String& name, const Containers::String& value);
|
void SetTag(const Containers::String& name, const Containers::String& value);
|
||||||
Containers::String GetTag(const Containers::String& name) const;
|
Containers::String GetTag(const Containers::String& name) const;
|
||||||
bool HasTag(const Containers::String& name) const;
|
bool HasTag(const Containers::String& name) const;
|
||||||
@@ -186,7 +176,6 @@ private:
|
|||||||
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||||
MaterialRenderState m_renderState;
|
MaterialRenderState m_renderState;
|
||||||
bool m_hasRenderStateOverride = false;
|
bool m_hasRenderStateOverride = false;
|
||||||
Containers::String m_legacyShaderPassHint;
|
|
||||||
Containers::Array<MaterialTagEntry> m_tags;
|
Containers::Array<MaterialTagEntry> m_tags;
|
||||||
ShaderKeywordSet m_keywordSet;
|
ShaderKeywordSet m_keywordSet;
|
||||||
Containers::HashMap<Containers::String, MaterialProperty> m_properties;
|
Containers::HashMap<Containers::String, MaterialProperty> m_properties;
|
||||||
|
|||||||
@@ -50,6 +50,57 @@ enum class MaterialBlendFactor : Core::uint8 {
|
|||||||
InvSrc1Alpha = 16
|
InvSrc1Alpha = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class MaterialStencilOp : Core::uint8 {
|
||||||
|
Keep = 0,
|
||||||
|
Zero = 1,
|
||||||
|
Replace = 2,
|
||||||
|
IncrSat = 3,
|
||||||
|
DecrSat = 4,
|
||||||
|
Invert = 5,
|
||||||
|
IncrWrap = 6,
|
||||||
|
DecrWrap = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MaterialStencilFaceState {
|
||||||
|
MaterialStencilOp failOp = MaterialStencilOp::Keep;
|
||||||
|
MaterialStencilOp passOp = MaterialStencilOp::Keep;
|
||||||
|
MaterialStencilOp depthFailOp = MaterialStencilOp::Keep;
|
||||||
|
MaterialComparisonFunc func = MaterialComparisonFunc::Always;
|
||||||
|
|
||||||
|
bool operator==(const MaterialStencilFaceState& other) const {
|
||||||
|
return failOp == other.failOp &&
|
||||||
|
passOp == other.passOp &&
|
||||||
|
depthFailOp == other.depthFailOp &&
|
||||||
|
func == other.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const MaterialStencilFaceState& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MaterialStencilState {
|
||||||
|
bool enabled = false;
|
||||||
|
Core::uint8 readMask = 0xFF;
|
||||||
|
Core::uint8 writeMask = 0xFF;
|
||||||
|
Core::uint8 reference = 0;
|
||||||
|
MaterialStencilFaceState front = {};
|
||||||
|
MaterialStencilFaceState back = {};
|
||||||
|
|
||||||
|
bool operator==(const MaterialStencilState& other) const {
|
||||||
|
return enabled == other.enabled &&
|
||||||
|
readMask == other.readMask &&
|
||||||
|
writeMask == other.writeMask &&
|
||||||
|
reference == other.reference &&
|
||||||
|
front == other.front &&
|
||||||
|
back == other.back;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const MaterialStencilState& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct MaterialRenderState {
|
struct MaterialRenderState {
|
||||||
bool blendEnable = false;
|
bool blendEnable = false;
|
||||||
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
|
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
|
||||||
@@ -65,6 +116,9 @@ struct MaterialRenderState {
|
|||||||
MaterialComparisonFunc depthFunc = MaterialComparisonFunc::Less;
|
MaterialComparisonFunc depthFunc = MaterialComparisonFunc::Less;
|
||||||
|
|
||||||
MaterialCullMode cullMode = MaterialCullMode::None;
|
MaterialCullMode cullMode = MaterialCullMode::None;
|
||||||
|
float depthBiasFactor = 0.0f;
|
||||||
|
Core::int32 depthBiasUnits = 0;
|
||||||
|
MaterialStencilState stencil = {};
|
||||||
|
|
||||||
bool operator==(const MaterialRenderState& other) const {
|
bool operator==(const MaterialRenderState& other) const {
|
||||||
return blendEnable == other.blendEnable &&
|
return blendEnable == other.blendEnable &&
|
||||||
@@ -78,7 +132,10 @@ struct MaterialRenderState {
|
|||||||
depthTestEnable == other.depthTestEnable &&
|
depthTestEnable == other.depthTestEnable &&
|
||||||
depthWriteEnable == other.depthWriteEnable &&
|
depthWriteEnable == other.depthWriteEnable &&
|
||||||
depthFunc == other.depthFunc &&
|
depthFunc == other.depthFunc &&
|
||||||
cullMode == other.cullMode;
|
cullMode == other.cullMode &&
|
||||||
|
depthBiasFactor == other.depthBiasFactor &&
|
||||||
|
depthBiasUnits == other.depthBiasUnits &&
|
||||||
|
stencil == other.stencil;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const MaterialRenderState& other) const {
|
bool operator!=(const MaterialRenderState& other) const {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Mutex.h"
|
#include "Mutex.h"
|
||||||
#include "SpinLock.h"
|
#include "SpinLock.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -102,7 +103,9 @@ void TaskSystem::ParallelFor(int32_t start, int32_t end, Func&& func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto& task : tasks) {
|
for (auto& task : tasks) {
|
||||||
Submit(std::make_unique<LambdaTask<std::function<void()>>>(std::move(task), TaskPriority::High));
|
Submit(std::make_unique<::XCEngine::Threading::LambdaTask<std::function<void()>>>(
|
||||||
|
std::move(task),
|
||||||
|
TaskPriority::High));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,12 +29,21 @@ struct UIColor {
|
|||||||
enum class UIDrawCommandType : std::uint8_t {
|
enum class UIDrawCommandType : std::uint8_t {
|
||||||
FilledRect = 0,
|
FilledRect = 0,
|
||||||
RectOutline,
|
RectOutline,
|
||||||
|
FilledRectLinearGradient,
|
||||||
|
Line,
|
||||||
|
FilledCircle,
|
||||||
|
CircleOutline,
|
||||||
Text,
|
Text,
|
||||||
Image,
|
Image,
|
||||||
PushClipRect,
|
PushClipRect,
|
||||||
PopClipRect
|
PopClipRect
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class UILinearGradientDirection : std::uint8_t {
|
||||||
|
Horizontal = 0,
|
||||||
|
Vertical
|
||||||
|
};
|
||||||
|
|
||||||
struct UIDrawCommand {
|
struct UIDrawCommand {
|
||||||
UIDrawCommandType type = UIDrawCommandType::FilledRect;
|
UIDrawCommandType type = UIDrawCommandType::FilledRect;
|
||||||
UIRect rect = {};
|
UIRect rect = {};
|
||||||
@@ -42,10 +51,13 @@ struct UIDrawCommand {
|
|||||||
UIPoint uvMin = {};
|
UIPoint uvMin = {};
|
||||||
UIPoint uvMax = UIPoint(1.0f, 1.0f);
|
UIPoint uvMax = UIPoint(1.0f, 1.0f);
|
||||||
UIColor color = {};
|
UIColor color = {};
|
||||||
|
UIColor secondaryColor = {};
|
||||||
float thickness = 1.0f;
|
float thickness = 1.0f;
|
||||||
float rounding = 0.0f;
|
float rounding = 0.0f;
|
||||||
|
float radius = 0.0f;
|
||||||
float fontSize = 0.0f;
|
float fontSize = 0.0f;
|
||||||
bool intersectWithCurrentClip = true;
|
bool intersectWithCurrentClip = true;
|
||||||
|
UILinearGradientDirection gradientDirection = UILinearGradientDirection::Horizontal;
|
||||||
UITextureHandle texture = {};
|
UITextureHandle texture = {};
|
||||||
std::string text;
|
std::string text;
|
||||||
};
|
};
|
||||||
@@ -107,6 +119,62 @@ public:
|
|||||||
return AddCommand(std::move(command));
|
return AddCommand(std::move(command));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIDrawCommand& AddFilledRectLinearGradient(
|
||||||
|
const UIRect& rect,
|
||||||
|
const UIColor& startColor,
|
||||||
|
const UIColor& endColor,
|
||||||
|
UILinearGradientDirection direction = UILinearGradientDirection::Horizontal,
|
||||||
|
float rounding = 0.0f) {
|
||||||
|
UIDrawCommand command = {};
|
||||||
|
command.type = UIDrawCommandType::FilledRectLinearGradient;
|
||||||
|
command.rect = rect;
|
||||||
|
command.color = startColor;
|
||||||
|
command.secondaryColor = endColor;
|
||||||
|
command.rounding = rounding;
|
||||||
|
command.gradientDirection = direction;
|
||||||
|
return AddCommand(std::move(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
UIDrawCommand& AddLine(
|
||||||
|
const UIPoint& start,
|
||||||
|
const UIPoint& end,
|
||||||
|
const UIColor& color,
|
||||||
|
float thickness = 1.0f) {
|
||||||
|
UIDrawCommand command = {};
|
||||||
|
command.type = UIDrawCommandType::Line;
|
||||||
|
command.position = start;
|
||||||
|
command.uvMin = end;
|
||||||
|
command.color = color;
|
||||||
|
command.thickness = thickness;
|
||||||
|
return AddCommand(std::move(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
UIDrawCommand& AddFilledCircle(
|
||||||
|
const UIPoint& center,
|
||||||
|
float radius,
|
||||||
|
const UIColor& color) {
|
||||||
|
UIDrawCommand command = {};
|
||||||
|
command.type = UIDrawCommandType::FilledCircle;
|
||||||
|
command.position = center;
|
||||||
|
command.radius = radius;
|
||||||
|
command.color = color;
|
||||||
|
return AddCommand(std::move(command));
|
||||||
|
}
|
||||||
|
|
||||||
|
UIDrawCommand& AddCircleOutline(
|
||||||
|
const UIPoint& center,
|
||||||
|
float radius,
|
||||||
|
const UIColor& color,
|
||||||
|
float thickness = 1.0f) {
|
||||||
|
UIDrawCommand command = {};
|
||||||
|
command.type = UIDrawCommandType::CircleOutline;
|
||||||
|
command.position = center;
|
||||||
|
command.radius = radius;
|
||||||
|
command.color = color;
|
||||||
|
command.thickness = thickness;
|
||||||
|
return AddCommand(std::move(command));
|
||||||
|
}
|
||||||
|
|
||||||
UIDrawCommand& AddText(
|
UIDrawCommand& AddText(
|
||||||
const UIPoint& position,
|
const UIPoint& position,
|
||||||
std::string text,
|
std::string text,
|
||||||
|
|||||||
32
engine/include/XCEngine/UI/Widgets/UIScrollModel.h
Normal file
32
engine/include/XCEngine/UI/Widgets/UIScrollModel.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace UI {
|
||||||
|
namespace Widgets {
|
||||||
|
|
||||||
|
struct UIScrollWheelResult {
|
||||||
|
bool changed = false;
|
||||||
|
float overflow = 0.0f;
|
||||||
|
float offsetBefore = 0.0f;
|
||||||
|
float offsetAfter = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
float ComputeUIScrollOverflow(float contentExtent, float viewportExtent);
|
||||||
|
float ClampUIScrollOffset(float offset, float contentExtent, float viewportExtent);
|
||||||
|
UIScrollWheelResult ApplyUIScrollWheel(
|
||||||
|
float offset,
|
||||||
|
float wheelDelta,
|
||||||
|
float contentExtent,
|
||||||
|
float viewportExtent,
|
||||||
|
float wheelStep = 48.0f,
|
||||||
|
float epsilon = 0.01f);
|
||||||
|
float EnsureUIScrollOffsetVisible(
|
||||||
|
float offset,
|
||||||
|
float itemStart,
|
||||||
|
float itemExtent,
|
||||||
|
float contentExtent,
|
||||||
|
float viewportExtent);
|
||||||
|
|
||||||
|
} // namespace Widgets
|
||||||
|
} // namespace UI
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -10,6 +10,45 @@ namespace XCEngine {
|
|||||||
namespace Audio {
|
namespace Audio {
|
||||||
namespace WASAPI {
|
namespace WASAPI {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string ConvertWaveOutDeviceName(const TCHAR* deviceName) {
|
||||||
|
if (deviceName == nullptr) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(UNICODE) || defined(_UNICODE)
|
||||||
|
const int requiredBytes = WideCharToMultiByte(
|
||||||
|
CP_UTF8,
|
||||||
|
0,
|
||||||
|
deviceName,
|
||||||
|
-1,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
nullptr);
|
||||||
|
if (requiredBytes <= 1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string utf8Name(static_cast<std::size_t>(requiredBytes - 1), '\0');
|
||||||
|
WideCharToMultiByte(
|
||||||
|
CP_UTF8,
|
||||||
|
0,
|
||||||
|
deviceName,
|
||||||
|
-1,
|
||||||
|
utf8Name.data(),
|
||||||
|
requiredBytes,
|
||||||
|
nullptr,
|
||||||
|
nullptr);
|
||||||
|
return utf8Name;
|
||||||
|
#else
|
||||||
|
return std::string(deviceName);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
WASAPIBackend::WASAPIBackend()
|
WASAPIBackend::WASAPIBackend()
|
||||||
: m_audioBuffer1(BufferSize * 2)
|
: m_audioBuffer1(BufferSize * 2)
|
||||||
, m_audioBuffer2(BufferSize * 2)
|
, m_audioBuffer2(BufferSize * 2)
|
||||||
@@ -65,7 +104,7 @@ std::string WASAPIBackend::GetDeviceName() const {
|
|||||||
void WASAPIBackend::GetAvailableDevices(std::vector<std::string>& devices) {
|
void WASAPIBackend::GetAvailableDevices(std::vector<std::string>& devices) {
|
||||||
devices.clear();
|
devices.clear();
|
||||||
for (const auto& caps : m_waveOutCaps) {
|
for (const auto& caps : m_waveOutCaps) {
|
||||||
devices.push_back(caps.szPname);
|
devices.push_back(ConvertWaveOutDeviceName(caps.szPname));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +112,7 @@ bool WASAPIBackend::SetDevice(const std::string& deviceName) {
|
|||||||
for (UINT i = 0; i < waveOutGetNumDevs(); ++i) {
|
for (UINT i = 0; i < waveOutGetNumDevs(); ++i) {
|
||||||
WAVEOUTCAPS caps;
|
WAVEOUTCAPS caps;
|
||||||
waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS));
|
waveOutGetDevCaps(i, &caps, sizeof(WAVEOUTCAPS));
|
||||||
if (deviceName == caps.szPname) {
|
if (deviceName == ConvertWaveOutDeviceName(caps.szPname)) {
|
||||||
m_deviceName = deviceName;
|
m_deviceName = deviceName;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ void MeshFilterComponent::Serialize(std::ostream& os) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
os << "meshRef=" << EncodeAssetRef(meshRef) << ";";
|
os << "meshRef=" << EncodeAssetRef(meshRef) << ";";
|
||||||
if (!meshRef.IsValid() && !m_meshPath.empty()) {
|
if (!meshRef.IsValid() && !m_meshPath.empty() && HasVirtualPathScheme(m_meshPath)) {
|
||||||
os << "meshPath=" << m_meshPath << ";";
|
os << "meshPath=" << m_meshPath << ";";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ void MeshFilterComponent::Deserialize(std::istream& is) {
|
|||||||
const std::string key = token.substr(0, eqPos);
|
const std::string key = token.substr(0, eqPos);
|
||||||
const std::string value = token.substr(eqPos + 1);
|
const std::string value = token.substr(eqPos + 1);
|
||||||
|
|
||||||
if (key == "mesh" || key == "meshPath") {
|
if (key == "meshPath") {
|
||||||
pendingMeshPath = value;
|
pendingMeshPath = value;
|
||||||
} else if (key == "meshRef") {
|
} else if (key == "meshRef") {
|
||||||
TryDecodeAssetRef(value, pendingMeshRef);
|
TryDecodeAssetRef(value, pendingMeshRef);
|
||||||
@@ -203,12 +203,9 @@ void MeshFilterComponent::Deserialize(std::istream& is) {
|
|||||||
} else {
|
} else {
|
||||||
m_meshPath = pendingMeshPath;
|
m_meshPath = pendingMeshPath;
|
||||||
}
|
}
|
||||||
} else if (!pendingMeshPath.empty()) {
|
} else if (!pendingMeshPath.empty() && HasVirtualPathScheme(pendingMeshPath)) {
|
||||||
if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) {
|
if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) {
|
||||||
m_meshPath = pendingMeshPath;
|
m_meshPath = pendingMeshPath;
|
||||||
if (!Resources::ResourceManager::Get().TryGetAssetRef(m_meshPath.c_str(), Resources::ResourceType::Mesh, m_meshRef)) {
|
|
||||||
m_meshRef.Reset();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -240,7 +240,6 @@ void MeshRendererComponent::Serialize(std::ostream& os) const {
|
|||||||
serializedRefs.resize(slotCount);
|
serializedRefs.resize(slotCount);
|
||||||
std::vector<std::string> serializedPaths = m_materialPaths;
|
std::vector<std::string> serializedPaths = m_materialPaths;
|
||||||
serializedPaths.resize(slotCount);
|
serializedPaths.resize(slotCount);
|
||||||
std::vector<std::string> fallbackPaths(slotCount);
|
|
||||||
for (size_t i = 0; i < slotCount; ++i) {
|
for (size_t i = 0; i < slotCount; ++i) {
|
||||||
if (!serializedRefs[i].IsValid() &&
|
if (!serializedRefs[i].IsValid() &&
|
||||||
!serializedPaths[i].empty() &&
|
!serializedPaths[i].empty() &&
|
||||||
@@ -251,8 +250,8 @@ void MeshRendererComponent::Serialize(std::ostream& os) const {
|
|||||||
serializedRefs[i])) {
|
serializedRefs[i])) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!serializedRefs[i].IsValid()) {
|
if (serializedRefs[i].IsValid() || !HasVirtualPathScheme(serializedPaths[i])) {
|
||||||
fallbackPaths[i] = serializedPaths[i];
|
serializedPaths[i].clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +260,7 @@ void MeshRendererComponent::Serialize(std::ostream& os) const {
|
|||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
os << "|";
|
os << "|";
|
||||||
}
|
}
|
||||||
os << fallbackPaths[i];
|
os << serializedPaths[i];
|
||||||
}
|
}
|
||||||
os << ";";
|
os << ";";
|
||||||
os << "materialRefs=";
|
os << "materialRefs=";
|
||||||
@@ -298,7 +297,7 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
|
|||||||
const std::string key = token.substr(0, eqPos);
|
const std::string key = token.substr(0, eqPos);
|
||||||
const std::string value = token.substr(eqPos + 1);
|
const std::string value = token.substr(eqPos + 1);
|
||||||
|
|
||||||
if (key == "materials" || key == "materialPaths") {
|
if (key == "materialPaths") {
|
||||||
m_materialPaths = SplitMaterialPaths(value);
|
m_materialPaths = SplitMaterialPaths(value);
|
||||||
m_materials.resize(m_materialPaths.size());
|
m_materials.resize(m_materialPaths.size());
|
||||||
m_materialRefs.resize(m_materialPaths.size());
|
m_materialRefs.resize(m_materialPaths.size());
|
||||||
@@ -362,15 +361,12 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!HasVirtualPathScheme(m_materialPaths[i])) {
|
||||||
|
m_materialPaths[i].clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) {
|
if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) {
|
||||||
if (!m_materialPaths[i].empty()) {
|
|
||||||
if (!m_materialRefs[i].IsValid() &&
|
|
||||||
!Resources::ResourceManager::Get().TryGetAssetRef(m_materialPaths[i].c_str(),
|
|
||||||
Resources::ResourceType::Material,
|
|
||||||
m_materialRefs[i])) {
|
|
||||||
m_materialRefs[i].Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,76 @@
|
|||||||
#include "XCEngine/RHI/D3D12/D3D12PipelineState.h"
|
#include "XCEngine/RHI/D3D12/D3D12PipelineState.h"
|
||||||
#include "XCEngine/RHI/D3D12/D3D12Shader.h"
|
#include "XCEngine/RHI/D3D12/D3D12Shader.h"
|
||||||
#include "XCEngine/Debug/Logger.h"
|
#include "XCEngine/Debug/Logger.h"
|
||||||
|
#include <d3d12sdklayers.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
namespace RHI {
|
namespace RHI {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void LogD3D12InfoQueueMessages(ID3D12Device* device) {
|
||||||
|
if (device == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<ID3D12InfoQueue> infoQueue;
|
||||||
|
if (FAILED(device->QueryInterface(IID_PPV_ARGS(&infoQueue))) || infoQueue == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UINT64 messageCount = infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter();
|
||||||
|
if (messageCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UINT64 firstMessage = messageCount > 12 ? messageCount - 12 : 0;
|
||||||
|
for (UINT64 messageIndex = firstMessage; messageIndex < messageCount; ++messageIndex) {
|
||||||
|
SIZE_T messageLength = 0;
|
||||||
|
if (FAILED(infoQueue->GetMessage(messageIndex, nullptr, &messageLength)) || messageLength == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> storage(messageLength);
|
||||||
|
auto* message = reinterpret_cast<D3D12_MESSAGE*>(storage.data());
|
||||||
|
if (FAILED(infoQueue->GetMessage(messageIndex, message, &messageLength)) || message == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message->pDescription != nullptr && message->pDescription[0] != '\0') {
|
||||||
|
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message->pDescription);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
infoQueue->ClearStoredMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LogGraphicsPipelineFailureDetails(
|
||||||
|
ID3D12Device* device,
|
||||||
|
HRESULT pipelineCreateResult,
|
||||||
|
const D3D12_GRAPHICS_PIPELINE_STATE_DESC& desc) {
|
||||||
|
char details[512] = {};
|
||||||
|
sprintf_s(
|
||||||
|
details,
|
||||||
|
"D3D12 PSO failure details: createHr=0x%08X removedReason=0x%08X rootSignature=%p rtCount=%u rtv0=0x%X dsv=0x%X sampleCount=%u vsBytes=%zu psBytes=%zu gsBytes=%zu",
|
||||||
|
static_cast<unsigned int>(pipelineCreateResult),
|
||||||
|
static_cast<unsigned int>(device != nullptr ? device->GetDeviceRemovedReason() : S_OK),
|
||||||
|
desc.pRootSignature,
|
||||||
|
desc.NumRenderTargets,
|
||||||
|
static_cast<unsigned int>(desc.RTVFormats[0]),
|
||||||
|
static_cast<unsigned int>(desc.DSVFormat),
|
||||||
|
desc.SampleDesc.Count,
|
||||||
|
static_cast<size_t>(desc.VS.BytecodeLength),
|
||||||
|
static_cast<size_t>(desc.PS.BytecodeLength),
|
||||||
|
static_cast<size_t>(desc.GS.BytecodeLength));
|
||||||
|
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, details);
|
||||||
|
|
||||||
|
LogD3D12InfoQueueMessages(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
D3D12PipelineState::D3D12PipelineState(ID3D12Device* device)
|
D3D12PipelineState::D3D12PipelineState(ID3D12Device* device)
|
||||||
: m_device(device), m_finalized(false) {
|
: m_device(device), m_finalized(false) {
|
||||||
m_renderTargetFormats[0] = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
m_renderTargetFormats[0] = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
|
||||||
@@ -176,6 +241,9 @@ bool D3D12PipelineState::CreateD3D12PSO() {
|
|||||||
desc.RasterizerState.DepthClipEnable = m_rasterizerDesc.depthClipEnable;
|
desc.RasterizerState.DepthClipEnable = m_rasterizerDesc.depthClipEnable;
|
||||||
desc.RasterizerState.MultisampleEnable = m_rasterizerDesc.multisampleEnable;
|
desc.RasterizerState.MultisampleEnable = m_rasterizerDesc.multisampleEnable;
|
||||||
desc.RasterizerState.AntialiasedLineEnable = m_rasterizerDesc.antialiasedLineEnable;
|
desc.RasterizerState.AntialiasedLineEnable = m_rasterizerDesc.antialiasedLineEnable;
|
||||||
|
desc.RasterizerState.DepthBias = m_rasterizerDesc.depthBias;
|
||||||
|
desc.RasterizerState.DepthBiasClamp = m_rasterizerDesc.depthBiasClamp;
|
||||||
|
desc.RasterizerState.SlopeScaledDepthBias = m_rasterizerDesc.slopeScaledDepthBias;
|
||||||
|
|
||||||
desc.BlendState.RenderTarget[0].BlendEnable = m_blendDesc.blendEnable;
|
desc.BlendState.RenderTarget[0].BlendEnable = m_blendDesc.blendEnable;
|
||||||
desc.BlendState.RenderTarget[0].SrcBlend = ToD3D12(static_cast<BlendFactor>(m_blendDesc.srcBlend));
|
desc.BlendState.RenderTarget[0].SrcBlend = ToD3D12(static_cast<BlendFactor>(m_blendDesc.srcBlend));
|
||||||
@@ -216,6 +284,7 @@ bool D3D12PipelineState::CreateD3D12PSO() {
|
|||||||
char errorMessage[256] = {};
|
char errorMessage[256] = {};
|
||||||
sprintf_s(errorMessage, "D3D12 CreateGraphicsPipelineState failed: hr=0x%08X", static_cast<unsigned int>(hr));
|
sprintf_s(errorMessage, "D3D12 CreateGraphicsPipelineState failed: hr=0x%08X", static_cast<unsigned int>(hr));
|
||||||
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, errorMessage);
|
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, errorMessage);
|
||||||
|
LogGraphicsPipelineFailureDetails(m_device, hr, desc);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -319,17 +319,26 @@ void OpenGLCommandList::SetPrimitiveType(PrimitiveType type) {
|
|||||||
|
|
||||||
void OpenGLCommandList::Draw(PrimitiveType type, unsigned int vertexCount, unsigned int startVertex) {
|
void OpenGLCommandList::Draw(PrimitiveType type, unsigned int vertexCount, unsigned int startVertex) {
|
||||||
m_primitiveType = ToOpenGL(type);
|
m_primitiveType = ToOpenGL(type);
|
||||||
|
if (m_currentVAO == 0) {
|
||||||
|
EnsureInternalVertexArrayBound();
|
||||||
|
}
|
||||||
glDrawArrays(m_primitiveType, startVertex, vertexCount);
|
glDrawArrays(m_primitiveType, startVertex, vertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::DrawInstanced(PrimitiveType type, unsigned int vertexCount, unsigned int instanceCount, unsigned int startVertex, unsigned int startInstance) {
|
void OpenGLCommandList::DrawInstanced(PrimitiveType type, unsigned int vertexCount, unsigned int instanceCount, unsigned int startVertex, unsigned int startInstance) {
|
||||||
m_primitiveType = ToOpenGL(type);
|
m_primitiveType = ToOpenGL(type);
|
||||||
|
if (m_currentVAO == 0) {
|
||||||
|
EnsureInternalVertexArrayBound();
|
||||||
|
}
|
||||||
glDrawArraysInstanced(m_primitiveType, startVertex, vertexCount, instanceCount);
|
glDrawArraysInstanced(m_primitiveType, startVertex, vertexCount, instanceCount);
|
||||||
(void)startInstance;
|
(void)startInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::DrawIndexed(PrimitiveType type, unsigned int indexCount, unsigned int startIndex, int baseVertex) {
|
void OpenGLCommandList::DrawIndexed(PrimitiveType type, unsigned int indexCount, unsigned int startIndex, int baseVertex) {
|
||||||
m_primitiveType = ToOpenGL(type);
|
m_primitiveType = ToOpenGL(type);
|
||||||
|
if (m_currentVAO == 0) {
|
||||||
|
EnsureInternalVertexArrayBound();
|
||||||
|
}
|
||||||
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
||||||
glDrawElements(m_primitiveType,
|
glDrawElements(m_primitiveType,
|
||||||
indexCount,
|
indexCount,
|
||||||
@@ -340,6 +349,9 @@ void OpenGLCommandList::DrawIndexed(PrimitiveType type, unsigned int indexCount,
|
|||||||
|
|
||||||
void OpenGLCommandList::DrawIndexedInstanced(PrimitiveType type, unsigned int indexCount, unsigned int instanceCount, unsigned int startIndex, int baseVertex, unsigned int startInstance) {
|
void OpenGLCommandList::DrawIndexedInstanced(PrimitiveType type, unsigned int indexCount, unsigned int instanceCount, unsigned int startIndex, int baseVertex, unsigned int startInstance) {
|
||||||
m_primitiveType = ToOpenGL(type);
|
m_primitiveType = ToOpenGL(type);
|
||||||
|
if (m_currentVAO == 0) {
|
||||||
|
EnsureInternalVertexArrayBound();
|
||||||
|
}
|
||||||
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
||||||
glDrawElementsInstanced(m_primitiveType,
|
glDrawElementsInstanced(m_primitiveType,
|
||||||
indexCount,
|
indexCount,
|
||||||
@@ -1055,6 +1067,9 @@ void OpenGLCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) {
|
void OpenGLCommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint32_t startVertex, uint32_t startInstance) {
|
||||||
|
if (m_currentVAO == 0) {
|
||||||
|
EnsureInternalVertexArrayBound();
|
||||||
|
}
|
||||||
glDrawArraysInstanced(m_primitiveType,
|
glDrawArraysInstanced(m_primitiveType,
|
||||||
static_cast<GLint>(startVertex),
|
static_cast<GLint>(startVertex),
|
||||||
static_cast<GLsizei>(vertexCount),
|
static_cast<GLsizei>(vertexCount),
|
||||||
@@ -1063,6 +1078,9 @@ void OpenGLCommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint3
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) {
|
void OpenGLCommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount, uint32_t startIndex, int32_t baseVertex, uint32_t startInstance) {
|
||||||
|
if (m_currentVAO == 0) {
|
||||||
|
EnsureInternalVertexArrayBound();
|
||||||
|
}
|
||||||
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
const uint64_t indexOffset = m_currentIndexOffset + static_cast<uint64_t>(startIndex) * GetIndexTypeSize(m_currentIndexType);
|
||||||
glDrawElementsInstanced(m_primitiveType,
|
glDrawElementsInstanced(m_primitiveType,
|
||||||
static_cast<GLsizei>(indexCount),
|
static_cast<GLsizei>(indexCount),
|
||||||
@@ -1078,7 +1096,23 @@ void OpenGLCommandList::SetPrimitiveTopology(PrimitiveTopology topology) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::SetStencilRef(uint8_t ref) {
|
void OpenGLCommandList::SetStencilRef(uint8_t ref) {
|
||||||
glStencilFunc(GL_FRONT_AND_BACK, ref, 0xFF);
|
if (m_currentPipelineState == nullptr) {
|
||||||
|
glStencilFuncSeparate(GL_FRONT, GL_ALWAYS, ref, 0xFF);
|
||||||
|
glStencilFuncSeparate(GL_BACK, GL_ALWAYS, ref, 0xFF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const OpenGLDepthStencilState& state = m_currentPipelineState->GetOpenGLDepthStencilState();
|
||||||
|
glStencilFuncSeparate(
|
||||||
|
GL_FRONT,
|
||||||
|
ToOpenGL(state.frontStencilFunc),
|
||||||
|
ref,
|
||||||
|
state.stencilReadMask);
|
||||||
|
glStencilFuncSeparate(
|
||||||
|
GL_BACK,
|
||||||
|
ToOpenGL(state.backStencilFunc),
|
||||||
|
ref,
|
||||||
|
state.stencilReadMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLCommandList::SetBlendFactor(const float factor[4]) {
|
void OpenGLCommandList::SetBlendFactor(const float factor[4]) {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ void OpenGLPipelineState::SetRasterizerState(const RasterizerDesc& state) {
|
|||||||
m_glRasterizerState.scissorTestEnable = state.scissorTestEnable;
|
m_glRasterizerState.scissorTestEnable = state.scissorTestEnable;
|
||||||
m_glRasterizerState.multisampleEnable = state.multisampleEnable;
|
m_glRasterizerState.multisampleEnable = state.multisampleEnable;
|
||||||
m_glRasterizerState.polygonOffsetFactor = state.slopeScaledDepthBias;
|
m_glRasterizerState.polygonOffsetFactor = state.slopeScaledDepthBias;
|
||||||
m_glRasterizerState.polygonOffsetUnits = state.depthBiasClamp;
|
m_glRasterizerState.polygonOffsetUnits = static_cast<float>(state.depthBias);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLPipelineState::SetBlendState(const BlendDesc& state) {
|
void OpenGLPipelineState::SetBlendState(const BlendDesc& state) {
|
||||||
@@ -52,10 +52,14 @@ void OpenGLPipelineState::SetDepthStencilState(const DepthStencilStateDesc& stat
|
|||||||
m_glDepthStencilState.stencilEnable = state.stencilEnable;
|
m_glDepthStencilState.stencilEnable = state.stencilEnable;
|
||||||
m_glDepthStencilState.stencilReadMask = state.stencilReadMask;
|
m_glDepthStencilState.stencilReadMask = state.stencilReadMask;
|
||||||
m_glDepthStencilState.stencilWriteMask = state.stencilWriteMask;
|
m_glDepthStencilState.stencilWriteMask = state.stencilWriteMask;
|
||||||
m_glDepthStencilState.stencilFunc = static_cast<ComparisonFunc>(state.front.func);
|
m_glDepthStencilState.frontStencilFunc = static_cast<ComparisonFunc>(state.front.func);
|
||||||
m_glDepthStencilState.stencilFailOp = static_cast<StencilOp>(state.front.failOp);
|
m_glDepthStencilState.frontStencilFailOp = static_cast<StencilOp>(state.front.failOp);
|
||||||
m_glDepthStencilState.stencilDepthFailOp = static_cast<StencilOp>(state.front.depthFailOp);
|
m_glDepthStencilState.frontStencilDepthFailOp = static_cast<StencilOp>(state.front.depthFailOp);
|
||||||
m_glDepthStencilState.stencilDepthPassOp = static_cast<StencilOp>(state.front.passOp);
|
m_glDepthStencilState.frontStencilDepthPassOp = static_cast<StencilOp>(state.front.passOp);
|
||||||
|
m_glDepthStencilState.backStencilFunc = static_cast<ComparisonFunc>(state.back.func);
|
||||||
|
m_glDepthStencilState.backStencilFailOp = static_cast<StencilOp>(state.back.failOp);
|
||||||
|
m_glDepthStencilState.backStencilDepthFailOp = static_cast<StencilOp>(state.back.depthFailOp);
|
||||||
|
m_glDepthStencilState.backStencilDepthPassOp = static_cast<StencilOp>(state.back.passOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLPipelineState::SetTopology(uint32_t topologyType) {
|
void OpenGLPipelineState::SetTopology(uint32_t topologyType) {
|
||||||
@@ -121,16 +125,31 @@ void OpenGLPipelineState::ApplyDepthStencil() {
|
|||||||
|
|
||||||
if (m_glDepthStencilState.stencilEnable) {
|
if (m_glDepthStencilState.stencilEnable) {
|
||||||
glEnable(GL_STENCIL_TEST);
|
glEnable(GL_STENCIL_TEST);
|
||||||
glStencilMask(m_glDepthStencilState.stencilWriteMask);
|
glStencilMaskSeparate(GL_FRONT, m_glDepthStencilState.stencilWriteMask);
|
||||||
glStencilFunc(
|
glStencilMaskSeparate(GL_BACK, m_glDepthStencilState.stencilWriteMask);
|
||||||
ToOpenGL(m_glDepthStencilState.stencilFunc),
|
glStencilFuncSeparate(
|
||||||
|
GL_FRONT,
|
||||||
|
ToOpenGL(m_glDepthStencilState.frontStencilFunc),
|
||||||
m_glDepthStencilState.stencilRef,
|
m_glDepthStencilState.stencilRef,
|
||||||
m_glDepthStencilState.stencilReadMask
|
m_glDepthStencilState.stencilReadMask
|
||||||
);
|
);
|
||||||
glStencilOp(
|
glStencilOpSeparate(
|
||||||
ToOpenGL(m_glDepthStencilState.stencilFailOp),
|
GL_FRONT,
|
||||||
ToOpenGL(m_glDepthStencilState.stencilDepthFailOp),
|
ToOpenGL(m_glDepthStencilState.frontStencilFailOp),
|
||||||
ToOpenGL(m_glDepthStencilState.stencilDepthPassOp)
|
ToOpenGL(m_glDepthStencilState.frontStencilDepthFailOp),
|
||||||
|
ToOpenGL(m_glDepthStencilState.frontStencilDepthPassOp)
|
||||||
|
);
|
||||||
|
glStencilFuncSeparate(
|
||||||
|
GL_BACK,
|
||||||
|
ToOpenGL(m_glDepthStencilState.backStencilFunc),
|
||||||
|
m_glDepthStencilState.stencilRef,
|
||||||
|
m_glDepthStencilState.stencilReadMask
|
||||||
|
);
|
||||||
|
glStencilOpSeparate(
|
||||||
|
GL_BACK,
|
||||||
|
ToOpenGL(m_glDepthStencilState.backStencilFailOp),
|
||||||
|
ToOpenGL(m_glDepthStencilState.backStencilDepthFailOp),
|
||||||
|
ToOpenGL(m_glDepthStencilState.backStencilDepthPassOp)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
glDisable(GL_STENCIL_TEST);
|
glDisable(GL_STENCIL_TEST);
|
||||||
@@ -177,6 +196,18 @@ void OpenGLPipelineState::ApplyRasterizer() {
|
|||||||
} else {
|
} else {
|
||||||
glDisable(GL_MULTISAMPLE);
|
glDisable(GL_MULTISAMPLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool polygonOffsetEnable =
|
||||||
|
m_glRasterizerState.polygonOffsetFactor != 0.0f ||
|
||||||
|
m_glRasterizerState.polygonOffsetUnits != 0.0f;
|
||||||
|
if (polygonOffsetEnable) {
|
||||||
|
glEnable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
glPolygonOffset(
|
||||||
|
m_glRasterizerState.polygonOffsetFactor,
|
||||||
|
m_glRasterizerState.polygonOffsetUnits);
|
||||||
|
} else {
|
||||||
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLPipelineState::ApplyViewport() {
|
void OpenGLPipelineState::ApplyViewport() {
|
||||||
|
|||||||
@@ -305,6 +305,7 @@ void VulkanCommandList::Reset() {
|
|||||||
m_renderPassActive = false;
|
m_renderPassActive = false;
|
||||||
m_hasViewport = false;
|
m_hasViewport = false;
|
||||||
m_hasScissor = false;
|
m_hasScissor = false;
|
||||||
|
m_stencilRef = 0;
|
||||||
|
|
||||||
VkCommandBufferBeginInfo beginInfo = {};
|
VkCommandBufferBeginInfo beginInfo = {};
|
||||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
@@ -586,7 +587,10 @@ void VulkanCommandList::SetRenderTargets(uint32_t count, RHIResourceView** rende
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommandList::SetStencilRef(uint8_t ref) {
|
void VulkanCommandList::SetStencilRef(uint8_t ref) {
|
||||||
(void)ref;
|
m_stencilRef = ref;
|
||||||
|
if (m_renderPassActive && m_currentPipelineState != nullptr) {
|
||||||
|
vkCmdSetStencilReference(m_commandBuffer, VK_STENCIL_FRONT_AND_BACK, m_stencilRef);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanCommandList::SetBlendFactor(const float factor[4]) {
|
void VulkanCommandList::SetBlendFactor(const float factor[4]) {
|
||||||
@@ -643,6 +647,9 @@ void VulkanCommandList::Draw(uint32_t vertexCount, uint32_t instanceCount, uint3
|
|||||||
}
|
}
|
||||||
|
|
||||||
vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_currentPipelineState->GetPipeline());
|
vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_currentPipelineState->GetPipeline());
|
||||||
|
if (m_currentPipelineState->GetDepthStencilState().stencilEnable) {
|
||||||
|
vkCmdSetStencilReference(m_commandBuffer, VK_STENCIL_FRONT_AND_BACK, m_stencilRef);
|
||||||
|
}
|
||||||
vkCmdDraw(m_commandBuffer, vertexCount, instanceCount, startVertex, startInstance);
|
vkCmdDraw(m_commandBuffer, vertexCount, instanceCount, startVertex, startInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,6 +659,9 @@ void VulkanCommandList::DrawIndexed(uint32_t indexCount, uint32_t instanceCount,
|
|||||||
}
|
}
|
||||||
|
|
||||||
vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_currentPipelineState->GetPipeline());
|
vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_currentPipelineState->GetPipeline());
|
||||||
|
if (m_currentPipelineState->GetDepthStencilState().stencilEnable) {
|
||||||
|
vkCmdSetStencilReference(m_commandBuffer, VK_STENCIL_FRONT_AND_BACK, m_stencilRef);
|
||||||
|
}
|
||||||
vkCmdDrawIndexed(m_commandBuffer, indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
vkCmdDrawIndexed(m_commandBuffer, indexCount, instanceCount, startIndex, baseVertex, startInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,40 @@ VkShaderModule CreateShaderModule(VkDevice device, const std::vector<uint32_t>&
|
|||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkStencilOp ToVulkanStencilOp(StencilOp op) {
|
||||||
|
switch (op) {
|
||||||
|
case StencilOp::Zero:
|
||||||
|
return VK_STENCIL_OP_ZERO;
|
||||||
|
case StencilOp::Replace:
|
||||||
|
return VK_STENCIL_OP_REPLACE;
|
||||||
|
case StencilOp::IncrSat:
|
||||||
|
return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
|
||||||
|
case StencilOp::DecrSat:
|
||||||
|
return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
|
||||||
|
case StencilOp::Invert:
|
||||||
|
return VK_STENCIL_OP_INVERT;
|
||||||
|
case StencilOp::Incr:
|
||||||
|
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
|
||||||
|
case StencilOp::Decr:
|
||||||
|
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
|
||||||
|
case StencilOp::Keep:
|
||||||
|
default:
|
||||||
|
return VK_STENCIL_OP_KEEP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkStencilOpState BuildVulkanStencilOpState(const StencilOpDesc& desc) {
|
||||||
|
VkStencilOpState state = {};
|
||||||
|
state.failOp = ToVulkanStencilOp(static_cast<StencilOp>(desc.failOp));
|
||||||
|
state.passOp = ToVulkanStencilOp(static_cast<StencilOp>(desc.passOp));
|
||||||
|
state.depthFailOp = ToVulkanStencilOp(static_cast<StencilOp>(desc.depthFailOp));
|
||||||
|
state.compareOp = ToVulkanCompareOp(static_cast<ComparisonFunc>(desc.func));
|
||||||
|
state.compareMask = 0xFFu;
|
||||||
|
state.writeMask = 0xFFu;
|
||||||
|
state.reference = 0u;
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
VulkanPipelineState::~VulkanPipelineState() {
|
VulkanPipelineState::~VulkanPipelineState() {
|
||||||
@@ -303,10 +337,17 @@ bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& des
|
|||||||
depthStencil.depthCompareOp = ToVulkanCompareOp(static_cast<ComparisonFunc>(m_depthStencilDesc.depthFunc));
|
depthStencil.depthCompareOp = ToVulkanCompareOp(static_cast<ComparisonFunc>(m_depthStencilDesc.depthFunc));
|
||||||
depthStencil.depthBoundsTestEnable = m_depthStencilDesc.depthBoundsEnable ? VK_TRUE : VK_FALSE;
|
depthStencil.depthBoundsTestEnable = m_depthStencilDesc.depthBoundsEnable ? VK_TRUE : VK_FALSE;
|
||||||
depthStencil.stencilTestEnable = m_depthStencilDesc.stencilEnable ? VK_TRUE : VK_FALSE;
|
depthStencil.stencilTestEnable = m_depthStencilDesc.stencilEnable ? VK_TRUE : VK_FALSE;
|
||||||
|
depthStencil.front = BuildVulkanStencilOpState(m_depthStencilDesc.front);
|
||||||
|
depthStencil.back = BuildVulkanStencilOpState(m_depthStencilDesc.back);
|
||||||
|
depthStencil.front.compareMask = m_depthStencilDesc.stencilReadMask;
|
||||||
|
depthStencil.front.writeMask = m_depthStencilDesc.stencilWriteMask;
|
||||||
|
depthStencil.back.compareMask = m_depthStencilDesc.stencilReadMask;
|
||||||
|
depthStencil.back.writeMask = m_depthStencilDesc.stencilWriteMask;
|
||||||
|
|
||||||
VkDynamicState dynamicStates[] = {
|
VkDynamicState dynamicStates[] = {
|
||||||
VK_DYNAMIC_STATE_VIEWPORT,
|
VK_DYNAMIC_STATE_VIEWPORT,
|
||||||
VK_DYNAMIC_STATE_SCISSOR
|
VK_DYNAMIC_STATE_SCISSOR,
|
||||||
|
VK_DYNAMIC_STATE_STENCIL_REFERENCE
|
||||||
};
|
};
|
||||||
VkPipelineDynamicStateCreateInfo dynamicState = {};
|
VkPipelineDynamicStateCreateInfo dynamicState = {};
|
||||||
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||||
|
|||||||
@@ -75,14 +75,27 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
|||||||
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
||||||
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::Always);
|
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::Always);
|
||||||
|
|
||||||
|
const Resources::ShaderPass* shaderPass = shader.FindPass(passName);
|
||||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||||
if (const Resources::ShaderStageVariant* vertexVariant =
|
if (const Resources::ShaderStageVariant* vertexVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*vertexVariant,
|
||||||
|
pipelineDesc.vertexShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (const Resources::ShaderStageVariant* fragmentVariant =
|
if (const Resources::ShaderStageVariant* fragmentVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*fragmentVariant,
|
||||||
|
pipelineDesc.fragmentShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pipelineDesc;
|
return pipelineDesc;
|
||||||
|
|||||||
@@ -77,14 +77,27 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
|||||||
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
||||||
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::Always);
|
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::Always);
|
||||||
|
|
||||||
|
const Resources::ShaderPass* shaderPass = shader.FindPass(passName);
|
||||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||||
if (const Resources::ShaderStageVariant* vertexVariant =
|
if (const Resources::ShaderStageVariant* vertexVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*vertexVariant,
|
||||||
|
pipelineDesc.vertexShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (const Resources::ShaderStageVariant* fragmentVariant =
|
if (const Resources::ShaderStageVariant* fragmentVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*fragmentVariant,
|
||||||
|
pipelineDesc.fragmentShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pipelineDesc;
|
return pipelineDesc;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <XCEngine/RHI/RHICommandList.h>
|
#include <XCEngine/RHI/RHICommandList.h>
|
||||||
#include <XCEngine/RHI/RHIDevice.h>
|
#include <XCEngine/RHI/RHIDevice.h>
|
||||||
|
|
||||||
|
#include "Rendering/Materials/RenderMaterialStateUtils.h"
|
||||||
#include "Rendering/Detail/ShaderVariantUtils.h"
|
#include "Rendering/Detail/ShaderVariantUtils.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -332,35 +333,50 @@ bool BuiltinInfiniteGridPass::CreateResources(const RenderContext& renderContext
|
|||||||
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
|
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
|
||||||
pipelineDesc.sampleCount = 1;
|
pipelineDesc.sampleCount = 1;
|
||||||
|
|
||||||
pipelineDesc.rasterizerState.fillMode = static_cast<uint32_t>(RHI::FillMode::Solid);
|
if (infiniteGridPass->hasFixedFunctionState) {
|
||||||
pipelineDesc.rasterizerState.cullMode = static_cast<uint32_t>(RHI::CullMode::None);
|
::XCEngine::Rendering::ApplyRenderState(infiniteGridPass->fixedFunctionState, pipelineDesc);
|
||||||
pipelineDesc.rasterizerState.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
|
} else {
|
||||||
pipelineDesc.rasterizerState.depthClipEnable = true;
|
Resources::MaterialRenderState fallbackState = {};
|
||||||
|
fallbackState.cullMode = Resources::MaterialCullMode::None;
|
||||||
pipelineDesc.blendState.blendEnable = true;
|
fallbackState.depthWriteEnable = false;
|
||||||
pipelineDesc.blendState.srcBlend = static_cast<uint32_t>(RHI::BlendFactor::SrcAlpha);
|
fallbackState.depthTestEnable = true;
|
||||||
pipelineDesc.blendState.dstBlend = static_cast<uint32_t>(RHI::BlendFactor::InvSrcAlpha);
|
fallbackState.depthFunc = Resources::MaterialComparisonFunc::LessEqual;
|
||||||
pipelineDesc.blendState.srcBlendAlpha = static_cast<uint32_t>(RHI::BlendFactor::One);
|
fallbackState.blendEnable = true;
|
||||||
pipelineDesc.blendState.dstBlendAlpha = static_cast<uint32_t>(RHI::BlendFactor::InvSrcAlpha);
|
fallbackState.srcBlend = Resources::MaterialBlendFactor::SrcAlpha;
|
||||||
pipelineDesc.blendState.blendOp = static_cast<uint32_t>(RHI::BlendOp::Add);
|
fallbackState.dstBlend = Resources::MaterialBlendFactor::InvSrcAlpha;
|
||||||
pipelineDesc.blendState.blendOpAlpha = static_cast<uint32_t>(RHI::BlendOp::Add);
|
fallbackState.srcBlendAlpha = Resources::MaterialBlendFactor::One;
|
||||||
pipelineDesc.blendState.colorWriteMask = 0xF;
|
fallbackState.dstBlendAlpha = Resources::MaterialBlendFactor::InvSrcAlpha;
|
||||||
|
fallbackState.blendOp = Resources::MaterialBlendOp::Add;
|
||||||
pipelineDesc.depthStencilState.depthTestEnable = true;
|
fallbackState.blendOpAlpha = Resources::MaterialBlendOp::Add;
|
||||||
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
fallbackState.colorWriteMask = static_cast<uint8_t>(RHI::ColorWriteMask::All);
|
||||||
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::LessEqual);
|
::XCEngine::Rendering::ApplyRenderState(fallbackState, pipelineDesc);
|
||||||
|
}
|
||||||
|
|
||||||
if (const Resources::ShaderStageVariant* vertexVariant = m_builtinInfiniteGridShader->FindVariant(
|
if (const Resources::ShaderStageVariant* vertexVariant = m_builtinInfiniteGridShader->FindVariant(
|
||||||
infiniteGridPass->name,
|
infiniteGridPass->name,
|
||||||
Resources::ShaderType::Vertex,
|
Resources::ShaderType::Vertex,
|
||||||
backend)) {
|
backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
const Resources::ShaderPass* shaderPass = m_builtinInfiniteGridShader->FindPass(infiniteGridPass->name);
|
||||||
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*vertexVariant,
|
||||||
|
pipelineDesc.vertexShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (const Resources::ShaderStageVariant* fragmentVariant = m_builtinInfiniteGridShader->FindVariant(
|
if (const Resources::ShaderStageVariant* fragmentVariant = m_builtinInfiniteGridShader->FindVariant(
|
||||||
infiniteGridPass->name,
|
infiniteGridPass->name,
|
||||||
Resources::ShaderType::Fragment,
|
Resources::ShaderType::Fragment,
|
||||||
backend)) {
|
backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
const Resources::ShaderPass* shaderPass = m_builtinInfiniteGridShader->FindPass(infiniteGridPass->name);
|
||||||
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*fragmentVariant,
|
||||||
|
pipelineDesc.fragmentShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pipelineState = m_device->CreatePipelineState(pipelineDesc);
|
m_pipelineState = m_device->CreatePipelineState(pipelineDesc);
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
#include "Debug/Logger.h"
|
#include "Debug/Logger.h"
|
||||||
#include "Rendering/Picking/ObjectIdCodec.h"
|
#include "Rendering/Picking/ObjectIdCodec.h"
|
||||||
#include "Rendering/Detail/ShaderVariantUtils.h"
|
#include "Rendering/Detail/ShaderVariantUtils.h"
|
||||||
|
#include "Rendering/Materials/RenderMaterialStateUtils.h"
|
||||||
#include "RHI/RHICommandList.h"
|
#include "RHI/RHICommandList.h"
|
||||||
#include "RHI/RHIDevice.h"
|
#include "RHI/RHIDevice.h"
|
||||||
|
#include <XCEngine/Resources/BuiltinResources.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -52,32 +54,46 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
|||||||
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::Unknown);
|
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::Unknown);
|
||||||
pipelineDesc.sampleCount = 1;
|
pipelineDesc.sampleCount = 1;
|
||||||
|
|
||||||
pipelineDesc.rasterizerState.fillMode = static_cast<uint32_t>(RHI::FillMode::Solid);
|
const Resources::ShaderPass* shaderPass = shader.FindPass(passName);
|
||||||
pipelineDesc.rasterizerState.cullMode = static_cast<uint32_t>(RHI::CullMode::None);
|
if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) {
|
||||||
pipelineDesc.rasterizerState.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
|
::XCEngine::Rendering::ApplyRenderState(shaderPass->fixedFunctionState, pipelineDesc);
|
||||||
pipelineDesc.rasterizerState.depthClipEnable = true;
|
} else {
|
||||||
|
Resources::MaterialRenderState fallbackState = {};
|
||||||
pipelineDesc.blendState.blendEnable = true;
|
fallbackState.cullMode = Resources::MaterialCullMode::None;
|
||||||
pipelineDesc.blendState.srcBlend = static_cast<uint32_t>(RHI::BlendFactor::SrcAlpha);
|
fallbackState.depthWriteEnable = false;
|
||||||
pipelineDesc.blendState.dstBlend = static_cast<uint32_t>(RHI::BlendFactor::InvSrcAlpha);
|
fallbackState.depthTestEnable = false;
|
||||||
pipelineDesc.blendState.srcBlendAlpha = static_cast<uint32_t>(RHI::BlendFactor::One);
|
fallbackState.depthFunc = Resources::MaterialComparisonFunc::Always;
|
||||||
pipelineDesc.blendState.dstBlendAlpha = static_cast<uint32_t>(RHI::BlendFactor::InvSrcAlpha);
|
fallbackState.blendEnable = true;
|
||||||
pipelineDesc.blendState.blendOp = static_cast<uint32_t>(RHI::BlendOp::Add);
|
fallbackState.srcBlend = Resources::MaterialBlendFactor::SrcAlpha;
|
||||||
pipelineDesc.blendState.blendOpAlpha = static_cast<uint32_t>(RHI::BlendOp::Add);
|
fallbackState.dstBlend = Resources::MaterialBlendFactor::InvSrcAlpha;
|
||||||
pipelineDesc.blendState.colorWriteMask = static_cast<uint8_t>(RHI::ColorWriteMask::All);
|
fallbackState.srcBlendAlpha = Resources::MaterialBlendFactor::One;
|
||||||
|
fallbackState.dstBlendAlpha = Resources::MaterialBlendFactor::InvSrcAlpha;
|
||||||
pipelineDesc.depthStencilState.depthTestEnable = false;
|
fallbackState.blendOp = Resources::MaterialBlendOp::Add;
|
||||||
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
fallbackState.blendOpAlpha = Resources::MaterialBlendOp::Add;
|
||||||
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::Always);
|
fallbackState.colorWriteMask = static_cast<uint8_t>(RHI::ColorWriteMask::All);
|
||||||
|
::XCEngine::Rendering::ApplyRenderState(fallbackState, pipelineDesc);
|
||||||
|
}
|
||||||
|
|
||||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||||
if (const Resources::ShaderStageVariant* vertexVariant =
|
if (const Resources::ShaderStageVariant* vertexVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*vertexVariant,
|
||||||
|
pipelineDesc.vertexShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (const Resources::ShaderStageVariant* fragmentVariant =
|
if (const Resources::ShaderStageVariant* fragmentVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*fragmentVariant,
|
||||||
|
pipelineDesc.fragmentShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pipelineDesc;
|
return pipelineDesc;
|
||||||
@@ -86,7 +102,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
BuiltinObjectIdOutlinePass::BuiltinObjectIdOutlinePass(Containers::String shaderPath)
|
BuiltinObjectIdOutlinePass::BuiltinObjectIdOutlinePass(Containers::String shaderPath)
|
||||||
: m_shaderPath(std::move(shaderPath)) {
|
: m_shaderPath(shaderPath.Empty() ? Resources::GetBuiltinObjectIdOutlineShaderPath() : std::move(shaderPath)) {
|
||||||
ResetState();
|
ResetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,14 +77,27 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
|||||||
pipelineDesc.depthStencilState.depthWriteEnable = true;
|
pipelineDesc.depthStencilState.depthWriteEnable = true;
|
||||||
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::LessEqual);
|
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::LessEqual);
|
||||||
|
|
||||||
|
const Resources::ShaderPass* shaderPass = shader.FindPass(passName);
|
||||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||||
if (const Resources::ShaderStageVariant* vertexVariant =
|
if (const Resources::ShaderStageVariant* vertexVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*vertexVariant,
|
||||||
|
pipelineDesc.vertexShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (const Resources::ShaderStageVariant* fragmentVariant =
|
if (const Resources::ShaderStageVariant* fragmentVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*fragmentVariant,
|
||||||
|
pipelineDesc.fragmentShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pipelineDesc;
|
return pipelineDesc;
|
||||||
|
|||||||
@@ -146,14 +146,27 @@ RHI::GraphicsPipelineDesc CreateSkyboxPipelineDesc(
|
|||||||
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
pipelineDesc.depthStencilState.depthWriteEnable = false;
|
||||||
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::LessEqual);
|
pipelineDesc.depthStencilState.depthFunc = static_cast<uint32_t>(RHI::ComparisonFunc::LessEqual);
|
||||||
|
|
||||||
|
const Resources::ShaderPass* shaderPass = shader.FindPass(passName);
|
||||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||||
if (const Resources::ShaderStageVariant* vertexVariant =
|
if (const Resources::ShaderStageVariant* vertexVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*vertexVariant,
|
||||||
|
pipelineDesc.vertexShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (const Resources::ShaderStageVariant* fragmentVariant =
|
if (const Resources::ShaderStageVariant* fragmentVariant =
|
||||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
||||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
if (shaderPass != nullptr) {
|
||||||
|
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(
|
||||||
|
*shaderPass,
|
||||||
|
backend,
|
||||||
|
*fragmentVariant,
|
||||||
|
pipelineDesc.fragmentShader);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pipelineDesc;
|
return pipelineDesc;
|
||||||
|
|||||||
@@ -324,7 +324,6 @@ void Material::Release() {
|
|||||||
m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||||
m_renderState = MaterialRenderState();
|
m_renderState = MaterialRenderState();
|
||||||
m_hasRenderStateOverride = false;
|
m_hasRenderStateOverride = false;
|
||||||
m_legacyShaderPassHint.Clear();
|
|
||||||
m_tags.Clear();
|
m_tags.Clear();
|
||||||
m_keywordSet.enabledKeywords.Clear();
|
m_keywordSet.enabledKeywords.Clear();
|
||||||
m_properties.Clear();
|
m_properties.Clear();
|
||||||
@@ -363,24 +362,6 @@ void Material::SetRenderStateOverrideEnabled(bool enabled) {
|
|||||||
MarkChanged(false);
|
MarkChanged(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Material::SetLegacyShaderPassHint(const Containers::String& shaderPass) {
|
|
||||||
m_legacyShaderPassHint = shaderPass;
|
|
||||||
MarkChanged(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::ClearLegacyShaderPassHint() {
|
|
||||||
if (m_legacyShaderPassHint.Empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_legacyShaderPassHint.Clear();
|
|
||||||
MarkChanged(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::SetShaderPass(const Containers::String& shaderPass) {
|
|
||||||
SetLegacyShaderPassHint(shaderPass);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Material::SetTag(const Containers::String& name, const Containers::String& value) {
|
void Material::SetTag(const Containers::String& name, const Containers::String& value) {
|
||||||
for (MaterialTagEntry& tag : m_tags) {
|
for (MaterialTagEntry& tag : m_tags) {
|
||||||
if (tag.name == name) {
|
if (tag.name == name) {
|
||||||
@@ -1120,7 +1101,6 @@ void Material::UpdateMemorySize() {
|
|||||||
m_memorySize = m_constantBufferData.Size() +
|
m_memorySize = m_constantBufferData.Size() +
|
||||||
m_constantLayout.Size() * sizeof(MaterialConstantFieldDesc) +
|
m_constantLayout.Size() * sizeof(MaterialConstantFieldDesc) +
|
||||||
sizeof(MaterialRenderState) +
|
sizeof(MaterialRenderState) +
|
||||||
m_legacyShaderPassHint.Length() +
|
|
||||||
m_tags.Size() * sizeof(MaterialTagEntry) +
|
m_tags.Size() * sizeof(MaterialTagEntry) +
|
||||||
m_keywordSet.enabledKeywords.Size() * sizeof(Containers::String) +
|
m_keywordSet.enabledKeywords.Size() * sizeof(Containers::String) +
|
||||||
m_textureBindings.Size() * sizeof(MaterialTextureBinding) +
|
m_textureBindings.Size() * sizeof(MaterialTextureBinding) +
|
||||||
|
|||||||
@@ -42,10 +42,25 @@ bool TryParseAuthoringBoolDirectiveToken(const std::string& token, bool& outValu
|
|||||||
bool TryParseAuthoringCullMode(const std::string& token, MaterialCullMode& outMode);
|
bool TryParseAuthoringCullMode(const std::string& token, MaterialCullMode& outMode);
|
||||||
bool TryParseAuthoringComparisonFunc(const std::string& token, MaterialComparisonFunc& outFunc);
|
bool TryParseAuthoringComparisonFunc(const std::string& token, MaterialComparisonFunc& outFunc);
|
||||||
bool TryParseAuthoringBlendFactor(const std::string& token, MaterialBlendFactor& outFactor);
|
bool TryParseAuthoringBlendFactor(const std::string& token, MaterialBlendFactor& outFactor);
|
||||||
|
bool TryParseAuthoringBlendOp(const std::string& token, MaterialBlendOp& outOp);
|
||||||
|
bool TryParseAuthoringStencilOp(const std::string& token, MaterialStencilOp& outOp);
|
||||||
bool TryParseAuthoringColorMask(const std::string& token, Core::uint8& outMask);
|
bool TryParseAuthoringColorMask(const std::string& token, Core::uint8& outMask);
|
||||||
bool TryParseAuthoringBlendDirective(
|
bool TryParseAuthoringBlendDirective(
|
||||||
const std::vector<std::string>& tokens,
|
const std::vector<std::string>& tokens,
|
||||||
MaterialRenderState& outState);
|
MaterialRenderState& outState);
|
||||||
|
bool TryParseAuthoringBlendOpDirective(
|
||||||
|
const std::vector<std::string>& tokens,
|
||||||
|
MaterialRenderState& outState);
|
||||||
|
bool TryParseAuthoringOffsetDirective(
|
||||||
|
const std::vector<std::string>& tokens,
|
||||||
|
MaterialRenderState& outState);
|
||||||
|
bool TryParseAuthoringStencilDirective(
|
||||||
|
const std::vector<std::string>& tokens,
|
||||||
|
MaterialStencilState& outState);
|
||||||
|
bool TryParseAuthoringUsePassReference(
|
||||||
|
const Containers::String& reference,
|
||||||
|
Containers::String& outShaderName,
|
||||||
|
Containers::String& outPassName);
|
||||||
|
|
||||||
void SetOrReplaceAuthoringTag(
|
void SetOrReplaceAuthoringTag(
|
||||||
std::vector<ShaderTagIR>& tags,
|
std::vector<ShaderTagIR>& tags,
|
||||||
|
|||||||
@@ -1,518 +0,0 @@
|
|||||||
#include "ShaderManifestLoader.h"
|
|
||||||
|
|
||||||
#include "ShaderFileUtils.h"
|
|
||||||
#include "ShaderRuntimeBuildUtils.h"
|
|
||||||
#include "../ShaderSourceUtils.h"
|
|
||||||
|
|
||||||
#include <cctype>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace XCEngine {
|
|
||||||
namespace Resources {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
bool FindValueStart(const std::string& json, const char* key, size_t& valuePos) {
|
|
||||||
const std::string token = std::string("\"") + key + "\"";
|
|
||||||
const size_t keyPos = json.find(token);
|
|
||||||
if (keyPos == std::string::npos) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t colonPos = json.find(':', keyPos + token.length());
|
|
||||||
if (colonPos == std::string::npos) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
valuePos = SkipWhitespace(json, colonPos + 1);
|
|
||||||
return valuePos < json.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryParseStringValue(const std::string& json, const char* key, Containers::String& outValue) {
|
|
||||||
size_t valuePos = 0;
|
|
||||||
if (!FindValueStart(json, key, valuePos)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseQuotedString(json, valuePos, outValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryExtractDelimitedValue(
|
|
||||||
const std::string& json,
|
|
||||||
const char* key,
|
|
||||||
char openChar,
|
|
||||||
char closeChar,
|
|
||||||
std::string& outValue) {
|
|
||||||
size_t valuePos = 0;
|
|
||||||
if (!FindValueStart(json, key, valuePos) || valuePos >= json.size() || json[valuePos] != openChar) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool inString = false;
|
|
||||||
bool escaped = false;
|
|
||||||
int depth = 0;
|
|
||||||
|
|
||||||
for (size_t pos = valuePos; pos < json.size(); ++pos) {
|
|
||||||
const char ch = json[pos];
|
|
||||||
if (escaped) {
|
|
||||||
escaped = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == '\\') {
|
|
||||||
escaped = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == '"') {
|
|
||||||
inString = !inString;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inString) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == openChar) {
|
|
||||||
++depth;
|
|
||||||
} else if (ch == closeChar) {
|
|
||||||
--depth;
|
|
||||||
if (depth == 0) {
|
|
||||||
outValue = json.substr(valuePos, pos - valuePos + 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryExtractObject(const std::string& json, const char* key, std::string& outObject) {
|
|
||||||
return TryExtractDelimitedValue(json, key, '{', '}', outObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryExtractArray(const std::string& json, const char* key, std::string& outArray) {
|
|
||||||
return TryExtractDelimitedValue(json, key, '[', ']', outArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryParseStringMapObject(
|
|
||||||
const std::string& objectText,
|
|
||||||
const std::function<void(const Containers::String&, const Containers::String&)>& onEntry) {
|
|
||||||
if (!onEntry || objectText.empty() || objectText.front() != '{' || objectText.back() != '}') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pos = 1;
|
|
||||||
while (pos < objectText.size()) {
|
|
||||||
pos = SkipWhitespace(objectText, pos);
|
|
||||||
if (pos >= objectText.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectText[pos] == '}') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String key;
|
|
||||||
if (!ParseQuotedString(objectText, pos, key, &pos)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = SkipWhitespace(objectText, pos);
|
|
||||||
if (pos >= objectText.size() || objectText[pos] != ':') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = SkipWhitespace(objectText, pos + 1);
|
|
||||||
Containers::String value;
|
|
||||||
if (!ParseQuotedString(objectText, pos, value, &pos)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onEntry(key, value);
|
|
||||||
|
|
||||||
pos = SkipWhitespace(objectText, pos);
|
|
||||||
if (pos >= objectText.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectText[pos] == ',') {
|
|
||||||
++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectText[pos] == '}') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SplitTopLevelArrayElements(const std::string& arrayText, std::vector<std::string>& outElements) {
|
|
||||||
outElements.clear();
|
|
||||||
if (arrayText.size() < 2 || arrayText.front() != '[' || arrayText.back() != ']') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool inString = false;
|
|
||||||
bool escaped = false;
|
|
||||||
int objectDepth = 0;
|
|
||||||
int arrayDepth = 0;
|
|
||||||
size_t elementStart = std::string::npos;
|
|
||||||
|
|
||||||
for (size_t pos = 1; pos + 1 < arrayText.size(); ++pos) {
|
|
||||||
const char ch = arrayText[pos];
|
|
||||||
if (escaped) {
|
|
||||||
escaped = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == '\\') {
|
|
||||||
escaped = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == '"') {
|
|
||||||
if (elementStart == std::string::npos) {
|
|
||||||
elementStart = pos;
|
|
||||||
}
|
|
||||||
inString = !inString;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inString) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std::isspace(static_cast<unsigned char>(ch)) != 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elementStart == std::string::npos) {
|
|
||||||
elementStart = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ch == '{') {
|
|
||||||
++objectDepth;
|
|
||||||
} else if (ch == '}') {
|
|
||||||
--objectDepth;
|
|
||||||
} else if (ch == '[') {
|
|
||||||
++arrayDepth;
|
|
||||||
} else if (ch == ']') {
|
|
||||||
--arrayDepth;
|
|
||||||
} else if (ch == ',' && objectDepth == 0 && arrayDepth == 0) {
|
|
||||||
outElements.push_back(TrimCopy(arrayText.substr(elementStart, pos - elementStart)));
|
|
||||||
elementStart = std::string::npos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elementStart != std::string::npos) {
|
|
||||||
outElements.push_back(TrimCopy(arrayText.substr(elementStart, arrayText.size() - 1 - elementStart)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryParseShaderKeywordsArray(
|
|
||||||
const std::string& arrayText,
|
|
||||||
ShaderKeywordSet& outKeywordSet) {
|
|
||||||
outKeywordSet = {};
|
|
||||||
|
|
||||||
std::vector<std::string> keywordElements;
|
|
||||||
if (!SplitTopLevelArrayElements(arrayText, keywordElements)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& keywordElement : keywordElements) {
|
|
||||||
if (keywordElement.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String keyword;
|
|
||||||
size_t nextPos = 0;
|
|
||||||
if (!ParseQuotedString(keywordElement, 0, keyword, &nextPos)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SkipWhitespace(keywordElement, nextPos) != keywordElement.size()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
outKeywordSet.enabledKeywords.PushBack(keyword);
|
|
||||||
}
|
|
||||||
|
|
||||||
NormalizeShaderKeywordSetInPlace(outKeywordSet);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TryParseUnsignedValue(const std::string& json, const char* key, Core::uint32& outValue) {
|
|
||||||
size_t valuePos = 0;
|
|
||||||
if (!FindValueStart(json, key, valuePos)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t endPos = valuePos;
|
|
||||||
while (endPos < json.size() && std::isdigit(static_cast<unsigned char>(json[endPos])) != 0) {
|
|
||||||
++endPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endPos == valuePos) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
outValue = static_cast<Core::uint32>(std::stoul(json.substr(valuePos, endPos - valuePos)));
|
|
||||||
return true;
|
|
||||||
} catch (...) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
bool LooksLikeShaderManifest(const std::string& sourceText) {
|
|
||||||
const size_t firstContentPos = SkipWhitespace(sourceText, 0);
|
|
||||||
return firstContentPos < sourceText.size() &&
|
|
||||||
sourceText[firstContentPos] == '{' &&
|
|
||||||
sourceText.find("\"passes\"") != std::string::npos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CollectShaderManifestDependencyPaths(
|
|
||||||
const Containers::String& path,
|
|
||||||
const std::string& jsonText,
|
|
||||||
Containers::Array<Containers::String>& outDependencies) {
|
|
||||||
outDependencies.Clear();
|
|
||||||
|
|
||||||
std::string passesArray;
|
|
||||||
if (!TryExtractArray(jsonText, "passes", passesArray)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> passObjects;
|
|
||||||
if (!SplitTopLevelArrayElements(passesArray, passObjects)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_set<std::string> seenPaths;
|
|
||||||
for (const std::string& passObject : passObjects) {
|
|
||||||
std::string variantsArray;
|
|
||||||
if (!TryExtractArray(passObject, "variants", variantsArray)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> variantObjects;
|
|
||||||
if (!SplitTopLevelArrayElements(variantsArray, variantObjects)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& variantObject : variantObjects) {
|
|
||||||
Containers::String sourcePath;
|
|
||||||
if (!TryParseStringValue(variantObject, "source", sourcePath) &&
|
|
||||||
!TryParseStringValue(variantObject, "sourcePath", sourcePath)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Containers::String resolvedPath = ResolveShaderDependencyPath(sourcePath, path);
|
|
||||||
const std::string key = ToStdString(resolvedPath);
|
|
||||||
if (!key.empty() && seenPaths.insert(key).second) {
|
|
||||||
outDependencies.PushBack(resolvedPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadResult LoadShaderManifest(
|
|
||||||
const Containers::String& path,
|
|
||||||
const std::string& jsonText) {
|
|
||||||
std::string passesArray;
|
|
||||||
if (!TryExtractArray(jsonText, "passes", passesArray)) {
|
|
||||||
return LoadResult("Shader manifest is missing a valid passes array: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> passObjects;
|
|
||||||
if (!SplitTopLevelArrayElements(passesArray, passObjects) || passObjects.empty()) {
|
|
||||||
return LoadResult("Shader manifest does not contain any pass objects: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto shader = std::make_unique<Shader>();
|
|
||||||
IResource::ConstructParams params;
|
|
||||||
params.path = path;
|
|
||||||
params.guid = ResourceGUID::Generate(path);
|
|
||||||
|
|
||||||
Containers::String manifestName;
|
|
||||||
if (TryParseStringValue(jsonText, "name", manifestName) && !manifestName.Empty()) {
|
|
||||||
params.name = manifestName;
|
|
||||||
} else {
|
|
||||||
const std::filesystem::path shaderPath(path.CStr());
|
|
||||||
const std::string stem = shaderPath.stem().generic_string();
|
|
||||||
params.name = stem.empty() ? path : Containers::String(stem.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->Initialize(params);
|
|
||||||
|
|
||||||
Containers::String fallback;
|
|
||||||
if (TryParseStringValue(jsonText, "fallback", fallback)) {
|
|
||||||
shader->SetFallback(fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string propertiesArray;
|
|
||||||
if (TryExtractArray(jsonText, "properties", propertiesArray)) {
|
|
||||||
std::vector<std::string> propertyObjects;
|
|
||||||
if (!SplitTopLevelArrayElements(propertiesArray, propertyObjects)) {
|
|
||||||
return LoadResult("Shader manifest properties array could not be parsed: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& propertyObject : propertyObjects) {
|
|
||||||
ShaderPropertyDesc property = {};
|
|
||||||
if (!TryParseStringValue(propertyObject, "name", property.name) || property.name.Empty()) {
|
|
||||||
return LoadResult("Shader manifest property is missing a valid name: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String propertyTypeName;
|
|
||||||
if (!TryParseStringValue(propertyObject, "type", propertyTypeName) ||
|
|
||||||
!TryParseShaderPropertyType(propertyTypeName, property.type)) {
|
|
||||||
return LoadResult("Shader manifest property has an invalid type: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryParseStringValue(propertyObject, "displayName", property.displayName)) {
|
|
||||||
property.displayName = property.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryParseStringValue(propertyObject, "defaultValue", property.defaultValue)) {
|
|
||||||
TryParseStringValue(propertyObject, "default", property.defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
TryParseStringValue(propertyObject, "semantic", property.semantic);
|
|
||||||
shader->AddProperty(property);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& passObject : passObjects) {
|
|
||||||
Containers::String passName;
|
|
||||||
if (!TryParseStringValue(passObject, "name", passName) || passName.Empty()) {
|
|
||||||
return LoadResult("Shader manifest pass is missing a valid name: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string tagsObject;
|
|
||||||
if (TryExtractObject(passObject, "tags", tagsObject)) {
|
|
||||||
if (!TryParseStringMapObject(
|
|
||||||
tagsObject,
|
|
||||||
[shaderPtr = shader.get(), &passName](const Containers::String& key, const Containers::String& value) {
|
|
||||||
shaderPtr->SetPassTag(passName, key, value);
|
|
||||||
})) {
|
|
||||||
return LoadResult("Shader manifest pass tags could not be parsed: " + path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string resourcesArray;
|
|
||||||
if (TryExtractArray(passObject, "resources", resourcesArray)) {
|
|
||||||
std::vector<std::string> resourceObjects;
|
|
||||||
if (!SplitTopLevelArrayElements(resourcesArray, resourceObjects)) {
|
|
||||||
return LoadResult("Shader manifest pass resources could not be parsed: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& resourceObject : resourceObjects) {
|
|
||||||
ShaderResourceBindingDesc resourceBinding = {};
|
|
||||||
if (!TryParseStringValue(resourceObject, "name", resourceBinding.name) ||
|
|
||||||
resourceBinding.name.Empty()) {
|
|
||||||
return LoadResult("Shader manifest pass resource is missing a valid name: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String resourceTypeName;
|
|
||||||
if (!TryParseStringValue(resourceObject, "type", resourceTypeName) ||
|
|
||||||
!TryParseShaderResourceType(resourceTypeName, resourceBinding.type)) {
|
|
||||||
return LoadResult("Shader manifest pass resource has an invalid type: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryParseUnsignedValue(resourceObject, "set", resourceBinding.set)) {
|
|
||||||
return LoadResult("Shader manifest pass resource is missing a valid set: " + path);
|
|
||||||
}
|
|
||||||
if (!TryParseUnsignedValue(resourceObject, "binding", resourceBinding.binding)) {
|
|
||||||
return LoadResult("Shader manifest pass resource is missing a valid binding: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
TryParseStringValue(resourceObject, "semantic", resourceBinding.semantic);
|
|
||||||
shader->AddPassResourceBinding(passName, resourceBinding);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string variantsArray;
|
|
||||||
if (!TryExtractArray(passObject, "variants", variantsArray)) {
|
|
||||||
return LoadResult("Shader manifest pass is missing variants: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> variantObjects;
|
|
||||||
if (!SplitTopLevelArrayElements(variantsArray, variantObjects) || variantObjects.empty()) {
|
|
||||||
return LoadResult("Shader manifest pass does not contain any variants: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const std::string& variantObject : variantObjects) {
|
|
||||||
ShaderStageVariant variant = {};
|
|
||||||
|
|
||||||
Containers::String stageName;
|
|
||||||
if (!TryParseStringValue(variantObject, "stage", stageName) ||
|
|
||||||
!TryParseShaderType(stageName, variant.stage)) {
|
|
||||||
return LoadResult("Shader manifest variant has an invalid stage: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String backendName;
|
|
||||||
if (!TryParseStringValue(variantObject, "backend", backendName) ||
|
|
||||||
!TryParseShaderBackend(backendName, variant.backend)) {
|
|
||||||
return LoadResult("Shader manifest variant has an invalid backend: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String languageName;
|
|
||||||
if (!TryParseStringValue(variantObject, "language", languageName) ||
|
|
||||||
!TryParseShaderLanguage(languageName, variant.language)) {
|
|
||||||
return LoadResult("Shader manifest variant has an invalid language: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Containers::String sourceCode;
|
|
||||||
if (TryParseStringValue(variantObject, "sourceCode", sourceCode)) {
|
|
||||||
variant.sourceCode = sourceCode;
|
|
||||||
} else {
|
|
||||||
Containers::String sourcePath;
|
|
||||||
if (!TryParseStringValue(variantObject, "source", sourcePath) &&
|
|
||||||
!TryParseStringValue(variantObject, "sourcePath", sourcePath)) {
|
|
||||||
return LoadResult("Shader manifest variant is missing source/sourceCode: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Containers::String resolvedSourcePath = ResolveShaderDependencyPath(sourcePath, path);
|
|
||||||
if (!ReadShaderTextFile(resolvedSourcePath, variant.sourceCode)) {
|
|
||||||
return LoadResult("Failed to read shader variant source: " + resolvedSourcePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryParseStringValue(variantObject, "entryPoint", variant.entryPoint)) {
|
|
||||||
variant.entryPoint = GetDefaultEntryPoint(variant.language, variant.stage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryParseStringValue(variantObject, "profile", variant.profile)) {
|
|
||||||
variant.profile = GetDefaultProfile(variant.language, variant.backend, variant.stage);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string keywordsArray;
|
|
||||||
if (TryExtractArray(variantObject, "keywords", keywordsArray) &&
|
|
||||||
!TryParseShaderKeywordsArray(keywordsArray, variant.requiredKeywords)) {
|
|
||||||
return LoadResult("Shader manifest variant keywords could not be parsed: " + path);
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->AddPassVariant(passName, variant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shader->m_memorySize = CalculateShaderMemorySize(*shader);
|
|
||||||
return LoadResult(shader.release());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Resources
|
|
||||||
} // namespace XCEngine
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <XCEngine/Core/Containers/Array.h>
|
|
||||||
#include <XCEngine/Core/Containers/String.h>
|
|
||||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace XCEngine {
|
|
||||||
namespace Resources {
|
|
||||||
|
|
||||||
bool LooksLikeShaderManifest(const std::string& sourceText);
|
|
||||||
|
|
||||||
bool CollectShaderManifestDependencyPaths(
|
|
||||||
const Containers::String& path,
|
|
||||||
const std::string& jsonText,
|
|
||||||
Containers::Array<Containers::String>& outDependencies);
|
|
||||||
|
|
||||||
LoadResult LoadShaderManifest(
|
|
||||||
const Containers::String& path,
|
|
||||||
const std::string& jsonText);
|
|
||||||
|
|
||||||
} // namespace Resources
|
|
||||||
} // namespace XCEngine
|
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <XCEngine/UI/Layout/LayoutEngine.h>
|
#include <XCEngine/UI/Layout/LayoutEngine.h>
|
||||||
#include <XCEngine/UI/Layout/UITabStripLayout.h>
|
#include <XCEngine/UI/Layout/UITabStripLayout.h>
|
||||||
#include <XCEngine/UI/Style/DocumentStyleCompiler.h>
|
#include <XCEngine/UI/Style/DocumentStyleCompiler.h>
|
||||||
|
#include <XCEngine/UI/Widgets/UIScrollModel.h>
|
||||||
#include <XCEngine/UI/Widgets/UITabStripModel.h>
|
#include <XCEngine/UI/Widgets/UITabStripModel.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -985,14 +986,6 @@ std::string BuildNodeStateKeySegment(
|
|||||||
return ToStdString(source.tagName) + "#" + std::to_string(siblingIndex);
|
return ToStdString(source.tagName) + "#" + std::to_string(siblingIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
float ComputeScrollOverflow(float contentExtent, float viewportExtent) {
|
|
||||||
return (std::max)(0.0f, contentExtent - viewportExtent);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ClampScrollOffset(float offset, float contentExtent, float viewportExtent) {
|
|
||||||
return (std::max)(0.0f, (std::min)(offset, ComputeScrollOverflow(contentExtent, viewportExtent)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RectContainsPoint(const UIRect& rect, const UIPoint& point) {
|
bool RectContainsPoint(const UIRect& rect, const UIPoint& point) {
|
||||||
return point.x >= rect.x &&
|
return point.x >= rect.x &&
|
||||||
point.x <= rect.x + rect.width &&
|
point.x <= rect.x + rect.width &&
|
||||||
@@ -1885,7 +1878,7 @@ void ArrangeNode(
|
|||||||
const auto found = verticalScrollOffsets.find(node.stateKey);
|
const auto found = verticalScrollOffsets.find(node.stateKey);
|
||||||
const float requestedOffset = found != verticalScrollOffsets.end() ? found->second : 0.0f;
|
const float requestedOffset = found != verticalScrollOffsets.end() ? found->second : 0.0f;
|
||||||
node.scrollViewportRect = contentRect;
|
node.scrollViewportRect = contentRect;
|
||||||
node.scrollOffsetY = ClampScrollOffset(
|
node.scrollOffsetY = Widgets::ClampUIScrollOffset(
|
||||||
requestedOffset,
|
requestedOffset,
|
||||||
node.contentDesiredSize.height,
|
node.contentDesiredSize.height,
|
||||||
contentRect.height);
|
contentRect.height);
|
||||||
@@ -1932,7 +1925,7 @@ RuntimeLayoutNode* FindDeepestScrollTarget(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ComputeScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height) <= 0.0f) {
|
if (Widgets::ComputeUIScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height) <= 0.0f) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2001,7 +1994,7 @@ bool ApplyScrollWheelEvent(
|
|||||||
|
|
||||||
scrollDebugSnapshot.lastTargetStateKey = hoveredScrollView->stateKey;
|
scrollDebugSnapshot.lastTargetStateKey = hoveredScrollView->stateKey;
|
||||||
scrollDebugSnapshot.lastViewportRect = hoveredScrollView->scrollViewportRect;
|
scrollDebugSnapshot.lastViewportRect = hoveredScrollView->scrollViewportRect;
|
||||||
scrollDebugSnapshot.lastOverflow = ComputeScrollOverflow(
|
scrollDebugSnapshot.lastOverflow = Widgets::ComputeUIScrollOverflow(
|
||||||
hoveredScrollView->contentDesiredSize.height,
|
hoveredScrollView->contentDesiredSize.height,
|
||||||
hoveredScrollView->scrollViewportRect.height);
|
hoveredScrollView->scrollViewportRect.height);
|
||||||
|
|
||||||
@@ -2022,18 +2015,18 @@ bool ApplyScrollWheelEvent(
|
|||||||
: target->scrollOffsetY;
|
: target->scrollOffsetY;
|
||||||
scrollDebugSnapshot.lastOffsetBefore = oldOffset;
|
scrollDebugSnapshot.lastOffsetBefore = oldOffset;
|
||||||
|
|
||||||
const float scrollUnits = event.wheelDelta / 120.0f;
|
const Widgets::UIScrollWheelResult scrollResult = Widgets::ApplyUIScrollWheel(
|
||||||
const float nextOffset = ClampScrollOffset(
|
oldOffset,
|
||||||
oldOffset - scrollUnits * 48.0f,
|
event.wheelDelta,
|
||||||
target->contentDesiredSize.height,
|
target->contentDesiredSize.height,
|
||||||
target->scrollViewportRect.height);
|
target->scrollViewportRect.height);
|
||||||
scrollDebugSnapshot.lastOffsetAfter = nextOffset;
|
scrollDebugSnapshot.lastOffsetAfter = scrollResult.offsetAfter;
|
||||||
if (std::fabs(nextOffset - oldOffset) <= 0.01f) {
|
if (!scrollResult.changed) {
|
||||||
scrollDebugSnapshot.lastResult = "Scroll delta clamped to current offset";
|
scrollDebugSnapshot.lastResult = "Scroll delta clamped to current offset";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
verticalScrollOffsets[target->stateKey] = nextOffset;
|
verticalScrollOffsets[target->stateKey] = scrollResult.offsetAfter;
|
||||||
++scrollDebugSnapshot.handledWheelEventCount;
|
++scrollDebugSnapshot.handledWheelEventCount;
|
||||||
scrollDebugSnapshot.lastResult = "Handled";
|
scrollDebugSnapshot.lastResult = "Handled";
|
||||||
return true;
|
return true;
|
||||||
@@ -2545,7 +2538,8 @@ void SyncScrollOffsets(
|
|||||||
const RuntimeLayoutNode& node,
|
const RuntimeLayoutNode& node,
|
||||||
std::unordered_map<std::string, float>& verticalScrollOffsets) {
|
std::unordered_map<std::string, float>& verticalScrollOffsets) {
|
||||||
if (node.isScrollView) {
|
if (node.isScrollView) {
|
||||||
const float overflow = ComputeScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height);
|
const float overflow =
|
||||||
|
Widgets::ComputeUIScrollOverflow(node.contentDesiredSize.height, node.scrollViewportRect.height);
|
||||||
if (overflow <= 0.0f || node.scrollOffsetY <= 0.0f) {
|
if (overflow <= 0.0f || node.scrollOffsetY <= 0.0f) {
|
||||||
verticalScrollOffsets.erase(node.stateKey);
|
verticalScrollOffsets.erase(node.stateKey);
|
||||||
} else {
|
} else {
|
||||||
@@ -3009,7 +3003,7 @@ UIScreenFrameResult UIDocumentScreenHost::BuildFrame(
|
|||||||
if (const RuntimeLayoutNode* primaryScrollView = FindFirstScrollView(root); primaryScrollView != nullptr) {
|
if (const RuntimeLayoutNode* primaryScrollView = FindFirstScrollView(root); primaryScrollView != nullptr) {
|
||||||
m_scrollDebugSnapshot.primaryTargetStateKey = primaryScrollView->stateKey;
|
m_scrollDebugSnapshot.primaryTargetStateKey = primaryScrollView->stateKey;
|
||||||
m_scrollDebugSnapshot.primaryViewportRect = primaryScrollView->scrollViewportRect;
|
m_scrollDebugSnapshot.primaryViewportRect = primaryScrollView->scrollViewportRect;
|
||||||
m_scrollDebugSnapshot.primaryOverflow = ComputeScrollOverflow(
|
m_scrollDebugSnapshot.primaryOverflow = Widgets::ComputeUIScrollOverflow(
|
||||||
primaryScrollView->contentDesiredSize.height,
|
primaryScrollView->contentDesiredSize.height,
|
||||||
primaryScrollView->scrollViewportRect.height);
|
primaryScrollView->scrollViewportRect.height);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
67
engine/src/UI/Widgets/UIScrollModel.cpp
Normal file
67
engine/src/UI/Widgets/UIScrollModel.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <XCEngine/UI/Widgets/UIScrollModel.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace UI {
|
||||||
|
namespace Widgets {
|
||||||
|
|
||||||
|
float ComputeUIScrollOverflow(float contentExtent, float viewportExtent) {
|
||||||
|
return (std::max)(0.0f, contentExtent - viewportExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ClampUIScrollOffset(float offset, float contentExtent, float viewportExtent) {
|
||||||
|
return (std::max)(0.0f, (std::min)(offset, ComputeUIScrollOverflow(contentExtent, viewportExtent)));
|
||||||
|
}
|
||||||
|
|
||||||
|
UIScrollWheelResult ApplyUIScrollWheel(
|
||||||
|
float offset,
|
||||||
|
float wheelDelta,
|
||||||
|
float contentExtent,
|
||||||
|
float viewportExtent,
|
||||||
|
float wheelStep,
|
||||||
|
float epsilon) {
|
||||||
|
UIScrollWheelResult result = {};
|
||||||
|
result.offsetBefore = ClampUIScrollOffset(offset, contentExtent, viewportExtent);
|
||||||
|
result.overflow = ComputeUIScrollOverflow(contentExtent, viewportExtent);
|
||||||
|
result.offsetAfter = result.offsetBefore;
|
||||||
|
if (result.overflow <= 0.0f || std::fabs(wheelDelta) <= epsilon || wheelStep <= 0.0f) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float scrollUnits = wheelDelta / 120.0f;
|
||||||
|
result.offsetAfter = ClampUIScrollOffset(
|
||||||
|
result.offsetBefore - scrollUnits * wheelStep,
|
||||||
|
contentExtent,
|
||||||
|
viewportExtent);
|
||||||
|
result.changed = std::fabs(result.offsetAfter - result.offsetBefore) > epsilon;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float EnsureUIScrollOffsetVisible(
|
||||||
|
float offset,
|
||||||
|
float itemStart,
|
||||||
|
float itemExtent,
|
||||||
|
float contentExtent,
|
||||||
|
float viewportExtent) {
|
||||||
|
const float clampedOffset = ClampUIScrollOffset(offset, contentExtent, viewportExtent);
|
||||||
|
if (viewportExtent <= 0.0f || itemExtent <= 0.0f) {
|
||||||
|
return clampedOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float itemEnd = itemStart + itemExtent;
|
||||||
|
const float viewportEnd = clampedOffset + viewportExtent;
|
||||||
|
float nextOffset = clampedOffset;
|
||||||
|
if (itemStart < clampedOffset) {
|
||||||
|
nextOffset = itemStart;
|
||||||
|
} else if (itemEnd > viewportEnd) {
|
||||||
|
nextOffset = itemEnd - viewportExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClampUIScrollOffset(nextOffset, contentExtent, viewportExtent);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Widgets
|
||||||
|
} // namespace UI
|
||||||
|
} // namespace XCEngine
|
||||||
@@ -1,42 +1,42 @@
|
|||||||
[Window][Hierarchy]
|
[Window][Hierarchy]
|
||||||
Pos=0,65
|
Pos=0,59
|
||||||
Size=435,735
|
Size=189,1122
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000003
|
DockId=0x00000003
|
||||||
|
|
||||||
[Window][Scene]
|
[Window][Scene]
|
||||||
Pos=437,65
|
Pos=191,59
|
||||||
Size=1311,735
|
Size=2099,1122
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004
|
DockId=0x00000005
|
||||||
|
|
||||||
[Window][Game]
|
[Window][Game]
|
||||||
Pos=437,65
|
Pos=191,59
|
||||||
Size=1311,735
|
Size=2099,1122
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000004
|
DockId=0x00000005
|
||||||
|
|
||||||
[Window][Inspector]
|
[Window][Inspector]
|
||||||
Pos=1750,65
|
Pos=2292,59
|
||||||
Size=810,1286
|
Size=268,1122
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000006
|
DockId=0x00000006
|
||||||
|
|
||||||
[Window][Console]
|
[Window][Console]
|
||||||
Pos=0,802
|
Pos=0,1183
|
||||||
Size=1748,549
|
Size=2560,168
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002
|
||||||
|
|
||||||
[Window][Project]
|
[Window][Project]
|
||||||
Pos=0,802
|
Pos=0,1183
|
||||||
Size=1748,549
|
Size=2560,168
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002
|
DockId=0x00000002
|
||||||
|
|
||||||
[Window][MainDockspace]
|
[Window][MainDockspace]
|
||||||
Pos=0,65
|
Pos=0,59
|
||||||
Size=2560,1286
|
Size=2560,1292
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Debug##Default]
|
[Window][Debug##Default]
|
||||||
@@ -53,12 +53,17 @@ Pos=1060,620
|
|||||||
Size=440,110
|
Size=440,110
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Docking][Data]
|
[Window][XCUI Demo]
|
||||||
DockSpace ID=0xA11B73D6 Window=0x1C358F53 Pos=0,65 Size=2560,1286 Split=X
|
Pos=506,204
|
||||||
DockNode ID=0x00000005 Parent=0xA11B73D6 SizeRef=1748,640 Split=Y
|
Size=501,40
|
||||||
DockNode ID=0x00000001 Parent=0x00000005 SizeRef=1262,735 Split=X
|
Collapsed=0
|
||||||
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=435,503 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0xBABDAE5E
|
|
||||||
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=1311,503 CentralNode=1 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0xE601B12F
|
[Docking][Data]
|
||||||
DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1262,549 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0xEA83D666
|
DockSpace ID=0xA11B73D6 Window=0x1C358F53 Pos=0,59 Size=2560,1292 Split=Y
|
||||||
DockNode ID=0x00000006 Parent=0xA11B73D6 SizeRef=810,640 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0x36DC96AB
|
DockNode ID=0x00000001 Parent=0xA11B73D6 SizeRef=1262,503 Split=X
|
||||||
|
DockNode ID=0x00000003 Parent=0x00000001 SizeRef=189,503 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0xBABDAE5E
|
||||||
|
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=1071,503 Split=X
|
||||||
|
DockNode ID=0x00000005 Parent=0x00000004 SizeRef=801,503 CentralNode=1 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0xE601B12F
|
||||||
|
DockNode ID=0x00000006 Parent=0x00000004 SizeRef=268,503 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0x36DC96AB
|
||||||
|
DockNode ID=0x00000002 Parent=0xA11B73D6 SizeRef=1262,168 NoTabBar=1 NoWindowMenuButton=1 NoCloseButton=1 Selected=0xEA83D666
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 96666a186a717b84e90236ea2978d91f
|
guid: 96666a186a717b84e90236ea2978d91f
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 9b99cbc7c30f5a7bbf6e5cc49690e2e7
|
guid: 9b99cbc7c30f5a7bbf6e5cc49690e2e7
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 7ad7534fed3a73dfeb84739d0d3aef92
|
guid: 7ad7534fed3a73dfeb84739d0d3aef92
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: ea210ba93a7a93260ae13bc84508bbe3
|
guid: ea210ba93a7a93260ae13bc84508bbe3
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 7eedc507fd8709abb2b19af32a52ab21
|
guid: 7eedc507fd8709abb2b19af32a52ab21
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: TextureImporter
|
importer: TextureImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 5efa3f1aa5ce5981cbbefe56c8f97128
|
guid: 5efa3f1aa5ce5981cbbefe56c8f97128
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: DefaultImporter
|
importer: DefaultImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 17cce01b8a084a5cdf1c79382a46c31f
|
guid: 17cce01b8a084a5cdf1c79382a46c31f
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: ModelImporter
|
importer: ModelImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 003ee132378579f4bfc59b39cf8e8d51
|
guid: 003ee132378579f4bfc59b39cf8e8d51
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: TextureImporter
|
importer: TextureImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: e94d89b26d6e920e2e73cb96d3beddbd
|
guid: e94d89b26d6e920e2e73cb96d3beddbd
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: TextureImporter
|
importer: TextureImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: c98fc6424b69b1a41adc91eabc2e82bd
|
guid: c98fc6424b69b1a41adc91eabc2e82bd
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: TextureImporter
|
importer: TextureImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 26aa3d5b0d4e0320a5286c0cc5a76c46
|
guid: 26aa3d5b0d4e0320a5286c0cc5a76c46
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: DefaultImporter
|
importer: DefaultImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: bac31a87fc5da61d66e2de6841990bb1
|
guid: bac31a87fc5da61d66e2de6841990bb1
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: TextureImporter
|
importer: TextureImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"shader": "builtin://shaders/unlit",
|
"shader": "builtin://shaders/unlit",
|
||||||
"renderQueue": "geometry",
|
"renderQueue": "geometry",
|
||||||
"properties": {
|
"properties": {
|
||||||
"_BaseColor": [1, 1, 1, 1]
|
"_BaseColor": [0.850877, 0.365728, 0.365728, 1]
|
||||||
},
|
},
|
||||||
"textures": {
|
"textures": {
|
||||||
"_MainTex": "Assets/Models/backpack/diffuse.jpg"
|
"_MainTex": "Assets/Models/backpack/diffuse.jpg"
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 9a608ceb121dae614903368e56e8e049
|
guid: 9a608ceb121dae614903368e56e8e049
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: MaterialImporter
|
importer: MaterialImporter
|
||||||
importerVersion: 5
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 95bf8c50b9011ec0375cd185053068c9
|
guid: 95bf8c50b9011ec0375cd185053068c9
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -6,30 +6,23 @@ gameobject_begin
|
|||||||
id=1
|
id=1
|
||||||
uuid=11806343893082442755
|
uuid=11806343893082442755
|
||||||
name=Main Camera
|
name=Main Camera
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=1.24194,0,-5.54416;rotation=0,0,0,1;scale=1,1,1;
|
transform=position=1.24194,0,-5.54416;rotation=0,0,0,1;scale=1,1,1;
|
||||||
component=Camera;projection=0;fov=60.7089;orthoSize=5;near=0.001;far=102.9;depth=1.6;primary=1;clearMode=0;stackType=0;cullingMask=4294967295;viewportRect=0,0,1,1;clearColor=0,0,0,1;
|
component=Camera;projection=0;fov=60.7089;orthoSize=5;near=0.001;far=102.9;depth=1.6;primary=1;clearMode=0;stackType=0;cullingMask=4294967295;viewportRect=0,0,1,1;clearColor=0,0,0,1;skyboxEnabled=0;skyboxMaterialPath=;skyboxMaterialRef=;skyboxTopColor=0.18,0.36,0.74,1;skyboxHorizonColor=0.78,0.84,0.92,1;skyboxBottomColor=0.92,0.93,0.95,1;finalColorOverrideOutputTransferEnabled=0;finalColorOverrideOutputTransferMode=0;finalColorOverrideExposureModeEnabled=0;finalColorOverrideExposureMode=0;finalColorOverrideExposureValueEnabled=0;finalColorOverrideExposureValue=1;finalColorOverrideToneMappingModeEnabled=0;finalColorOverrideToneMappingMode=0;finalColorOverrideScaleEnabled=0;finalColorOverrideScale=1,1,1,1;postProcessPassCount=0;
|
||||||
gameobject_end
|
|
||||||
|
|
||||||
gameobject_begin
|
|
||||||
id=20
|
|
||||||
uuid=8649195733451829943
|
|
||||||
name=GameObject
|
|
||||||
active=1
|
|
||||||
parent=0
|
|
||||||
transform=position=-3.68003,0.160159,0.360709;rotation=0,0,0,1;scale=1,1,1;
|
|
||||||
component=MeshFilter;meshRef=17cce01b8a084a5cdf1c79382a46c31f,1,2;
|
|
||||||
component=MeshRenderer;materialPaths=;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
|
||||||
gameobject_end
|
gameobject_end
|
||||||
|
|
||||||
gameobject_begin
|
gameobject_begin
|
||||||
id=29
|
id=29
|
||||||
uuid=16627428025886468363
|
uuid=16627428025886468363
|
||||||
name=Light
|
name=Light
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=0,3.00762,3.94704;rotation=0.394142,-0.000640735,-0.0613786,0.916998;scale=1,1,1;
|
transform=position=0,3.00762,4.6342;rotation=0.930636,0.196101,-0.052872,0.304409;scale=1,1,1;
|
||||||
component=Light;type=0;color=1,1,1,1;intensity=1;range=10;spotAngle=30;shadows=0;
|
component=Light;type=0;color=1,1,1,1;intensity=1;range=10;spotAngle=30;shadows=0;
|
||||||
gameobject_end
|
gameobject_end
|
||||||
|
|
||||||
@@ -37,7 +30,9 @@ gameobject_begin
|
|||||||
id=75
|
id=75
|
||||||
uuid=5125863825480013902
|
uuid=5125863825480013902
|
||||||
name=Cube
|
name=Cube
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=5.97411,0,0;rotation=0,0,0,1;scale=1,1,1;
|
transform=position=5.97411,0,0;rotation=0,0,0,1;scale=1,1,1;
|
||||||
component=MeshFilter;meshRef=;meshPath=builtin://meshes/cube;
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/cube;
|
||||||
@@ -45,12 +40,39 @@ component=MeshRenderer;materialPaths=builtin://materials/default-primitive;mater
|
|||||||
gameobject_end
|
gameobject_end
|
||||||
|
|
||||||
gameobject_begin
|
gameobject_begin
|
||||||
id=79
|
id=128
|
||||||
uuid=14087659263782399666
|
uuid=9183622432480476442
|
||||||
name=GameObject
|
name=Light
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=2.32896,0,-0.076026;rotation=0,0,0,1;scale=1,1,1;
|
transform=position=1.04812,1.32087,6.70265;rotation=0.295208,0,0,0.955433;scale=1,1,1;
|
||||||
|
component=Light;type=1;color=1,0.614035,0.991319,1;intensity=11.5;range=10;spotAngle=30;shadows=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=129
|
||||||
|
uuid=2162319538989844123
|
||||||
|
name=Plane
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=0,-2.16998,0;rotation=0,0,0,1;scale=1,1,1;
|
||||||
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/plane;
|
||||||
|
component=MeshRenderer;materialPaths=builtin://materials/default-primitive;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=141
|
||||||
|
uuid=11103014809698991785
|
||||||
|
name=GameObject
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=0,0,-1.77895;rotation=0,0,0,1;scale=1,1,-1.7;
|
||||||
component=MeshFilter;meshRef=17cce01b8a084a5cdf1c79382a46c31f,1,2;
|
component=MeshFilter;meshRef=17cce01b8a084a5cdf1c79382a46c31f,1,2;
|
||||||
component=MeshRenderer;materialPaths=;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
component=MeshRenderer;materialPaths=;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
gameobject_end
|
gameobject_end
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 97d8a1008ece4eab2930a9caf3c11f54
|
guid: 97d8a1008ece4eab2930a9caf3c11f54
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: DefaultImporter
|
importer: DefaultImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -6,50 +6,157 @@ gameobject_begin
|
|||||||
id=64
|
id=64
|
||||||
uuid=5744812053316050663
|
uuid=5744812053316050663
|
||||||
name=Camera
|
name=Camera
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=-0.352908,0,-3.0611;rotation=0.111657,0.15678,0.890189,-0.412937;scale=1,1,1;
|
transform=position=1.40968,2.17144,-6.14578;rotation=0.0850599,0,0,0.996376;scale=1,1,1;
|
||||||
component=Camera;projection=0;fov=60;orthoSize=5;near=1.43;far=1000;depth=0;primary=1;clearMode=0;stackType=0;cullingMask=4294967295;viewportRect=0,0,1,1;clearColor=0,0,0,0.552941;
|
component=Camera;projection=0;fov=60;orthoSize=5;near=1.43;far=1000;depth=0;primary=1;clearMode=0;stackType=0;cullingMask=4294967295;viewportRect=0,0,1,1;clearColor=0,0,0,0.552941;skyboxEnabled=1;skyboxMaterialPath=;skyboxMaterialRef=;skyboxTopColor=0.269544,0.501514,0.991228,1;skyboxHorizonColor=0.570175,0.570175,0.570175,1;skyboxBottomColor=0.141582,0.228891,0.403509,1;finalColorOverrideOutputTransferEnabled=0;finalColorOverrideOutputTransferMode=0;finalColorOverrideExposureModeEnabled=0;finalColorOverrideExposureMode=0;finalColorOverrideExposureValueEnabled=0;finalColorOverrideExposureValue=1;finalColorOverrideToneMappingModeEnabled=0;finalColorOverrideToneMappingMode=0;finalColorOverrideScaleEnabled=0;finalColorOverrideScale=1,1,1,1;postProcessPassCount=0;
|
||||||
gameobject_end
|
gameobject_end
|
||||||
|
|
||||||
gameobject_begin
|
gameobject_begin
|
||||||
id=66
|
id=66
|
||||||
uuid=18270436544925411184
|
uuid=18270436544925411184
|
||||||
name=Light
|
name=Light
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=0,3.3245,0;rotation=-0.12466,0.596834,-0.0945426,0.786963;scale=1,1,1;
|
transform=position=5.87107,3.04462,0.305848;rotation=0.717254,0.587471,-0.0433283,0.372219;scale=1,1,1;
|
||||||
component=Light;type=0;color=1,1,1,1;intensity=1;range=10;spotAngle=30;shadows=0;
|
component=Light;type=0;color=1,1,1,1;intensity=0.2;range=10;spotAngle=30;shadows=0;
|
||||||
gameobject_end
|
gameobject_end
|
||||||
|
|
||||||
gameobject_begin
|
gameobject_begin
|
||||||
id=87
|
id=87
|
||||||
uuid=9751737136539126565
|
uuid=9751737136539126565
|
||||||
name=GameObject
|
name=GameObject
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=0,0,0;rotation=-0.345665,0.580459,-0.0801843,-0.732908;scale=1,1,1;
|
transform=position=0,0,0;rotation=-0.345665,0.580459,-0.0801843,-0.732908;scale=1,1,1;
|
||||||
gameobject_end
|
gameobject_end
|
||||||
|
|
||||||
gameobject_begin
|
|
||||||
id=89
|
|
||||||
uuid=3783751572903533469
|
|
||||||
name=Sphere
|
|
||||||
active=1
|
|
||||||
parent=0
|
|
||||||
transform=position=-0.0171053,0,-0.2493;rotation=0.514887,0.648149,-0.409349,0.383703;scale=1,1,1;
|
|
||||||
component=MeshFilter;meshRef=;meshPath=builtin://meshes/sphere;
|
|
||||||
component=MeshRenderer;materialPaths=builtin://materials/default-primitive;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
|
||||||
component=ScriptComponent;scriptComponentUUID=14953097718051232299;assembly=GameScripts;namespace=ProjectScripts;class=TickLogProbe;fields=;
|
|
||||||
gameobject_end
|
|
||||||
|
|
||||||
gameobject_begin
|
gameobject_begin
|
||||||
id=91
|
id=91
|
||||||
uuid=12782062714694956708
|
uuid=12782062714694956708
|
||||||
name=GameObject
|
name=GameObject
|
||||||
|
tag=Untagged
|
||||||
active=1
|
active=1
|
||||||
|
layer=0
|
||||||
parent=0
|
parent=0
|
||||||
transform=position=0,0,0;rotation=0,0,0,1;scale=1,1,1;
|
transform=position=0,0,0;rotation=0,0,0,1;scale=1,1,1;
|
||||||
component=ScriptComponent;scriptComponentUUID=13495247810881627157;assembly=GameScripts;namespace=;class=;fields=;
|
component=ScriptComponent;scriptComponentUUID=13495247810881627157;assembly=GameScripts;namespace=;class=;fields=;
|
||||||
gameobject_end
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=113
|
||||||
|
uuid=15803379621740129390
|
||||||
|
name=Plane
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=0,0,0;rotation=0,0,0,1;scale=1,1,1;
|
||||||
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/plane;
|
||||||
|
component=MeshRenderer;materialPaths=builtin://materials/default-primitive;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
|
gameobject_end
|
||||||
|
gameobject_begin
|
||||||
|
id=126
|
||||||
|
uuid=12456668029646601964
|
||||||
|
name=Cube
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=113
|
||||||
|
transform=position=2.91271,0.679955,2.94568;rotation=-0.288105,0,0,0.957599;scale=1,1,1;
|
||||||
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/cube;
|
||||||
|
component=MeshRenderer;materialPaths=builtin://materials/default-primitive;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
|
gameobject_end
|
||||||
|
gameobject_begin
|
||||||
|
id=115
|
||||||
|
uuid=6890014658586654960
|
||||||
|
name=Cube
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=113
|
||||||
|
transform=position=-1.28823,0.525337,1.45296;rotation=-0.0763061,0.299091,-0.0189699,0.95098;scale=1,1,1;
|
||||||
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/cube;
|
||||||
|
component=MeshRenderer;materialPaths=builtin://materials/default-primitive;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
|
gameobject_end
|
||||||
|
gameobject_begin
|
||||||
|
id=130
|
||||||
|
uuid=16092524035934325911
|
||||||
|
name=Cube
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=113
|
||||||
|
transform=position=2.6382,0.525337,-3.07507;rotation=-0.0763061,0.299091,-0.0189699,0.95098;scale=1,1,1;
|
||||||
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/cube;
|
||||||
|
component=MeshRenderer;materialPaths=;materialRefs=9a608ceb121dae614903368e56e8e049,1,3;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=117
|
||||||
|
uuid=6470155182778634572
|
||||||
|
name=Light
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=0.484716,0.961334,2.89361;rotation=0,0,0,1;scale=1,1,1;
|
||||||
|
component=Light;type=1;color=1,0,0.0287113,1;intensity=1;range=10;spotAngle=30;shadows=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=119
|
||||||
|
uuid=11541908339887058565
|
||||||
|
name=Light
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=2.94791,0.869651,-2.61555;rotation=0,0,0,1;scale=1,1,1;
|
||||||
|
component=Light;type=1;color=0,1,0.318094,1;intensity=1;range=10;spotAngle=30;shadows=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=120
|
||||||
|
uuid=6386081964352732701
|
||||||
|
name=Light
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=-1.55514,1.18028,-0.698302;rotation=0,0,0,1;scale=1,1,1;
|
||||||
|
component=Light;type=1;color=0,0.141337,1,1;intensity=1;range=10;spotAngle=30;shadows=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=132
|
||||||
|
uuid=4498534432540015846
|
||||||
|
name=Cube
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=-1.08156,0.874702,-3.56394;rotation=0,0,0,1;scale=1,1,1;
|
||||||
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/cube;
|
||||||
|
component=MeshRenderer;materialPaths=builtin://materials/default-primitive;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
gameobject_begin
|
||||||
|
id=133
|
||||||
|
uuid=17783067283336270389
|
||||||
|
name=Capsule
|
||||||
|
tag=Untagged
|
||||||
|
active=1
|
||||||
|
layer=0
|
||||||
|
parent=0
|
||||||
|
transform=position=2.8096,1.4917,0;rotation=0,0,0,1;scale=1,1,1;
|
||||||
|
component=MeshFilter;meshRef=;meshPath=builtin://meshes/capsule;
|
||||||
|
component=MeshRenderer;materialPaths=builtin://materials/default-primitive;materialRefs=;castShadows=1;receiveShadows=1;renderLayer=0;
|
||||||
|
gameobject_end
|
||||||
|
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: d291a8a36c1395b2199e6e32abfa7b84
|
guid: d291a8a36c1395b2199e6e32abfa7b84
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: DefaultImporter
|
importer: DefaultImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: f8af940001366ccd64b1b8bb5e071cdb
|
guid: f8af940001366ccd64b1b8bb5e071cdb
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: e63714c47b794207e7310ca83f50537b
|
guid: e63714c47b794207e7310ca83f50537b
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 6607cf0bad154aa86768fd90d630d84f
|
guid: 6607cf0bad154aa86768fd90d630d84f
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: DefaultImporter
|
importer: DefaultImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: ec851a44fdf97e9e2d7464b259d4e5b8
|
guid: ec851a44fdf97e9e2d7464b259d4e5b8
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 09908ff9ea86f69e5951b763ec5a65e4
|
guid: 09908ff9ea86f69e5951b763ec5a65e4
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: TextureImporter
|
importer: TextureImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 907d8d9c76e51c3ff41daf2c272bbc2e
|
guid: 907d8d9c76e51c3ff41daf2c272bbc2e
|
||||||
folderAsset: true
|
folderAsset: true
|
||||||
importer: FolderImporter
|
importer: FolderImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 3292abd66fd3c24f29b432371a671609
|
guid: 3292abd66fd3c24f29b432371a671609
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: DefaultImporter
|
importer: DefaultImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: 4ebde4198801fc49d0d13e3ef7f65633
|
guid: 4ebde4198801fc49d0d13e3ef7f65633
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: TextureImporter
|
importer: TextureImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ fileFormatVersion: 1
|
|||||||
guid: b5bf687835df60784ce27f81737e95c4
|
guid: b5bf687835df60784ce27f81737e95c4
|
||||||
folderAsset: false
|
folderAsset: false
|
||||||
importer: DefaultImporter
|
importer: DefaultImporter
|
||||||
importerVersion: 3
|
importerVersion: 7
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
[CmdletBinding()]
|
|
||||||
param(
|
|
||||||
[string]$BuildDir = "build",
|
|
||||||
[string]$Config = "Debug",
|
|
||||||
[string]$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path,
|
|
||||||
[int]$SmokeSeconds = 5,
|
|
||||||
[switch]$SkipBuild,
|
|
||||||
[switch]$SkipSmoke,
|
|
||||||
[switch]$CleanBuild
|
|
||||||
)
|
|
||||||
|
|
||||||
Set-StrictMode -Version Latest
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
function Invoke-Step {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $true)]
|
|
||||||
[string]$Label,
|
|
||||||
[Parameter(Mandatory = $true)]
|
|
||||||
[scriptblock]$Action
|
|
||||||
)
|
|
||||||
|
|
||||||
Write-Host "==> $Label"
|
|
||||||
& $Action
|
|
||||||
}
|
|
||||||
|
|
||||||
function Invoke-Native {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $true)]
|
|
||||||
[string]$FilePath,
|
|
||||||
[Parameter()]
|
|
||||||
[string[]]$Arguments = @(),
|
|
||||||
[string]$WorkingDirectory = $RepoRoot
|
|
||||||
)
|
|
||||||
|
|
||||||
Push-Location $WorkingDirectory
|
|
||||||
try {
|
|
||||||
& $FilePath @Arguments
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw ("Command failed with exit code {0}: {1} {2}" -f $LASTEXITCODE, $FilePath, ($Arguments -join ' '))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
Pop-Location
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Get-ExePath {
|
|
||||||
param(
|
|
||||||
[Parameter(Mandatory = $true)]
|
|
||||||
[string[]]$Parts
|
|
||||||
)
|
|
||||||
|
|
||||||
$path = $BuildDir
|
|
||||||
foreach ($part in $Parts) {
|
|
||||||
$path = Join-Path $path $part
|
|
||||||
}
|
|
||||||
|
|
||||||
$resolved = if ([System.IO.Path]::IsPathRooted($path)) {
|
|
||||||
$path
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Join-Path $RepoRoot $path
|
|
||||||
}
|
|
||||||
if (-not (Test-Path $resolved)) {
|
|
||||||
throw "Expected executable does not exist: $resolved"
|
|
||||||
}
|
|
||||||
|
|
||||||
return $resolved
|
|
||||||
}
|
|
||||||
|
|
||||||
$RepoRoot = [System.IO.Path]::GetFullPath($RepoRoot)
|
|
||||||
$BuildDir = if ([System.IO.Path]::IsPathRooted($BuildDir)) {
|
|
||||||
[System.IO.Path]::GetFullPath($BuildDir)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
[System.IO.Path]::GetFullPath((Join-Path $RepoRoot $BuildDir))
|
|
||||||
}
|
|
||||||
|
|
||||||
$msbuildArguments = @(
|
|
||||||
"--", "/m:1",
|
|
||||||
"/p:UseMultiToolTask=false",
|
|
||||||
"/p:CL_MPCount=1"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (-not $SkipBuild) {
|
|
||||||
if ($CleanBuild) {
|
|
||||||
Invoke-Step "Clean rebuild XCEngine" {
|
|
||||||
Invoke-Native -FilePath "cmake" -Arguments (@(
|
|
||||||
"--build", $BuildDir,
|
|
||||||
"--config", $Config,
|
|
||||||
"--target", "XCEngine",
|
|
||||||
"--clean-first"
|
|
||||||
) + $msbuildArguments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Invoke-Step "Build rendering regression targets" {
|
|
||||||
Invoke-Native -FilePath "cmake" -Arguments (@(
|
|
||||||
"--build", $BuildDir,
|
|
||||||
"--config", $Config,
|
|
||||||
"--target", "rendering_phase_regression_build"
|
|
||||||
) + $msbuildArguments)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$executables = @(
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "unit", $Config, "rendering_unit_tests.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "textured_quad_scene", $Config, "rendering_integration_textured_quad_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "backpack_scene", $Config, "rendering_integration_backpack_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "backpack_lit_scene", $Config, "rendering_integration_backpack_lit_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "camera_stack_scene", $Config, "rendering_integration_camera_stack_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "transparent_material_scene", $Config, "rendering_integration_transparent_material_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "cull_material_scene", $Config, "rendering_integration_cull_material_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "depth_sort_scene", $Config, "rendering_integration_depth_sort_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "material_state_scene", $Config, "rendering_integration_material_state_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Rendering", "integration", "offscreen_scene", $Config, "rendering_integration_offscreen_scene.exe")),
|
|
||||||
(Get-ExePath -Parts @("tests", "Editor", $Config, "editor_tests.exe"))
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach ($executable in $executables) {
|
|
||||||
$label = [System.IO.Path]::GetFileName($executable)
|
|
||||||
$workingDirectory = Split-Path -Parent $executable
|
|
||||||
Invoke-Step "Run $label" {
|
|
||||||
Invoke-Native -FilePath $executable -WorkingDirectory $workingDirectory
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not $SkipSmoke) {
|
|
||||||
$editorExe = Join-Path $RepoRoot "editor\bin\$Config\XCEngine.exe"
|
|
||||||
if (-not (Test-Path $editorExe)) {
|
|
||||||
throw "Expected editor executable does not exist: $editorExe"
|
|
||||||
}
|
|
||||||
|
|
||||||
Invoke-Step "Smoke launch XCEditor ($SmokeSeconds seconds)" {
|
|
||||||
$process = Start-Process -FilePath $editorExe -WorkingDirectory $RepoRoot -PassThru
|
|
||||||
try {
|
|
||||||
Start-Sleep -Seconds $SmokeSeconds
|
|
||||||
if ($process.HasExited) {
|
|
||||||
if ($process.ExitCode -ne 0) {
|
|
||||||
throw "XCEditor exited early with code $($process.ExitCode)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Stop-Process -Id $process.Id -Force
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (-not $process.HasExited) {
|
|
||||||
Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "Renderer phase regression passed."
|
|
||||||
@@ -106,35 +106,30 @@ TEST(MeshFilterComponent_Test, SetMeshCachesResourceAndPath) {
|
|||||||
|
|
||||||
TEST(MeshFilterComponent_Test, SerializeAndDeserializePreservesPath) {
|
TEST(MeshFilterComponent_Test, SerializeAndDeserializePreservesPath) {
|
||||||
MeshFilterComponent source;
|
MeshFilterComponent source;
|
||||||
Mesh* mesh = CreateTestMesh("Quad", "Meshes/serialized.mesh");
|
source.SetMeshPath("builtin://meshes/cube");
|
||||||
source.SetMesh(mesh);
|
|
||||||
|
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
source.Serialize(stream);
|
source.Serialize(stream);
|
||||||
const std::string serialized = stream.str();
|
const std::string serialized = stream.str();
|
||||||
EXPECT_NE(serialized.find("meshRef="), std::string::npos);
|
EXPECT_NE(serialized.find("meshRef="), std::string::npos);
|
||||||
EXPECT_NE(serialized.find("meshPath=Meshes/serialized.mesh;"), std::string::npos);
|
EXPECT_NE(serialized.find("meshPath=builtin://meshes/cube;"), std::string::npos);
|
||||||
EXPECT_EQ(serialized.find("mesh=Meshes/serialized.mesh;"), std::string::npos);
|
|
||||||
|
|
||||||
MeshFilterComponent target;
|
MeshFilterComponent target;
|
||||||
std::stringstream deserializeStream(serialized);
|
std::stringstream deserializeStream(serialized);
|
||||||
target.Deserialize(deserializeStream);
|
target.Deserialize(deserializeStream);
|
||||||
|
|
||||||
EXPECT_EQ(target.GetMeshPath(), "Meshes/serialized.mesh");
|
EXPECT_EQ(target.GetMeshPath(), "builtin://meshes/cube");
|
||||||
EXPECT_EQ(target.GetMesh(), nullptr);
|
ASSERT_NE(target.GetMesh(), nullptr);
|
||||||
EXPECT_FALSE(target.GetMeshAssetRef().IsValid());
|
EXPECT_FALSE(target.GetMeshAssetRef().IsValid());
|
||||||
|
|
||||||
source.ClearMesh();
|
|
||||||
delete mesh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MeshFilterComponent_Test, DeserializeSupportsLegacyMeshKey) {
|
TEST(MeshFilterComponent_Test, DeserializeIgnoresPlainMeshPathWithoutAssetRef) {
|
||||||
MeshFilterComponent target;
|
MeshFilterComponent target;
|
||||||
|
|
||||||
std::stringstream stream("mesh=Meshes/legacy.mesh;meshRef=;");
|
std::stringstream stream("meshPath=Meshes/legacy.mesh;meshRef=;");
|
||||||
target.Deserialize(stream);
|
target.Deserialize(stream);
|
||||||
|
|
||||||
EXPECT_EQ(target.GetMeshPath(), "Meshes/legacy.mesh");
|
EXPECT_TRUE(target.GetMeshPath().empty());
|
||||||
EXPECT_EQ(target.GetMesh(), nullptr);
|
EXPECT_EQ(target.GetMesh(), nullptr);
|
||||||
EXPECT_FALSE(target.GetMeshAssetRef().IsValid());
|
EXPECT_FALSE(target.GetMeshAssetRef().IsValid());
|
||||||
}
|
}
|
||||||
@@ -165,16 +160,16 @@ TEST(MeshFilterComponent_Test, DeferredSceneDeserializeLoadsMeshAsyncByPath) {
|
|||||||
{
|
{
|
||||||
ResourceManager::ScopedDeferredSceneLoad deferredLoadScope;
|
ResourceManager::ScopedDeferredSceneLoad deferredLoadScope;
|
||||||
EXPECT_TRUE(manager.IsDeferredSceneLoadEnabled());
|
EXPECT_TRUE(manager.IsDeferredSceneLoadEnabled());
|
||||||
std::stringstream stream("mesh=Meshes/async.mesh;meshRef=;");
|
std::stringstream stream("meshPath=test://meshes/async.mesh;meshRef=;");
|
||||||
target.Deserialize(stream);
|
target.Deserialize(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(target.GetMeshPath(), "Meshes/async.mesh");
|
EXPECT_EQ(target.GetMeshPath(), "test://meshes/async.mesh");
|
||||||
EXPECT_EQ(target.GetMesh(), nullptr);
|
EXPECT_EQ(target.GetMesh(), nullptr);
|
||||||
EXPECT_GT(manager.GetAsyncPendingCount(), pendingBeforeDeserialize);
|
EXPECT_GT(manager.GetAsyncPendingCount(), pendingBeforeDeserialize);
|
||||||
ASSERT_TRUE(PumpAsyncLoadsUntilIdle(manager));
|
ASSERT_TRUE(PumpAsyncLoadsUntilIdle(manager));
|
||||||
ASSERT_NE(target.GetMesh(), nullptr);
|
ASSERT_NE(target.GetMesh(), nullptr);
|
||||||
EXPECT_EQ(target.GetMeshPath(), "Meshes/async.mesh");
|
EXPECT_EQ(target.GetMeshPath(), "test://meshes/async.mesh");
|
||||||
EXPECT_EQ(target.GetMesh()->GetVertexCount(), 3u);
|
EXPECT_EQ(target.GetMesh()->GetVertexCount(), 3u);
|
||||||
|
|
||||||
manager.RegisterLoader(originalLoader);
|
manager.RegisterLoader(originalLoader);
|
||||||
@@ -209,10 +204,8 @@ TEST(MeshRendererComponent_Test, SetMaterialsKeepsSlotsAndFlags) {
|
|||||||
|
|
||||||
TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesMaterialPathsAndSettings) {
|
TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesMaterialPathsAndSettings) {
|
||||||
MeshRendererComponent source;
|
MeshRendererComponent source;
|
||||||
Material* material0 = CreateTestMaterial("M0", "Materials/serialized0.mat");
|
source.SetMaterialPath(0, "builtin://materials/default-primitive");
|
||||||
Material* material1 = CreateTestMaterial("M1", "Materials/serialized1.mat");
|
source.SetMaterialPath(1, "builtin://materials/default-primitive");
|
||||||
source.SetMaterial(0, material0);
|
|
||||||
source.SetMaterial(1, material1);
|
|
||||||
source.SetCastShadows(false);
|
source.SetCastShadows(false);
|
||||||
source.SetReceiveShadows(true);
|
source.SetReceiveShadows(true);
|
||||||
source.SetRenderLayer(3);
|
source.SetRenderLayer(3);
|
||||||
@@ -221,33 +214,27 @@ TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesMaterialPathsAn
|
|||||||
source.Serialize(stream);
|
source.Serialize(stream);
|
||||||
const std::string serialized = stream.str();
|
const std::string serialized = stream.str();
|
||||||
EXPECT_NE(
|
EXPECT_NE(
|
||||||
serialized.find("materialPaths=Materials/serialized0.mat|Materials/serialized1.mat;"),
|
serialized.find("materialPaths=builtin://materials/default-primitive|builtin://materials/default-primitive;"),
|
||||||
std::string::npos);
|
std::string::npos);
|
||||||
EXPECT_NE(serialized.find("materialRefs=|;"), std::string::npos);
|
EXPECT_NE(serialized.find("materialRefs=|;"), std::string::npos);
|
||||||
EXPECT_EQ(serialized.find("materials="), std::string::npos);
|
|
||||||
|
|
||||||
MeshRendererComponent target;
|
MeshRendererComponent target;
|
||||||
std::stringstream deserializeStream(serialized);
|
std::stringstream deserializeStream(serialized);
|
||||||
target.Deserialize(deserializeStream);
|
target.Deserialize(deserializeStream);
|
||||||
|
|
||||||
ASSERT_EQ(target.GetMaterialCount(), 2u);
|
ASSERT_EQ(target.GetMaterialCount(), 2u);
|
||||||
EXPECT_EQ(target.GetMaterial(0), nullptr);
|
ASSERT_NE(target.GetMaterial(0), nullptr);
|
||||||
EXPECT_EQ(target.GetMaterial(1), nullptr);
|
ASSERT_NE(target.GetMaterial(1), nullptr);
|
||||||
EXPECT_EQ(target.GetMaterialPaths()[0], "Materials/serialized0.mat");
|
EXPECT_EQ(target.GetMaterialPaths()[0], "builtin://materials/default-primitive");
|
||||||
EXPECT_EQ(target.GetMaterialPaths()[1], "Materials/serialized1.mat");
|
EXPECT_EQ(target.GetMaterialPaths()[1], "builtin://materials/default-primitive");
|
||||||
EXPECT_FALSE(target.GetCastShadows());
|
EXPECT_FALSE(target.GetCastShadows());
|
||||||
EXPECT_TRUE(target.GetReceiveShadows());
|
EXPECT_TRUE(target.GetReceiveShadows());
|
||||||
EXPECT_EQ(target.GetRenderLayer(), 3u);
|
EXPECT_EQ(target.GetRenderLayer(), 3u);
|
||||||
|
|
||||||
source.ClearMaterials();
|
|
||||||
delete material0;
|
|
||||||
delete material1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesTrailingEmptyMaterialSlots) {
|
TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesTrailingEmptyMaterialSlots) {
|
||||||
MeshRendererComponent source;
|
MeshRendererComponent source;
|
||||||
Material* material0 = CreateTestMaterial("M0", "Materials/serialized0.mat");
|
source.SetMaterialPath(0, "builtin://materials/default-primitive");
|
||||||
source.SetMaterial(0, material0);
|
|
||||||
source.SetMaterialPath(1, "");
|
source.SetMaterialPath(1, "");
|
||||||
source.SetCastShadows(false);
|
source.SetCastShadows(false);
|
||||||
source.SetReceiveShadows(true);
|
source.SetReceiveShadows(true);
|
||||||
@@ -256,25 +243,21 @@ TEST(MeshRendererComponent_Test, SerializeAndDeserializePreservesTrailingEmptyMa
|
|||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
source.Serialize(stream);
|
source.Serialize(stream);
|
||||||
const std::string serialized = stream.str();
|
const std::string serialized = stream.str();
|
||||||
EXPECT_NE(serialized.find("materialPaths=Materials/serialized0.mat|;"), std::string::npos);
|
EXPECT_NE(serialized.find("materialPaths=builtin://materials/default-primitive|;"), std::string::npos);
|
||||||
EXPECT_NE(serialized.find("materialRefs=|;"), std::string::npos);
|
EXPECT_NE(serialized.find("materialRefs=|;"), std::string::npos);
|
||||||
EXPECT_EQ(serialized.find("materials="), std::string::npos);
|
|
||||||
|
|
||||||
MeshRendererComponent target;
|
MeshRendererComponent target;
|
||||||
std::stringstream deserializeStream(serialized);
|
std::stringstream deserializeStream(serialized);
|
||||||
target.Deserialize(deserializeStream);
|
target.Deserialize(deserializeStream);
|
||||||
|
|
||||||
ASSERT_EQ(target.GetMaterialCount(), 2u);
|
ASSERT_EQ(target.GetMaterialCount(), 2u);
|
||||||
EXPECT_EQ(target.GetMaterial(0), nullptr);
|
ASSERT_NE(target.GetMaterial(0), nullptr);
|
||||||
EXPECT_EQ(target.GetMaterial(1), nullptr);
|
EXPECT_EQ(target.GetMaterial(1), nullptr);
|
||||||
EXPECT_EQ(target.GetMaterialPath(0), "Materials/serialized0.mat");
|
EXPECT_EQ(target.GetMaterialPath(0), "builtin://materials/default-primitive");
|
||||||
EXPECT_EQ(target.GetMaterialPath(1), "");
|
EXPECT_EQ(target.GetMaterialPath(1), "");
|
||||||
EXPECT_FALSE(target.GetCastShadows());
|
EXPECT_FALSE(target.GetCastShadows());
|
||||||
EXPECT_TRUE(target.GetReceiveShadows());
|
EXPECT_TRUE(target.GetReceiveShadows());
|
||||||
EXPECT_EQ(target.GetRenderLayer(), 9u);
|
EXPECT_EQ(target.GetRenderLayer(), 9u);
|
||||||
|
|
||||||
source.ClearMaterials();
|
|
||||||
delete material0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MeshRendererComponent_Test, SetMaterialPathPreservesPathWithoutLoadedResource) {
|
TEST(MeshRendererComponent_Test, SetMaterialPathPreservesPathWithoutLoadedResource) {
|
||||||
@@ -293,15 +276,15 @@ TEST(MeshRendererComponent_Test, SetMaterialPathPreservesPathWithoutLoadedResour
|
|||||||
EXPECT_EQ(component.GetMaterial(1), nullptr);
|
EXPECT_EQ(component.GetMaterial(1), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MeshRendererComponent_Test, DeserializeSupportsLegacyMaterialsKey) {
|
TEST(MeshRendererComponent_Test, DeserializeIgnoresPlainMaterialPathsWithoutAssetRefs) {
|
||||||
MeshRendererComponent target;
|
MeshRendererComponent target;
|
||||||
|
|
||||||
std::stringstream stream(
|
std::stringstream stream(
|
||||||
"materials=Materials/legacy0.mat|;materialRefs=|;castShadows=0;receiveShadows=1;renderLayer=5;");
|
"materialPaths=Materials/legacy0.mat|;materialRefs=|;castShadows=0;receiveShadows=1;renderLayer=5;");
|
||||||
target.Deserialize(stream);
|
target.Deserialize(stream);
|
||||||
|
|
||||||
ASSERT_EQ(target.GetMaterialCount(), 2u);
|
ASSERT_EQ(target.GetMaterialCount(), 2u);
|
||||||
EXPECT_EQ(target.GetMaterialPath(0), "Materials/legacy0.mat");
|
EXPECT_EQ(target.GetMaterialPath(0), "");
|
||||||
EXPECT_EQ(target.GetMaterialPath(1), "");
|
EXPECT_EQ(target.GetMaterialPath(1), "");
|
||||||
EXPECT_EQ(target.GetMaterial(0), nullptr);
|
EXPECT_EQ(target.GetMaterial(0), nullptr);
|
||||||
EXPECT_EQ(target.GetMaterial(1), nullptr);
|
EXPECT_EQ(target.GetMaterial(1), nullptr);
|
||||||
@@ -351,7 +334,6 @@ TEST(MeshRendererComponent_Test, SerializeAndDeserializeLoadsProjectMaterialByAs
|
|||||||
EXPECT_NE(serialized.find("materialPaths=;"), std::string::npos);
|
EXPECT_NE(serialized.find("materialPaths=;"), std::string::npos);
|
||||||
EXPECT_NE(serialized.find("materialRefs="), std::string::npos);
|
EXPECT_NE(serialized.find("materialRefs="), std::string::npos);
|
||||||
EXPECT_EQ(serialized.find("materialRefs=;"), std::string::npos);
|
EXPECT_EQ(serialized.find("materialRefs=;"), std::string::npos);
|
||||||
EXPECT_EQ(serialized.find("materials="), std::string::npos);
|
|
||||||
|
|
||||||
std::stringstream deserializeStream(serialized);
|
std::stringstream deserializeStream(serialized);
|
||||||
MeshRendererComponent target;
|
MeshRendererComponent target;
|
||||||
|
|||||||
@@ -191,4 +191,16 @@ TEST(ResourceHandle, EqualityOperators) {
|
|||||||
handle2.Reset();
|
handle2.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ResourceHandle, ResetDoesNotDereferenceDestroyedResourcePointer) {
|
||||||
|
TestResource* resource = new TestResource();
|
||||||
|
resource->Initialize({ "Test", "test.png", ResourceGUID(321), 100 });
|
||||||
|
|
||||||
|
ResourceHandle<TestResource> handle(resource);
|
||||||
|
delete resource;
|
||||||
|
|
||||||
|
handle.Reset();
|
||||||
|
EXPECT_EQ(handle.Get(), nullptr);
|
||||||
|
EXPECT_EQ(handle.GetGUID().value, 0u);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <XCEngine/UI/Layout/LayoutEngine.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using XCEngine::UI::UIRect;
|
|
||||||
using XCEngine::UI::UISize;
|
|
||||||
using XCEngine::UI::Layout::ArrangeOverlayLayout;
|
|
||||||
using XCEngine::UI::Layout::ArrangeStackLayout;
|
|
||||||
using XCEngine::UI::Layout::MeasureOverlayLayout;
|
|
||||||
using XCEngine::UI::Layout::MeasureStackLayout;
|
|
||||||
using XCEngine::UI::Layout::UILayoutAlignment;
|
|
||||||
using XCEngine::UI::Layout::UILayoutAxis;
|
|
||||||
using XCEngine::UI::Layout::UILayoutConstraints;
|
|
||||||
using XCEngine::UI::Layout::UILayoutItem;
|
|
||||||
using XCEngine::UI::Layout::UILayoutLength;
|
|
||||||
using XCEngine::UI::Layout::UILayoutThickness;
|
|
||||||
using XCEngine::UI::Layout::UIOverlayLayoutOptions;
|
|
||||||
using XCEngine::UI::Layout::UIStackLayoutOptions;
|
|
||||||
|
|
||||||
void ExpectRect(
|
|
||||||
const UIRect& rect,
|
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
float width,
|
|
||||||
float height) {
|
|
||||||
EXPECT_FLOAT_EQ(rect.x, x);
|
|
||||||
EXPECT_FLOAT_EQ(rect.y, y);
|
|
||||||
EXPECT_FLOAT_EQ(rect.width, width);
|
|
||||||
EXPECT_FLOAT_EQ(rect.height, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
TEST(UI_Layout, MeasureHorizontalStackAccumulatesSpacingPaddingAndCrossExtent) {
|
|
||||||
UIStackLayoutOptions options = {};
|
|
||||||
options.axis = UILayoutAxis::Horizontal;
|
|
||||||
options.spacing = 5.0f;
|
|
||||||
options.padding = UILayoutThickness::Symmetric(10.0f, 6.0f);
|
|
||||||
|
|
||||||
std::vector<UILayoutItem> items(2);
|
|
||||||
items[0].desiredContentSize = UISize(40.0f, 20.0f);
|
|
||||||
items[1].desiredContentSize = UISize(60.0f, 30.0f);
|
|
||||||
|
|
||||||
const auto result = MeasureStackLayout(options, items);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(result.desiredSize.width, 125.0f);
|
|
||||||
EXPECT_FLOAT_EQ(result.desiredSize.height, 42.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UI_Layout, ArrangeHorizontalStackDistributesRemainingSpaceToStretchChildren) {
|
|
||||||
UIStackLayoutOptions options = {};
|
|
||||||
options.axis = UILayoutAxis::Horizontal;
|
|
||||||
options.spacing = 5.0f;
|
|
||||||
options.padding = UILayoutThickness::Uniform(10.0f);
|
|
||||||
|
|
||||||
std::vector<UILayoutItem> items(3);
|
|
||||||
items[0].width = UILayoutLength::Pixels(100.0f);
|
|
||||||
items[0].desiredContentSize = UISize(10.0f, 20.0f);
|
|
||||||
|
|
||||||
items[1].width = UILayoutLength::Stretch(1.0f);
|
|
||||||
items[1].desiredContentSize = UISize(30.0f, 20.0f);
|
|
||||||
|
|
||||||
items[2].width = UILayoutLength::Pixels(50.0f);
|
|
||||||
items[2].desiredContentSize = UISize(10.0f, 20.0f);
|
|
||||||
|
|
||||||
const auto result = ArrangeStackLayout(options, items, UIRect(0.0f, 0.0f, 300.0f, 80.0f));
|
|
||||||
|
|
||||||
ExpectRect(result.children[0].arrangedRect, 10.0f, 10.0f, 100.0f, 20.0f);
|
|
||||||
ExpectRect(result.children[1].arrangedRect, 115.0f, 10.0f, 120.0f, 20.0f);
|
|
||||||
ExpectRect(result.children[2].arrangedRect, 240.0f, 10.0f, 50.0f, 20.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UI_Layout, ArrangeVerticalStackSupportsCrossAxisStretch) {
|
|
||||||
UIStackLayoutOptions options = {};
|
|
||||||
options.axis = UILayoutAxis::Vertical;
|
|
||||||
options.spacing = 4.0f;
|
|
||||||
options.padding = UILayoutThickness::Symmetric(8.0f, 6.0f);
|
|
||||||
|
|
||||||
std::vector<UILayoutItem> items(2);
|
|
||||||
items[0].desiredContentSize = UISize(40.0f, 10.0f);
|
|
||||||
items[0].horizontalAlignment = UILayoutAlignment::Stretch;
|
|
||||||
items[1].desiredContentSize = UISize(60.0f, 20.0f);
|
|
||||||
|
|
||||||
const auto result = ArrangeStackLayout(options, items, UIRect(0.0f, 0.0f, 200.0f, 100.0f));
|
|
||||||
|
|
||||||
ExpectRect(result.children[0].arrangedRect, 8.0f, 6.0f, 184.0f, 10.0f);
|
|
||||||
ExpectRect(result.children[1].arrangedRect, 8.0f, 20.0f, 60.0f, 20.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UI_Layout, ArrangeOverlaySupportsCenterAndStretch) {
|
|
||||||
UIOverlayLayoutOptions options = {};
|
|
||||||
options.padding = UILayoutThickness::Uniform(10.0f);
|
|
||||||
|
|
||||||
std::vector<UILayoutItem> items(2);
|
|
||||||
items[0].desiredContentSize = UISize(40.0f, 20.0f);
|
|
||||||
items[0].horizontalAlignment = UILayoutAlignment::Center;
|
|
||||||
items[0].verticalAlignment = UILayoutAlignment::Center;
|
|
||||||
|
|
||||||
items[1].desiredContentSize = UISize(10.0f, 10.0f);
|
|
||||||
items[1].width = UILayoutLength::Stretch();
|
|
||||||
items[1].height = UILayoutLength::Stretch();
|
|
||||||
items[1].margin = UILayoutThickness::Uniform(5.0f);
|
|
||||||
|
|
||||||
const auto result = ArrangeOverlayLayout(options, items, UIRect(0.0f, 0.0f, 100.0f, 60.0f));
|
|
||||||
|
|
||||||
ExpectRect(result.children[0].arrangedRect, 30.0f, 20.0f, 40.0f, 20.0f);
|
|
||||||
ExpectRect(result.children[1].arrangedRect, 15.0f, 15.0f, 70.0f, 30.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UI_Layout, MeasureOverlayRespectsItemMinMaxAndAvailableConstraints) {
|
|
||||||
UIOverlayLayoutOptions options = {};
|
|
||||||
|
|
||||||
std::vector<UILayoutItem> items(1);
|
|
||||||
items[0].width = UILayoutLength::Pixels(500.0f);
|
|
||||||
items[0].desiredContentSize = UISize(10.0f, 10.0f);
|
|
||||||
items[0].minSize = UISize(0.0f, 50.0f);
|
|
||||||
items[0].maxSize = UISize(200.0f, 120.0f);
|
|
||||||
|
|
||||||
const auto result = MeasureOverlayLayout(
|
|
||||||
options,
|
|
||||||
items,
|
|
||||||
UILayoutConstraints::Bounded(150.0f, 100.0f));
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(result.children[0].measuredSize.width, 150.0f);
|
|
||||||
EXPECT_FLOAT_EQ(result.children[0].measuredSize.height, 50.0f);
|
|
||||||
EXPECT_FLOAT_EQ(result.desiredSize.width, 150.0f);
|
|
||||||
EXPECT_FLOAT_EQ(result.desiredSize.height, 50.0f);
|
|
||||||
}
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <XCEngine/Core/Asset/IResource.h>
|
|
||||||
#include <XCEngine/Resources/UI/UIDocuments.h>
|
|
||||||
#include <XCEngine/UI/Core/UIContext.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using XCEngine::UI::HasAnyDirtyFlags;
|
|
||||||
using XCEngine::UI::IUIViewModel;
|
|
||||||
using XCEngine::UI::RevisionedViewModelBase;
|
|
||||||
using XCEngine::UI::UIBuildContext;
|
|
||||||
using XCEngine::UI::UIBuildElementDesc;
|
|
||||||
using XCEngine::UI::UIContext;
|
|
||||||
using XCEngine::UI::UIDirtyFlags;
|
|
||||||
using XCEngine::UI::UIElementChangeKind;
|
|
||||||
using XCEngine::UI::UIElementId;
|
|
||||||
using XCEngine::UI::UIElementNode;
|
|
||||||
using XCEngine::UI::UIElementTree;
|
|
||||||
using XCEngine::Resources::UIDocumentKind;
|
|
||||||
using XCEngine::Resources::UIDocumentModel;
|
|
||||||
using XCEngine::Resources::UISchema;
|
|
||||||
using XCEngine::Resources::UIView;
|
|
||||||
|
|
||||||
class TestViewModel : public RevisionedViewModelBase {
|
|
||||||
public:
|
|
||||||
void Touch() {
|
|
||||||
MarkViewModelChanged();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
UIBuildElementDesc MakeElement(
|
|
||||||
UIElementId id,
|
|
||||||
const char* typeName,
|
|
||||||
std::uint64_t localStateRevision = 0,
|
|
||||||
const IUIViewModel* viewModel = nullptr,
|
|
||||||
std::uint64_t structuralRevision = 0) {
|
|
||||||
UIBuildElementDesc desc = {};
|
|
||||||
desc.id = id;
|
|
||||||
desc.typeName = typeName;
|
|
||||||
desc.localStateRevision = localStateRevision;
|
|
||||||
desc.viewModel = viewModel;
|
|
||||||
desc.structuralRevision = structuralRevision;
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildBasicTree(UIBuildContext& buildContext) {
|
|
||||||
auto root = buildContext.PushElement(MakeElement(1, "Root"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(root));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(2, "Label")));
|
|
||||||
auto panel = buildContext.PushElement(MakeElement(3, "Panel"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(panel));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(4, "Button")));
|
|
||||||
}
|
|
||||||
|
|
||||||
const UIElementNode& RequireNode(const UIElementTree& tree, UIElementId id) {
|
|
||||||
const UIElementNode* node = tree.FindNode(id);
|
|
||||||
EXPECT_NE(node, nullptr);
|
|
||||||
return *node;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UICoreTest, RebuildCreatesStableParentChildTree) {
|
|
||||||
UIContext context = {};
|
|
||||||
const auto result = context.Rebuild(BuildBasicTree);
|
|
||||||
|
|
||||||
EXPECT_TRUE(result.succeeded);
|
|
||||||
EXPECT_TRUE(result.treeChanged);
|
|
||||||
EXPECT_EQ(result.generation, 1u);
|
|
||||||
EXPECT_EQ(context.GetElementTree().GetRootId(), 1u);
|
|
||||||
EXPECT_EQ(context.GetElementTree().GetNodeCount(), 4u);
|
|
||||||
EXPECT_TRUE(result.HasChange(1));
|
|
||||||
EXPECT_TRUE(result.HasChange(4));
|
|
||||||
|
|
||||||
const UIElementNode& root = RequireNode(context.GetElementTree(), 1);
|
|
||||||
ASSERT_EQ(root.childIds.size(), 2u);
|
|
||||||
EXPECT_EQ(root.childIds[0], 2u);
|
|
||||||
EXPECT_EQ(root.childIds[1], 3u);
|
|
||||||
EXPECT_EQ(root.depth, 0u);
|
|
||||||
|
|
||||||
const UIElementNode& panel = RequireNode(context.GetElementTree(), 3);
|
|
||||||
ASSERT_EQ(panel.childIds.size(), 1u);
|
|
||||||
EXPECT_EQ(panel.childIds[0], 4u);
|
|
||||||
EXPECT_EQ(panel.depth, 1u);
|
|
||||||
|
|
||||||
ASSERT_EQ(result.dirtyRootIds.size(), 1u);
|
|
||||||
EXPECT_EQ(result.dirtyRootIds[0], 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UICoreTest, RebuildSkipsUnchangedTreeAfterDirtyFlagsAreCleared) {
|
|
||||||
UIContext context = {};
|
|
||||||
const auto initial = context.Rebuild(BuildBasicTree);
|
|
||||||
ASSERT_TRUE(initial.succeeded);
|
|
||||||
context.GetElementTree().ClearAllDirtyFlags();
|
|
||||||
|
|
||||||
const auto result = context.Rebuild(BuildBasicTree);
|
|
||||||
|
|
||||||
EXPECT_TRUE(result.succeeded);
|
|
||||||
EXPECT_FALSE(result.treeChanged);
|
|
||||||
EXPECT_TRUE(result.changes.empty());
|
|
||||||
EXPECT_TRUE(result.dirtyRootIds.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UICoreTest, LocalStateChangeOnlyInvalidatesTheChangedLeaf) {
|
|
||||||
UIContext context = {};
|
|
||||||
ASSERT_TRUE(context.Rebuild(BuildBasicTree).succeeded);
|
|
||||||
context.GetElementTree().ClearAllDirtyFlags();
|
|
||||||
|
|
||||||
const auto result = context.Rebuild([](UIBuildContext& buildContext) {
|
|
||||||
auto root = buildContext.PushElement(MakeElement(1, "Root"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(root));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(2, "Label", 1)));
|
|
||||||
auto panel = buildContext.PushElement(MakeElement(3, "Panel"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(panel));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(4, "Button")));
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(result.succeeded);
|
|
||||||
EXPECT_TRUE(result.treeChanged);
|
|
||||||
ASSERT_EQ(result.changes.size(), 1u);
|
|
||||||
ASSERT_NE(result.FindChange(2), nullptr);
|
|
||||||
EXPECT_EQ(result.FindChange(2)->kind, UIElementChangeKind::Updated);
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(result.FindChange(2)->dirtyFlags, UIDirtyFlags::LocalState));
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(result.FindChange(2)->dirtyFlags, UIDirtyFlags::Paint));
|
|
||||||
|
|
||||||
ASSERT_EQ(result.dirtyRootIds.size(), 1u);
|
|
||||||
EXPECT_EQ(result.dirtyRootIds[0], 2u);
|
|
||||||
|
|
||||||
const UIElementNode& leaf = RequireNode(context.GetElementTree(), 2);
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(leaf.dirtyFlags, UIDirtyFlags::LocalState));
|
|
||||||
EXPECT_FALSE(RequireNode(context.GetElementTree(), 1).IsDirty());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UICoreTest, ViewModelRevisionChangeInvalidatesBoundElement) {
|
|
||||||
TestViewModel viewModel = {};
|
|
||||||
UIContext context = {};
|
|
||||||
ASSERT_TRUE(context.Rebuild([&](UIBuildContext& buildContext) {
|
|
||||||
auto root = buildContext.PushElement(MakeElement(1, "Root"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(root));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(2, "Inspector", 0, &viewModel)));
|
|
||||||
}).succeeded);
|
|
||||||
context.GetElementTree().ClearAllDirtyFlags();
|
|
||||||
|
|
||||||
viewModel.Touch();
|
|
||||||
const auto result = context.Rebuild([&](UIBuildContext& buildContext) {
|
|
||||||
auto root = buildContext.PushElement(MakeElement(1, "Root"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(root));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(2, "Inspector", 0, &viewModel)));
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(result.succeeded);
|
|
||||||
ASSERT_NE(result.FindChange(2), nullptr);
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(result.FindChange(2)->dirtyFlags, UIDirtyFlags::ViewModel));
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(result.FindChange(2)->dirtyFlags, UIDirtyFlags::Paint));
|
|
||||||
ASSERT_EQ(result.dirtyRootIds.size(), 1u);
|
|
||||||
EXPECT_EQ(result.dirtyRootIds[0], 2u);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UICoreTest, StructuralChangesBubbleLayoutInvalidationToAncestors) {
|
|
||||||
UIContext context = {};
|
|
||||||
ASSERT_TRUE(context.Rebuild([](UIBuildContext& buildContext) {
|
|
||||||
auto root = buildContext.PushElement(MakeElement(1, "Root"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(root));
|
|
||||||
auto panel = buildContext.PushElement(MakeElement(2, "Panel"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(panel));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(3, "Text")));
|
|
||||||
}).succeeded);
|
|
||||||
context.GetElementTree().ClearAllDirtyFlags();
|
|
||||||
|
|
||||||
const auto result = context.Rebuild([](UIBuildContext& buildContext) {
|
|
||||||
auto root = buildContext.PushElement(MakeElement(1, "Root"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(root));
|
|
||||||
auto panel = buildContext.PushElement(MakeElement(2, "Panel"));
|
|
||||||
EXPECT_TRUE(static_cast<bool>(panel));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(3, "Text")));
|
|
||||||
EXPECT_TRUE(buildContext.AddLeaf(MakeElement(4, "Icon")));
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_TRUE(result.succeeded);
|
|
||||||
EXPECT_TRUE(result.HasChange(4));
|
|
||||||
ASSERT_NE(result.FindChange(2), nullptr);
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(result.FindChange(2)->dirtyFlags, UIDirtyFlags::Structure));
|
|
||||||
|
|
||||||
const UIElementNode& root = RequireNode(context.GetElementTree(), 1);
|
|
||||||
const UIElementNode& panel = RequireNode(context.GetElementTree(), 2);
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(root.dirtyFlags, UIDirtyFlags::Layout));
|
|
||||||
EXPECT_TRUE(HasAnyDirtyFlags(panel.dirtyFlags, UIDirtyFlags::Structure));
|
|
||||||
|
|
||||||
ASSERT_EQ(result.dirtyRootIds.size(), 1u);
|
|
||||||
EXPECT_EQ(result.dirtyRootIds[0], 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UICoreTest, RebuildFailsWhenElementScopesRemainOpen) {
|
|
||||||
UIContext context = {};
|
|
||||||
const auto result = context.Rebuild([](UIBuildContext& buildContext) {
|
|
||||||
EXPECT_TRUE(buildContext.BeginElement(MakeElement(1, "Root")));
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_FALSE(result.succeeded);
|
|
||||||
EXPECT_FALSE(result.errorMessage.empty());
|
|
||||||
EXPECT_EQ(context.GetElementTree().GetNodeCount(), 0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UICoreTest, UIDocumentResourcesAcceptMovedDocumentModels) {
|
|
||||||
UIDocumentModel viewDocument = {};
|
|
||||||
viewDocument.kind = UIDocumentKind::View;
|
|
||||||
viewDocument.sourcePath = "Assets/UI/Test.xcui";
|
|
||||||
viewDocument.displayName = "TestView";
|
|
||||||
viewDocument.rootNode.tagName = "View";
|
|
||||||
viewDocument.valid = true;
|
|
||||||
|
|
||||||
UIView view = {};
|
|
||||||
XCEngine::Resources::IResource::ConstructParams params = {};
|
|
||||||
params.name = "TestView";
|
|
||||||
params.path = viewDocument.sourcePath;
|
|
||||||
params.guid = XCEngine::Resources::ResourceGUID::Generate(params.path);
|
|
||||||
view.Initialize(params);
|
|
||||||
view.SetDocumentModel(std::move(viewDocument));
|
|
||||||
EXPECT_EQ(view.GetRootNode().tagName, "View");
|
|
||||||
EXPECT_EQ(view.GetSourcePath(), "Assets/UI/Test.xcui");
|
|
||||||
|
|
||||||
UIDocumentModel schemaDocument = {};
|
|
||||||
schemaDocument.kind = UIDocumentKind::Schema;
|
|
||||||
schemaDocument.sourcePath = "Assets/UI/Test.xcschema";
|
|
||||||
schemaDocument.displayName = "TestSchema";
|
|
||||||
schemaDocument.rootNode.tagName = "Schema";
|
|
||||||
schemaDocument.schemaDefinition.name = "TestSchema";
|
|
||||||
schemaDocument.schemaDefinition.valid = true;
|
|
||||||
schemaDocument.valid = true;
|
|
||||||
|
|
||||||
UISchema schema = {};
|
|
||||||
params.name = "TestSchema";
|
|
||||||
params.path = schemaDocument.sourcePath;
|
|
||||||
params.guid = XCEngine::Resources::ResourceGUID::Generate(params.path);
|
|
||||||
schema.Initialize(params);
|
|
||||||
schema.SetDocumentModel(std::move(schemaDocument));
|
|
||||||
EXPECT_TRUE(schema.GetSchemaDefinition().valid);
|
|
||||||
EXPECT_EQ(schema.GetSchemaDefinition().name, "TestSchema");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <XCEngine/UI/Style/Theme.h>
|
|
||||||
#include <XCEngine/UI/Style/StyleTypes.h>
|
|
||||||
#include <XCEngine/UI/Widgets/UIEditorCollectionPrimitives.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
namespace Style = XCEngine::UI::Style;
|
|
||||||
namespace UIWidgets = XCEngine::UI::Widgets;
|
|
||||||
|
|
||||||
Style::UITheme BuildEditorPrimitiveTheme() {
|
|
||||||
Style::UIThemeDefinition definition = {};
|
|
||||||
definition.SetToken("space.cardInset", Style::UIStyleValue(14.0f));
|
|
||||||
definition.SetToken("size.treeItemHeight", Style::UIStyleValue(30.0f));
|
|
||||||
definition.SetToken("size.listItemHeight", Style::UIStyleValue(64.0f));
|
|
||||||
definition.SetToken("size.fieldRowHeight", Style::UIStyleValue(36.0f));
|
|
||||||
definition.SetToken("size.propertySectionHeight", Style::UIStyleValue(156.0f));
|
|
||||||
definition.SetToken("size.treeIndent", Style::UIStyleValue(20.0f));
|
|
||||||
return Style::BuildTheme(definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UIEditorCollectionPrimitivesTest, ClassifyAndFlagsMatchEditorCollectionTags) {
|
|
||||||
using Kind = UIWidgets::UIEditorCollectionPrimitiveKind;
|
|
||||||
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("ScrollView"), Kind::ScrollView);
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("TreeView"), Kind::TreeView);
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("TreeItem"), Kind::TreeItem);
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("ListView"), Kind::ListView);
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("ListItem"), Kind::ListItem);
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("PropertySection"), Kind::PropertySection);
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("FieldRow"), Kind::FieldRow);
|
|
||||||
EXPECT_EQ(UIWidgets::ClassifyUIEditorCollectionPrimitive("Column"), Kind::None);
|
|
||||||
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveContainer(Kind::ScrollView));
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveContainer(Kind::TreeView));
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveContainer(Kind::ListView));
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveContainer(Kind::PropertySection));
|
|
||||||
EXPECT_FALSE(UIWidgets::IsUIEditorCollectionPrimitiveContainer(Kind::TreeItem));
|
|
||||||
|
|
||||||
EXPECT_TRUE(UIWidgets::UsesUIEditorCollectionPrimitiveColumnLayout(Kind::TreeView));
|
|
||||||
EXPECT_TRUE(UIWidgets::UsesUIEditorCollectionPrimitiveColumnLayout(Kind::ListView));
|
|
||||||
EXPECT_TRUE(UIWidgets::UsesUIEditorCollectionPrimitiveColumnLayout(Kind::PropertySection));
|
|
||||||
EXPECT_FALSE(UIWidgets::UsesUIEditorCollectionPrimitiveColumnLayout(Kind::ScrollView));
|
|
||||||
|
|
||||||
EXPECT_TRUE(UIWidgets::DoesUIEditorCollectionPrimitiveClipChildren(Kind::ScrollView));
|
|
||||||
EXPECT_TRUE(UIWidgets::DoesUIEditorCollectionPrimitiveClipChildren(Kind::TreeView));
|
|
||||||
EXPECT_TRUE(UIWidgets::DoesUIEditorCollectionPrimitiveClipChildren(Kind::ListView));
|
|
||||||
EXPECT_FALSE(UIWidgets::DoesUIEditorCollectionPrimitiveClipChildren(Kind::PropertySection));
|
|
||||||
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::TreeItem));
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::ListItem));
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::PropertySection));
|
|
||||||
EXPECT_TRUE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::FieldRow));
|
|
||||||
EXPECT_FALSE(UIWidgets::IsUIEditorCollectionPrimitiveHoverable(Kind::TreeView));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UIEditorCollectionPrimitivesTest, ResolveMetricsUseThemeTokensAndFallbacks) {
|
|
||||||
using Kind = UIWidgets::UIEditorCollectionPrimitiveKind;
|
|
||||||
|
|
||||||
const Style::UITheme themed = BuildEditorPrimitiveTheme();
|
|
||||||
const Style::UITheme fallback = Style::UITheme();
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitivePadding(Kind::TreeView, themed), 14.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitivePadding(Kind::ListView, themed), 14.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitivePadding(Kind::PropertySection, themed), 14.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitivePadding(Kind::ScrollView, themed), 0.0f);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::TreeItem, themed), 30.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::ListItem, themed), 64.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::FieldRow, themed), 36.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::PropertySection, themed), 156.0f);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::TreeItem, fallback), 28.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::ListItem, fallback), 60.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::FieldRow, fallback), 32.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveDefaultHeight(Kind::PropertySection, fallback), 148.0f);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveIndent(Kind::TreeItem, themed, 2.0f), 40.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveIndent(Kind::TreeItem, fallback, 2.0f), 36.0f);
|
|
||||||
EXPECT_FLOAT_EQ(UIWidgets::ResolveUIEditorCollectionPrimitiveIndent(Kind::ListItem, themed, 2.0f), 0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <XCEngine/UI/DrawData.h>
|
|
||||||
#include <XCEngine/UI/Widgets/UIEditorPanelChrome.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using XCEngine::UI::UIColor;
|
|
||||||
using XCEngine::UI::UIDrawCommandType;
|
|
||||||
using XCEngine::UI::UIDrawList;
|
|
||||||
using XCEngine::UI::UIRect;
|
|
||||||
using XCEngine::UI::Widgets::AppendUIEditorPanelChromeBackground;
|
|
||||||
using XCEngine::UI::Widgets::AppendUIEditorPanelChromeForeground;
|
|
||||||
using XCEngine::UI::Widgets::BuildUIEditorPanelChromeHeaderRect;
|
|
||||||
using XCEngine::UI::Widgets::ResolveUIEditorPanelChromeBorderColor;
|
|
||||||
using XCEngine::UI::Widgets::ResolveUIEditorPanelChromeBorderThickness;
|
|
||||||
using XCEngine::UI::Widgets::UIEditorPanelChromePalette;
|
|
||||||
using XCEngine::UI::Widgets::UIEditorPanelChromeState;
|
|
||||||
using XCEngine::UI::Widgets::UIEditorPanelChromeText;
|
|
||||||
|
|
||||||
void ExpectColorEq(
|
|
||||||
const UIColor& actual,
|
|
||||||
const UIColor& expected) {
|
|
||||||
EXPECT_FLOAT_EQ(actual.r, expected.r);
|
|
||||||
EXPECT_FLOAT_EQ(actual.g, expected.g);
|
|
||||||
EXPECT_FLOAT_EQ(actual.b, expected.b);
|
|
||||||
EXPECT_FLOAT_EQ(actual.a, expected.a);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UIEditorPanelChromeTest, HeaderRectAndBorderPolicyMatchNativeShellCardChrome) {
|
|
||||||
const UIRect panelRect(100.0f, 200.0f, 320.0f, 180.0f);
|
|
||||||
const UIEditorPanelChromePalette palette = {};
|
|
||||||
|
|
||||||
const auto headerRect = BuildUIEditorPanelChromeHeaderRect(panelRect);
|
|
||||||
EXPECT_FLOAT_EQ(headerRect.x, 100.0f);
|
|
||||||
EXPECT_FLOAT_EQ(headerRect.y, 200.0f);
|
|
||||||
EXPECT_FLOAT_EQ(headerRect.width, 320.0f);
|
|
||||||
EXPECT_FLOAT_EQ(headerRect.height, 42.0f);
|
|
||||||
|
|
||||||
ExpectColorEq(
|
|
||||||
ResolveUIEditorPanelChromeBorderColor(UIEditorPanelChromeState(), palette),
|
|
||||||
palette.borderColor);
|
|
||||||
ExpectColorEq(
|
|
||||||
ResolveUIEditorPanelChromeBorderColor(UIEditorPanelChromeState{ false, true }, palette),
|
|
||||||
palette.hoveredAccentColor);
|
|
||||||
ExpectColorEq(
|
|
||||||
ResolveUIEditorPanelChromeBorderColor(UIEditorPanelChromeState{ true, true }, palette),
|
|
||||||
palette.accentColor);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(ResolveUIEditorPanelChromeBorderThickness(UIEditorPanelChromeState()), 1.0f);
|
|
||||||
EXPECT_FLOAT_EQ(ResolveUIEditorPanelChromeBorderThickness(UIEditorPanelChromeState{ true, false }), 2.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UIEditorPanelChromeTest, BackgroundAppendEmitsSurfaceOutlineAndHeaderFill) {
|
|
||||||
UIDrawList drawList("PanelChrome");
|
|
||||||
const UIRect panelRect(40.0f, 60.0f, 400.0f, 280.0f);
|
|
||||||
const UIEditorPanelChromeState state{ true, false };
|
|
||||||
const UIEditorPanelChromePalette palette = {};
|
|
||||||
|
|
||||||
AppendUIEditorPanelChromeBackground(drawList, panelRect, state, palette);
|
|
||||||
|
|
||||||
ASSERT_EQ(drawList.GetCommandCount(), 3u);
|
|
||||||
const auto& commands = drawList.GetCommands();
|
|
||||||
EXPECT_EQ(commands[0].type, UIDrawCommandType::FilledRect);
|
|
||||||
EXPECT_EQ(commands[1].type, UIDrawCommandType::RectOutline);
|
|
||||||
EXPECT_EQ(commands[2].type, UIDrawCommandType::FilledRect);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(commands[0].rect.x, 40.0f);
|
|
||||||
EXPECT_FLOAT_EQ(commands[0].rounding, 18.0f);
|
|
||||||
ExpectColorEq(commands[0].color, palette.surfaceColor);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(commands[1].thickness, 2.0f);
|
|
||||||
EXPECT_FLOAT_EQ(commands[1].rounding, 18.0f);
|
|
||||||
ExpectColorEq(commands[1].color, palette.accentColor);
|
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(commands[2].rect.height, 42.0f);
|
|
||||||
EXPECT_FLOAT_EQ(commands[2].rounding, 18.0f);
|
|
||||||
ExpectColorEq(commands[2].color, palette.headerColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UIEditorPanelChromeTest, ForegroundAppendPlacesTitleSubtitleAndFooterAtCurrentOffsets) {
|
|
||||||
UIDrawList drawList("PanelChromeText");
|
|
||||||
const UIRect panelRect(100.0f, 200.0f, 320.0f, 180.0f);
|
|
||||||
const UIEditorPanelChromePalette palette = {};
|
|
||||||
const UIEditorPanelChromeText text{
|
|
||||||
"XCUI Demo",
|
|
||||||
"native queued offscreen surface",
|
|
||||||
"Active | 42 elements | 9 cmds"
|
|
||||||
};
|
|
||||||
|
|
||||||
AppendUIEditorPanelChromeForeground(drawList, panelRect, text, palette);
|
|
||||||
|
|
||||||
ASSERT_EQ(drawList.GetCommandCount(), 3u);
|
|
||||||
const auto& commands = drawList.GetCommands();
|
|
||||||
EXPECT_EQ(commands[0].type, UIDrawCommandType::Text);
|
|
||||||
EXPECT_EQ(commands[1].type, UIDrawCommandType::Text);
|
|
||||||
EXPECT_EQ(commands[2].type, UIDrawCommandType::Text);
|
|
||||||
|
|
||||||
EXPECT_EQ(commands[0].text, "XCUI Demo");
|
|
||||||
EXPECT_FLOAT_EQ(commands[0].position.x, 116.0f);
|
|
||||||
EXPECT_FLOAT_EQ(commands[0].position.y, 212.0f);
|
|
||||||
ExpectColorEq(commands[0].color, palette.textPrimary);
|
|
||||||
|
|
||||||
EXPECT_EQ(commands[1].text, "native queued offscreen surface");
|
|
||||||
EXPECT_FLOAT_EQ(commands[1].position.x, 116.0f);
|
|
||||||
EXPECT_FLOAT_EQ(commands[1].position.y, 228.0f);
|
|
||||||
ExpectColorEq(commands[1].color, palette.textSecondary);
|
|
||||||
|
|
||||||
EXPECT_EQ(commands[2].text, "Active | 42 elements | 9 cmds");
|
|
||||||
EXPECT_FLOAT_EQ(commands[2].position.x, 116.0f);
|
|
||||||
EXPECT_FLOAT_EQ(commands[2].position.y, 362.0f);
|
|
||||||
ExpectColorEq(commands[2].color, palette.textMuted);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UIEditorPanelChromeTest, ForegroundAppendSkipsEmptyStrings) {
|
|
||||||
UIDrawList drawList("PanelChromeEmptyText");
|
|
||||||
|
|
||||||
AppendUIEditorPanelChromeForeground(
|
|
||||||
drawList,
|
|
||||||
UIRect(0.0f, 0.0f, 320.0f, 180.0f),
|
|
||||||
UIEditorPanelChromeText{});
|
|
||||||
|
|
||||||
EXPECT_EQ(drawList.GetCommandCount(), 0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user