UI系统更新
This commit is contained in:
parent
dcd4af87a2
commit
75d5fed693
Binary file not shown.
@ -2850,6 +2850,7 @@ void MetaCoreDrawUiRendererComponentInspector(MetaCoreEditorContext& editorConte
|
||||
};
|
||||
drawBoolField("可见", &MetaCoreUiRendererComponent::Visible);
|
||||
drawBoolField("接收输入", &MetaCoreUiRendererComponent::InputEnabled);
|
||||
drawBoolField("始终朝向相机", &MetaCoreUiRendererComponent::FaceCamera);
|
||||
|
||||
int layer = MetaCoreGetSharedSelectedValue<std::int32_t>(
|
||||
editorContext,
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "MetaCoreFoundation/MetaCorePackage.h"
|
||||
#include "MetaCoreFoundation/MetaCoreProject.h"
|
||||
#include "MetaCoreFoundation/MetaCoreReflection.h"
|
||||
#include "MetaCoreScene/MetaCoreTransformUtils.h"
|
||||
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
|
||||
#include "MetaCoreEditorCameraController.h"
|
||||
|
||||
@ -28,6 +29,7 @@
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
@ -1103,6 +1105,88 @@ void MetaCoreDrawUiDocumentPreview(
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreProjectUiLocalPoint(
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::mat4& worldMatrix,
|
||||
const glm::vec2& localPoint,
|
||||
ImVec2& screenPoint
|
||||
) {
|
||||
const glm::vec3 worldPoint = glm::vec3(worldMatrix * glm::vec4(localPoint.x, localPoint.y, 0.0F, 1.0F));
|
||||
return MetaCoreProjectWorldPointToViewport(sceneView, viewportState, worldPoint, screenPoint);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreProjectUiQuad(
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::mat4& worldMatrix,
|
||||
const glm::vec2& minPoint,
|
||||
const glm::vec2& maxPoint,
|
||||
std::array<ImVec2, 4>& screenPoints
|
||||
) {
|
||||
return MetaCoreProjectUiLocalPoint(sceneView, viewportState, worldMatrix, glm::vec2(minPoint.x, minPoint.y), screenPoints[0]) &&
|
||||
MetaCoreProjectUiLocalPoint(sceneView, viewportState, worldMatrix, glm::vec2(maxPoint.x, minPoint.y), screenPoints[1]) &&
|
||||
MetaCoreProjectUiLocalPoint(sceneView, viewportState, worldMatrix, glm::vec2(maxPoint.x, maxPoint.y), screenPoints[2]) &&
|
||||
MetaCoreProjectUiLocalPoint(sceneView, viewportState, worldMatrix, glm::vec2(minPoint.x, maxPoint.y), screenPoints[3]);
|
||||
}
|
||||
|
||||
void MetaCoreDrawProjectedUiDocumentPreview(
|
||||
ImDrawList& drawList,
|
||||
const MetaCoreUiDocument& document,
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::mat4& worldMatrix
|
||||
) {
|
||||
const float referenceWidth = static_cast<float>(std::max(1, document.ReferenceWidth));
|
||||
const float referenceHeight = static_cast<float>(std::max(1, document.ReferenceHeight));
|
||||
|
||||
for (const MetaCoreUiNodeDocument& node : document.Nodes) {
|
||||
if (!node.Visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float left = node.RectTransform.Position.x / referenceWidth - 0.5F;
|
||||
const float right = (node.RectTransform.Position.x + node.RectTransform.Size.x) / referenceWidth - 0.5F;
|
||||
const float top = 0.5F - node.RectTransform.Position.y / referenceHeight;
|
||||
const float bottom = 0.5F - (node.RectTransform.Position.y + node.RectTransform.Size.y) / referenceHeight;
|
||||
std::array<ImVec2, 4> points{};
|
||||
if (!MetaCoreProjectUiQuad(sceneView, viewportState, worldMatrix, glm::vec2(left, top), glm::vec2(right, bottom), points)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool isText = node.Type == MetaCoreUiNodeType::Text;
|
||||
const bool isButton = node.Type == MetaCoreUiNodeType::Button;
|
||||
const bool isImage = node.Type == MetaCoreUiNodeType::Image;
|
||||
if (!isText) {
|
||||
drawList.AddConvexPolyFilled(points.data(), static_cast<int>(points.size()), MetaCoreUiPreviewColor(node.Style.BackgroundColor, isButton ? 0.92F : 0.82F));
|
||||
}
|
||||
drawList.AddPolyline(
|
||||
points.data(),
|
||||
static_cast<int>(points.size()),
|
||||
isButton ? IM_COL32(130, 190, 255, 220) : IM_COL32(132, 145, 166, isText ? 90 : 170),
|
||||
ImDrawFlags_Closed,
|
||||
isButton ? 1.4F : 1.0F
|
||||
);
|
||||
|
||||
if (isImage) {
|
||||
drawList.AddLine(points[0], points[2], IM_COL32(210, 220, 235, 115), 1.0F);
|
||||
drawList.AddLine(points[1], points[3], IM_COL32(210, 220, 235, 115), 1.0F);
|
||||
}
|
||||
|
||||
const std::string label = !node.Text.empty() ? node.Text : (!node.Name.empty() ? node.Name : node.Id);
|
||||
if (!label.empty()) {
|
||||
ImVec2 textPoint{};
|
||||
const glm::vec2 textLocal(
|
||||
left + std::max(3.0F, node.Style.Padding.x) / referenceWidth,
|
||||
top - std::max(3.0F, node.Style.Padding.y) / referenceHeight
|
||||
);
|
||||
if (MetaCoreProjectUiLocalPoint(sceneView, viewportState, worldMatrix, textLocal, textPoint)) {
|
||||
drawList.AddText(textPoint, MetaCoreUiPreviewColor(node.Style.TextColor, 0.96F), label.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreDrawScreenSpaceUiPreviewOverlay(
|
||||
const MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreScene& scene,
|
||||
@ -1222,6 +1306,42 @@ void MetaCoreDrawWorldSpaceUiPreviewOverlay(
|
||||
}
|
||||
|
||||
const MetaCoreTransformComponent& transform = gameObject.GetComponent<MetaCoreTransformComponent>();
|
||||
const bool selected = editorContext.GetSelectedObjectId() == gameObject.GetId();
|
||||
const ImU32 fillColor = selected ? IM_COL32(44, 136, 110, 48) : IM_COL32(44, 136, 110, 30);
|
||||
const ImU32 strokeColor = selected ? IM_COL32(104, 224, 188, 230) : IM_COL32(104, 224, 188, 150);
|
||||
const std::optional<MetaCoreUiDocument> document = MetaCoreLoadUiPreviewDocument(editorContext, uiRenderer);
|
||||
const std::string label = "3D UI: " + gameObject.GetName();
|
||||
|
||||
if (!uiRenderer.FaceCamera) {
|
||||
const glm::mat4 worldMatrix = MetaCoreBuildTransformMatrix(transform);
|
||||
std::array<ImVec2, 4> panelPoints{};
|
||||
if (!MetaCoreProjectUiQuad(
|
||||
sceneView,
|
||||
viewportState,
|
||||
worldMatrix,
|
||||
glm::vec2(-0.5F, 0.5F),
|
||||
glm::vec2(0.5F, -0.5F),
|
||||
panelPoints
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drawList->AddConvexPolyFilled(panelPoints.data(), static_cast<int>(panelPoints.size()), fillColor);
|
||||
drawList->AddPolyline(panelPoints.data(), static_cast<int>(panelPoints.size()), strokeColor, ImDrawFlags_Closed, selected ? 2.0F : 1.2F);
|
||||
if (document.has_value()) {
|
||||
MetaCoreDrawProjectedUiDocumentPreview(*drawList, *document, sceneView, viewportState, worldMatrix);
|
||||
} else {
|
||||
drawList->AddText(ImVec2(panelPoints[0].x + 8.0F, panelPoints[0].y + 27.0F), IM_COL32(255, 196, 120, 230), "未能读取 UI 文档");
|
||||
}
|
||||
drawList->AddText(ImVec2(panelPoints[0].x + 8.0F, panelPoints[0].y + 7.0F), strokeColor, label.c_str());
|
||||
|
||||
ImVec2 centerPoint{};
|
||||
if (MetaCoreProjectWorldPointToViewport(sceneView, viewportState, transform.Position, centerPoint)) {
|
||||
drawList->AddCircleFilled(centerPoint, 3.0F, strokeColor, 12);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ImVec2 centerPoint{};
|
||||
if (!MetaCoreProjectWorldPointToViewport(sceneView, viewportState, transform.Position, centerPoint)) {
|
||||
continue;
|
||||
@ -1237,7 +1357,6 @@ void MetaCoreDrawWorldSpaceUiPreviewOverlay(
|
||||
}
|
||||
pixelsPerWorldUnit = viewportState.Height / (2.0F * distanceAlongView * std::tan(fovRadians * 0.5F));
|
||||
}
|
||||
|
||||
const float rawPreviewWidth = worldWidth * pixelsPerWorldUnit;
|
||||
const float rawPreviewHeight = worldHeight * pixelsPerWorldUnit;
|
||||
const float maxPreviewWidth = std::max(64.0F, viewportState.Width * 0.9F);
|
||||
@ -1247,21 +1366,16 @@ void MetaCoreDrawWorldSpaceUiPreviewOverlay(
|
||||
const float previewHeight = std::max(24.0F, rawPreviewHeight * fitScale);
|
||||
const ImVec2 rectMin(centerPoint.x - previewWidth * 0.5F, centerPoint.y - previewHeight * 0.5F);
|
||||
const ImVec2 rectMax(rectMin.x + previewWidth, rectMin.y + previewHeight);
|
||||
const bool selected = editorContext.GetSelectedObjectId() == gameObject.GetId();
|
||||
const ImU32 fillColor = selected ? IM_COL32(44, 136, 110, 48) : IM_COL32(44, 136, 110, 30);
|
||||
const ImU32 strokeColor = selected ? IM_COL32(104, 224, 188, 230) : IM_COL32(104, 224, 188, 150);
|
||||
|
||||
drawList->AddRectFilled(rectMin, rectMax, fillColor, 4.0F);
|
||||
drawList->AddRect(rectMin, rectMax, strokeColor, 4.0F, 0, selected ? 2.0F : 1.2F);
|
||||
|
||||
const std::optional<MetaCoreUiDocument> document = MetaCoreLoadUiPreviewDocument(editorContext, uiRenderer);
|
||||
if (document.has_value()) {
|
||||
MetaCoreDrawUiDocumentPreview(*drawList, *document, rectMin, ImVec2(previewWidth, previewHeight));
|
||||
} else {
|
||||
drawList->AddText(ImVec2(rectMin.x + 8.0F, rectMin.y + 27.0F), IM_COL32(255, 196, 120, 230), "未能读取 UI 文档");
|
||||
}
|
||||
|
||||
const std::string label = "3D UI: " + gameObject.GetName();
|
||||
drawList->AddText(ImVec2(rectMin.x + 8.0F, rectMin.y + 7.0F), strokeColor, label.c_str());
|
||||
drawList->AddCircleFilled(centerPoint, 3.0F, strokeColor, 12);
|
||||
}
|
||||
|
||||
@ -179,6 +179,7 @@ private:
|
||||
lhs.UiRenderer->RenderMode != rhs.UiRenderer->RenderMode ||
|
||||
lhs.UiRenderer->Visible != rhs.UiRenderer->Visible ||
|
||||
lhs.UiRenderer->InputEnabled != rhs.UiRenderer->InputEnabled ||
|
||||
lhs.UiRenderer->FaceCamera != rhs.UiRenderer->FaceCamera ||
|
||||
lhs.UiRenderer->Layer != rhs.UiRenderer->Layer ||
|
||||
!MetaCoreNearlyEqual(lhs.UiRenderer->PixelsPerUnit, rhs.UiRenderer->PixelsPerUnit) ||
|
||||
!MetaCoreNearlyEqualVec3(lhs.UiRenderer->WorldSize, rhs.UiRenderer->WorldSize)) {
|
||||
|
||||
@ -169,6 +169,7 @@ static json GameObjectDataToJson(const MetaCoreGameObjectData& obj, const MetaCo
|
||||
else if (uiField.Name == "RenderMode") uiJson["RenderMode"] = static_cast<int>(ui.RenderMode);
|
||||
else if (uiField.Name == "Visible") uiJson["Visible"] = ui.Visible;
|
||||
else if (uiField.Name == "InputEnabled") uiJson["InputEnabled"] = ui.InputEnabled;
|
||||
else if (uiField.Name == "FaceCamera") uiJson["FaceCamera"] = ui.FaceCamera;
|
||||
else if (uiField.Name == "Layer") uiJson["Layer"] = ui.Layer;
|
||||
else if (uiField.Name == "PixelsPerUnit") uiJson["PixelsPerUnit"] = ui.PixelsPerUnit;
|
||||
else if (uiField.Name == "WorldSize") uiJson["WorldSize"] = Vec3ToJson(ui.WorldSize);
|
||||
@ -337,6 +338,7 @@ static MetaCoreGameObjectData JsonToGameObjectData(const json& objJson, const Me
|
||||
else if (uiField.Name == "RenderMode" && uiJson.contains("RenderMode")) ui.RenderMode = static_cast<MetaCoreUiRenderMode>(uiJson["RenderMode"].get<int>());
|
||||
else if (uiField.Name == "Visible" && uiJson.contains("Visible")) ui.Visible = uiJson["Visible"].get<bool>();
|
||||
else if (uiField.Name == "InputEnabled" && uiJson.contains("InputEnabled")) ui.InputEnabled = uiJson["InputEnabled"].get<bool>();
|
||||
else if (uiField.Name == "FaceCamera" && uiJson.contains("FaceCamera")) ui.FaceCamera = uiJson["FaceCamera"].get<bool>();
|
||||
else if (uiField.Name == "Layer" && uiJson.contains("Layer")) ui.Layer = uiJson["Layer"].get<std::int32_t>();
|
||||
else if (uiField.Name == "PixelsPerUnit" && uiJson.contains("PixelsPerUnit")) ui.PixelsPerUnit = uiJson["PixelsPerUnit"].get<float>();
|
||||
else if (uiField.Name == "WorldSize" && uiJson.contains("WorldSize")) ui.WorldSize = JsonToVec3(uiJson["WorldSize"]);
|
||||
|
||||
@ -211,6 +211,8 @@ struct MetaCoreUiRendererComponent {
|
||||
MC_PROPERTY()
|
||||
bool InputEnabled = true;
|
||||
MC_PROPERTY()
|
||||
bool FaceCamera = false;
|
||||
MC_PROPERTY()
|
||||
std::int32_t Layer = 0;
|
||||
MC_PROPERTY()
|
||||
float PixelsPerUnit = 1000.0F;
|
||||
|
||||
@ -4090,6 +4090,7 @@ void MetaCoreTestUiRendererComponentSnapshotAndJson() {
|
||||
uiRenderer.RenderMode = MetaCore::MetaCoreUiRenderMode::WorldSpace;
|
||||
uiRenderer.Visible = false;
|
||||
uiRenderer.InputEnabled = false;
|
||||
uiRenderer.FaceCamera = true;
|
||||
uiRenderer.Layer = 42;
|
||||
uiRenderer.PixelsPerUnit = 512.0F;
|
||||
uiRenderer.WorldSize = glm::vec3(3.0F, 2.0F, 0.0F);
|
||||
@ -4105,6 +4106,7 @@ void MetaCoreTestUiRendererComponentSnapshotAndJson() {
|
||||
const auto& restoredRenderer = restoredObject.GetComponent<MetaCore::MetaCoreUiRendererComponent>();
|
||||
MetaCoreExpect(restoredRenderer.UiDocumentAssetGuid == uiGuid, "恢复快照后 UI 资产 Guid 应一致");
|
||||
MetaCoreExpect(restoredRenderer.RenderMode == MetaCore::MetaCoreUiRenderMode::WorldSpace, "恢复快照后渲染模式应一致");
|
||||
MetaCoreExpect(restoredRenderer.FaceCamera, "恢复快照后朝向相机设置应一致");
|
||||
MetaCoreExpect(restoredRenderer.Layer == 42, "恢复快照后层级应一致");
|
||||
MetaCoreExpectVec3Near(restoredRenderer.WorldSize, glm::vec3(3.0F, 2.0F, 0.0F), "恢复快照后世界尺寸应一致");
|
||||
|
||||
@ -4133,6 +4135,7 @@ void MetaCoreTestUiRendererComponentSnapshotAndJson() {
|
||||
MetaCoreExpect(loadedRenderer.RenderMode == MetaCore::MetaCoreUiRenderMode::WorldSpace, "JSON 往返后渲染模式应一致");
|
||||
MetaCoreExpect(!loadedRenderer.Visible, "JSON 往返后可见状态应一致");
|
||||
MetaCoreExpect(!loadedRenderer.InputEnabled, "JSON 往返后输入状态应一致");
|
||||
MetaCoreExpect(loadedRenderer.FaceCamera, "JSON 往返后朝向相机设置应一致");
|
||||
MetaCoreExpect(loadedRenderer.Layer == 42, "JSON 往返后层级应一致");
|
||||
MetaCoreExpect(std::abs(loadedRenderer.PixelsPerUnit - 512.0F) <= 0.0001F, "JSON 往返后 PPU 应一致");
|
||||
MetaCoreExpectVec3Near(loadedRenderer.WorldSize, glm::vec3(3.0F, 2.0F, 0.0F), "JSON 往返后世界尺寸应一致");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user