相机更新
This commit is contained in:
parent
a1cd721203
commit
bcec8b6a8d
@ -1530,7 +1530,7 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
|
||||
const bool multiEdit = editorContext.GetSelectedObjectIds().size() > 1;
|
||||
if (multiEdit) {
|
||||
ImGui::TextDisabled("Multi-edit %zu objects", editorContext.GetSelectedObjectIds().size());
|
||||
ImGui::TextDisabled("多选编辑 %zu 个对象", editorContext.GetSelectedObjectIds().size());
|
||||
}
|
||||
|
||||
float fov = MetaCoreGetSharedSelectedValue<float>(
|
||||
@ -1547,22 +1547,22 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
},
|
||||
[](float lhs, float rhs) { return MetaCoreNearlyEqual(lhs, rhs); }
|
||||
).has_value()) {
|
||||
ImGui::TextDisabled("FOV: Mixed");
|
||||
ImGui::TextDisabled("视野 FOV: 多值");
|
||||
}
|
||||
if (prefabObject != nullptr && prefabObject->Camera.has_value() &&
|
||||
!MetaCoreNearlyEqual(gameObject.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees, prefabObject->Camera->FieldOfViewDegrees)) {
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "FOV Overrides");
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "视野 FOV 已覆盖");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Apply##CameraFOV")) {
|
||||
if (ImGui::SmallButton("应用##CameraFOV")) {
|
||||
(void)MetaCoreApplySelectedPrefabField(editorContext, "Camera", "fov");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Revert##CameraFOV")) {
|
||||
if (ImGui::SmallButton("回退##CameraFOV")) {
|
||||
(void)MetaCoreRevertSelectedPrefabField(editorContext, "Camera", "fov");
|
||||
fov = prefabObject->Camera->FieldOfViewDegrees;
|
||||
}
|
||||
}
|
||||
ImGui::DragFloat("FOV", &fov, 0.2F, 1.0F, 179.0F);
|
||||
ImGui::DragFloat("视野 FOV", &fov, 0.2F, 1.0F, 179.0F);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
@ -1583,22 +1583,22 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
);
|
||||
float nearClip = sharedNearClip.value_or(gameObject.GetComponent<MetaCoreCameraComponent>().NearClip);
|
||||
if (multiEdit && !sharedNearClip.has_value()) {
|
||||
ImGui::TextDisabled("Near Clip: Mixed");
|
||||
ImGui::TextDisabled("近裁剪: 多值");
|
||||
}
|
||||
if (prefabObject != nullptr && prefabObject->Camera.has_value() &&
|
||||
!MetaCoreNearlyEqual(gameObject.GetComponent<MetaCoreCameraComponent>().NearClip, prefabObject->Camera->NearClip)) {
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "Near Clip Overrides");
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "近裁剪已覆盖");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Apply##CameraNear")) {
|
||||
if (ImGui::SmallButton("应用##CameraNear")) {
|
||||
(void)MetaCoreApplySelectedPrefabField(editorContext, "Camera", "near_clip");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Revert##CameraNear")) {
|
||||
if (ImGui::SmallButton("回退##CameraNear")) {
|
||||
(void)MetaCoreRevertSelectedPrefabField(editorContext, "Camera", "near_clip");
|
||||
nearClip = prefabObject->Camera->NearClip;
|
||||
}
|
||||
}
|
||||
ImGui::DragFloat("Near Clip", &nearClip, 0.01F, 0.001F, gameObject.GetComponent<MetaCoreCameraComponent>().FarClip);
|
||||
ImGui::DragFloat("近裁剪", &nearClip, 0.01F, 0.001F, gameObject.GetComponent<MetaCoreCameraComponent>().FarClip);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
@ -1619,22 +1619,22 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
);
|
||||
float farClip = sharedFarClip.value_or(gameObject.GetComponent<MetaCoreCameraComponent>().FarClip);
|
||||
if (multiEdit && !sharedFarClip.has_value()) {
|
||||
ImGui::TextDisabled("Far Clip: Mixed");
|
||||
ImGui::TextDisabled("远裁剪: 多值");
|
||||
}
|
||||
if (prefabObject != nullptr && prefabObject->Camera.has_value() &&
|
||||
!MetaCoreNearlyEqual(gameObject.GetComponent<MetaCoreCameraComponent>().FarClip, prefabObject->Camera->FarClip)) {
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "Far Clip Overrides");
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "远裁剪已覆盖");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Apply##CameraFar")) {
|
||||
if (ImGui::SmallButton("应用##CameraFar")) {
|
||||
(void)MetaCoreApplySelectedPrefabField(editorContext, "Camera", "far_clip");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Revert##CameraFar")) {
|
||||
if (ImGui::SmallButton("回退##CameraFar")) {
|
||||
(void)MetaCoreRevertSelectedPrefabField(editorContext, "Camera", "far_clip");
|
||||
farClip = prefabObject->Camera->FarClip;
|
||||
}
|
||||
}
|
||||
ImGui::DragFloat("Far Clip", &farClip, 0.1F, nearClip, 10000.0F);
|
||||
ImGui::DragFloat("远裁剪", &farClip, 0.1F, nearClip, 10000.0F);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
@ -1655,22 +1655,22 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
);
|
||||
bool isPrimary = sharedPrimary.value_or(gameObject.GetComponent<MetaCoreCameraComponent>().IsPrimary);
|
||||
if (multiEdit && !sharedPrimary.has_value()) {
|
||||
ImGui::TextDisabled("Primary: Mixed");
|
||||
ImGui::TextDisabled("主相机: 多值");
|
||||
}
|
||||
if (prefabObject != nullptr && prefabObject->Camera.has_value() &&
|
||||
gameObject.GetComponent<MetaCoreCameraComponent>().IsPrimary != prefabObject->Camera->IsPrimary) {
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "Primary Overrides");
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "主相机已覆盖");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Apply##CameraPrimary")) {
|
||||
if (ImGui::SmallButton("应用##CameraPrimary")) {
|
||||
(void)MetaCoreApplySelectedPrefabField(editorContext, "Camera", "primary");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Revert##CameraPrimary")) {
|
||||
if (ImGui::SmallButton("回退##CameraPrimary")) {
|
||||
(void)MetaCoreRevertSelectedPrefabField(editorContext, "Camera", "primary");
|
||||
isPrimary = prefabObject->Camera->IsPrimary;
|
||||
}
|
||||
}
|
||||
ImGui::Checkbox("Primary", &isPrimary);
|
||||
ImGui::Checkbox("主相机", &isPrimary);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", false);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
@ -1682,7 +1682,7 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
);
|
||||
}
|
||||
|
||||
// 获取所有选中相机的共享启用状态(支持多选和 Mixed 提示)
|
||||
// 获取所有选中相机的共享启用状态(支持多选和多值提示)
|
||||
const auto sharedEnabled = MetaCoreGetSharedSelectedValue<bool>(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> std::optional<bool> {
|
||||
@ -1692,23 +1692,23 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
);
|
||||
bool isEnabled = sharedEnabled.value_or(gameObject.GetComponent<MetaCoreCameraComponent>().Enabled);
|
||||
if (multiEdit && !sharedEnabled.has_value()) {
|
||||
ImGui::TextDisabled("Enabled: Mixed");
|
||||
ImGui::TextDisabled("启用: 多值");
|
||||
}
|
||||
// 处理 Prefab 覆盖标记以及应用和回退按钮
|
||||
if (prefabObject != nullptr && prefabObject->Camera.has_value() &&
|
||||
gameObject.GetComponent<MetaCoreCameraComponent>().Enabled != prefabObject->Camera->Enabled) {
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "Enabled Overrides");
|
||||
ImGui::TextColored(ImVec4(0.93F, 0.72F, 0.18F, 1.0F), "启用已覆盖");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Apply##CameraEnabled")) {
|
||||
if (ImGui::SmallButton("应用##CameraEnabled")) {
|
||||
(void)MetaCoreApplySelectedPrefabField(editorContext, "Camera", "enabled");
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::SmallButton("Revert##CameraEnabled")) {
|
||||
if (ImGui::SmallButton("回退##CameraEnabled")) {
|
||||
(void)MetaCoreRevertSelectedPrefabField(editorContext, "Camera", "enabled");
|
||||
isEnabled = prefabObject->Camera->Enabled;
|
||||
}
|
||||
}
|
||||
ImGui::Checkbox("Enabled##Camera", &isEnabled);
|
||||
ImGui::Checkbox("启用##Camera", &isEnabled);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", false);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
// 应用新的启用状态到所有选中的相机组件
|
||||
@ -1720,6 +1720,111 @@ void MetaCoreDrawCameraComponentInspector(MetaCoreEditorContext& editorContext,
|
||||
isEnabled
|
||||
);
|
||||
}
|
||||
|
||||
auto& camera = gameObject.GetComponent<MetaCoreCameraComponent>();
|
||||
|
||||
int projectionModeIndex = camera.ProjectionMode == MetaCoreCameraProjectionMode::Orthographic ? 1 : 0;
|
||||
const char* projectionItems[] = {"透视", "正交"};
|
||||
if (ImGui::Combo("投影模式", &projectionModeIndex, projectionItems, IM_ARRAYSIZE(projectionItems))) {
|
||||
const auto projectionMode = projectionModeIndex == 1
|
||||
? MetaCoreCameraProjectionMode::Orthographic
|
||||
: MetaCoreCameraProjectionMode::Perspective;
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", false);
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> MetaCoreCameraProjectionMode* {
|
||||
return selectedObject.HasComponent<MetaCoreCameraComponent>() ? &selectedObject.GetComponent<MetaCoreCameraComponent>().ProjectionMode : nullptr;
|
||||
},
|
||||
projectionMode
|
||||
);
|
||||
}
|
||||
|
||||
float orthographicSize = camera.OrthographicSize;
|
||||
ImGui::DragFloat("正交大小", &orthographicSize, 0.05F, 0.01F, 10000.0F);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> float* {
|
||||
return selectedObject.HasComponent<MetaCoreCameraComponent>() ? &selectedObject.GetComponent<MetaCoreCameraComponent>().OrthographicSize : nullptr;
|
||||
},
|
||||
orthographicSize
|
||||
);
|
||||
}
|
||||
|
||||
float depth = camera.Depth;
|
||||
ImGui::DragFloat("深度", &depth, 0.1F, -10000.0F, 10000.0F);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> float* {
|
||||
return selectedObject.HasComponent<MetaCoreCameraComponent>() ? &selectedObject.GetComponent<MetaCoreCameraComponent>().Depth : nullptr;
|
||||
},
|
||||
depth
|
||||
);
|
||||
}
|
||||
|
||||
std::uint32_t cullingMask = camera.CullingMask;
|
||||
ImGui::InputScalar(
|
||||
"剔除遮罩",
|
||||
ImGuiDataType_U32,
|
||||
&cullingMask,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"%08X",
|
||||
ImGuiInputTextFlags_CharsHexadecimal
|
||||
);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> std::uint32_t* {
|
||||
return selectedObject.HasComponent<MetaCoreCameraComponent>() ? &selectedObject.GetComponent<MetaCoreCameraComponent>().CullingMask : nullptr;
|
||||
},
|
||||
cullingMask
|
||||
);
|
||||
}
|
||||
|
||||
int clearFlagsIndex = static_cast<int>(camera.ClearFlags);
|
||||
const char* clearFlagItems[] = {"天空盒", "纯色", "仅深度", "不清屏"};
|
||||
if (ImGui::Combo("清屏方式", &clearFlagsIndex, clearFlagItems, IM_ARRAYSIZE(clearFlagItems))) {
|
||||
const auto clearFlags = static_cast<MetaCoreCameraClearFlags>(std::clamp(clearFlagsIndex, 0, 3));
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", false);
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> MetaCoreCameraClearFlags* {
|
||||
return selectedObject.HasComponent<MetaCoreCameraComponent>() ? &selectedObject.GetComponent<MetaCoreCameraComponent>().ClearFlags : nullptr;
|
||||
},
|
||||
clearFlags
|
||||
);
|
||||
}
|
||||
|
||||
glm::vec3 backgroundColor = camera.BackgroundColor;
|
||||
ImGui::ColorEdit3("背景色", glm::value_ptr(backgroundColor));
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> glm::vec3* {
|
||||
return selectedObject.HasComponent<MetaCoreCameraComponent>() ? &selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundColor : nullptr;
|
||||
},
|
||||
backgroundColor
|
||||
);
|
||||
}
|
||||
|
||||
float backgroundAlpha = camera.BackgroundAlpha;
|
||||
ImGui::DragFloat("背景 Alpha", &backgroundAlpha, 0.01F, 0.0F, 1.0F);
|
||||
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", true);
|
||||
if (ImGui::IsItemEdited()) {
|
||||
MetaCoreApplyValueToSelectedObjects(
|
||||
editorContext,
|
||||
[](MetaCoreGameObject& selectedObject) -> float* {
|
||||
return selectedObject.HasComponent<MetaCoreCameraComponent>() ? &selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundAlpha : nullptr;
|
||||
},
|
||||
backgroundAlpha
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreDrawLightComponentInspector(MetaCoreEditorContext& editorContext, MetaCoreGameObject& gameObject) {
|
||||
@ -2914,6 +3019,27 @@ template <typename T>
|
||||
// 应用相机的启用状态到 Prefab 模板中
|
||||
prefabObjectIterator->Camera->Enabled = selectedObject.GetComponent<MetaCoreCameraComponent>().Enabled;
|
||||
changed = true;
|
||||
} else if (fieldId == "projection_mode") {
|
||||
prefabObjectIterator->Camera->ProjectionMode = selectedObject.GetComponent<MetaCoreCameraComponent>().ProjectionMode;
|
||||
changed = true;
|
||||
} else if (fieldId == "orthographic_size") {
|
||||
prefabObjectIterator->Camera->OrthographicSize = selectedObject.GetComponent<MetaCoreCameraComponent>().OrthographicSize;
|
||||
changed = true;
|
||||
} else if (fieldId == "depth") {
|
||||
prefabObjectIterator->Camera->Depth = selectedObject.GetComponent<MetaCoreCameraComponent>().Depth;
|
||||
changed = true;
|
||||
} else if (fieldId == "culling_mask") {
|
||||
prefabObjectIterator->Camera->CullingMask = selectedObject.GetComponent<MetaCoreCameraComponent>().CullingMask;
|
||||
changed = true;
|
||||
} else if (fieldId == "clear_flags") {
|
||||
prefabObjectIterator->Camera->ClearFlags = selectedObject.GetComponent<MetaCoreCameraComponent>().ClearFlags;
|
||||
changed = true;
|
||||
} else if (fieldId == "background_color") {
|
||||
prefabObjectIterator->Camera->BackgroundColor = selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundColor;
|
||||
changed = true;
|
||||
} else if (fieldId == "background_alpha") {
|
||||
prefabObjectIterator->Camera->BackgroundAlpha = selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundAlpha;
|
||||
changed = true;
|
||||
}
|
||||
} else if (componentTypeId == "Light" && selectedObject.HasComponent<MetaCoreLightComponent>()) {
|
||||
if (!prefabObjectIterator->Light.has_value()) {
|
||||
@ -2997,6 +3123,34 @@ template <typename T>
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().Enabled = prefabObject->Camera->Enabled;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "projection_mode") {
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().ProjectionMode = prefabObject->Camera->ProjectionMode;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "orthographic_size") {
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().OrthographicSize = prefabObject->Camera->OrthographicSize;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "depth") {
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().Depth = prefabObject->Camera->Depth;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "culling_mask") {
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().CullingMask = prefabObject->Camera->CullingMask;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "clear_flags") {
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().ClearFlags = prefabObject->Camera->ClearFlags;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "background_color") {
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundColor = prefabObject->Camera->BackgroundColor;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "background_alpha") {
|
||||
selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundAlpha = prefabObject->Camera->BackgroundAlpha;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (componentTypeId == "Light" && prefabObject->Light.has_value()) {
|
||||
@ -6030,6 +6184,10 @@ public:
|
||||
bool withMeshRenderer,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
) override;
|
||||
[[nodiscard]] std::optional<MetaCoreId> CreateCamera(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
) override;
|
||||
[[nodiscard]] std::optional<MetaCoreId> InstantiateModelAsset(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreAssetGuid& assetGuid,
|
||||
@ -7720,6 +7878,52 @@ std::optional<MetaCoreId> MetaCoreBuiltinSceneEditingService::CreateGameObject(
|
||||
return createdObjectId;
|
||||
}
|
||||
|
||||
std::optional<MetaCoreId> MetaCoreBuiltinSceneEditingService::CreateCamera(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
) {
|
||||
MetaCoreId createdObjectId = 0;
|
||||
const bool created = editorContext.ExecuteSnapshotCommand("创建相机", [&]() {
|
||||
MetaCoreId parentId = forcedParentId.value_or(editorContext.GetActiveObjectId());
|
||||
if (parentId != 0 && !editorContext.GetScene().FindGameObject(parentId)) {
|
||||
parentId = 0;
|
||||
}
|
||||
|
||||
bool hasEnabledPrimaryCamera = false;
|
||||
for (const MetaCoreGameObject object : editorContext.GetScene().GetGameObjects()) {
|
||||
if (!object.HasComponent<MetaCoreCameraComponent>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const MetaCoreCameraComponent& camera = object.GetComponent<MetaCoreCameraComponent>();
|
||||
if (camera.Enabled && camera.IsPrimary) {
|
||||
hasEnabledPrimaryCamera = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MetaCoreGameObject cameraObject = editorContext.GetScene().CreateGameObject("Camera", parentId);
|
||||
MetaCoreCameraComponent& camera = cameraObject.AddComponent<MetaCoreCameraComponent>(MetaCoreCameraComponent{});
|
||||
camera.IsPrimary = !hasEnabledPrimaryCamera;
|
||||
|
||||
MetaCoreTransformComponent& transform = cameraObject.GetComponent<MetaCoreTransformComponent>();
|
||||
transform.Position = glm::vec3(0.0F, 2.2F, 6.5F);
|
||||
transform.RotationEulerDegrees = glm::vec3(-15.0F, 0.0F, 0.0F);
|
||||
|
||||
createdObjectId = cameraObject.GetId();
|
||||
editorContext.SelectOnly(createdObjectId);
|
||||
editorContext.SetSelectionAnchorId(createdObjectId);
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!created || createdObjectId == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
editorContext.AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "已创建相机");
|
||||
return createdObjectId;
|
||||
}
|
||||
|
||||
std::optional<MetaCoreId> MetaCoreBuiltinSceneEditingService::InstantiateModelAsset(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreAssetGuid& assetGuid,
|
||||
|
||||
@ -429,6 +429,17 @@ void MetaCoreApplyMaterialPreviewToScene(
|
||||
return sceneEditingService->CreateGameObject(editorContext, objectName, withMeshRenderer, forcedParentId);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<MetaCoreId> MetaCoreCreateCamera(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
) {
|
||||
const auto sceneEditingService = editorContext.GetModuleRegistry().ResolveService<MetaCoreISceneEditingService>();
|
||||
if (sceneEditingService == nullptr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return sceneEditingService->CreateCamera(editorContext, forcedParentId);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreDuplicateSelection(MetaCoreEditorContext& editorContext) {
|
||||
const auto clipboardService = editorContext.GetModuleRegistry().ResolveService<MetaCoreIClipboardService>();
|
||||
if (clipboardService == nullptr) {
|
||||
@ -1452,10 +1463,20 @@ struct MetaCoreGeneratedAssetChoice {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !MetaCoreNearlyEqual(instanceObject.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees, prefabObject.Camera->FieldOfViewDegrees) ||
|
||||
!MetaCoreNearlyEqual(instanceObject.GetComponent<MetaCoreCameraComponent>().NearClip, prefabObject.Camera->NearClip) ||
|
||||
!MetaCoreNearlyEqual(instanceObject.GetComponent<MetaCoreCameraComponent>().FarClip, prefabObject.Camera->FarClip) ||
|
||||
instanceObject.GetComponent<MetaCoreCameraComponent>().IsPrimary != prefabObject.Camera->IsPrimary;
|
||||
const MetaCoreCameraComponent& instanceCamera = instanceObject.GetComponent<MetaCoreCameraComponent>();
|
||||
const MetaCoreCameraComponent& prefabCamera = *prefabObject.Camera;
|
||||
return !MetaCoreNearlyEqual(instanceCamera.FieldOfViewDegrees, prefabCamera.FieldOfViewDegrees) ||
|
||||
!MetaCoreNearlyEqual(instanceCamera.NearClip, prefabCamera.NearClip) ||
|
||||
!MetaCoreNearlyEqual(instanceCamera.FarClip, prefabCamera.FarClip) ||
|
||||
instanceCamera.IsPrimary != prefabCamera.IsPrimary ||
|
||||
instanceCamera.Enabled != prefabCamera.Enabled ||
|
||||
instanceCamera.ProjectionMode != prefabCamera.ProjectionMode ||
|
||||
!MetaCoreNearlyEqual(instanceCamera.OrthographicSize, prefabCamera.OrthographicSize) ||
|
||||
!MetaCoreNearlyEqual(instanceCamera.Depth, prefabCamera.Depth) ||
|
||||
instanceCamera.CullingMask != prefabCamera.CullingMask ||
|
||||
instanceCamera.ClearFlags != prefabCamera.ClearFlags ||
|
||||
!MetaCoreNearlyEqualVec3(instanceCamera.BackgroundColor, prefabCamera.BackgroundColor) ||
|
||||
!MetaCoreNearlyEqual(instanceCamera.BackgroundAlpha, prefabCamera.BackgroundAlpha);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreHasLightOverride(const MetaCoreGameObject& instanceObject, const MetaCoreGameObjectData& prefabObject) {
|
||||
@ -1862,6 +1883,38 @@ void MetaCoreTrackInspectorEdit(MetaCoreEditorContext& editorContext, const char
|
||||
prefabObject.Camera->IsPrimary = selectedObject.GetComponent<MetaCoreCameraComponent>().IsPrimary;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "enabled") {
|
||||
prefabObject.Camera->Enabled = selectedObject.GetComponent<MetaCoreCameraComponent>().Enabled;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "projection_mode") {
|
||||
prefabObject.Camera->ProjectionMode = selectedObject.GetComponent<MetaCoreCameraComponent>().ProjectionMode;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "orthographic_size") {
|
||||
prefabObject.Camera->OrthographicSize = selectedObject.GetComponent<MetaCoreCameraComponent>().OrthographicSize;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "depth") {
|
||||
prefabObject.Camera->Depth = selectedObject.GetComponent<MetaCoreCameraComponent>().Depth;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "culling_mask") {
|
||||
prefabObject.Camera->CullingMask = selectedObject.GetComponent<MetaCoreCameraComponent>().CullingMask;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "clear_flags") {
|
||||
prefabObject.Camera->ClearFlags = selectedObject.GetComponent<MetaCoreCameraComponent>().ClearFlags;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "background_color") {
|
||||
prefabObject.Camera->BackgroundColor = selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundColor;
|
||||
return true;
|
||||
}
|
||||
if (fieldId == "background_alpha") {
|
||||
prefabObject.Camera->BackgroundAlpha = selectedObject.GetComponent<MetaCoreCameraComponent>().BackgroundAlpha;
|
||||
return true;
|
||||
}
|
||||
} else if (componentTypeId == "Light" && selectedObject.HasComponent<MetaCoreLightComponent>()) {
|
||||
if (!prefabObject.Light.has_value()) {
|
||||
prefabObject.Light = MetaCoreLightComponent{};
|
||||
@ -2077,6 +2130,9 @@ public:
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::MenuItem("创建相机")) {
|
||||
(void)MetaCoreCreateCamera(editorContext, std::nullopt);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("应用 Prefab", nullptr, false, editorContext.GetActiveObjectId() != 0)) {
|
||||
(void)MetaCoreApplySelectedPrefabInstance(editorContext);
|
||||
@ -2281,6 +2337,9 @@ private:
|
||||
if (ImGui::MenuItem("创建平面")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Plane", true, objectId);
|
||||
}
|
||||
if (ImGui::MenuItem("创建相机")) {
|
||||
(void)MetaCoreCreateCamera(editorContext, objectId);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (gameObject &&
|
||||
gameObject.HasComponent<MetaCoreMeshRendererComponent>() &&
|
||||
@ -2336,6 +2395,9 @@ private:
|
||||
if (ImGui::MenuItem("创建平面")) {
|
||||
(void)MetaCoreCreateObject(editorContext, "Plane", true, 0);
|
||||
}
|
||||
if (ImGui::MenuItem("创建相机")) {
|
||||
(void)MetaCoreCreateCamera(editorContext, 0);
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("重命名", "F2", false, editorContext.GetActiveObjectId() != 0)) {
|
||||
BeginRename(editorContext, editorContext.GetActiveObjectId());
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "MetaCoreEditor/MetaCoreBuiltinModules.h"
|
||||
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
||||
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
|
||||
#include "MetaCoreEditorCameraController.h"
|
||||
|
||||
#include <imgui.h>
|
||||
@ -349,7 +350,8 @@ void MetaCoreDrawInfernuxToolbar(MetaCoreEditorContext& editorContext, const ImV
|
||||
sceneView.CameraPosition.y,
|
||||
sceneView.CameraPosition.z
|
||||
);
|
||||
ImGui::TextDisabled("快捷键: Q/W/E/R, Z, F, A, G, O");
|
||||
ImGui::TextDisabled("场景相机: 右键+WASD/QE 飞行,Shift 加速");
|
||||
ImGui::TextDisabled("常用: 中键平移,滚轮缩放,F 聚焦");
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
@ -585,33 +587,184 @@ bool MetaCoreProjectWorldPointToViewport(
|
||||
return false;
|
||||
}
|
||||
|
||||
const glm::mat4 viewMatrix = glm::lookAt(
|
||||
sceneView.CameraPosition,
|
||||
sceneView.CameraTarget,
|
||||
sceneView.CameraUp
|
||||
const auto projectedPoint = MetaCoreWorldToScreenPoint(
|
||||
sceneView,
|
||||
MetaCoreViewportRect{viewportState.Left, viewportState.Top, viewportState.Width, viewportState.Height},
|
||||
worldPoint
|
||||
);
|
||||
const glm::mat4 projectionMatrix = glm::perspective(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
viewportState.Width / viewportState.Height,
|
||||
0.05F,
|
||||
500.0F
|
||||
);
|
||||
|
||||
const glm::vec4 clipPosition = projectionMatrix * viewMatrix * glm::vec4(worldPoint, 1.0F);
|
||||
if (clipPosition.w <= 0.0001F) {
|
||||
if (!projectedPoint.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const glm::vec3 ndc = glm::vec3(clipPosition) / clipPosition.w;
|
||||
if (ndc.z < -1.0F || ndc.z > 1.0F) {
|
||||
if (projectedPoint->z < -1.0F || projectedPoint->z > 1.0F) {
|
||||
return false;
|
||||
}
|
||||
|
||||
screenPoint.x = viewportState.Left + ((ndc.x + 1.0F) * 0.5F) * viewportState.Width;
|
||||
screenPoint.y = viewportState.Top + ((1.0F - ndc.y) * 0.5F) * viewportState.Height;
|
||||
screenPoint.x = projectedPoint->x;
|
||||
screenPoint.y = projectedPoint->y;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct MetaCoreGameCameraSelectorItem {
|
||||
MetaCoreId ObjectId = 0;
|
||||
std::string Label{};
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<MetaCoreGameCameraSelectorItem> MetaCoreBuildGameCameraSelectorItems(
|
||||
const MetaCoreScene& scene
|
||||
) {
|
||||
std::vector<MetaCoreGameCameraSelectorItem> items;
|
||||
for (const MetaCoreId objectId : scene.BuildHierarchyPreorder()) {
|
||||
const MetaCoreGameObject object = scene.FindGameObject(objectId);
|
||||
if (!object || !object.HasComponent<MetaCoreCameraComponent>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const MetaCoreCameraComponent& camera = object.GetComponent<MetaCoreCameraComponent>();
|
||||
if (!camera.Enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char suffix[96]{};
|
||||
std::snprintf(
|
||||
suffix,
|
||||
sizeof(suffix),
|
||||
" #%llu Depth %.2f%s",
|
||||
static_cast<unsigned long long>(object.GetId()),
|
||||
static_cast<double>(camera.Depth),
|
||||
camera.IsPrimary ? " 主相机" : ""
|
||||
);
|
||||
|
||||
MetaCoreGameCameraSelectorItem item;
|
||||
item.ObjectId = object.GetId();
|
||||
item.Label = object.GetName() + suffix;
|
||||
items.push_back(std::move(item));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MetaCoreGetGameCameraPreviewLabel(
|
||||
const MetaCoreScene& scene,
|
||||
const MetaCoreSceneRenderSyncSnapshot& snapshot
|
||||
) {
|
||||
if (!snapshot.PrimaryCamera.has_value()) {
|
||||
return "没有可用相机";
|
||||
}
|
||||
|
||||
const MetaCoreGameObject cameraObject = scene.FindGameObject(snapshot.PrimaryCamera->ObjectId);
|
||||
if (!cameraObject) {
|
||||
return "相机已失效";
|
||||
}
|
||||
|
||||
return cameraObject.GetName() + " #" + std::to_string(static_cast<unsigned long long>(cameraObject.GetId()));
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreSetGamePrimaryCamera(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreId cameraObjectId
|
||||
) {
|
||||
return editorContext.ExecuteSnapshotCommand("切换游戏相机", [&]() {
|
||||
MetaCoreScene& scene = editorContext.GetScene();
|
||||
bool foundTargetCamera = false;
|
||||
bool changed = false;
|
||||
|
||||
for (MetaCoreGameObject object : scene.GetGameObjects()) {
|
||||
if (!object.HasComponent<MetaCoreCameraComponent>()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MetaCoreCameraComponent& camera = object.GetComponent<MetaCoreCameraComponent>();
|
||||
const bool shouldBePrimary = object.GetId() == cameraObjectId;
|
||||
if (shouldBePrimary) {
|
||||
if (!camera.Enabled) {
|
||||
return false;
|
||||
}
|
||||
foundTargetCamera = true;
|
||||
}
|
||||
|
||||
if (camera.IsPrimary != shouldBePrimary) {
|
||||
camera.IsPrimary = shouldBePrimary;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundTargetCamera || !changed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
scene.IncrementRevision();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void MetaCoreDrawGameCameraSelector(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreSceneRenderSyncSnapshot& snapshot,
|
||||
const ImVec2& viewportPos,
|
||||
const ImVec2& viewportSize
|
||||
) {
|
||||
if (viewportSize.x < 160.0F || viewportSize.y < 80.0F) {
|
||||
return;
|
||||
}
|
||||
|
||||
MetaCoreScene& scene = editorContext.GetScene();
|
||||
const std::vector<MetaCoreGameCameraSelectorItem> items = MetaCoreBuildGameCameraSelectorItems(scene);
|
||||
const MetaCoreId currentCameraId = snapshot.PrimaryCamera.has_value() ? snapshot.PrimaryCamera->ObjectId : 0;
|
||||
const std::string previewLabel = MetaCoreGetGameCameraPreviewLabel(scene, snapshot);
|
||||
|
||||
constexpr float selectorWidth = 300.0F;
|
||||
const ImVec2 selectorPos(
|
||||
viewportPos.x + std::max(8.0F, viewportSize.x - selectorWidth - 12.0F),
|
||||
viewportPos.y + 12.0F
|
||||
);
|
||||
|
||||
ImGui::SetNextWindowPos(selectorPos, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(selectorWidth, 0.0F), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowBgAlpha(0.88F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0F);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(10.0F, 8.0F));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.10F, 0.10F, 0.11F, 0.88F));
|
||||
|
||||
constexpr ImGuiWindowFlags windowFlags =
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoDocking |
|
||||
ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_AlwaysAutoResize;
|
||||
|
||||
if (ImGui::Begin("##MetaCoreGameCameraSelector", nullptr, windowFlags)) {
|
||||
ImGui::TextUnformatted("游戏相机");
|
||||
ImGui::SetNextItemWidth(selectorWidth - 20.0F);
|
||||
if (items.empty()) {
|
||||
ImGui::BeginDisabled();
|
||||
if (ImGui::BeginCombo("##MetaCoreGameCameraCombo", "没有启用的相机")) {
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
} else if (ImGui::BeginCombo("##MetaCoreGameCameraCombo", previewLabel.c_str())) {
|
||||
for (const MetaCoreGameCameraSelectorItem& item : items) {
|
||||
const bool selected = item.ObjectId == currentCameraId;
|
||||
if (ImGui::Selectable(item.Label.c_str(), selected)) {
|
||||
(void)MetaCoreSetGamePrimaryCamera(editorContext, item.ObjectId);
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (currentCameraId != 0 && ImGui::SmallButton("选中当前游戏相机")) {
|
||||
editorContext.SetSelectedObjectId(currentCameraId);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar(2);
|
||||
}
|
||||
|
||||
void MetaCoreDrawWorldOriginOverlay(
|
||||
const MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreSceneView& sceneView,
|
||||
@ -1415,6 +1568,10 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
bool gizmoCanvasOpen = true;
|
||||
bool gizmoUsing = false;
|
||||
bool gizmoHovering = false;
|
||||
MetaCoreSceneRenderSyncSnapshot gameViewSnapshot;
|
||||
bool shouldDrawGameCameraSelector = false;
|
||||
ImVec2 gameCameraSelectorViewportPos(0.0F, 0.0F);
|
||||
ImVec2 gameCameraSelectorViewportSize(0.0F, 0.0F);
|
||||
|
||||
if (ImGui::Begin("MetaCoreSceneGizmoCanvas", &gizmoCanvasOpen, gizmoCanvasFlags)) {
|
||||
// 2. 拿到最精确的屏幕绝对位置和画布大小
|
||||
@ -1441,9 +1598,13 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
mousePosition.y >= viewportState.Top &&
|
||||
mousePosition.y <= viewportState.Top + viewportState.Height;
|
||||
viewportState.Focused = viewportState.Hovered && !ImGui::GetIO().WantCaptureMouse;
|
||||
const bool isGameView = activeTab == 1;
|
||||
|
||||
// 处理聚焦快捷键
|
||||
if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && EditorContext_->GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
|
||||
if (!isGameView &&
|
||||
viewportState.Focused &&
|
||||
!ImGui::GetIO().WantCaptureKeyboard &&
|
||||
EditorContext_->GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
|
||||
if (MetaCoreGameObject selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject) {
|
||||
EditorContext_->GetCameraController().FocusGameObject(selectedObject);
|
||||
}
|
||||
@ -1456,14 +1617,34 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
|
||||
// 5. 更新相机并构建 SceneView (在渲染之前!)
|
||||
gizmoUsing = ImGuizmo::IsUsing();
|
||||
if (!gizmoUsing) {
|
||||
EditorContext_->GetCameraController().Update(viewportState, EditorContext_->GetInput());
|
||||
if (!isGameView && !gizmoUsing) {
|
||||
EditorContext_->GetCameraController().Update(
|
||||
viewportState,
|
||||
EditorContext_->GetInput(),
|
||||
Window_.GetDeltaSeconds()
|
||||
);
|
||||
}
|
||||
MetaCoreSceneView sceneView = EditorContext_->GetCameraController().BuildSceneView();
|
||||
sceneView.SelectedObjectId = EditorContext_->GetSelectedObjectId();
|
||||
bool hasGameCamera = true;
|
||||
if (isGameView) {
|
||||
static MetaCoreSceneRenderSync gameViewRenderSync;
|
||||
gameViewSnapshot = gameViewRenderSync.BuildSnapshot(Scene_);
|
||||
hasGameCamera = MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera(gameViewSnapshot, sceneView);
|
||||
sceneView.SelectedObjectId = EditorContext_->GetSelectedObjectId();
|
||||
shouldDrawGameCameraSelector = true;
|
||||
gameCameraSelectorViewportPos = viewportPos;
|
||||
gameCameraSelectorViewportSize = viewportSize;
|
||||
}
|
||||
|
||||
// 6. 驱动 Filament 渲染
|
||||
ViewportRenderer_.RenderSceneToViewport(Scene_, sceneView);
|
||||
if (isGameView) {
|
||||
if (hasGameCamera) {
|
||||
ViewportRenderer_.RenderSnapshotToViewport(gameViewSnapshot, &Scene_, sceneView);
|
||||
}
|
||||
} else {
|
||||
ViewportRenderer_.RenderSceneToViewport(Scene_, sceneView);
|
||||
}
|
||||
|
||||
// 7. 绘制画面 (应用 UV 翻转:Filament Y轴起点在底部,ImGui 在顶部)
|
||||
void* texPtr = ViewportRenderer_.GetFilamentTexturePointer();
|
||||
@ -1484,18 +1665,32 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
lastDebugPlayModeState = currentPlayModeState;
|
||||
}
|
||||
}
|
||||
if (texPtr) {
|
||||
if (isGameView && !hasGameCamera) {
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const ImVec2 viewportMax(viewportPos.x + viewportSize.x, viewportPos.y + viewportSize.y);
|
||||
drawList->AddRectFilled(viewportPos, viewportMax, IM_COL32(25, 26, 30, 255));
|
||||
const char* message = "没有可用游戏相机";
|
||||
const ImVec2 textSize = ImGui::CalcTextSize(message);
|
||||
drawList->AddText(
|
||||
ImVec2(
|
||||
viewportPos.x + (viewportSize.x - textSize.x) * 0.5F,
|
||||
viewportPos.y + (viewportSize.y - textSize.y) * 0.5F
|
||||
),
|
||||
IM_COL32(220, 220, 220, 255),
|
||||
message
|
||||
);
|
||||
} else if (texPtr) {
|
||||
ImGui::Image(texPtr, viewportSize, ImVec2(0, 1), ImVec2(1, 0));
|
||||
}
|
||||
|
||||
// 7.5. 视口拖拽接收器:从项目面板拖拽资产进入 3D 视口时,在此处响应放置并完成实例化
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (!isGameView && ImGui::BeginDragDropTarget()) {
|
||||
(void)MetaCoreHandleProjectAssetDrop(*EditorContext_, std::nullopt);
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
// 8. 绘制 Gizmo (此时坐标和矩阵已完全对齐)
|
||||
if (GMetaCoreEnableImGuizmo) {
|
||||
if (!isGameView && GMetaCoreEnableImGuizmo) {
|
||||
SceneInteractionService_.HandleGizmoManipulation(*EditorContext_);
|
||||
gizmoHovering = ImGuizmo::IsOver();
|
||||
gizmoUsing = ImGuizmo::IsUsing();
|
||||
@ -1528,16 +1723,20 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
}
|
||||
|
||||
// 绘制 Overlays
|
||||
MetaCoreDrawViewportGridOverlay(*EditorContext_, viewportState);
|
||||
MetaCoreDrawWorldOriginOverlay(*EditorContext_, sceneView, viewportState);
|
||||
MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||
MetaCoreDrawSelectionHierarchyOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||
if (!isGameView) {
|
||||
MetaCoreDrawViewportGridOverlay(*EditorContext_, viewportState);
|
||||
MetaCoreDrawWorldOriginOverlay(*EditorContext_, sceneView, viewportState);
|
||||
MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||
MetaCoreDrawSelectionHierarchyOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||
}
|
||||
|
||||
// 启用资产拖拽到 3D 场景视口的高亮响应遮罩,并显示操作提示
|
||||
MetaCoreDrawSceneViewportDropTarget(*EditorContext_, viewportPos, viewportSize);
|
||||
if (!isGameView) {
|
||||
MetaCoreDrawSceneViewportDropTarget(*EditorContext_, viewportPos, viewportSize);
|
||||
}
|
||||
|
||||
// 处理拾取
|
||||
if (viewportState.Hovered && !gizmoHovering && !gizmoUsing &&
|
||||
if (!isGameView && viewportState.Hovered && !gizmoHovering && !gizmoUsing &&
|
||||
!ImGui::GetIO().WantCaptureMouse && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
const MetaCoreId pickedObjectId = SceneInteractionService_.PickGameObjectFromViewport(
|
||||
Scene_,
|
||||
@ -1552,7 +1751,17 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
SceneInteractionService_.HandleGizmoEndUse(*EditorContext_, gizmoUsing);
|
||||
SceneInteractionService_.DrawViewportToolbar(*EditorContext_);
|
||||
if (activeTab == 1 && shouldDrawGameCameraSelector) {
|
||||
MetaCoreDrawGameCameraSelector(
|
||||
*EditorContext_,
|
||||
gameViewSnapshot,
|
||||
gameCameraSelectorViewportPos,
|
||||
gameCameraSelectorViewportSize
|
||||
);
|
||||
}
|
||||
if (activeTab == 0) {
|
||||
SceneInteractionService_.DrawViewportToolbar(*EditorContext_);
|
||||
}
|
||||
// --- 补丁三重构结束 ---
|
||||
} else {
|
||||
SceneInteractionService_.ResetFrameState();
|
||||
|
||||
@ -12,7 +12,11 @@
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
void MetaCoreEditorCameraController::Update(const MetaCoreSceneViewportState& viewportState, const MetaCoreInput& input) {
|
||||
void MetaCoreEditorCameraController::Update(
|
||||
const MetaCoreSceneViewportState& viewportState,
|
||||
const MetaCoreInput& input,
|
||||
float deltaSeconds
|
||||
) {
|
||||
if (!viewportState.Hovered && !viewportState.Focused) {
|
||||
return;
|
||||
}
|
||||
@ -57,6 +61,38 @@ void MetaCoreEditorCameraController::Update(const MetaCoreSceneViewportState& vi
|
||||
Target_ -= right * io.MouseDelta.x * panScale;
|
||||
Target_ += up * io.MouseDelta.y * panScale;
|
||||
}
|
||||
|
||||
if (viewportState.Focused && !altDown && ImGui::IsMouseDown(ImGuiMouseButton_Right)) {
|
||||
glm::vec3 movement(0.0F);
|
||||
const glm::vec3 forward = GetForwardDirection();
|
||||
const glm::vec3 right = GetRightDirection();
|
||||
constexpr glm::vec3 worldUp(0.0F, 0.0F, 1.0F);
|
||||
|
||||
if (input.IsKeyDown(MetaCoreInputKey::MoveForward)) {
|
||||
movement += forward;
|
||||
}
|
||||
if (input.IsKeyDown(MetaCoreInputKey::MoveBackward)) {
|
||||
movement -= forward;
|
||||
}
|
||||
if (input.IsKeyDown(MetaCoreInputKey::MoveRight)) {
|
||||
movement += right;
|
||||
}
|
||||
if (input.IsKeyDown(MetaCoreInputKey::MoveLeft)) {
|
||||
movement -= right;
|
||||
}
|
||||
if (input.IsKeyDown(MetaCoreInputKey::MoveUp)) {
|
||||
movement += worldUp;
|
||||
}
|
||||
if (input.IsKeyDown(MetaCoreInputKey::MoveDown)) {
|
||||
movement -= worldUp;
|
||||
}
|
||||
|
||||
if (glm::length(movement) > 0.0001F) {
|
||||
const float clampedDeltaSeconds = std::clamp(deltaSeconds, 0.0F, 0.1F);
|
||||
const float speed = MoveSpeed_ * (input.IsKeyDown(MetaCoreInputKey::Shift) ? MoveSpeedBoost_ : 1.0F);
|
||||
Target_ += glm::normalize(movement) * speed * clampedDeltaSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreEditorCameraController::FocusGameObject(const MetaCoreGameObject& gameObject) {
|
||||
|
||||
@ -12,7 +12,7 @@ class MetaCoreGameObject;
|
||||
|
||||
class MetaCoreEditorCameraController {
|
||||
public:
|
||||
void Update(const MetaCoreSceneViewportState& viewportState, const MetaCoreInput& input);
|
||||
void Update(const MetaCoreSceneViewportState& viewportState, const MetaCoreInput& input, float deltaSeconds);
|
||||
void FocusGameObject(const MetaCoreGameObject& gameObject);
|
||||
void FocusBounds(const glm::vec3& center, float radius);
|
||||
[[nodiscard]] MetaCoreSceneView BuildSceneView() const;
|
||||
@ -29,6 +29,8 @@ private:
|
||||
float YawDegrees_ = 45.0F;
|
||||
float PitchDegrees_ = -35.0F;
|
||||
float FieldOfViewDegrees_ = 60.0F;
|
||||
float MoveSpeed_ = 6.0F;
|
||||
float MoveSpeedBoost_ = 4.0F;
|
||||
};
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -103,6 +103,21 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreCameraComponentEqual(const MetaCoreCameraComponent& lhs, const MetaCoreCameraComponent& rhs) {
|
||||
return MetaCoreNearlyEqual(lhs.FieldOfViewDegrees, rhs.FieldOfViewDegrees) &&
|
||||
MetaCoreNearlyEqual(lhs.NearClip, rhs.NearClip) &&
|
||||
MetaCoreNearlyEqual(lhs.FarClip, rhs.FarClip) &&
|
||||
lhs.IsPrimary == rhs.IsPrimary &&
|
||||
lhs.Enabled == rhs.Enabled &&
|
||||
lhs.ProjectionMode == rhs.ProjectionMode &&
|
||||
MetaCoreNearlyEqual(lhs.OrthographicSize, rhs.OrthographicSize) &&
|
||||
MetaCoreNearlyEqual(lhs.Depth, rhs.Depth) &&
|
||||
lhs.CullingMask == rhs.CullingMask &&
|
||||
lhs.ClearFlags == rhs.ClearFlags &&
|
||||
MetaCoreNearlyEqualVec3(lhs.BackgroundColor, rhs.BackgroundColor) &&
|
||||
MetaCoreNearlyEqual(lhs.BackgroundAlpha, rhs.BackgroundAlpha);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MetaCoreGameObjectEqual(const MetaCoreGameObjectData& lhs, const MetaCoreGameObjectData& rhs) {
|
||||
if (lhs.Id != rhs.Id || lhs.ParentId != rhs.ParentId || lhs.Name != rhs.Name || !MetaCoreTransformEqual(lhs.Transform, rhs.Transform)) {
|
||||
return false;
|
||||
@ -112,11 +127,7 @@ private:
|
||||
return false;
|
||||
}
|
||||
if (lhs.Camera.has_value()) {
|
||||
if (!MetaCoreNearlyEqual(lhs.Camera->FieldOfViewDegrees, rhs.Camera->FieldOfViewDegrees) ||
|
||||
!MetaCoreNearlyEqual(lhs.Camera->NearClip, rhs.Camera->NearClip) ||
|
||||
!MetaCoreNearlyEqual(lhs.Camera->FarClip, rhs.Camera->FarClip) ||
|
||||
lhs.Camera->IsPrimary != rhs.Camera->IsPrimary ||
|
||||
lhs.Camera->Enabled != rhs.Camera->Enabled) {
|
||||
if (!MetaCoreCameraComponentEqual(*lhs.Camera, *rhs.Camera)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
||||
#include "MetaCoreEditorCameraController.h"
|
||||
#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h"
|
||||
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
|
||||
#include "MetaCoreScene/MetaCoreScene.h"
|
||||
#include "MetaCoreScene/MetaCoreGameObject.h"
|
||||
#include "MetaCoreScene/MetaCoreComponents.h"
|
||||
@ -104,34 +105,13 @@ MetaCoreId MetaCoreSceneInteractionService::PickGameObjectFromViewport(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const glm::mat4 viewMatrix = glm::lookAt(
|
||||
sceneView.CameraPosition,
|
||||
sceneView.CameraTarget,
|
||||
sceneView.CameraUp
|
||||
const MetaCoreCameraRay ray = MetaCoreScreenPointToRay(
|
||||
sceneView,
|
||||
MetaCoreViewportRect{viewportState.Left, viewportState.Top, viewportState.Width, viewportState.Height},
|
||||
cursorPosition
|
||||
);
|
||||
const glm::mat4 projectionMatrix = glm::perspective(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
viewportState.Width / viewportState.Height,
|
||||
0.05F,
|
||||
500.0F
|
||||
);
|
||||
|
||||
const float normalizedX = (localCursor.x / viewportState.Width) * 2.0F - 1.0F;
|
||||
const float normalizedY = 1.0F - (localCursor.y / viewportState.Height) * 2.0F;
|
||||
const glm::vec4 rayStartClip(normalizedX, normalizedY, -1.0F, 1.0F);
|
||||
const glm::vec4 rayEndClip(normalizedX, normalizedY, 1.0F, 1.0F);
|
||||
const glm::mat4 inverseViewProjection = glm::inverse(projectionMatrix * viewMatrix);
|
||||
glm::vec4 rayStartWorld = inverseViewProjection * rayStartClip;
|
||||
glm::vec4 rayEndWorld = inverseViewProjection * rayEndClip;
|
||||
if (std::abs(rayStartWorld.w) < 0.0001F || std::abs(rayEndWorld.w) < 0.0001F) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rayStartWorld /= rayStartWorld.w;
|
||||
rayEndWorld /= rayEndWorld.w;
|
||||
|
||||
const glm::vec3 rayOrigin(rayStartWorld);
|
||||
const glm::vec3 rayDirection = glm::normalize(glm::vec3(rayEndWorld - rayStartWorld));
|
||||
const glm::vec3 rayOrigin = ray.Origin;
|
||||
const glm::vec3 rayDirection = ray.Direction;
|
||||
|
||||
std::unordered_map<MetaCoreId, glm::mat4> worldMatrixCache;
|
||||
const auto buildWorldMatrix = [&](const auto& self, MetaCoreId objectId) -> glm::mat4 {
|
||||
@ -362,15 +342,27 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
|
||||
const int h = static_cast<int>(viewportState.Height);
|
||||
const float gizmoAspect = (h > 0) ? (static_cast<float>(w) / static_cast<float>(h)) : 1.0f;
|
||||
const glm::mat4 gizmoViewMatrix = glm::lookAt(sceneView.CameraPosition, sceneView.CameraTarget, sceneView.CameraUp);
|
||||
const glm::mat4 gizmoProjectionMatrix = glm::perspective(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
gizmoAspect, 0.1F, 1000.0F
|
||||
);
|
||||
const glm::mat4 gizmoProjectionMatrix =
|
||||
sceneView.ProjectionMode == MetaCoreCameraProjectionMode::Orthographic
|
||||
? glm::ortho(
|
||||
-sceneView.OrthographicSize * gizmoAspect,
|
||||
sceneView.OrthographicSize * gizmoAspect,
|
||||
-sceneView.OrthographicSize,
|
||||
sceneView.OrthographicSize,
|
||||
sceneView.NearClip,
|
||||
sceneView.FarClip
|
||||
)
|
||||
: glm::perspective(
|
||||
glm::radians(sceneView.VerticalFieldOfViewDegrees),
|
||||
gizmoAspect,
|
||||
sceneView.NearClip,
|
||||
sceneView.FarClip
|
||||
);
|
||||
|
||||
glm::mat4 worldMatrixMetaCore = MetaCoreBuildWorldTransformMatrix(editorContext.GetScene(), selectedObject.GetId());
|
||||
const glm::mat4 originalWorldMatrixMetaCore = worldMatrixMetaCore;
|
||||
|
||||
ImGuizmo::SetOrthographic(false);
|
||||
ImGuizmo::SetOrthographic(sceneView.ProjectionMode == MetaCoreCameraProjectionMode::Orthographic);
|
||||
ImGuizmo::Enable(true);
|
||||
ImGuizmo::AllowAxisFlip(false);
|
||||
const MetaCoreGizmoOperation currentOp = editorContext.GetGizmoOperation();
|
||||
|
||||
@ -250,6 +250,11 @@ public:
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
) = 0;
|
||||
|
||||
[[nodiscard]] virtual std::optional<MetaCoreId> CreateCamera(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
std::optional<MetaCoreId> forcedParentId
|
||||
) = 0;
|
||||
|
||||
[[nodiscard]] virtual std::optional<MetaCoreId> InstantiateModelAsset(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreAssetGuid& assetGuid,
|
||||
|
||||
@ -248,6 +248,13 @@ void MetaCoreWindow::BeginFrame() {
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::RightAlt, MetaCoreIsVirtualKeyDown(VK_RMENU));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::Control, MetaCoreIsVirtualKeyDown(VK_LCONTROL) || MetaCoreIsVirtualKeyDown(VK_RCONTROL));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::Focus, MetaCoreIsVirtualKeyDown('F'));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveForward, MetaCoreIsVirtualKeyDown('W'));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveBackward, MetaCoreIsVirtualKeyDown('S'));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveLeft, MetaCoreIsVirtualKeyDown('A'));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveRight, MetaCoreIsVirtualKeyDown('D'));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveDown, MetaCoreIsVirtualKeyDown('Q'));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveUp, MetaCoreIsVirtualKeyDown('E'));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::Shift, MetaCoreIsVirtualKeyDown(VK_LSHIFT) || MetaCoreIsVirtualKeyDown(VK_RSHIFT));
|
||||
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Left, MetaCoreIsVirtualKeyDown(VK_LBUTTON));
|
||||
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Right, MetaCoreIsVirtualKeyDown(VK_RBUTTON));
|
||||
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Middle, MetaCoreIsVirtualKeyDown(VK_MBUTTON));
|
||||
|
||||
@ -176,6 +176,16 @@ void MetaCoreWindow::BeginFrame() {
|
||||
MetaCoreIsGlfwKeyDown(window, GLFW_KEY_LEFT_CONTROL) || MetaCoreIsGlfwKeyDown(window, GLFW_KEY_RIGHT_CONTROL)
|
||||
);
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::Focus, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_F));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveForward, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_W));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveBackward, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_S));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveLeft, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_A));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveRight, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_D));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveDown, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_Q));
|
||||
Impl_->Input.SetKeyState(MetaCoreInputKey::MoveUp, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_E));
|
||||
Impl_->Input.SetKeyState(
|
||||
MetaCoreInputKey::Shift,
|
||||
MetaCoreIsGlfwKeyDown(window, GLFW_KEY_LEFT_SHIFT) || MetaCoreIsGlfwKeyDown(window, GLFW_KEY_RIGHT_SHIFT)
|
||||
);
|
||||
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Left, MetaCoreIsGlfwMouseButtonDown(window, GLFW_MOUSE_BUTTON_LEFT));
|
||||
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Right, MetaCoreIsGlfwMouseButtonDown(window, GLFW_MOUSE_BUTTON_RIGHT));
|
||||
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Middle, MetaCoreIsGlfwMouseButtonDown(window, GLFW_MOUSE_BUTTON_MIDDLE));
|
||||
|
||||
@ -12,6 +12,13 @@ enum class MetaCoreInputKey : unsigned int {
|
||||
RightAlt,
|
||||
Control,
|
||||
Focus,
|
||||
MoveForward,
|
||||
MoveBackward,
|
||||
MoveLeft,
|
||||
MoveRight,
|
||||
MoveDown,
|
||||
MoveUp,
|
||||
Shift,
|
||||
Count
|
||||
};
|
||||
|
||||
|
||||
@ -103,6 +103,21 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(MetaCoreScene& scene,
|
||||
FilamentSceneBridge_.SyncScene(scene, false, useScenePrimaryCamera);
|
||||
}
|
||||
|
||||
void MetaCoreEditorViewportRenderer::RenderSnapshotToViewport(
|
||||
const MetaCoreSceneRenderSyncSnapshot& snapshot,
|
||||
MetaCoreScene* scene,
|
||||
const MetaCoreSceneView& sceneView
|
||||
) {
|
||||
if (RenderDevice_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
RenderDevice_->SetSceneViewportRect(ViewportRect_);
|
||||
FilamentSceneBridge_.Resize(static_cast<int>(ViewportRect_.Width), static_cast<int>(ViewportRect_.Height));
|
||||
FilamentSceneBridge_.ApplySceneView(sceneView);
|
||||
FilamentSceneBridge_.SyncScene(snapshot, scene, false, false);
|
||||
}
|
||||
|
||||
void MetaCoreEditorViewportRenderer::RenderAll() {
|
||||
FilamentSceneBridge_.RenderAll();
|
||||
}
|
||||
|
||||
@ -1052,6 +1052,7 @@ public:
|
||||
|
||||
void ApplySceneView(const MetaCoreSceneView& sceneView) {
|
||||
if (!Camera_ || !View_) return;
|
||||
CurrentSceneView_ = sceneView;
|
||||
|
||||
// 1. 视图矩阵同步
|
||||
const glm::mat4 viewMatrix = glm::lookAt(sceneView.CameraPosition, sceneView.CameraTarget, sceneView.CameraUp);
|
||||
@ -1060,16 +1061,45 @@ public:
|
||||
|
||||
// 2. 投影矩阵:回归 Filament 原生设置
|
||||
const auto& viewport = View_->getViewport();
|
||||
float aspect = (viewport.height > 0) ? (static_cast<float>(viewport.width) / static_cast<float>(viewport.height)) : 1.0f;
|
||||
const float aspect = (viewport.height > 0) ? (static_cast<float>(viewport.width) / static_cast<float>(viewport.height)) : 1.0f;
|
||||
const double nearClip = std::max(0.0001, static_cast<double>(sceneView.NearClip));
|
||||
const double farClip = std::max(nearClip + 0.0001, static_cast<double>(sceneView.FarClip));
|
||||
|
||||
Camera_->setProjection(
|
||||
sceneView.VerticalFieldOfViewDegrees,
|
||||
aspect,
|
||||
sceneView.NearClip,
|
||||
sceneView.FarClip,
|
||||
filament::Camera::Fov::VERTICAL
|
||||
);
|
||||
if (sceneView.ProjectionMode == MetaCoreCameraProjectionMode::Orthographic) {
|
||||
const double halfHeight = std::max(0.0001, static_cast<double>(sceneView.OrthographicSize));
|
||||
const double halfWidth = halfHeight * std::max(0.0001, static_cast<double>(aspect));
|
||||
Camera_->setProjection(
|
||||
filament::Camera::Projection::ORTHO,
|
||||
-halfWidth,
|
||||
halfWidth,
|
||||
-halfHeight,
|
||||
halfHeight,
|
||||
nearClip,
|
||||
farClip
|
||||
);
|
||||
} else {
|
||||
Camera_->setProjection(
|
||||
std::clamp(static_cast<double>(sceneView.VerticalFieldOfViewDegrees), 1.0, 179.0),
|
||||
std::max(0.0001, static_cast<double>(aspect)),
|
||||
nearClip,
|
||||
farClip,
|
||||
filament::Camera::Fov::VERTICAL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] filament::Renderer::ClearOptions BuildSceneClearOptions() const {
|
||||
filament::Renderer::ClearOptions options;
|
||||
options.clearColor = {
|
||||
CurrentSceneView_.BackgroundColor.x,
|
||||
CurrentSceneView_.BackgroundColor.y,
|
||||
CurrentSceneView_.BackgroundColor.z,
|
||||
CurrentSceneView_.BackgroundAlpha
|
||||
};
|
||||
options.clear = CurrentSceneView_.ClearFlags != MetaCoreCameraClearFlags::DontClear;
|
||||
return options;
|
||||
}
|
||||
|
||||
void RenderAll() {
|
||||
if (!Renderer_ || !SwapChain_ || !View_) {
|
||||
return;
|
||||
@ -1095,9 +1125,7 @@ public:
|
||||
if (Renderer_->beginFrame(SwapChain_)) {
|
||||
if (RenderTarget_) {
|
||||
// 1. 渲染 3D 离屏视口 (输出到 RenderTarget)
|
||||
filament::Renderer::ClearOptions options;
|
||||
options.clearColor = { 0.11f, 0.12f, 0.14f, 1.0f }; // 深灰色
|
||||
options.clear = true;
|
||||
filament::Renderer::ClearOptions options = BuildSceneClearOptions();
|
||||
Renderer_->setClearOptions(options);
|
||||
|
||||
Renderer_->render(View_);
|
||||
@ -1111,9 +1139,7 @@ public:
|
||||
}
|
||||
} else {
|
||||
// 3. 直接上屏渲染
|
||||
filament::Renderer::ClearOptions options;
|
||||
options.clearColor = { 0.11f, 0.12f, 0.14f, 1.0f }; // 深灰色
|
||||
options.clear = true;
|
||||
filament::Renderer::ClearOptions options = BuildSceneClearOptions();
|
||||
Renderer_->setClearOptions(options);
|
||||
|
||||
Renderer_->render(View_);
|
||||
@ -1532,6 +1558,7 @@ private:
|
||||
|
||||
MetaCoreSceneRenderSync RenderSync_{};
|
||||
MetaCoreSceneRenderSyncSnapshot LastSyncSnapshot_{};
|
||||
MetaCoreSceneView CurrentSceneView_{};
|
||||
std::unordered_map<MetaCoreId, glm::mat4> ObjectWorldMatrices_{};
|
||||
std::unordered_map<MetaCoreId, std::pair<filament::gltfio::FilamentAsset*, utils::Entity>> ObjectToFilamentEntity_;
|
||||
std::unordered_map<MetaCoreId, filament::gltfio::FilamentAsset*> LoadedAssets_;
|
||||
|
||||
@ -4,7 +4,10 @@
|
||||
#include "MetaCoreScene/MetaCoreScene.h"
|
||||
#include "MetaCoreScene/MetaCoreTransformUtils.h"
|
||||
|
||||
#include <glm/ext/matrix_clip_space.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/geometric.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -29,6 +32,45 @@ namespace {
|
||||
return path;
|
||||
}
|
||||
|
||||
[[nodiscard]] float MetaCoreSafeAspectRatio(float width, float height) {
|
||||
return height > 0.0001F ? std::max(0.0001F, width / height) : 1.0F;
|
||||
}
|
||||
|
||||
[[nodiscard]] glm::mat4 MetaCoreBuildProjectionMatrix(
|
||||
const MetaCoreSceneView& sceneView,
|
||||
float aspectRatio
|
||||
) {
|
||||
const float nearClip = std::max(0.0001F, sceneView.NearClip);
|
||||
const float farClip = std::max(nearClip + 0.0001F, sceneView.FarClip);
|
||||
if (sceneView.ProjectionMode == MetaCoreCameraProjectionMode::Orthographic) {
|
||||
const float halfHeight = std::max(0.0001F, sceneView.OrthographicSize);
|
||||
const float halfWidth = halfHeight * aspectRatio;
|
||||
return glm::ortho(-halfWidth, halfWidth, -halfHeight, halfHeight, nearClip, farClip);
|
||||
}
|
||||
|
||||
const float fovDegrees = std::clamp(sceneView.VerticalFieldOfViewDegrees, 1.0F, 179.0F);
|
||||
return glm::perspective(glm::radians(fovDegrees), aspectRatio, nearClip, farClip);
|
||||
}
|
||||
|
||||
[[nodiscard]] glm::vec3 MetaCoreSafeForward(const MetaCoreSceneView& sceneView) {
|
||||
glm::vec3 forward = sceneView.CameraTarget - sceneView.CameraPosition;
|
||||
if (!MetaCoreIsUsableDirection(forward)) {
|
||||
forward = glm::vec3(0.0F, 0.0F, -1.0F);
|
||||
}
|
||||
return glm::normalize(forward);
|
||||
}
|
||||
|
||||
[[nodiscard]] glm::vec3 MetaCoreSafeUp(const MetaCoreSceneView& sceneView, const glm::vec3& forward) {
|
||||
glm::vec3 up = sceneView.CameraUp;
|
||||
if (!MetaCoreIsUsableDirection(up) || glm::length(glm::cross(forward, up)) <= 0.0001F) {
|
||||
up = glm::vec3(0.0F, 1.0F, 0.0F);
|
||||
}
|
||||
if (glm::length(glm::cross(forward, up)) <= 0.0001F) {
|
||||
up = glm::vec3(0.0F, 0.0F, 1.0F);
|
||||
}
|
||||
return glm::normalize(up);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const MetaCoreScene& scene) const {
|
||||
@ -144,15 +186,21 @@ MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const Met
|
||||
|
||||
if (gameObject.HasComponent<MetaCoreCameraComponent>()) {
|
||||
const auto& camera = gameObject.GetComponent<MetaCoreCameraComponent>();
|
||||
MetaCoreRenderSyncCamera syncCamera{
|
||||
objectId,
|
||||
camera.IsPrimary,
|
||||
camera.FieldOfViewDegrees,
|
||||
camera.NearClip,
|
||||
camera.FarClip,
|
||||
worldMatrix,
|
||||
camera.Enabled
|
||||
};
|
||||
MetaCoreRenderSyncCamera syncCamera;
|
||||
syncCamera.ObjectId = objectId;
|
||||
syncCamera.IsPrimary = camera.IsPrimary;
|
||||
syncCamera.FieldOfViewDegrees = camera.FieldOfViewDegrees;
|
||||
syncCamera.NearClip = camera.NearClip;
|
||||
syncCamera.FarClip = camera.FarClip;
|
||||
syncCamera.WorldMatrix = worldMatrix;
|
||||
syncCamera.Enabled = camera.Enabled;
|
||||
syncCamera.ProjectionMode = camera.ProjectionMode;
|
||||
syncCamera.OrthographicSize = camera.OrthographicSize;
|
||||
syncCamera.Depth = camera.Depth;
|
||||
syncCamera.CullingMask = camera.CullingMask;
|
||||
syncCamera.ClearFlags = camera.ClearFlags;
|
||||
syncCamera.BackgroundColor = camera.BackgroundColor;
|
||||
syncCamera.BackgroundAlpha = camera.BackgroundAlpha;
|
||||
snapshot.Cameras.push_back(syncCamera);
|
||||
if (syncCamera.IsPrimary && syncCamera.Enabled && !snapshot.PrimaryCamera.has_value()) {
|
||||
snapshot.PrimaryCamera = syncCamera;
|
||||
@ -172,14 +220,17 @@ MetaCoreSceneRenderSyncSnapshot MetaCoreSceneRenderSync::BuildSnapshot(const Met
|
||||
}
|
||||
|
||||
if (!snapshot.PrimaryCamera.has_value() && !snapshot.Cameras.empty()) {
|
||||
for (const auto& cam : snapshot.Cameras) {
|
||||
if (cam.Enabled) {
|
||||
snapshot.PrimaryCamera = cam;
|
||||
break;
|
||||
const MetaCoreRenderSyncCamera* bestCamera = nullptr;
|
||||
for (const MetaCoreRenderSyncCamera& camera : snapshot.Cameras) {
|
||||
if (!camera.Enabled) {
|
||||
continue;
|
||||
}
|
||||
if (bestCamera == nullptr || camera.Depth < bestCamera->Depth) {
|
||||
bestCamera = &camera;
|
||||
}
|
||||
}
|
||||
if (!snapshot.PrimaryCamera.has_value()) {
|
||||
snapshot.PrimaryCamera = snapshot.Cameras.front();
|
||||
if (bestCamera != nullptr) {
|
||||
snapshot.PrimaryCamera = *bestCamera;
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,6 +246,17 @@ bool MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera(
|
||||
}
|
||||
|
||||
const MetaCoreRenderSyncCamera& camera = *snapshot.PrimaryCamera;
|
||||
return TryBuildSceneViewFromCamera(camera, sceneView);
|
||||
}
|
||||
|
||||
bool MetaCoreSceneRenderSync::TryBuildSceneViewFromCamera(
|
||||
const MetaCoreRenderSyncCamera& camera,
|
||||
MetaCoreSceneView& sceneView
|
||||
) {
|
||||
if (!camera.Enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const glm::vec3 cameraPosition = MetaCoreExtractTranslation(camera.WorldMatrix);
|
||||
glm::vec3 cameraForward = glm::vec3(camera.WorldMatrix * glm::vec4(0.0F, 0.0F, -1.0F, 0.0F));
|
||||
glm::vec3 cameraUp = glm::vec3(camera.WorldMatrix * glm::vec4(0.0F, 1.0F, 0.0F, 0.0F));
|
||||
@ -212,7 +274,76 @@ bool MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera(
|
||||
sceneView.VerticalFieldOfViewDegrees = camera.FieldOfViewDegrees;
|
||||
sceneView.NearClip = camera.NearClip;
|
||||
sceneView.FarClip = camera.FarClip;
|
||||
sceneView.ProjectionMode = camera.ProjectionMode;
|
||||
sceneView.OrthographicSize = camera.OrthographicSize;
|
||||
sceneView.ClearFlags = camera.ClearFlags;
|
||||
sceneView.BackgroundColor = camera.BackgroundColor;
|
||||
sceneView.BackgroundAlpha = camera.BackgroundAlpha;
|
||||
sceneView.CullingMask = camera.CullingMask;
|
||||
sceneView.SourceCameraObjectId = camera.ObjectId;
|
||||
return true;
|
||||
}
|
||||
|
||||
MetaCoreCameraRay MetaCoreScreenPointToRay(
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreViewportRect& viewportRect,
|
||||
const glm::vec2& screenPosition
|
||||
) {
|
||||
const float width = std::max(1.0F, viewportRect.Width);
|
||||
const float height = std::max(1.0F, viewportRect.Height);
|
||||
const float localX = std::clamp(screenPosition.x - viewportRect.Left, 0.0F, width);
|
||||
const float localY = std::clamp(screenPosition.y - viewportRect.Top, 0.0F, height);
|
||||
const float normalizedX = (localX / width) * 2.0F - 1.0F;
|
||||
const float normalizedY = 1.0F - (localY / height) * 2.0F;
|
||||
|
||||
const glm::vec3 forward = MetaCoreSafeForward(sceneView);
|
||||
const glm::vec3 up = MetaCoreSafeUp(sceneView, forward);
|
||||
const glm::vec3 right = glm::normalize(glm::cross(forward, up));
|
||||
const float aspectRatio = MetaCoreSafeAspectRatio(width, height);
|
||||
|
||||
if (sceneView.ProjectionMode == MetaCoreCameraProjectionMode::Orthographic) {
|
||||
const float halfHeight = std::max(0.0001F, sceneView.OrthographicSize);
|
||||
const float halfWidth = halfHeight * aspectRatio;
|
||||
return MetaCoreCameraRay{
|
||||
sceneView.CameraPosition + right * (normalizedX * halfWidth) + up * (normalizedY * halfHeight),
|
||||
forward
|
||||
};
|
||||
}
|
||||
|
||||
const float fovDegrees = std::clamp(sceneView.VerticalFieldOfViewDegrees, 1.0F, 179.0F);
|
||||
const float tanHalfFov = std::tan(glm::radians(fovDegrees) * 0.5F);
|
||||
return MetaCoreCameraRay{
|
||||
sceneView.CameraPosition,
|
||||
glm::normalize(forward + right * (normalizedX * tanHalfFov * aspectRatio) + up * (normalizedY * tanHalfFov))
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<glm::vec3> MetaCoreWorldToScreenPoint(
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreViewportRect& viewportRect,
|
||||
const glm::vec3& worldPosition
|
||||
) {
|
||||
const float width = std::max(1.0F, viewportRect.Width);
|
||||
const float height = std::max(1.0F, viewportRect.Height);
|
||||
const glm::vec3 forward = MetaCoreSafeForward(sceneView);
|
||||
const glm::vec3 up = MetaCoreSafeUp(sceneView, forward);
|
||||
const glm::mat4 viewMatrix = glm::lookAt(sceneView.CameraPosition, sceneView.CameraTarget, up);
|
||||
const glm::mat4 projectionMatrix = MetaCoreBuildProjectionMatrix(sceneView, MetaCoreSafeAspectRatio(width, height));
|
||||
const glm::vec4 clipPosition = projectionMatrix * viewMatrix * glm::vec4(worldPosition, 1.0F);
|
||||
if (std::abs(clipPosition.w) <= 0.000001F) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const glm::vec3 ndc = glm::vec3(clipPosition) / clipPosition.w;
|
||||
if (!std::isfinite(ndc.x) || !std::isfinite(ndc.y) || !std::isfinite(ndc.z)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return glm::vec3(
|
||||
viewportRect.Left + (ndc.x + 1.0F) * 0.5F * width,
|
||||
viewportRect.Top + (1.0F - ndc.y) * 0.5F * height,
|
||||
ndc.z
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "MetaCoreRender/MetaCoreFilamentSceneBridge.h"
|
||||
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
||||
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
|
||||
|
||||
|
||||
#include <filesystem>
|
||||
@ -19,6 +20,7 @@ public:
|
||||
void Shutdown();
|
||||
void SetViewportRect(const MetaCoreViewportRect& viewportRect);
|
||||
void RenderSceneToViewport(MetaCoreScene& scene, const MetaCoreSceneView& sceneView, bool useScenePrimaryCamera = false);
|
||||
void RenderSnapshotToViewport(const MetaCoreSceneRenderSyncSnapshot& snapshot, MetaCoreScene* scene, const MetaCoreSceneView& sceneView);
|
||||
void RenderAll();
|
||||
void SetProjectRootPath(const std::filesystem::path& projectRootPath);
|
||||
[[nodiscard]] uint32_t GetFilamentGLTextureId() const;
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "MetaCoreFoundation/MetaCoreId.h"
|
||||
#include "MetaCoreScene/MetaCoreComponents.h"
|
||||
|
||||
#include <glm/vec3.hpp>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
@ -20,9 +23,21 @@ struct MetaCoreSceneView {
|
||||
float VerticalFieldOfViewDegrees = 60.0F;
|
||||
float NearClip = 0.1F;
|
||||
float FarClip = 1000.0F;
|
||||
MetaCoreCameraProjectionMode ProjectionMode = MetaCoreCameraProjectionMode::Perspective;
|
||||
float OrthographicSize = 5.0F;
|
||||
MetaCoreCameraClearFlags ClearFlags = MetaCoreCameraClearFlags::Skybox;
|
||||
glm::vec3 BackgroundColor{0.1F, 0.1F, 0.1F};
|
||||
float BackgroundAlpha = 1.0F;
|
||||
std::uint32_t CullingMask = 0xFFFFFFFFU;
|
||||
std::optional<MetaCoreId> SourceCameraObjectId{};
|
||||
MetaCoreId SelectedObjectId = 0;
|
||||
};
|
||||
|
||||
struct MetaCoreCameraRay {
|
||||
glm::vec3 Origin{0.0F, 0.0F, 0.0F};
|
||||
glm::vec3 Direction{0.0F, 0.0F, -1.0F};
|
||||
};
|
||||
|
||||
struct MetaCoreMeshVertex {
|
||||
float px, py, pz;
|
||||
float nx, ny, nz;
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
|
||||
#include "MetaCoreFoundation/MetaCoreAssetGuid.h"
|
||||
#include "MetaCoreFoundation/MetaCoreId.h"
|
||||
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
||||
#include "MetaCoreScene/MetaCoreComponents.h"
|
||||
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include <optional>
|
||||
@ -15,7 +17,6 @@
|
||||
namespace MetaCore {
|
||||
|
||||
class MetaCoreScene;
|
||||
struct MetaCoreSceneView;
|
||||
|
||||
struct MetaCoreRenderSyncRenderable {
|
||||
MetaCoreId ObjectId = 0;
|
||||
@ -45,6 +46,13 @@ struct MetaCoreRenderSyncCamera {
|
||||
float FarClip = 100.0F;
|
||||
glm::mat4 WorldMatrix{1.0F};
|
||||
bool Enabled = true;
|
||||
MetaCoreCameraProjectionMode ProjectionMode = MetaCoreCameraProjectionMode::Perspective;
|
||||
float OrthographicSize = 5.0F;
|
||||
float Depth = 0.0F;
|
||||
std::uint32_t CullingMask = 0xFFFFFFFFU;
|
||||
MetaCoreCameraClearFlags ClearFlags = MetaCoreCameraClearFlags::Skybox;
|
||||
glm::vec3 BackgroundColor{0.1F, 0.1F, 0.1F};
|
||||
float BackgroundAlpha = 1.0F;
|
||||
};
|
||||
|
||||
struct MetaCoreRenderSyncLight {
|
||||
@ -73,9 +81,25 @@ public:
|
||||
const MetaCoreSceneRenderSyncSnapshot& snapshot,
|
||||
MetaCoreSceneView& sceneView
|
||||
);
|
||||
[[nodiscard]] static bool TryBuildSceneViewFromCamera(
|
||||
const MetaCoreRenderSyncCamera& camera,
|
||||
MetaCoreSceneView& sceneView
|
||||
);
|
||||
|
||||
private:
|
||||
mutable std::uint64_t FrameId_ = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] MetaCoreCameraRay MetaCoreScreenPointToRay(
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreViewportRect& viewportRect,
|
||||
const glm::vec2& screenPosition
|
||||
);
|
||||
|
||||
[[nodiscard]] std::optional<glm::vec3> MetaCoreWorldToScreenPoint(
|
||||
const MetaCoreSceneView& sceneView,
|
||||
const MetaCoreViewportRect& viewportRect,
|
||||
const glm::vec3& worldPosition
|
||||
);
|
||||
|
||||
} // namespace MetaCore
|
||||
|
||||
@ -142,6 +142,13 @@ static json GameObjectDataToJson(const MetaCoreGameObjectData& obj, const MetaCo
|
||||
else if (camField.Name == "FarClip") camJson["FarClip"] = obj.Camera->FarClip;
|
||||
else if (camField.Name == "IsPrimary") camJson["IsPrimary"] = obj.Camera->IsPrimary;
|
||||
else if (camField.Name == "Enabled") camJson["Enabled"] = obj.Camera->Enabled;
|
||||
else if (camField.Name == "ProjectionMode") camJson["ProjectionMode"] = static_cast<int>(obj.Camera->ProjectionMode);
|
||||
else if (camField.Name == "OrthographicSize") camJson["OrthographicSize"] = obj.Camera->OrthographicSize;
|
||||
else if (camField.Name == "Depth") camJson["Depth"] = obj.Camera->Depth;
|
||||
else if (camField.Name == "CullingMask") camJson["CullingMask"] = obj.Camera->CullingMask;
|
||||
else if (camField.Name == "ClearFlags") camJson["ClearFlags"] = static_cast<int>(obj.Camera->ClearFlags);
|
||||
else if (camField.Name == "BackgroundColor") camJson["BackgroundColor"] = Vec3ToJson(obj.Camera->BackgroundColor);
|
||||
else if (camField.Name == "BackgroundAlpha") camJson["BackgroundAlpha"] = obj.Camera->BackgroundAlpha;
|
||||
}
|
||||
objJson["Camera"] = camJson;
|
||||
}
|
||||
@ -288,6 +295,13 @@ static MetaCoreGameObjectData JsonToGameObjectData(const json& objJson, const Me
|
||||
else if (camField.Name == "FarClip" && camJson.contains("FarClip")) cam.FarClip = camJson["FarClip"].get<float>();
|
||||
else if (camField.Name == "IsPrimary" && camJson.contains("IsPrimary")) cam.IsPrimary = camJson["IsPrimary"].get<bool>();
|
||||
else if (camField.Name == "Enabled" && camJson.contains("Enabled")) cam.Enabled = camJson["Enabled"].get<bool>();
|
||||
else if (camField.Name == "ProjectionMode" && camJson.contains("ProjectionMode")) cam.ProjectionMode = static_cast<MetaCoreCameraProjectionMode>(camJson["ProjectionMode"].get<int>());
|
||||
else if (camField.Name == "OrthographicSize" && camJson.contains("OrthographicSize")) cam.OrthographicSize = camJson["OrthographicSize"].get<float>();
|
||||
else if (camField.Name == "Depth" && camJson.contains("Depth")) cam.Depth = camJson["Depth"].get<float>();
|
||||
else if (camField.Name == "CullingMask" && camJson.contains("CullingMask")) cam.CullingMask = camJson["CullingMask"].get<std::uint32_t>();
|
||||
else if (camField.Name == "ClearFlags" && camJson.contains("ClearFlags")) cam.ClearFlags = static_cast<MetaCoreCameraClearFlags>(camJson["ClearFlags"].get<int>());
|
||||
else if (camField.Name == "BackgroundColor" && camJson.contains("BackgroundColor")) cam.BackgroundColor = JsonToVec3(camJson["BackgroundColor"]);
|
||||
else if (camField.Name == "BackgroundAlpha" && camJson.contains("BackgroundAlpha")) cam.BackgroundAlpha = camJson["BackgroundAlpha"].get<float>();
|
||||
}
|
||||
obj.Camera = cam;
|
||||
}
|
||||
|
||||
@ -31,6 +31,20 @@ enum class MetaCoreMeshAlphaMode {
|
||||
Blend
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreCameraProjectionMode {
|
||||
Perspective = 0,
|
||||
Orthographic
|
||||
};
|
||||
|
||||
MC_ENUM()
|
||||
enum class MetaCoreCameraClearFlags {
|
||||
Skybox = 0,
|
||||
SolidColor,
|
||||
DepthOnly,
|
||||
DontClear
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
struct MetaCoreTransformComponent {
|
||||
MC_GENERATED_BODY()
|
||||
@ -57,6 +71,20 @@ struct MetaCoreCameraComponent {
|
||||
bool IsPrimary = false;
|
||||
MC_PROPERTY()
|
||||
bool Enabled = true;
|
||||
MC_PROPERTY()
|
||||
MetaCoreCameraProjectionMode ProjectionMode = MetaCoreCameraProjectionMode::Perspective;
|
||||
MC_PROPERTY()
|
||||
float OrthographicSize = 5.0F;
|
||||
MC_PROPERTY()
|
||||
float Depth = 0.0F;
|
||||
MC_PROPERTY()
|
||||
std::uint32_t CullingMask = 0xFFFFFFFFU;
|
||||
MC_PROPERTY()
|
||||
MetaCoreCameraClearFlags ClearFlags = MetaCoreCameraClearFlags::Skybox;
|
||||
MC_PROPERTY()
|
||||
glm::vec3 BackgroundColor{0.1F, 0.1F, 0.1F};
|
||||
MC_PROPERTY()
|
||||
float BackgroundAlpha = 1.0F;
|
||||
};
|
||||
|
||||
MC_STRUCT()
|
||||
|
||||
@ -647,6 +647,82 @@ void MetaCoreTestComponentRegistryDescriptors() {
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
}
|
||||
|
||||
void MetaCoreTestSceneEditingServiceCreateCamera() {
|
||||
{
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
|
||||
MetaCore::MetaCoreWindow window;
|
||||
MetaCore::MetaCoreRenderDevice renderDevice;
|
||||
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
|
||||
MetaCore::MetaCoreScene scene;
|
||||
MetaCore::MetaCoreLogService logService;
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
auto coreServicesModule = MetaCore::MetaCoreCreateBuiltinCoreServicesModule();
|
||||
coreServicesModule->Startup(moduleRegistry);
|
||||
|
||||
MetaCore::MetaCoreEditorContext editorContext(
|
||||
window,
|
||||
renderDevice,
|
||||
viewportRenderer,
|
||||
scene,
|
||||
logService,
|
||||
moduleRegistry
|
||||
);
|
||||
|
||||
const auto sceneEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreISceneEditingService>();
|
||||
MetaCoreExpect(sceneEditingService != nullptr, "应解析到 SceneEditingService");
|
||||
const auto cameraId = sceneEditingService->CreateCamera(editorContext, std::nullopt);
|
||||
MetaCoreExpect(cameraId.has_value(), "应能创建相机对象");
|
||||
MetaCore::MetaCoreGameObject cameraObject = scene.FindGameObject(*cameraId);
|
||||
MetaCoreExpect(cameraObject, "创建后的相机对象应存在");
|
||||
MetaCoreExpect(cameraObject.HasComponent<MetaCore::MetaCoreCameraComponent>(), "创建后的对象应带 Camera 组件");
|
||||
MetaCoreExpect(cameraObject.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary, "空场景创建的首个相机应成为主相机");
|
||||
MetaCoreExpect(editorContext.GetActiveObjectId() == *cameraId, "创建相机后应选中新相机");
|
||||
MetaCoreExpect(editorContext.GetCommandService().CanUndo(), "创建相机应进入 Undo 栈");
|
||||
|
||||
MetaCoreExpect(editorContext.UndoCommand(), "创建相机应可 Undo");
|
||||
MetaCoreExpect(!scene.FindGameObject(*cameraId), "Undo 后相机对象应被移除");
|
||||
|
||||
coreServicesModule->Shutdown(moduleRegistry);
|
||||
moduleRegistry.ShutdownServices();
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
}
|
||||
|
||||
{
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
|
||||
MetaCore::MetaCoreWindow window;
|
||||
MetaCore::MetaCoreRenderDevice renderDevice;
|
||||
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
|
||||
MetaCore::MetaCoreScene scene = MetaCore::MetaCoreCreateDefaultScene();
|
||||
MetaCore::MetaCoreLogService logService;
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
auto coreServicesModule = MetaCore::MetaCoreCreateBuiltinCoreServicesModule();
|
||||
coreServicesModule->Startup(moduleRegistry);
|
||||
|
||||
MetaCore::MetaCoreEditorContext editorContext(
|
||||
window,
|
||||
renderDevice,
|
||||
viewportRenderer,
|
||||
scene,
|
||||
logService,
|
||||
moduleRegistry
|
||||
);
|
||||
|
||||
const auto sceneEditingService = moduleRegistry.ResolveService<MetaCore::MetaCoreISceneEditingService>();
|
||||
MetaCoreExpect(sceneEditingService != nullptr, "默认场景应解析到 SceneEditingService");
|
||||
const auto cameraId = sceneEditingService->CreateCamera(editorContext, std::nullopt);
|
||||
MetaCoreExpect(cameraId.has_value(), "默认场景应能创建第二个相机");
|
||||
const MetaCore::MetaCoreGameObject cameraObject = scene.FindGameObject(*cameraId);
|
||||
MetaCoreExpect(cameraObject.HasComponent<MetaCore::MetaCoreCameraComponent>(), "第二个相机对象应带 Camera 组件");
|
||||
MetaCoreExpect(!cameraObject.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary, "已有主相机时新相机不应抢占主相机");
|
||||
|
||||
coreServicesModule->Shutdown(moduleRegistry);
|
||||
moduleRegistry.ShutdownServices();
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreTestPlayModeStateMachineAndLifecycle() {
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
|
||||
@ -1173,6 +1249,143 @@ void MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot() {
|
||||
MetaCoreExpect(std::abs(primarySceneView.FarClip - 250.0F) <= 0.0001F, "主相机 FarClip 应同步");
|
||||
}
|
||||
|
||||
void MetaCoreTestCameraComponentJsonRoundTripAndLegacyDefaults() {
|
||||
MetaCore::MetaCoreTypeRegistry registry;
|
||||
MetaCore::MetaCoreRegisterFoundationGeneratedTypes(registry);
|
||||
MetaCore::MetaCoreRegisterSceneGeneratedTypes(registry);
|
||||
|
||||
const std::filesystem::path tempRoot = std::filesystem::temp_directory_path() / "MetaCoreCameraJsonRoundTrip";
|
||||
std::filesystem::remove_all(tempRoot);
|
||||
std::filesystem::create_directories(tempRoot);
|
||||
const std::filesystem::path scenePath = tempRoot / "camera.mcscene";
|
||||
|
||||
MetaCore::MetaCoreSceneDocument sceneDocument;
|
||||
sceneDocument.Name = "CameraFields";
|
||||
MetaCore::MetaCoreGameObjectData cameraObject;
|
||||
cameraObject.Id = 1001;
|
||||
cameraObject.Name = "Camera";
|
||||
cameraObject.Camera = MetaCore::MetaCoreCameraComponent{};
|
||||
cameraObject.Camera->FieldOfViewDegrees = 72.0F;
|
||||
cameraObject.Camera->NearClip = 0.2F;
|
||||
cameraObject.Camera->FarClip = 600.0F;
|
||||
cameraObject.Camera->IsPrimary = true;
|
||||
cameraObject.Camera->ProjectionMode = MetaCore::MetaCoreCameraProjectionMode::Orthographic;
|
||||
cameraObject.Camera->OrthographicSize = 8.0F;
|
||||
cameraObject.Camera->Depth = -2.0F;
|
||||
cameraObject.Camera->CullingMask = 0x0000000FU;
|
||||
cameraObject.Camera->ClearFlags = MetaCore::MetaCoreCameraClearFlags::SolidColor;
|
||||
cameraObject.Camera->BackgroundColor = glm::vec3(0.2F, 0.3F, 0.4F);
|
||||
cameraObject.Camera->BackgroundAlpha = 0.75F;
|
||||
sceneDocument.GameObjects.push_back(cameraObject);
|
||||
|
||||
MetaCoreExpect(
|
||||
MetaCore::MetaCoreSceneSerializer::SaveSceneToJson(scenePath, sceneDocument, registry),
|
||||
"Camera JSON round-trip 应能保存场景"
|
||||
);
|
||||
const auto loadedSceneDocument = MetaCore::MetaCoreSceneSerializer::LoadSceneFromJson(scenePath, registry);
|
||||
MetaCoreExpect(loadedSceneDocument.has_value(), "Camera JSON round-trip 应能读取场景");
|
||||
MetaCoreExpect(loadedSceneDocument->GameObjects.size() == 1, "Camera JSON round-trip 应保留对象");
|
||||
const auto& loadedCamera = *loadedSceneDocument->GameObjects.front().Camera;
|
||||
MetaCoreExpect(loadedCamera.ProjectionMode == MetaCore::MetaCoreCameraProjectionMode::Orthographic, "Camera ProjectionMode 应保留");
|
||||
MetaCoreExpect(std::abs(loadedCamera.OrthographicSize - 8.0F) <= 0.0001F, "Camera OrthographicSize 应保留");
|
||||
MetaCoreExpect(std::abs(loadedCamera.Depth - -2.0F) <= 0.0001F, "Camera Depth 应保留");
|
||||
MetaCoreExpect(loadedCamera.CullingMask == 0x0000000FU, "Camera CullingMask 应保留");
|
||||
MetaCoreExpect(loadedCamera.ClearFlags == MetaCore::MetaCoreCameraClearFlags::SolidColor, "Camera ClearFlags 应保留");
|
||||
MetaCoreExpectVec3Near(loadedCamera.BackgroundColor, glm::vec3(0.2F, 0.3F, 0.4F), "Camera BackgroundColor 应保留");
|
||||
MetaCoreExpect(std::abs(loadedCamera.BackgroundAlpha - 0.75F) <= 0.0001F, "Camera BackgroundAlpha 应保留");
|
||||
|
||||
const std::filesystem::path legacyScenePath = tempRoot / "legacy_camera.mcscene";
|
||||
{
|
||||
std::ofstream legacyScene(legacyScenePath, std::ios::trunc);
|
||||
legacyScene
|
||||
<< "{\n"
|
||||
<< " \"SceneName\": \"LegacyCamera\",\n"
|
||||
<< " \"GameObjects\": [{\n"
|
||||
<< " \"Id\": 42,\n"
|
||||
<< " \"ParentId\": 0,\n"
|
||||
<< " \"Name\": \"Legacy Camera\",\n"
|
||||
<< " \"Transform\": {\"Position\": [0, 0, 0], \"RotationEulerDegrees\": [0, 0, 0], \"Scale\": [1, 1, 1]},\n"
|
||||
<< " \"Camera\": {\"FieldOfViewDegrees\": 61.0, \"NearClip\": 0.3, \"FarClip\": 333.0, \"IsPrimary\": true, \"Enabled\": true}\n"
|
||||
<< " }]\n"
|
||||
<< "}\n";
|
||||
}
|
||||
const auto legacyDocument = MetaCore::MetaCoreSceneSerializer::LoadSceneFromJson(legacyScenePath, registry);
|
||||
MetaCoreExpect(legacyDocument.has_value(), "旧 Camera JSON 缺字段应能读取");
|
||||
const auto& legacyCamera = *legacyDocument->GameObjects.front().Camera;
|
||||
MetaCoreExpect(legacyCamera.ProjectionMode == MetaCore::MetaCoreCameraProjectionMode::Perspective, "旧 Camera ProjectionMode 应使用默认值");
|
||||
MetaCoreExpect(std::abs(legacyCamera.OrthographicSize - 5.0F) <= 0.0001F, "旧 Camera OrthographicSize 应使用默认值");
|
||||
MetaCoreExpect(legacyCamera.ClearFlags == MetaCore::MetaCoreCameraClearFlags::Skybox, "旧 Camera ClearFlags 应使用默认值");
|
||||
|
||||
std::filesystem::remove_all(tempRoot);
|
||||
}
|
||||
|
||||
void MetaCoreTestCameraSelectionAndProjectionUtilities() {
|
||||
MetaCore::MetaCoreScene scene;
|
||||
|
||||
MetaCore::MetaCoreGameObject disabledPrimary = scene.CreateGameObject("DisabledPrimary");
|
||||
auto& disabledPrimaryCamera = disabledPrimary.AddComponent<MetaCore::MetaCoreCameraComponent>();
|
||||
disabledPrimaryCamera.IsPrimary = true;
|
||||
disabledPrimaryCamera.Enabled = false;
|
||||
disabledPrimaryCamera.Depth = -100.0F;
|
||||
|
||||
MetaCore::MetaCoreGameObject depthCamera = scene.CreateGameObject("DepthCamera");
|
||||
auto& depthCameraComponent = depthCamera.AddComponent<MetaCore::MetaCoreCameraComponent>();
|
||||
depthCameraComponent.Depth = -5.0F;
|
||||
depthCameraComponent.ProjectionMode = MetaCore::MetaCoreCameraProjectionMode::Orthographic;
|
||||
depthCameraComponent.OrthographicSize = 4.0F;
|
||||
depthCameraComponent.ClearFlags = MetaCore::MetaCoreCameraClearFlags::SolidColor;
|
||||
depthCameraComponent.BackgroundColor = glm::vec3(0.4F, 0.5F, 0.6F);
|
||||
|
||||
MetaCore::MetaCoreGameObject fallbackCamera = scene.CreateGameObject("FallbackCamera");
|
||||
auto& fallbackCameraComponent = fallbackCamera.AddComponent<MetaCore::MetaCoreCameraComponent>();
|
||||
fallbackCameraComponent.Depth = 3.0F;
|
||||
|
||||
MetaCore::MetaCoreSceneRenderSync renderSync;
|
||||
MetaCore::MetaCoreSceneRenderSyncSnapshot snapshot = renderSync.BuildSnapshot(scene);
|
||||
MetaCoreExpect(snapshot.PrimaryCamera.has_value(), "无启用主相机时应按 Depth 自动选择相机");
|
||||
MetaCoreExpect(snapshot.PrimaryCamera->ObjectId == depthCamera.GetId(), "Depth 最小的启用相机应成为 Game 相机");
|
||||
|
||||
fallbackCameraComponent.IsPrimary = true;
|
||||
snapshot = renderSync.BuildSnapshot(scene);
|
||||
MetaCoreExpect(snapshot.PrimaryCamera.has_value(), "启用主相机应可被选中");
|
||||
MetaCoreExpect(snapshot.PrimaryCamera->ObjectId == fallbackCamera.GetId(), "启用主相机应优先于 Depth");
|
||||
|
||||
fallbackCameraComponent.Enabled = false;
|
||||
depthCameraComponent.Enabled = false;
|
||||
snapshot = renderSync.BuildSnapshot(scene);
|
||||
MetaCoreExpect(!snapshot.PrimaryCamera.has_value(), "全部相机禁用时不应选择 Game 相机");
|
||||
MetaCore::MetaCoreSceneView noCameraView;
|
||||
MetaCoreExpect(!MetaCore::MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera(snapshot, noCameraView), "无 Game 相机时不应构建 SceneView");
|
||||
|
||||
MetaCore::MetaCoreSceneView perspectiveView;
|
||||
perspectiveView.CameraPosition = glm::vec3(0.0F, 0.0F, 0.0F);
|
||||
perspectiveView.CameraTarget = glm::vec3(0.0F, 1.0F, 0.0F);
|
||||
perspectiveView.CameraUp = glm::vec3(0.0F, 0.0F, 1.0F);
|
||||
perspectiveView.VerticalFieldOfViewDegrees = 60.0F;
|
||||
const MetaCore::MetaCoreViewportRect viewport{0.0F, 0.0F, 100.0F, 100.0F};
|
||||
const MetaCore::MetaCoreCameraRay centerRay =
|
||||
MetaCore::MetaCoreScreenPointToRay(perspectiveView, viewport, glm::vec2(50.0F, 50.0F));
|
||||
MetaCoreExpectVec3Near(centerRay.Origin, glm::vec3(0.0F), "透视中心射线起点应为相机位置");
|
||||
MetaCoreExpectVec3Near(centerRay.Direction, glm::vec3(0.0F, 1.0F, 0.0F), "透视中心射线方向应等于相机 forward");
|
||||
|
||||
MetaCore::MetaCoreSceneView orthographicView = perspectiveView;
|
||||
orthographicView.ProjectionMode = MetaCore::MetaCoreCameraProjectionMode::Orthographic;
|
||||
orthographicView.OrthographicSize = 5.0F;
|
||||
const MetaCore::MetaCoreCameraRay orthoRightRay =
|
||||
MetaCore::MetaCoreScreenPointToRay(orthographicView, viewport, glm::vec2(100.0F, 50.0F));
|
||||
MetaCoreExpectVec3Near(orthoRightRay.Origin, glm::vec3(5.0F, 0.0F, 0.0F), "正交右侧射线起点应按视平面偏移");
|
||||
MetaCoreExpectVec3Near(orthoRightRay.Direction, glm::vec3(0.0F, 1.0F, 0.0F), "正交射线方向应等于相机 forward");
|
||||
|
||||
const auto projectedPoint = MetaCore::MetaCoreWorldToScreenPoint(
|
||||
perspectiveView,
|
||||
viewport,
|
||||
glm::vec3(0.0F, 5.0F, 0.0F)
|
||||
);
|
||||
MetaCoreExpect(projectedPoint.has_value(), "WorldToScreen 应能投影相机前方点");
|
||||
MetaCoreExpect(std::abs(projectedPoint->x - 50.0F) <= 0.001F, "WorldToScreen X 应位于中心");
|
||||
MetaCoreExpect(std::abs(projectedPoint->y - 50.0F) <= 0.001F, "WorldToScreen Y 应位于中心");
|
||||
}
|
||||
|
||||
void MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson() {
|
||||
const std::filesystem::path tempProjectRoot =
|
||||
std::filesystem::temp_directory_path() / "MetaCoreSceneRoundTripProject";
|
||||
@ -2465,6 +2678,13 @@ void MetaCoreTestComponentRegistryOperations() {
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().NearClip = 0.25F;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().FarClip = 250.0F;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary = true;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().ProjectionMode = MetaCore::MetaCoreCameraProjectionMode::Orthographic;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().OrthographicSize = 9.0F;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().Depth = -3.0F;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().CullingMask = 0x000000FFU;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().ClearFlags = MetaCore::MetaCoreCameraClearFlags::SolidColor;
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().BackgroundColor = glm::vec3(0.1F, 0.2F, 0.3F);
|
||||
object.GetComponent<MetaCore::MetaCoreCameraComponent>().BackgroundAlpha = 0.5F;
|
||||
MetaCoreExpect(componentRegistry->CopyComponent("Camera", object), "应能复制 Camera 组件值");
|
||||
|
||||
MetaCore::MetaCoreGameObject pasteTarget = scene.CreateGameObject("PasteTarget");
|
||||
@ -2480,6 +2700,23 @@ void MetaCoreTestComponentRegistryOperations() {
|
||||
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().NearClip - 0.25F) <= 0.0001F, "粘贴后 NearClip 应一致");
|
||||
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FarClip - 250.0F) <= 0.0001F, "粘贴后 FarClip 应一致");
|
||||
MetaCoreExpect(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary, "粘贴后 IsPrimary 应一致");
|
||||
MetaCoreExpect(
|
||||
pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().ProjectionMode == MetaCore::MetaCoreCameraProjectionMode::Orthographic,
|
||||
"粘贴后 ProjectionMode 应一致"
|
||||
);
|
||||
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().OrthographicSize - 9.0F) <= 0.0001F, "粘贴后 OrthographicSize 应一致");
|
||||
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().Depth - -3.0F) <= 0.0001F, "粘贴后 Depth 应一致");
|
||||
MetaCoreExpect(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().CullingMask == 0x000000FFU, "粘贴后 CullingMask 应一致");
|
||||
MetaCoreExpect(
|
||||
pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().ClearFlags == MetaCore::MetaCoreCameraClearFlags::SolidColor,
|
||||
"粘贴后 ClearFlags 应一致"
|
||||
);
|
||||
MetaCoreExpectVec3Near(
|
||||
pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().BackgroundColor,
|
||||
glm::vec3(0.1F, 0.2F, 0.3F),
|
||||
"粘贴后 BackgroundColor 应一致"
|
||||
);
|
||||
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().BackgroundAlpha - 0.5F) <= 0.0001F, "粘贴后 BackgroundAlpha 应一致");
|
||||
MetaCoreExpect(std::abs(secondPasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees - 77.0F) <= 0.0001F, "第二个对象粘贴后 FOV 应一致");
|
||||
|
||||
MetaCoreExpect(componentRegistry->CopyComponent("Script", object), "应能复制 Script 组件值");
|
||||
@ -2497,8 +2734,13 @@ void MetaCoreTestComponentRegistryOperations() {
|
||||
|
||||
MetaCoreExpect(cameraDescriptor->ResetComponent != nullptr, "Camera descriptor 应提供 Reset");
|
||||
pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees = 10.0F;
|
||||
pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().ProjectionMode = MetaCore::MetaCoreCameraProjectionMode::Orthographic;
|
||||
MetaCoreExpect(cameraDescriptor->ResetComponent(pasteTarget), "应能重置 Camera");
|
||||
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees - 60.0F) <= 0.0001F, "重置后 Camera 应恢复默认值");
|
||||
MetaCoreExpect(
|
||||
pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().ProjectionMode == MetaCore::MetaCoreCameraProjectionMode::Perspective,
|
||||
"重置后 Camera ProjectionMode 应恢复默认值"
|
||||
);
|
||||
MetaCoreExpect(scriptDescriptor->ResetComponent != nullptr, "Script descriptor 应提供 Reset");
|
||||
MetaCoreExpect(scriptDescriptor->ResetComponent(pasteTarget), "应能重置 Script");
|
||||
MetaCoreExpect(pasteTarget.GetComponent<MetaCore::MetaCoreScriptComponent>().Instances.empty(), "重置后 Script 实例列表应清空");
|
||||
@ -3977,6 +4219,8 @@ int main() {
|
||||
MetaCoreTestAssetDatabaseMovePathUpdatesProjectDescriptor();
|
||||
std::cout << "[RUN] MetaCoreTestComponentRegistryDescriptors..." << std::endl;
|
||||
MetaCoreTestComponentRegistryDescriptors();
|
||||
std::cout << "[RUN] MetaCoreTestSceneEditingServiceCreateCamera..." << std::endl;
|
||||
MetaCoreTestSceneEditingServiceCreateCamera();
|
||||
std::cout << "[RUN] MetaCoreTestPlayModeStateMachineAndLifecycle..." << std::endl;
|
||||
MetaCoreTestPlayModeStateMachineAndLifecycle();
|
||||
std::cout << "[RUN] MetaCoreTestPlayModeRotatorRestoresPrePlaySnapshotOnStop..." << std::endl;
|
||||
@ -3991,6 +4235,10 @@ int main() {
|
||||
MetaCoreTestNativeScriptBehavioursRestorePrePlaySnapshotOnStop();
|
||||
std::cout << "[RUN] MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot..." << std::endl;
|
||||
MetaCoreTestSceneRenderSyncBuildsRenderableCameraLightSnapshot();
|
||||
std::cout << "[RUN] MetaCoreTestCameraComponentJsonRoundTripAndLegacyDefaults..." << std::endl;
|
||||
MetaCoreTestCameraComponentJsonRoundTripAndLegacyDefaults();
|
||||
std::cout << "[RUN] MetaCoreTestCameraSelectionAndProjectionUtilities..." << std::endl;
|
||||
MetaCoreTestCameraSelectionAndProjectionUtilities();
|
||||
std::cout << "[RUN] MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson..." << std::endl;
|
||||
MetaCoreTestJsonSceneSaveCurrentSceneUsesMcsceneJson();
|
||||
std::cout << "[RUN] MetaCoreTestImportPipelineAndCook..." << std::endl;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user