UI系统更新
This commit is contained in:
parent
b78e0275bb
commit
429a574205
@ -2138,7 +2138,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
if (!isGameView && viewportState.Hovered && !gizmoHovering && !gizmoUsing &&
|
||||
!ImGui::GetIO().WantCaptureMouse && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
const MetaCoreId pickedObjectId = SceneInteractionService_.PickGameObjectFromViewport(
|
||||
Scene_,
|
||||
*EditorContext_,
|
||||
sceneView,
|
||||
viewportState,
|
||||
EditorContext_->GetInput().GetCursorPosition()
|
||||
|
||||
@ -23,7 +23,9 @@
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace MetaCore {
|
||||
@ -50,6 +52,157 @@ glm::mat4 MetaCoreBuildWorldTransformMatrix(const MetaCoreScene& scene, MetaCore
|
||||
return worldMatrix;
|
||||
}
|
||||
|
||||
int MetaCoreGetHierarchyDepth(const MetaCoreScene& scene, MetaCoreId objectId) {
|
||||
int depth = 0;
|
||||
MetaCoreGameObject object = scene.FindGameObject(objectId);
|
||||
while (object && object.GetParentId() != 0) {
|
||||
++depth;
|
||||
object = scene.FindGameObject(object.GetParentId());
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
bool MetaCoreIsFiniteVec3(const glm::vec3& value) {
|
||||
return std::isfinite(value.x) && std::isfinite(value.y) && std::isfinite(value.z);
|
||||
}
|
||||
|
||||
struct MetaCorePickBounds {
|
||||
glm::vec3 Min{-0.5F, -0.5F, -0.5F};
|
||||
glm::vec3 Max{0.5F, 0.5F, 0.5F};
|
||||
};
|
||||
|
||||
bool MetaCoreIsValidPickBounds(const MetaCorePickBounds& bounds) {
|
||||
if (!MetaCoreIsFiniteVec3(bounds.Min) || !MetaCoreIsFiniteVec3(bounds.Max)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return bounds.Min.x <= bounds.Max.x &&
|
||||
bounds.Min.y <= bounds.Max.y &&
|
||||
bounds.Min.z <= bounds.Max.z;
|
||||
}
|
||||
|
||||
std::optional<MetaCorePickBounds> MetaCoreResolveMeshAssetPickBounds(
|
||||
const MetaCoreMeshRendererComponent& meshRenderer,
|
||||
const MetaCoreModelAssetDocument& modelDocument
|
||||
) {
|
||||
const MetaCoreMeshAssetDocument* meshAsset = nullptr;
|
||||
|
||||
if (meshRenderer.MeshAssetGuid.IsValid()) {
|
||||
const auto meshIterator = std::find_if(
|
||||
modelDocument.GeneratedMeshAssets.begin(),
|
||||
modelDocument.GeneratedMeshAssets.end(),
|
||||
[&](const MetaCoreMeshAssetDocument& candidate) {
|
||||
return candidate.AssetGuid == meshRenderer.MeshAssetGuid;
|
||||
}
|
||||
);
|
||||
if (meshIterator != modelDocument.GeneratedMeshAssets.end()) {
|
||||
meshAsset = &(*meshIterator);
|
||||
}
|
||||
}
|
||||
|
||||
if (meshAsset == nullptr &&
|
||||
meshRenderer.ModelNodeIndex >= 0 &&
|
||||
static_cast<std::size_t>(meshRenderer.ModelNodeIndex) < modelDocument.Nodes.size()) {
|
||||
const MetaCoreImportedGltfNodeDocument& node =
|
||||
modelDocument.Nodes[static_cast<std::size_t>(meshRenderer.ModelNodeIndex)];
|
||||
if (node.MeshIndex >= 0 && static_cast<std::size_t>(node.MeshIndex) < modelDocument.GeneratedMeshAssets.size()) {
|
||||
meshAsset = &modelDocument.GeneratedMeshAssets[static_cast<std::size_t>(node.MeshIndex)];
|
||||
}
|
||||
}
|
||||
|
||||
if (meshAsset == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MetaCorePickBounds bounds;
|
||||
bounds.Min = meshAsset->BoundingBoxMin;
|
||||
bounds.Max = meshAsset->BoundingBoxMax;
|
||||
if (!MetaCoreIsValidPickBounds(bounds)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
MetaCorePickBounds MetaCoreResolvePickBounds(
|
||||
const MetaCoreMeshRendererComponent& meshRenderer,
|
||||
const std::shared_ptr<MetaCoreIAssetEditingService>& assetEditingService,
|
||||
std::unordered_map<MetaCoreAssetGuid, std::optional<MetaCoreModelAssetDocument>, MetaCoreAssetGuidHasher>& modelAssetCache
|
||||
) {
|
||||
if (meshRenderer.MeshSource == MetaCoreMeshSourceKind::Asset &&
|
||||
meshRenderer.SourceModelAssetGuid.IsValid() &&
|
||||
assetEditingService != nullptr) {
|
||||
auto modelIterator = modelAssetCache.find(meshRenderer.SourceModelAssetGuid);
|
||||
if (modelIterator == modelAssetCache.end()) {
|
||||
modelIterator = modelAssetCache.emplace(
|
||||
meshRenderer.SourceModelAssetGuid,
|
||||
assetEditingService->LoadModelAsset(meshRenderer.SourceModelAssetGuid)
|
||||
).first;
|
||||
}
|
||||
|
||||
if (modelIterator->second.has_value()) {
|
||||
if (const auto resolvedBounds = MetaCoreResolveMeshAssetPickBounds(meshRenderer, *modelIterator->second)) {
|
||||
return *resolvedBounds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (meshRenderer.MeshSource == MetaCoreMeshSourceKind::Builtin &&
|
||||
meshRenderer.BuiltinMesh == MetaCoreBuiltinMeshType::Plane) {
|
||||
return MetaCorePickBounds{
|
||||
glm::vec3(-0.5F, -0.5F, -0.001F),
|
||||
glm::vec3(0.5F, 0.5F, 0.001F)
|
||||
};
|
||||
}
|
||||
|
||||
return MetaCorePickBounds{};
|
||||
}
|
||||
|
||||
bool MetaCoreIntersectLocalRayWithAabb(
|
||||
const glm::vec3& rayOrigin,
|
||||
const glm::vec3& rayDirection,
|
||||
const MetaCorePickBounds& bounds,
|
||||
float& outHitDistance
|
||||
) {
|
||||
if (!MetaCoreIsFiniteVec3(rayOrigin) || !MetaCoreIsFiniteVec3(rayDirection) || !MetaCoreIsValidPickBounds(bounds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float nearDistance = 0.0F;
|
||||
float farDistance = std::numeric_limits<float>::max();
|
||||
|
||||
for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
|
||||
const float rayAxisOrigin = rayOrigin[axisIndex];
|
||||
const float rayAxisDirection = rayDirection[axisIndex];
|
||||
|
||||
if (std::abs(rayAxisDirection) < 0.0001F) {
|
||||
if (rayAxisOrigin < bounds.Min[axisIndex] || rayAxisOrigin > bounds.Max[axisIndex]) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float axisNearDistance = (bounds.Min[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
float axisFarDistance = (bounds.Max[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
if (axisNearDistance > axisFarDistance) {
|
||||
std::swap(axisNearDistance, axisFarDistance);
|
||||
}
|
||||
|
||||
nearDistance = std::max(nearDistance, axisNearDistance);
|
||||
farDistance = std::min(farDistance, axisFarDistance);
|
||||
if (nearDistance > farDistance) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (farDistance < 0.0F) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outHitDistance = nearDistance >= 0.0F ? nearDistance : farDistance;
|
||||
return outHitDistance >= 0.0F;
|
||||
}
|
||||
|
||||
// 使用 MetaCoreTransformUtils.h 中的 MetaCoreApplyMatrixToTransform
|
||||
|
||||
ImGuizmo::OPERATION MetaCoreToImGuizmoOperation(MetaCoreGizmoOperation operation) {
|
||||
@ -87,11 +240,12 @@ void MetaCoreSceneInteractionService::ResetFrameState() {
|
||||
}
|
||||
|
||||
MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
|
||||
const MetaCoreScene& scene,
|
||||
const MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::vec2& cursorPosition
|
||||
) const {
|
||||
const MetaCoreScene& scene = editorContext.GetScene();
|
||||
if (viewportState.Width <= 1.0F || viewportState.Height <= 1.0F) {
|
||||
return 0;
|
||||
}
|
||||
@ -111,7 +265,11 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
|
||||
cursorPosition
|
||||
);
|
||||
const glm::vec3 rayOrigin = ray.Origin;
|
||||
const glm::vec3 rayDirection = ray.Direction;
|
||||
const float rayDirectionLength = glm::length(ray.Direction);
|
||||
if (!std::isfinite(rayDirectionLength) || rayDirectionLength <= 0.000001F) {
|
||||
return 0;
|
||||
}
|
||||
const glm::vec3 rayDirection = ray.Direction / rayDirectionLength;
|
||||
|
||||
std::unordered_map<MetaCoreId, glm::mat4> worldMatrixCache;
|
||||
const auto buildWorldMatrix = [&](const auto& self, MetaCoreId objectId) -> glm::mat4 {
|
||||
@ -139,54 +297,42 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
|
||||
|
||||
MetaCoreId bestObjectId = 0;
|
||||
float bestHitDistance = std::numeric_limits<float>::max();
|
||||
int bestHierarchyDepth = -1;
|
||||
const auto assetEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIAssetEditingService>();
|
||||
std::unordered_map<MetaCoreAssetGuid, std::optional<MetaCoreModelAssetDocument>, MetaCoreAssetGuidHasher> modelAssetCache;
|
||||
|
||||
for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) {
|
||||
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>() || !gameObject.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const MetaCoreMeshRendererComponent& meshRenderer = gameObject.GetComponent<MetaCoreMeshRendererComponent>();
|
||||
const MetaCorePickBounds localBounds = MetaCoreResolvePickBounds(meshRenderer, assetEditingService, modelAssetCache);
|
||||
const glm::mat4 worldMatrix = buildWorldMatrix(buildWorldMatrix, gameObject.GetId());
|
||||
const glm::mat4 inverseWorldMatrix = glm::inverse(worldMatrix);
|
||||
const glm::vec3 localRayOrigin = glm::vec3(inverseWorldMatrix * glm::vec4(rayOrigin, 1.0F));
|
||||
const glm::vec3 localRayDirection = glm::normalize(glm::vec3(inverseWorldMatrix * glm::vec4(rayDirection, 0.0F)));
|
||||
const glm::vec3 localRayDirectionRaw = glm::vec3(inverseWorldMatrix * glm::vec4(rayDirection, 0.0F));
|
||||
const float localRayDirectionLength = glm::length(localRayDirectionRaw);
|
||||
if (!std::isfinite(localRayDirectionLength) || localRayDirectionLength <= 0.000001F) {
|
||||
continue;
|
||||
}
|
||||
const glm::vec3 localRayDirection = localRayDirectionRaw / localRayDirectionLength;
|
||||
|
||||
constexpr glm::vec3 localBoundsMin(-0.5F, -0.5F, -0.5F);
|
||||
constexpr glm::vec3 localBoundsMax(0.5F, 0.5F, 0.5F);
|
||||
|
||||
float nearDistance = 0.0F;
|
||||
float farDistance = bestHitDistance;
|
||||
bool hit = true;
|
||||
|
||||
for (int axisIndex = 0; axisIndex < 3; ++axisIndex) {
|
||||
const float rayAxisOrigin = localRayOrigin[axisIndex];
|
||||
const float rayAxisDirection = localRayDirection[axisIndex];
|
||||
|
||||
if (std::abs(rayAxisDirection) < 0.0001F) {
|
||||
if (rayAxisOrigin < localBoundsMin[axisIndex] || rayAxisOrigin > localBoundsMax[axisIndex]) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
float localHitDistance = 0.0F;
|
||||
if (MetaCoreIntersectLocalRayWithAabb(localRayOrigin, localRayDirection, localBounds, localHitDistance)) {
|
||||
const glm::vec3 localHitPosition = localRayOrigin + localRayDirection * localHitDistance;
|
||||
const glm::vec3 worldHitPosition = glm::vec3(worldMatrix * glm::vec4(localHitPosition, 1.0F));
|
||||
const float hitDistance = glm::dot(worldHitPosition - rayOrigin, rayDirection);
|
||||
if (!std::isfinite(hitDistance) || hitDistance < 0.0F) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float axisNearDistance = (localBoundsMin[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
float axisFarDistance = (localBoundsMax[axisIndex] - rayAxisOrigin) / rayAxisDirection;
|
||||
if (axisNearDistance > axisFarDistance) {
|
||||
std::swap(axisNearDistance, axisFarDistance);
|
||||
}
|
||||
|
||||
nearDistance = std::max(nearDistance, axisNearDistance);
|
||||
farDistance = std::min(farDistance, axisFarDistance);
|
||||
if (nearDistance > farDistance) {
|
||||
hit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (hit && farDistance >= 0.0F) {
|
||||
const float hitDistance = nearDistance >= 0.0F ? nearDistance : farDistance;
|
||||
if (hitDistance >= 0.0F && hitDistance < bestHitDistance) {
|
||||
const int hierarchyDepth = MetaCoreGetHierarchyDepth(scene, gameObject.GetId());
|
||||
constexpr float hitDistanceTieEpsilon = 0.001F;
|
||||
if (hitDistance + hitDistanceTieEpsilon < bestHitDistance ||
|
||||
(std::abs(hitDistance - bestHitDistance) <= hitDistanceTieEpsilon && hierarchyDepth > bestHierarchyDepth)) {
|
||||
bestHitDistance = hitDistance;
|
||||
bestHierarchyDepth = hierarchyDepth;
|
||||
bestObjectId = gameObject.GetId();
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ public:
|
||||
void ResetFrameState();
|
||||
|
||||
[[nodiscard]] MetaCoreId PickGameObjectFromViewport(
|
||||
const MetaCoreScene& scene,
|
||||
const MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const glm::vec2& cursorPosition
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "MetaCoreEditor/MetaCoreEditorContext.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorModule.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
||||
#include "MetaCoreEditor/MetaCoreSceneInteractionService.h"
|
||||
#include "MetaCoreFoundation/MetaCoreGeneratedReflection.h"
|
||||
#include "MetaCoreFoundation/MetaCoreLogService.h"
|
||||
#include "MetaCoreFoundation/MetaCoreProject.h"
|
||||
@ -1596,6 +1597,56 @@ void MetaCoreTestCameraSelectionAndProjectionUtilities() {
|
||||
MetaCoreExpect(std::abs(projectedPoint->y - 50.0F) <= 0.001F, "WorldToScreen Y 应位于中心");
|
||||
}
|
||||
|
||||
void MetaCoreTestViewportPickingPrefersModelChildNode() {
|
||||
MetaCore::MetaCoreWindow window;
|
||||
MetaCore::MetaCoreRenderDevice renderDevice;
|
||||
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
|
||||
MetaCore::MetaCoreScene scene;
|
||||
MetaCore::MetaCoreLogService logService;
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
|
||||
MetaCore::MetaCoreEditorContext editorContext(
|
||||
window,
|
||||
renderDevice,
|
||||
viewportRenderer,
|
||||
scene,
|
||||
logService,
|
||||
moduleRegistry
|
||||
);
|
||||
|
||||
MetaCore::MetaCoreGameObject modelRoot = scene.CreateGameObject("Imported Model Root");
|
||||
modelRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 5.0F, 0.0F);
|
||||
modelRoot.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
||||
|
||||
MetaCore::MetaCoreGameObject childNode = scene.CreateGameObject("Imported Mesh Child", modelRoot.GetId());
|
||||
childNode.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
||||
|
||||
MetaCore::MetaCoreSceneView sceneView;
|
||||
sceneView.CameraPosition = glm::vec3(0.0F, 0.0F, 0.0F);
|
||||
sceneView.CameraTarget = glm::vec3(0.0F, 1.0F, 0.0F);
|
||||
sceneView.CameraUp = glm::vec3(0.0F, 0.0F, 1.0F);
|
||||
sceneView.VerticalFieldOfViewDegrees = 60.0F;
|
||||
|
||||
const MetaCore::MetaCoreSceneViewportState viewportState{
|
||||
0.0F,
|
||||
0.0F,
|
||||
100.0F,
|
||||
100.0F,
|
||||
true,
|
||||
true
|
||||
};
|
||||
|
||||
const MetaCore::MetaCoreSceneInteractionService interactionService;
|
||||
const MetaCore::MetaCoreId pickedObjectId = interactionService.PickGameObjectFromViewport(
|
||||
editorContext,
|
||||
sceneView,
|
||||
viewportState,
|
||||
glm::vec2(50.0F, 50.0F)
|
||||
);
|
||||
|
||||
MetaCoreExpect(pickedObjectId == childNode.GetId(), "视口拾取应优先选中模型内被点击的更具体子节点");
|
||||
}
|
||||
|
||||
void MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson() {
|
||||
const std::filesystem::path tempProjectRoot =
|
||||
std::filesystem::temp_directory_path() / "MetaCoreSceneRoundTripProject";
|
||||
@ -4926,6 +4977,8 @@ int main() {
|
||||
MetaCoreTestCameraComponentJsonRoundTripAndLegacyDefaults();
|
||||
std::cout << "[RUN] MetaCoreTestCameraSelectionAndProjectionUtilities..." << std::endl;
|
||||
MetaCoreTestCameraSelectionAndProjectionUtilities();
|
||||
std::cout << "[RUN] MetaCoreTestViewportPickingPrefersModelChildNode..." << std::endl;
|
||||
MetaCoreTestViewportPickingPrefersModelChildNode();
|
||||
std::cout << "[RUN] MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson..." << std::endl;
|
||||
MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson();
|
||||
std::cout << "[RUN] MetaCoreTestImportPipelineAndCook..." << std::endl;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user