engine: sync editor rendering and ui changes
This commit is contained in:
@@ -78,6 +78,7 @@ add_executable(${PROJECT_NAME} WIN32
|
||||
src/panels/MenuBar.cpp
|
||||
src/panels/HierarchyPanel.cpp
|
||||
src/panels/SceneViewPanel.cpp
|
||||
src/panels/MaterialInspectorMaterialStateIO.cpp
|
||||
src/Viewport/SceneViewportPicker.cpp
|
||||
src/Viewport/SceneViewportMoveGizmo.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",
|
||||
"passes": [
|
||||
HLSLINCLUDE
|
||||
// XC_EDITOR_SCENE_VIEW_INFINITE_GRID_D3D12_SHARED
|
||||
cbuffer GridConstants
|
||||
{
|
||||
"name": "InfiniteGrid",
|
||||
"tags": {
|
||||
"LightMode": "InfiniteGrid"
|
||||
},
|
||||
"variants": [
|
||||
{
|
||||
"stage": "Vertex",
|
||||
"backend": "D3D12",
|
||||
"language": "HLSL",
|
||||
"source": "infinite-grid.vs.hlsl",
|
||||
"entryPoint": "MainVS",
|
||||
"profile": "vs_5_0"
|
||||
},
|
||||
{
|
||||
"stage": "Fragment",
|
||||
"backend": "D3D12",
|
||||
"language": "HLSL",
|
||||
"source": "infinite-grid.ps.hlsl",
|
||||
"entryPoint": "MainPS",
|
||||
"profile": "ps_5_0"
|
||||
}
|
||||
]
|
||||
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)
|
||||
{
|
||||
// XC_EDITOR_SCENE_VIEW_INFINITE_GRID_D3D12_VS
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
::XCEngine::Components::GameObject* parent = nullptr,
|
||||
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(
|
||||
context,
|
||||
commandLabel,
|
||||
entityName,
|
||||
parent,
|
||||
[](::XCEngine::Components::GameObject& entity, ISceneManager&) {
|
||||
entity.AddComponent<::XCEngine::Components::LightComponent>();
|
||||
[lightType](::XCEngine::Components::GameObject& entity, ISceneManager&) {
|
||||
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(
|
||||
IEditorContext& context,
|
||||
::XCEngine::Resources::BuiltinPrimitiveType primitiveType,
|
||||
|
||||
@@ -99,7 +99,9 @@ struct BuiltInIconState {
|
||||
BuiltInTexture gameObject;
|
||||
BuiltInTexture scene;
|
||||
BuiltInTexture cameraGizmo;
|
||||
BuiltInTexture mainLightGizmo;
|
||||
BuiltInTexture directionalLightGizmo;
|
||||
BuiltInTexture pointLightGizmo;
|
||||
BuiltInTexture spotLightGizmo;
|
||||
struct CachedAssetPreview {
|
||||
BuiltInTexture texture;
|
||||
std::unique_ptr<LoadedTexturePixels> decodedPixels;
|
||||
@@ -157,9 +159,19 @@ std::filesystem::path ResolveCameraGizmoIconPath() {
|
||||
return (exeDir / ".." / ".." / "resources" / "Icons" / "camera_gizmo.png").lexically_normal();
|
||||
}
|
||||
|
||||
std::filesystem::path ResolveMainLightGizmoIconPath() {
|
||||
std::filesystem::path ResolveDirectionalLightGizmoIconPath() {
|
||||
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) {
|
||||
@@ -975,8 +987,12 @@ BuiltInTexture* ResolveEditorTexture(EditorTextureIconKind kind) {
|
||||
switch (kind) {
|
||||
case EditorTextureIconKind::CameraGizmo:
|
||||
return &g_icons.cameraGizmo;
|
||||
case EditorTextureIconKind::MainLightGizmo:
|
||||
return &g_icons.mainLightGizmo;
|
||||
case EditorTextureIconKind::DirectionalLightGizmo:
|
||||
return &g_icons.directionalLightGizmo;
|
||||
case EditorTextureIconKind::PointLightGizmo:
|
||||
return &g_icons.pointLightGizmo;
|
||||
case EditorTextureIconKind::SpotLightGizmo:
|
||||
return &g_icons.spotLightGizmo;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1025,8 +1041,30 @@ void InitializeBuiltInIcons(
|
||||
backend,
|
||||
device,
|
||||
commandQueue,
|
||||
ResolveMainLightGizmoIconPath(),
|
||||
g_icons.mainLightGizmo,
|
||||
ResolveDirectionalLightGizmoIconPath(),
|
||||
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)) {
|
||||
g_icons.pendingIconUploads.push_back(std::move(pendingUpload));
|
||||
}
|
||||
@@ -1043,7 +1081,9 @@ void ShutdownBuiltInIcons() {
|
||||
ResetTexture(g_icons.gameObject);
|
||||
ResetTexture(g_icons.scene);
|
||||
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.device = nullptr;
|
||||
g_icons.commandQueue = nullptr;
|
||||
|
||||
@@ -21,7 +21,10 @@ enum class AssetIconKind {
|
||||
|
||||
enum class EditorTextureIconKind {
|
||||
CameraGizmo,
|
||||
MainLightGizmo
|
||||
DirectionalLightGizmo,
|
||||
PointLightGizmo,
|
||||
SpotLightGizmo,
|
||||
MainLightGizmo = DirectionalLightGizmo
|
||||
};
|
||||
|
||||
void InitializeBuiltInIcons(
|
||||
|
||||
@@ -14,8 +14,17 @@ namespace XCEngine {
|
||||
namespace Editor {
|
||||
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) {
|
||||
if (!label || std::string(label) != "Create") {
|
||||
if (!ShouldTracePopupSubmenuLabel(label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,7 +101,7 @@ inline bool DrawMenuScope(const char* label, DrawContentFn&& drawContent) {
|
||||
}
|
||||
|
||||
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') {
|
||||
return false;
|
||||
}
|
||||
@@ -103,18 +112,24 @@ inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent
|
||||
const ImVec2 rowPos = ImGui::GetCursorScreenPos();
|
||||
const float rowHeight = labelSize.y;
|
||||
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(
|
||||
"##PopupSubmenuRow",
|
||||
popupOpen,
|
||||
ImGuiSelectableFlags_NoAutoClosePopups,
|
||||
selectableFlags,
|
||||
ImVec2(rowWidth, rowHeight))) {
|
||||
TracePopupSubmenuIfNeeded(label, "Hierarchy create submenu selectable clicked -> OpenPopup");
|
||||
ImGui::OpenPopup(popupId);
|
||||
if (enabled) {
|
||||
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) {
|
||||
TracePopupSubmenuIfNeeded(label, "Hierarchy create submenu hovered -> OpenPopup");
|
||||
ImGui::OpenPopup(popupId);
|
||||
@@ -127,9 +142,10 @@ inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent
|
||||
const float parentWindowRight = parentWindowPos.x + parentWindowSize.x;
|
||||
const float itemHeight = itemMax.y - itemMin.y;
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const ImU32 textColor = ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled);
|
||||
drawList->AddText(
|
||||
ImVec2(itemMin.x + ImGui::GetStyle().FramePadding.x, itemMin.y + (itemHeight - labelSize.y) * 0.5f),
|
||||
ImGui::GetColorU32(ImGuiCol_Text),
|
||||
textColor,
|
||||
label);
|
||||
|
||||
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.50f, arrowCenterY),
|
||||
ImGui::GetColorU32(ImGuiCol_Text));
|
||||
textColor);
|
||||
|
||||
if (!enabled) {
|
||||
ImGui::PopID();
|
||||
return false;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(parentWindowRight + PopupSubmenuOpenOffsetX(), rowPos.y - PopupWindowPadding().y),
|
||||
@@ -149,7 +170,7 @@ inline bool DrawPopupSubmenuScope(const char* label, DrawContentFn&& drawContent
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
if (std::string(label) == "Create") {
|
||||
if (ShouldTracePopupSubmenuLabel(label)) {
|
||||
static bool s_lastCreateOpen = false;
|
||||
if (open != s_lastCreateOpen) {
|
||||
TracePopupSubmenuIfNeeded(
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
|
||||
#include "Passes/SceneViewportSelectionOutlinePass.h"
|
||||
|
||||
#include "Viewport/SceneViewportShaderPaths.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
@@ -45,7 +41,7 @@ private:
|
||||
} // namespace
|
||||
|
||||
SceneViewportSelectionOutlinePassRenderer::SceneViewportSelectionOutlinePassRenderer()
|
||||
: m_outlinePass(GetSceneViewportObjectIdOutlineShaderPath()) {
|
||||
: m_outlinePass() {
|
||||
}
|
||||
|
||||
void SceneViewportSelectionOutlinePassRenderer::Shutdown() {
|
||||
|
||||
@@ -20,7 +20,10 @@ enum class SceneViewportOverlayDepthMode : uint8_t {
|
||||
|
||||
enum class SceneViewportOverlaySpriteTextureKind : uint8_t {
|
||||
Camera = 0,
|
||||
Light = 1
|
||||
DirectionalLight = 1,
|
||||
PointLight = 2,
|
||||
SpotLight = 3,
|
||||
Light = DirectionalLight
|
||||
};
|
||||
|
||||
enum class SceneViewportOverlayHandleKind : uint8_t {
|
||||
|
||||
@@ -29,12 +29,24 @@ bool DrawSceneViewportSpriteIcon(
|
||||
min,
|
||||
max,
|
||||
UI::EditorTextureIconKind::CameraGizmo);
|
||||
case SceneViewportOverlaySpriteTextureKind::Light:
|
||||
case SceneViewportOverlaySpriteTextureKind::DirectionalLight:
|
||||
return UI::DrawEditorTextureIcon(
|
||||
drawList,
|
||||
min,
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -105,6 +105,16 @@ inline bool ShouldBeginSceneViewportNavigationDrag(
|
||||
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(
|
||||
const SceneViewportNavigationRequest& request,
|
||||
int button) {
|
||||
@@ -139,13 +149,21 @@ inline SceneViewportNavigationUpdate UpdateSceneViewportNavigationState(
|
||||
request.state.panDragging,
|
||||
request.gizmoActive,
|
||||
request.clickedRight);
|
||||
update.beginMiddlePanDrag = ShouldBeginSceneViewportNavigationDrag(
|
||||
request.hasInteractiveViewport,
|
||||
request.viewportHovered,
|
||||
request.state.panDragging,
|
||||
request.state.lookDragging,
|
||||
request.gizmoActive,
|
||||
request.clickedMiddle);
|
||||
update.beginMiddlePanDrag =
|
||||
ShouldBeginSceneViewportNavigationDrag(
|
||||
request.hasInteractiveViewport,
|
||||
request.viewportHovered,
|
||||
request.state.panDragging,
|
||||
request.state.lookDragging,
|
||||
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;
|
||||
|
||||
if (update.beginLookDrag) {
|
||||
|
||||
@@ -73,6 +73,42 @@ void AppendWorldLine(
|
||||
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(
|
||||
SceneViewportOverlayFrameData& frameData,
|
||||
const Math::Vector3& worldPosition,
|
||||
@@ -164,6 +200,20 @@ void AppendSceneIconOverlay(
|
||||
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(
|
||||
SceneViewportOverlayFrameData& frameData,
|
||||
const Components::CameraComponent& camera,
|
||||
@@ -251,10 +301,10 @@ void AppendDirectionalLightOverlay(
|
||||
}
|
||||
|
||||
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 up = transform->GetUp().Normalized();
|
||||
if (lightDirection.SqrMagnitude() <= Math::EPSILON ||
|
||||
if (lightRayDirection.SqrMagnitude() <= Math::EPSILON ||
|
||||
right.SqrMagnitude() <= Math::EPSILON ||
|
||||
up.SqrMagnitude() <= Math::EPSILON) {
|
||||
return;
|
||||
@@ -265,8 +315,6 @@ void AppendDirectionalLightOverlay(
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr Math::Color kDirectionalLightColor(1.0f, 0.92f, 0.24f, 1.0f);
|
||||
constexpr float kLineThickness = 1.8f;
|
||||
constexpr size_t kRingSegmentCount = 32u;
|
||||
constexpr std::array<float, 6> kRayAngles = {{
|
||||
0.0f,
|
||||
@@ -281,7 +329,7 @@ void AppendDirectionalLightOverlay(
|
||||
const float ringOffset = worldUnitsPerPixel * 54.0f;
|
||||
const float innerRayRadius = ringRadius * 0.52f;
|
||||
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) {
|
||||
const float angle0 =
|
||||
@@ -296,8 +344,8 @@ void AppendDirectionalLightOverlay(
|
||||
frameData,
|
||||
p0,
|
||||
p1,
|
||||
kDirectionalLightColor,
|
||||
kLineThickness,
|
||||
kSelectedLightHelperColor,
|
||||
kSelectedLightHelperLineThickness,
|
||||
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||
}
|
||||
|
||||
@@ -305,15 +353,15 @@ void AppendDirectionalLightOverlay(
|
||||
frameData,
|
||||
position,
|
||||
ringCenter,
|
||||
kDirectionalLightColor,
|
||||
kLineThickness,
|
||||
kSelectedLightHelperColor,
|
||||
kSelectedLightHelperLineThickness,
|
||||
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||
AppendWorldLine(
|
||||
frameData,
|
||||
ringCenter,
|
||||
ringCenter + lightDirection * rayLength,
|
||||
kDirectionalLightColor,
|
||||
kLineThickness,
|
||||
ringCenter + lightRayDirection * rayLength,
|
||||
kSelectedLightHelperColor,
|
||||
kSelectedLightHelperLineThickness,
|
||||
SceneViewportOverlayDepthMode::AlwaysOnTop);
|
||||
|
||||
for (float angle : kRayAngles) {
|
||||
@@ -322,9 +370,119 @@ void AppendDirectionalLightOverlay(
|
||||
AppendWorldLine(
|
||||
frameData,
|
||||
rayStart,
|
||||
rayStart + lightDirection * rayLength,
|
||||
kDirectionalLightColor,
|
||||
kLineThickness,
|
||||
rayStart + lightRayDirection * rayLength,
|
||||
kSelectedLightHelperColor,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -421,7 +579,7 @@ public:
|
||||
context.viewportHeight,
|
||||
*gameObject,
|
||||
kLightIconSize,
|
||||
SceneViewportOverlaySpriteTextureKind::Light);
|
||||
ResolveLightSceneIconTextureKind(*light));
|
||||
}
|
||||
|
||||
for (uint64_t entityId : *context.selectedObjectIds) {
|
||||
@@ -435,17 +593,25 @@ public:
|
||||
}
|
||||
|
||||
Components::LightComponent* light = gameObject->GetComponent<Components::LightComponent>();
|
||||
if (light == nullptr ||
|
||||
!light->IsEnabled() ||
|
||||
light->GetLightType() != Components::LightType::Directional) {
|
||||
if (light == nullptr || !light->IsEnabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AppendDirectionalLightOverlay(
|
||||
frameData,
|
||||
*gameObject,
|
||||
*context.overlay,
|
||||
context.viewportHeight);
|
||||
switch (light->GetLightType()) {
|
||||
case Components::LightType::Directional:
|
||||
AppendDirectionalLightOverlay(
|
||||
frameData,
|
||||
*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 {
|
||||
|
||||
inline constexpr std::array<SceneViewportOverlaySpriteTextureKind, 2> kSceneViewportOverlaySpriteTextureKinds = {
|
||||
inline constexpr std::array<SceneViewportOverlaySpriteTextureKind, 4> kSceneViewportOverlaySpriteTextureKinds = {
|
||||
SceneViewportOverlaySpriteTextureKind::Camera,
|
||||
SceneViewportOverlaySpriteTextureKind::Light
|
||||
SceneViewportOverlaySpriteTextureKind::DirectionalLight,
|
||||
SceneViewportOverlaySpriteTextureKind::PointLight,
|
||||
SceneViewportOverlaySpriteTextureKind::SpotLight
|
||||
};
|
||||
|
||||
inline constexpr size_t kSceneViewportOverlaySpriteResourceCount =
|
||||
@@ -35,8 +37,12 @@ inline size_t GetSceneViewportOverlaySpriteResourceIndex(
|
||||
switch (textureKind) {
|
||||
case SceneViewportOverlaySpriteTextureKind::Camera:
|
||||
return 0u;
|
||||
case SceneViewportOverlaySpriteTextureKind::Light:
|
||||
case SceneViewportOverlaySpriteTextureKind::DirectionalLight:
|
||||
return 1u;
|
||||
case SceneViewportOverlaySpriteTextureKind::PointLight:
|
||||
return 2u;
|
||||
case SceneViewportOverlaySpriteTextureKind::SpotLight:
|
||||
return 3u;
|
||||
default:
|
||||
return 0u;
|
||||
}
|
||||
@@ -63,8 +69,14 @@ inline SceneViewportOverlaySpriteAssetSpec GetSceneViewportOverlaySpriteAssetSpe
|
||||
case SceneViewportOverlaySpriteTextureKind::Camera:
|
||||
spec.resourcePath = GetSceneViewportCameraGizmoIconPath();
|
||||
break;
|
||||
case SceneViewportOverlaySpriteTextureKind::Light:
|
||||
spec.resourcePath = GetSceneViewportMainLightGizmoIconPath();
|
||||
case SceneViewportOverlaySpriteTextureKind::DirectionalLight:
|
||||
spec.resourcePath = GetSceneViewportDirectionalLightGizmoIconPath();
|
||||
break;
|
||||
case SceneViewportOverlaySpriteTextureKind::PointLight:
|
||||
spec.resourcePath = GetSceneViewportPointLightGizmoIconPath();
|
||||
break;
|
||||
case SceneViewportOverlaySpriteTextureKind::SpotLight:
|
||||
spec.resourcePath = GetSceneViewportSpotLightGizmoIconPath();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -39,22 +39,28 @@ inline Containers::String GetSceneViewportInfiniteGridShaderPath() {
|
||||
"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() {
|
||||
return Detail::BuildSceneViewportEditorResourcePath(
|
||||
std::filesystem::path("Icons") / "camera_gizmo.png");
|
||||
}
|
||||
|
||||
inline Containers::String GetSceneViewportMainLightGizmoIconPath() {
|
||||
inline Containers::String GetSceneViewportDirectionalLightGizmoIconPath() {
|
||||
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
|
||||
|
||||
@@ -77,10 +77,15 @@ inline void RenderViewportInteractionSurface(
|
||||
|
||||
result.itemMin = ImGui::GetItemRectMin();
|
||||
result.itemMax = ImGui::GetItemRectMax();
|
||||
result.hovered = ImGui::IsItemHovered();
|
||||
result.clickedLeft = result.hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left, false);
|
||||
result.clickedRight = result.hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right, false);
|
||||
result.clickedMiddle = result.hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Middle, false);
|
||||
// Use explicit viewport-rect hit testing instead of ImGui item hover state. Dear ImGui ties
|
||||
// hovered items to click ownership, so a middle-button press that arrives before the panel
|
||||
// has established hover can fail to start panning in the live docked editor even though the
|
||||
// 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(
|
||||
|
||||
Reference in New Issue
Block a user