feat: 实现模型导入基础版,支持纯色渲染排查,修复UI卡顿
This commit is contained in:
parent
c8021a9206
commit
2799ddc0e3
@ -32,7 +32,7 @@ find_package(imgui CONFIG REQUIRED)
|
|||||||
|
|
||||||
set(METACORE_COMMON_WARNINGS)
|
set(METACORE_COMMON_WARNINGS)
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
set(METACORE_COMMON_WARNINGS /W4 /permissive- /EHsc)
|
set(METACORE_COMMON_WARNINGS /W4 /permissive- /EHsc /utf-8)
|
||||||
else()
|
else()
|
||||||
set(METACORE_COMMON_WARNINGS -Wall -Wextra -Wpedantic)
|
set(METACORE_COMMON_WARNINGS -Wall -Wextra -Wpedantic)
|
||||||
endif()
|
endif()
|
||||||
@ -302,12 +302,15 @@ set(METACORE_EDITOR_HEADERS
|
|||||||
set(METACORE_EDITOR_PRIVATE_HEADERS
|
set(METACORE_EDITOR_PRIVATE_HEADERS
|
||||||
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h
|
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h
|
||||||
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h
|
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h
|
||||||
|
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.h
|
||||||
third_party/ImGuizmo/ImGuizmo.h
|
third_party/ImGuizmo/ImGuizmo.h
|
||||||
third_party/cgltf/cgltf.h
|
third_party/cgltf/cgltf.h
|
||||||
|
third_party/stb/stb_image.h
|
||||||
)
|
)
|
||||||
|
|
||||||
set(METACORE_EDITOR_SOURCES
|
set(METACORE_EDITOR_SOURCES
|
||||||
Source/MetaCoreEditor/Private/MetaCoreBuiltinCoreServicesModule.cpp
|
Source/MetaCoreEditor/Private/MetaCoreBuiltinCoreServicesModule.cpp
|
||||||
|
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.cpp
|
||||||
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp
|
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp
|
||||||
Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp
|
Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp
|
||||||
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
|
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
|
||||||
@ -318,6 +321,7 @@ set(METACORE_EDITOR_SOURCES
|
|||||||
Source/MetaCoreEditor/Private/MetaCoreSceneInteractionService.cpp
|
Source/MetaCoreEditor/Private/MetaCoreSceneInteractionService.cpp
|
||||||
third_party/ImGuizmo/ImGuizmo.cpp
|
third_party/ImGuizmo/ImGuizmo.cpp
|
||||||
third_party/cgltf/cgltf.cpp
|
third_party/cgltf/cgltf.cpp
|
||||||
|
third_party/stb/stb_image_impl.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
metacore_generate_reflection(
|
metacore_generate_reflection(
|
||||||
@ -343,6 +347,7 @@ target_include_directories(MetaCoreEditor
|
|||||||
third_party
|
third_party
|
||||||
third_party/ImGuizmo
|
third_party/ImGuizmo
|
||||||
third_party/cgltf
|
third_party/cgltf
|
||||||
|
third_party/stb
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(MetaCoreEditor
|
target_link_libraries(MetaCoreEditor
|
||||||
@ -400,6 +405,7 @@ if(METACORE_BUILD_TESTS)
|
|||||||
MetaCoreEditor
|
MetaCoreEditor
|
||||||
MetaCoreRuntimeData
|
MetaCoreRuntimeData
|
||||||
)
|
)
|
||||||
|
target_compile_options(MetaCoreSmokeTests PRIVATE ${METACORE_COMMON_WARNINGS})
|
||||||
|
|
||||||
metacore_stage_panda3d_runtime(MetaCoreSmokeTests)
|
metacore_stage_panda3d_runtime(MetaCoreSmokeTests)
|
||||||
metacore_stage_simplepbr_runtime(MetaCoreSmokeTests)
|
metacore_stage_simplepbr_runtime(MetaCoreSmokeTests)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -136,11 +136,11 @@ void MetaCoreDrawSceneViewportDropTarget(
|
|||||||
ImGui::SetCursorScreenPos(min);
|
ImGui::SetCursorScreenPos(min);
|
||||||
ImGui::InvisibleButton("MetaCoreSceneViewportDropButton", viewportSize);
|
ImGui::InvisibleButton("MetaCoreSceneViewportDropButton", viewportSize);
|
||||||
|
|
||||||
const ImVec2 textSize = ImGui::CalcTextSize("拖拽 Model / Prefab 到场景");
|
const ImVec2 textSize = ImGui::CalcTextSize("\u62D6\u62FD Model / Prefab \u5230\u573A\u666F");
|
||||||
drawList->AddText(
|
drawList->AddText(
|
||||||
ImVec2(min.x + (viewportSize.x - textSize.x) * 0.5F, min.y + (viewportSize.y - textSize.y) * 0.5F),
|
ImVec2(min.x + (viewportSize.x - textSize.x) * 0.5F, min.y + (viewportSize.y - textSize.y) * 0.5F),
|
||||||
IM_COL32(230, 236, 245, 255),
|
IM_COL32(230, 236, 245, 255),
|
||||||
"拖拽 Model / Prefab 到场景"
|
"\u62D6\u62FD Model / Prefab \u5230\u573A\u666F"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ImGui::BeginDragDropTarget()) {
|
if (ImGui::BeginDragDropTarget()) {
|
||||||
@ -728,22 +728,11 @@ bool MetaCoreEditorApp::Initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MetaCoreTraceStartup("metacore.app: add console messages");
|
MetaCoreTraceStartup("metacore.app: add console messages");
|
||||||
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "System", "MetaCore 编辑器已初始化");
|
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "System", "\u004D\u0065\u0074\u0061\u0043\u006F\u0072\u0065\u0020\u7F16\u8F91\u5668\u5DF2\u521D\u59CB\u5316");
|
||||||
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Render", "MetaCore 场景视口已准备完成");
|
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Render", "\u004D\u0065\u0074\u0061\u0043\u006F\u0072\u0065\u0020\u573A\u666F\u89C6\u53E3\u5DF2\u51C6\u5907\u5B8C\u6210");
|
||||||
if (ViewportRenderer_.IsSceneSyncDegraded()) {
|
|
||||||
EditorContext_->AddConsoleMessage(
|
|
||||||
MetaCoreLogLevel::Warning,
|
|
||||||
"Render",
|
|
||||||
"Debug 兼容模式已启用:当前优先保证 MetaCore 编辑器稳定运行,部分 3D 场景内容会暂时简化显示"
|
|
||||||
);
|
|
||||||
EditorContext_->AddConsoleMessage(
|
|
||||||
MetaCoreLogLevel::Info,
|
|
||||||
"Render",
|
|
||||||
"如需继续排查场景同步问题,可设置开发环境变量 METACORE_ENABLE_DEBUG_SCENE_SYNC=1 后重启编辑器"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (loadedStartupScene) {
|
if (loadedStartupScene) {
|
||||||
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Project", "已加载启动场景");
|
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Project", "\u5DF2\u52A0\u8F7D\u542F\u52A8\u573A\u666F");
|
||||||
}
|
}
|
||||||
|
|
||||||
Initialized_ = true;
|
Initialized_ = true;
|
||||||
@ -962,8 +951,8 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
|||||||
|
|
||||||
if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && EditorContext_->GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
|
if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && EditorContext_->GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
|
||||||
if (MetaCoreGameObject selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject) {
|
if (MetaCoreGameObject selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject) {
|
||||||
EditorContext_->GetCameraController().FocusGameObject(*selectedObject);
|
EditorContext_->GetCameraController().FocusGameObject(selectedObject);
|
||||||
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象");
|
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "\u76F8\u673A\u5DF2\u805A\u7126\u5230\u5F53\u524D\u5BF9\u8C61");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1058,9 +1047,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
|||||||
|
|
||||||
|
|
||||||
MetaCoreDrawViewportGridOverlay(*EditorContext_, viewportState);
|
MetaCoreDrawViewportGridOverlay(*EditorContext_, viewportState);
|
||||||
if (ViewportRenderer_.IsSceneSyncDegraded()) {
|
|
||||||
MetaCoreDrawCompatibilityScenePreviewOverlay(Scene_, sceneView, viewportState);
|
|
||||||
}
|
|
||||||
MetaCoreDrawWorldOriginOverlay(*EditorContext_, sceneView, viewportState);
|
MetaCoreDrawWorldOriginOverlay(*EditorContext_, sceneView, viewportState);
|
||||||
MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||||
MetaCoreDrawSelectionGroupCenterOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
MetaCoreDrawSelectionGroupCenterOverlay(*EditorContext_, Scene_, sceneView, viewportState);
|
||||||
@ -1071,41 +1058,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
|
|||||||
ImVec2(viewportState.Width, viewportState.Height)
|
ImVec2(viewportState.Width, viewportState.Height)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (ViewportRenderer_.IsSceneSyncDegraded()) {
|
|
||||||
ImDrawList* overlayDrawList = ImGui::GetForegroundDrawList();
|
|
||||||
const ImVec2 overlayMin(
|
|
||||||
centralNode->Pos.x + 12.0F,
|
|
||||||
centralNode->Pos.y + sceneToolbarHeight + tabHeaderHeight + 12.0F
|
|
||||||
);
|
|
||||||
const ImVec2 overlayMax(
|
|
||||||
overlayMin.x + 420.0F,
|
|
||||||
overlayMin.y + 70.0F
|
|
||||||
);
|
|
||||||
overlayDrawList->AddRectFilled(overlayMin, overlayMax, IM_COL32(32, 24, 18, 220), 4.0F);
|
|
||||||
overlayDrawList->AddRect(overlayMin, overlayMax, IM_COL32(214, 150, 82, 220), 4.0F, 0, 1.5F);
|
|
||||||
overlayDrawList->AddText(
|
|
||||||
ImVec2(overlayMin.x + 12.0F, overlayMin.y + 10.0F),
|
|
||||||
IM_COL32(255, 214, 160, 255),
|
|
||||||
"Debug 兼容模式"
|
|
||||||
);
|
|
||||||
overlayDrawList->AddText(
|
|
||||||
ImVec2(overlayMin.x + 12.0F, overlayMin.y + 28.0F),
|
|
||||||
IM_COL32(240, 226, 208, 255),
|
|
||||||
"当前优先保证 MetaCore 编辑器稳定运行,3D 视图会先显示可渲染对象"
|
|
||||||
);
|
|
||||||
overlayDrawList->AddText(
|
|
||||||
ImVec2(overlayMin.x + 12.0F, overlayMin.y + 44.0F),
|
|
||||||
IM_COL32(209, 199, 184, 255),
|
|
||||||
"Camera/Light 等非渲染节点暂时简化,可设置 METACORE_ENABLE_DEBUG_SCENE_SYNC=1 继续开发测试"
|
|
||||||
);
|
|
||||||
if (!ViewportRenderer_.GetLastSceneSyncFailure().empty()) {
|
|
||||||
overlayDrawList->AddText(
|
|
||||||
ImVec2(overlayMin.x + 12.0F, overlayMin.y + 58.0F),
|
|
||||||
IM_COL32(255, 186, 150, 255),
|
|
||||||
ViewportRenderer_.GetLastSceneSyncFailure().c_str()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SceneInteractionService_.HandleGizmoEndUse(*EditorContext_, gizmoUsing);
|
SceneInteractionService_.HandleGizmoEndUse(*EditorContext_, gizmoUsing);
|
||||||
SceneInteractionService_.DrawViewportToolbar(*EditorContext_);
|
SceneInteractionService_.DrawViewportToolbar(*EditorContext_);
|
||||||
@ -1133,10 +1086,10 @@ void MetaCoreEditorApp::EnsureDefaultDockLayout(unsigned int dockSpaceId) {
|
|||||||
const ImGuiID rightNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Right, 0.24F, nullptr, &mainNodeId);
|
const ImGuiID rightNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Right, 0.24F, nullptr, &mainNodeId);
|
||||||
const ImGuiID bottomNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Down, 0.28F, nullptr, &mainNodeId);
|
const ImGuiID bottomNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Down, 0.28F, nullptr, &mainNodeId);
|
||||||
|
|
||||||
ImGui::DockBuilderDockWindow("层级", leftNodeId);
|
ImGui::DockBuilderDockWindow("\u5C42\u7EA7", leftNodeId);
|
||||||
ImGui::DockBuilderDockWindow("检查器", rightNodeId);
|
ImGui::DockBuilderDockWindow("\u68C0\u67E5\u5668", rightNodeId);
|
||||||
ImGui::DockBuilderDockWindow("项目", bottomNodeId);
|
ImGui::DockBuilderDockWindow("\u9879\u76EE", bottomNodeId);
|
||||||
ImGui::DockBuilderDockWindow("控制台", bottomNodeId);
|
ImGui::DockBuilderDockWindow("\u63A7\u5236\u53F0", bottomNodeId);
|
||||||
ImGui::DockBuilderFinish(dockSpaceId);
|
ImGui::DockBuilderFinish(dockSpaceId);
|
||||||
|
|
||||||
EditorContext_->SetDockLayoutBuilt(true);
|
EditorContext_->SetDockLayoutBuilt(true);
|
||||||
|
|||||||
@ -61,8 +61,8 @@ void MetaCoreEditorCameraController::Update(const MetaCoreSceneViewportState& vi
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreEditorCameraController::FocusGameObject(const MetaCoreGameObject& gameObject) {
|
void MetaCoreEditorCameraController::FocusGameObject(const MetaCoreGameObject& gameObject) {
|
||||||
Target_ = gameObject.Transform.Position;
|
Target_ = gameObject.GetComponent<MetaCoreTransformComponent>().Position;
|
||||||
const float scaleRadius = std::max({gameObject.Transform.Scale.x, gameObject.Transform.Scale.y, gameObject.Transform.Scale.z});
|
const float scaleRadius = std::max({gameObject.GetComponent<MetaCoreTransformComponent>().Scale.x, gameObject.GetComponent<MetaCoreTransformComponent>().Scale.y, gameObject.GetComponent<MetaCoreTransformComponent>().Scale.z});
|
||||||
Distance_ = std::max(3.5F, scaleRadius * 4.0F + 2.5F);
|
Distance_ = std::max(3.5F, scaleRadius * 4.0F + 2.5F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
#include "MetaCoreEditor/MetaCoreEditorContext.h"
|
#include "MetaCoreEditor/MetaCoreEditorContext.h"
|
||||||
|
#if defined(_MSC_VER)
|
||||||
|
#pragma execution_character_set("utf-8")
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "MetaCoreEditor/MetaCoreEditorModule.h"
|
#include "MetaCoreEditor/MetaCoreEditorModule.h"
|
||||||
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
#include "MetaCoreEditor/MetaCoreEditorServices.h"
|
||||||
@ -81,40 +84,40 @@ private:
|
|||||||
MetaCoreNearlyEqualVec3(lhs.Scale, rhs.Scale);
|
MetaCoreNearlyEqualVec3(lhs.Scale, rhs.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool MetaCoreGameObjectEqual(const MetaCoreGameObject& lhs, const MetaCoreGameObject& rhs) {
|
[[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)) {
|
if (lhs.Id != rhs.Id || lhs.ParentId != rhs.ParentId || lhs.Name != rhs.Name || !MetaCoreTransformEqual(lhs.Transform, rhs.Transform)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhs.HasComponent<MetaCoreCameraComponent>() != rhs.HasComponent<MetaCoreCameraComponent>()) {
|
if (lhs.Camera.has_value() != rhs.Camera.has_value()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (lhs.HasComponent<MetaCoreCameraComponent>()) {
|
if (lhs.Camera.has_value()) {
|
||||||
if (!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees, rhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees) ||
|
if (!MetaCoreNearlyEqual(lhs.Camera->FieldOfViewDegrees, rhs.Camera->FieldOfViewDegrees) ||
|
||||||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().NearClip, rhs.GetComponent<MetaCoreCameraComponent>().NearClip) ||
|
!MetaCoreNearlyEqual(lhs.Camera->NearClip, rhs.Camera->NearClip) ||
|
||||||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FarClip, rhs.GetComponent<MetaCoreCameraComponent>().FarClip) ||
|
!MetaCoreNearlyEqual(lhs.Camera->FarClip, rhs.Camera->FarClip) ||
|
||||||
lhs.GetComponent<MetaCoreCameraComponent>().IsPrimary != rhs.GetComponent<MetaCoreCameraComponent>().IsPrimary) {
|
lhs.Camera->IsPrimary != rhs.Camera->IsPrimary) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhs.HasComponent<MetaCoreMeshRendererComponent>() != rhs.HasComponent<MetaCoreMeshRendererComponent>()) {
|
if (lhs.MeshRenderer.has_value() != rhs.MeshRenderer.has_value()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (lhs.HasComponent<MetaCoreMeshRendererComponent>()) {
|
if (lhs.MeshRenderer.has_value()) {
|
||||||
if (lhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh != rhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh ||
|
if (lhs.MeshRenderer->BuiltinMesh != rhs.MeshRenderer->BuiltinMesh ||
|
||||||
!MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor, rhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor) ||
|
!MetaCoreNearlyEqualVec3(lhs.MeshRenderer->BaseColor, rhs.MeshRenderer->BaseColor) ||
|
||||||
lhs.GetComponent<MetaCoreMeshRendererComponent>().Visible != rhs.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
|
lhs.MeshRenderer->Visible != rhs.MeshRenderer->Visible) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhs.HasComponent<MetaCoreLightComponent>() != rhs.HasComponent<MetaCoreLightComponent>()) {
|
if (lhs.Light.has_value() != rhs.Light.has_value()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (lhs.HasComponent<MetaCoreLightComponent>()) {
|
if (lhs.Light.has_value()) {
|
||||||
if (!MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreLightComponent>().Color, rhs.GetComponent<MetaCoreLightComponent>().Color) ||
|
if (!MetaCoreNearlyEqualVec3(lhs.Light->Color, rhs.Light->Color) ||
|
||||||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreLightComponent>().Intensity, rhs.GetComponent<MetaCoreLightComponent>().Intensity)) {
|
!MetaCoreNearlyEqual(lhs.Light->Intensity, rhs.Light->Intensity)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,7 +224,7 @@ void MetaCoreEditorContext::SelectOnly(MetaCoreId objectId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreEditorContext::ToggleSelection(MetaCoreId objectId) {
|
void MetaCoreEditorContext::ToggleSelection(MetaCoreId objectId) {
|
||||||
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr) {
|
if (objectId == 0 || !Scene_.FindGameObject(objectId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +245,7 @@ void MetaCoreEditorContext::ToggleSelection(MetaCoreId objectId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreEditorContext::AddToSelection(MetaCoreId objectId, bool makeActive) {
|
void MetaCoreEditorContext::AddToSelection(MetaCoreId objectId, bool makeActive) {
|
||||||
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr) {
|
if (objectId == 0 || !Scene_.FindGameObject(objectId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +266,7 @@ void MetaCoreEditorContext::SetSelection(const std::vector<MetaCoreId>& objectId
|
|||||||
|
|
||||||
std::unordered_set<MetaCoreId> deduplicatedIds;
|
std::unordered_set<MetaCoreId> deduplicatedIds;
|
||||||
for (MetaCoreId objectId : objectIds) {
|
for (MetaCoreId objectId : objectIds) {
|
||||||
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr || deduplicatedIds.contains(objectId)) {
|
if (objectId == 0 || !Scene_.FindGameObject(objectId) || deduplicatedIds.contains(objectId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
deduplicatedIds.insert(objectId);
|
deduplicatedIds.insert(objectId);
|
||||||
@ -284,11 +287,11 @@ void MetaCoreEditorContext::SetSelection(const std::vector<MetaCoreId>& objectId
|
|||||||
MetaCoreId MetaCoreEditorContext::GetSelectionAnchorId() const { return SelectionAnchorId_; }
|
MetaCoreId MetaCoreEditorContext::GetSelectionAnchorId() const { return SelectionAnchorId_; }
|
||||||
|
|
||||||
void MetaCoreEditorContext::SetSelectionAnchorId(MetaCoreId objectId) {
|
void MetaCoreEditorContext::SetSelectionAnchorId(MetaCoreId objectId) {
|
||||||
SelectionAnchorId_ = (objectId != 0 && Scene_.FindGameObject(objectId) != nullptr) ? objectId : 0;
|
SelectionAnchorId_ = (objectId != 0 && (bool)Scene_.FindGameObject(objectId)) ? objectId : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreEditorContext::SelectRangeByOrderedIds(const std::vector<MetaCoreId>& orderedIds, MetaCoreId endId, bool additive) {
|
void MetaCoreEditorContext::SelectRangeByOrderedIds(const std::vector<MetaCoreId>& orderedIds, MetaCoreId endId, bool additive) {
|
||||||
if (endId == 0 || Scene_.FindGameObject(endId) == nullptr) {
|
if (endId == 0 || !Scene_.FindGameObject(endId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,7 +334,7 @@ void MetaCoreEditorContext::SelectRangeByOrderedIds(const std::vector<MetaCoreId
|
|||||||
MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return ActiveObjectId_; }
|
MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return ActiveObjectId_; }
|
||||||
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectOnly(objectId); }
|
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectOnly(objectId); }
|
||||||
MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(ActiveObjectId_); }
|
MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() { return Scene_.FindGameObject(ActiveObjectId_); }
|
||||||
const MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(ActiveObjectId_); }
|
MetaCoreGameObject MetaCoreEditorContext::GetSelectedGameObject() const { return Scene_.FindGameObject(ActiveObjectId_); }
|
||||||
MetaCoreGizmoOperation MetaCoreEditorContext::GetGizmoOperation() const { return GizmoOperation_; }
|
MetaCoreGizmoOperation MetaCoreEditorContext::GetGizmoOperation() const { return GizmoOperation_; }
|
||||||
void MetaCoreEditorContext::SetGizmoOperation(MetaCoreGizmoOperation operation) { GizmoOperation_ = operation; }
|
void MetaCoreEditorContext::SetGizmoOperation(MetaCoreGizmoOperation operation) { GizmoOperation_ = operation; }
|
||||||
MetaCoreGizmoMode MetaCoreEditorContext::GetGizmoMode() const { return GizmoMode_; }
|
MetaCoreGizmoMode MetaCoreEditorContext::GetGizmoMode() const { return GizmoMode_; }
|
||||||
@ -391,7 +394,7 @@ void MetaCoreEditorContext::RestoreStateSnapshot(const MetaCoreEditorStateSnapsh
|
|||||||
Scene_.RestoreSnapshot(snapshot.SceneSnapshot);
|
Scene_.RestoreSnapshot(snapshot.SceneSnapshot);
|
||||||
SetSelection(snapshot.SelectionSnapshot.SelectedObjectIds, snapshot.SelectionSnapshot.ActiveObjectId);
|
SetSelection(snapshot.SelectionSnapshot.SelectedObjectIds, snapshot.SelectionSnapshot.ActiveObjectId);
|
||||||
if (snapshot.SelectionSnapshot.SelectionAnchorId != 0 &&
|
if (snapshot.SelectionSnapshot.SelectionAnchorId != 0 &&
|
||||||
Scene_.FindGameObject(snapshot.SelectionSnapshot.SelectionAnchorId) != nullptr) {
|
(bool)Scene_.FindGameObject(snapshot.SelectionSnapshot.SelectionAnchorId)) {
|
||||||
SelectionAnchorId_ = snapshot.SelectionSnapshot.SelectionAnchorId;
|
SelectionAnchorId_ = snapshot.SelectionSnapshot.SelectionAnchorId;
|
||||||
}
|
}
|
||||||
NormalizeSelection();
|
NormalizeSelection();
|
||||||
@ -480,10 +483,10 @@ bool MetaCoreEditorContext::EnsureRuntimeDataConfigLoaded() {
|
|||||||
RuntimeDataConfigLoaded_ = true;
|
RuntimeDataConfigLoaded_ = true;
|
||||||
|
|
||||||
if (!loadedSources.has_value() || !loadedBindings.has_value()) {
|
if (!loadedSources.has_value() || !loadedBindings.has_value()) {
|
||||||
AddConsoleMessage(MetaCoreLogLevel::Info, "RuntimeData", "未找到完整 Runtime 配置,已使用空配置初始化");
|
AddConsoleMessage(MetaCoreLogLevel::Info, "RuntimeData", "Runtime config not found, using empty config");
|
||||||
}
|
}
|
||||||
if (!loadedProjectRuntime.has_value()) {
|
if (!loadedProjectRuntime.has_value()) {
|
||||||
AddConsoleMessage(MetaCoreLogLevel::Info, "RuntimeData", "未找到 ProjectRuntime 配置,将按默认项目运行路径保存");
|
AddConsoleMessage(MetaCoreLogLevel::Info, "RuntimeData", "ProjectRuntime config not found, using default path");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -606,7 +609,7 @@ void MetaCoreEditorContext::NormalizeSelection() {
|
|||||||
sanitizedSelection.reserve(SelectedObjectIds_.size());
|
sanitizedSelection.reserve(SelectedObjectIds_.size());
|
||||||
std::unordered_set<MetaCoreId> deduplicatedIds;
|
std::unordered_set<MetaCoreId> deduplicatedIds;
|
||||||
for (MetaCoreId objectId : SelectedObjectIds_) {
|
for (MetaCoreId objectId : SelectedObjectIds_) {
|
||||||
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr || deduplicatedIds.contains(objectId)) {
|
if (objectId == 0 || !Scene_.FindGameObject(objectId) || deduplicatedIds.contains(objectId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
deduplicatedIds.insert(objectId);
|
deduplicatedIds.insert(objectId);
|
||||||
|
|||||||
675
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.cpp
Normal file
675
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.cpp
Normal file
@ -0,0 +1,675 @@
|
|||||||
|
#include "MetaCoreGltfImporter.h"
|
||||||
|
#include "MetaCoreFoundation/MetaCoreHash.h"
|
||||||
|
|
||||||
|
#include <cgltf.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace MetaCore {
|
||||||
|
|
||||||
|
void MetaCoreGltfTrace(const char* message) {
|
||||||
|
FILE* file = nullptr;
|
||||||
|
if (fopen_s(&file, "editor.crash.log", "a") == 0 && file != nullptr) {
|
||||||
|
std::fputs("[GLTF Import] ", file);
|
||||||
|
std::fputs(message, file);
|
||||||
|
std::fputs("\n", file);
|
||||||
|
std::fclose(file);
|
||||||
|
}
|
||||||
|
std::printf("[GLTF Import] %s\n", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 基础导入辅助函数(从 monolithic 文件中复制以解耦) ---
|
||||||
|
|
||||||
|
void MetaCoreAddImportMessage(
|
||||||
|
MetaCoreModelImportReportDocument& report,
|
||||||
|
MetaCoreImportMessageSeverity severity,
|
||||||
|
std::string code,
|
||||||
|
std::string message
|
||||||
|
) {
|
||||||
|
report.Messages.push_back(MetaCoreImportMessageDocument{
|
||||||
|
severity,
|
||||||
|
std::move(code),
|
||||||
|
std::move(message)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetaCoreFinalizeImportReport(MetaCoreModelImportReportDocument& report) {
|
||||||
|
report.Result = MetaCoreModelImportResult::Success;
|
||||||
|
for (const MetaCoreImportMessageDocument& message : report.Messages) {
|
||||||
|
if (message.Severity == MetaCoreImportMessageSeverity::Error) {
|
||||||
|
report.Result = MetaCoreModelImportResult::Error;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] MetaCoreAssetGuid MetaCoreMakeDeterministicGeneratedAssetGuid(
|
||||||
|
const std::filesystem::path& relativeSourcePath,
|
||||||
|
std::string_view generatedKind,
|
||||||
|
std::string_view stableName,
|
||||||
|
std::size_t stableIndex
|
||||||
|
) {
|
||||||
|
const std::string stableKey =
|
||||||
|
relativeSourcePath.generic_string() +
|
||||||
|
"|" + std::string(generatedKind) +
|
||||||
|
"|" + std::string(stableName) +
|
||||||
|
"|" + std::to_string(stableIndex);
|
||||||
|
const std::uint64_t hashA = MetaCoreHashString(stableKey);
|
||||||
|
const std::uint64_t hashB = MetaCoreHashCombine(hashA, MetaCoreHashString("MetaCoreGeneratedAsset"));
|
||||||
|
|
||||||
|
MetaCoreAssetGuid guid;
|
||||||
|
for (std::size_t index = 0; index < 8; ++index) {
|
||||||
|
guid.Bytes[index] = static_cast<std::uint8_t>((hashA >> (index * 8)) & 0xFFU);
|
||||||
|
guid.Bytes[index + 8] = static_cast<std::uint8_t>((hashB >> (index * 8)) & 0xFFU);
|
||||||
|
}
|
||||||
|
return guid;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string MetaCoreBuildStableImportKey(std::string_view kind, std::string_view stableName, std::size_t stableIndex) {
|
||||||
|
return std::string(kind) + ":" + std::string(stableName) + ":" + std::to_string(stableIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string MetaCoreBuildNodeStablePath(
|
||||||
|
const std::vector<MetaCoreImportedGltfNodeDocument>& nodes,
|
||||||
|
std::size_t nodeIndex
|
||||||
|
) {
|
||||||
|
if (nodeIndex >= nodes.size()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> segments;
|
||||||
|
std::int32_t currentIndex = static_cast<std::int32_t>(nodeIndex);
|
||||||
|
while (currentIndex >= 0 && static_cast<std::size_t>(currentIndex) < nodes.size()) {
|
||||||
|
const MetaCoreImportedGltfNodeDocument& node = nodes[static_cast<std::size_t>(currentIndex)];
|
||||||
|
segments.push_back(node.Name.empty() ? ("Node_" + std::to_string(currentIndex)) : node.Name);
|
||||||
|
currentIndex = node.ParentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string stablePath;
|
||||||
|
for (auto iterator = segments.rbegin(); iterator != segments.rend(); ++iterator) {
|
||||||
|
if (!stablePath.empty()) {
|
||||||
|
stablePath += "/";
|
||||||
|
}
|
||||||
|
stablePath += *iterator;
|
||||||
|
}
|
||||||
|
return stablePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助浮点数比较
|
||||||
|
bool MetaCoreNearlyEqual(float a, float b, float epsilon = 1e-5f) {
|
||||||
|
return std::abs(a - b) < epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MetaCoreNearlyEqualVec3(const glm::vec3& a, const glm::vec3& b, float epsilon = 1e-5f) {
|
||||||
|
return glm::distance(a, b) < epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetaCoreApplyImportSettingsToNodes(
|
||||||
|
std::vector<MetaCoreImportedGltfNodeDocument>& nodes,
|
||||||
|
const MetaCoreModelImportSettingsDocument& settings
|
||||||
|
) {
|
||||||
|
if (nodes.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float scaleFactor = settings.ScaleFactor <= 0.0F ? 1.0F : settings.ScaleFactor;
|
||||||
|
if (!MetaCoreNearlyEqual(scaleFactor, 1.0F)) {
|
||||||
|
for (MetaCoreImportedGltfNodeDocument& node : nodes) {
|
||||||
|
node.Position *= scaleFactor;
|
||||||
|
node.Scale *= scaleFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.PreserveEmptyNodes) {
|
||||||
|
std::vector<MetaCoreImportedGltfNodeDocument> filteredNodes;
|
||||||
|
filteredNodes.reserve(nodes.size());
|
||||||
|
std::vector<std::int32_t> newIndexByOldIndex(nodes.size(), -1);
|
||||||
|
|
||||||
|
for (std::size_t nodeIndex = 0; nodeIndex < nodes.size(); ++nodeIndex) {
|
||||||
|
const MetaCoreImportedGltfNodeDocument& node = nodes[nodeIndex];
|
||||||
|
const bool keepNode = node.MeshIndex >= 0;
|
||||||
|
if (keepNode) {
|
||||||
|
newIndexByOldIndex[nodeIndex] = static_cast<std::int32_t>(filteredNodes.size());
|
||||||
|
filteredNodes.push_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filteredNodes.empty() && !nodes.empty()) {
|
||||||
|
newIndexByOldIndex[0] = 0;
|
||||||
|
filteredNodes.push_back(nodes.front());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t filteredIndex = 0; filteredIndex < filteredNodes.size(); ++filteredIndex) {
|
||||||
|
std::int32_t parentIndex = filteredNodes[filteredIndex].ParentIndex;
|
||||||
|
while (parentIndex >= 0 &&
|
||||||
|
static_cast<std::size_t>(parentIndex) < newIndexByOldIndex.size() &&
|
||||||
|
newIndexByOldIndex[static_cast<std::size_t>(parentIndex)] < 0) {
|
||||||
|
parentIndex = nodes[static_cast<std::size_t>(parentIndex)].ParentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
filteredNodes[filteredIndex].ParentIndex =
|
||||||
|
parentIndex >= 0 && static_cast<std::size_t>(parentIndex) < newIndexByOldIndex.size()
|
||||||
|
? newIndexByOldIndex[static_cast<std::size_t>(parentIndex)]
|
||||||
|
: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes = std::move(filteredNodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetaCoreApplyImportSettingsToMeshes(
|
||||||
|
MetaCoreModelAssetDocument& document,
|
||||||
|
const MetaCoreModelImportSettingsDocument& settings
|
||||||
|
) {
|
||||||
|
if (settings.PreserveMaterialSlots) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MetaCoreImportedGltfMeshDocument& mesh : document.Meshes) {
|
||||||
|
if (!mesh.MaterialSlots.empty()) {
|
||||||
|
mesh.MaterialSlots.resize(1);
|
||||||
|
mesh.MaterialSlots[0] = std::max(mesh.MaterialSlots[0], 0);
|
||||||
|
} else if (!document.GeneratedMaterialAssets.empty()) {
|
||||||
|
mesh.MaterialSlots = {0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (MetaCoreMeshAssetDocument& meshAsset : document.GeneratedMeshAssets) {
|
||||||
|
for (MetaCoreMeshSubMeshDocument& subMesh : meshAsset.SubMeshes) {
|
||||||
|
if (!document.GeneratedMaterialAssets.empty()) {
|
||||||
|
subMesh.MaterialSlotIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetaCoreApplyModelImportSettings(MetaCoreModelAssetDocument& document) {
|
||||||
|
if (document.ImportSettings.ScaleFactor <= 0.0F) {
|
||||||
|
document.ImportSettings.ScaleFactor = 1.0F;
|
||||||
|
}
|
||||||
|
MetaCoreApplyImportSettingsToNodes(document.Nodes, document.ImportSettings);
|
||||||
|
if (!document.ImportSettings.PreserveHierarchy) {
|
||||||
|
for (MetaCoreImportedGltfNodeDocument& node : document.Nodes) {
|
||||||
|
node.ParentIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glm::vec3 upAxisRotation{0.0F, 0.0F, 0.0F};
|
||||||
|
switch (document.ImportSettings.UpAxis) {
|
||||||
|
case MetaCoreModelImportUpAxis::ZUp:
|
||||||
|
upAxisRotation.x = -90.0F;
|
||||||
|
break;
|
||||||
|
case MetaCoreModelImportUpAxis::XUp:
|
||||||
|
upAxisRotation.z = 90.0F;
|
||||||
|
break;
|
||||||
|
case MetaCoreModelImportUpAxis::YUp:
|
||||||
|
case MetaCoreModelImportUpAxis::PreserveSource:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!MetaCoreNearlyEqualVec3(upAxisRotation, glm::vec3(0.0F))) {
|
||||||
|
for (MetaCoreImportedGltfNodeDocument& node : document.Nodes) {
|
||||||
|
if (node.ParentIndex < 0) {
|
||||||
|
node.RotationEulerDegrees += upAxisRotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MetaCoreApplyImportSettingsToMeshes(document, document.ImportSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- JSON 字符串解码辅助函数(专用于 GLTF 名字解析) ---
|
||||||
|
|
||||||
|
void MetaCoreAppendUtf8Codepoint(std::string& output, std::uint32_t codepoint) {
|
||||||
|
if (codepoint <= 0x7F) {
|
||||||
|
output.push_back(static_cast<char>(codepoint));
|
||||||
|
} else if (codepoint <= 0x7FF) {
|
||||||
|
output.push_back(static_cast<char>(0xC0 | ((codepoint >> 6) & 0x1F)));
|
||||||
|
output.push_back(static_cast<char>(0x80 | (codepoint & 0x3F)));
|
||||||
|
} else if (codepoint <= 0xFFFF) {
|
||||||
|
output.push_back(static_cast<char>(0xE0 | ((codepoint >> 12) & 0x0F)));
|
||||||
|
output.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||||
|
output.push_back(static_cast<char>(0x80 | (codepoint & 0x3F)));
|
||||||
|
} else {
|
||||||
|
output.push_back(static_cast<char>(0xF0 | ((codepoint >> 18) & 0x07)));
|
||||||
|
output.push_back(static_cast<char>(0x80 | ((codepoint >> 12) & 0x3F)));
|
||||||
|
output.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F)));
|
||||||
|
output.push_back(static_cast<char>(0x80 | (codepoint & 0x3F)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<std::uint32_t> MetaCoreParseHexCodepoint(std::string_view text, std::size_t offset) {
|
||||||
|
if (offset + 4 > text.size()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t value = 0;
|
||||||
|
for (std::size_t i = 0; i < 4; ++i) {
|
||||||
|
const char ch = text[offset + i];
|
||||||
|
value <<= 4;
|
||||||
|
if (ch >= '0' && ch <= '9') {
|
||||||
|
value += static_cast<std::uint32_t>(ch - '0');
|
||||||
|
} else if (ch >= 'a' && ch <= 'f') {
|
||||||
|
value += static_cast<std::uint32_t>(10 + ch - 'a');
|
||||||
|
} else if (ch >= 'A' && ch <= 'F') {
|
||||||
|
value += static_cast<std::uint32_t>(10 + ch - 'A');
|
||||||
|
} else {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string MetaCoreDecodeJsonEscapedUtf8(std::string_view text) {
|
||||||
|
std::string output;
|
||||||
|
output.reserve(text.size());
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < text.size(); ++i) {
|
||||||
|
if (text[i] != '\\' || i + 1 >= text.size()) {
|
||||||
|
output.push_back(text[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char escaped = text[++i];
|
||||||
|
switch (escaped) {
|
||||||
|
case '"': output.push_back('"'); break;
|
||||||
|
case '\\': output.push_back('\\'); break;
|
||||||
|
case '/': output.push_back('/'); break;
|
||||||
|
case 'b': output.push_back('\b'); break;
|
||||||
|
case 'f': output.push_back('\f'); break;
|
||||||
|
case 'n': output.push_back('\n'); break;
|
||||||
|
case 'r': output.push_back('\r'); break;
|
||||||
|
case 't': output.push_back('\t'); break;
|
||||||
|
case 'u': {
|
||||||
|
const auto first = MetaCoreParseHexCodepoint(text, i + 1);
|
||||||
|
if (!first.has_value()) {
|
||||||
|
output += "\\u";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += 4;
|
||||||
|
std::uint32_t codepoint = *first;
|
||||||
|
if (codepoint >= 0xD800 && codepoint <= 0xDBFF &&
|
||||||
|
i + 6 < text.size() && text[i + 1] == '\\' && text[i + 2] == 'u') {
|
||||||
|
const auto second = MetaCoreParseHexCodepoint(text, i + 3);
|
||||||
|
if (second.has_value() && *second >= 0xDC00 && *second <= 0xDFFF) {
|
||||||
|
codepoint = 0x10000 + ((codepoint - 0xD800) << 10) + (*second - 0xDC00);
|
||||||
|
i += 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MetaCoreAppendUtf8Codepoint(output, codepoint);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
output.push_back(escaped);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string MetaCoreGltfDisplayName(const char* rawName, std::string_view fallbackName) {
|
||||||
|
if (rawName == nullptr || rawName[0] == '\0') {
|
||||||
|
return std::string(fallbackName);
|
||||||
|
}
|
||||||
|
return MetaCoreDecodeJsonEscapedUtf8(rawName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GLTF 核心导入逻辑 ---
|
||||||
|
|
||||||
|
[[nodiscard]] MetaCoreModelAssetDocument MetaCoreBuildImportedGltfAssetDocument(
|
||||||
|
const std::filesystem::path& absoluteSourcePath,
|
||||||
|
const std::filesystem::path& relativeSourcePath,
|
||||||
|
std::uint64_t sourceHash,
|
||||||
|
std::vector<std::vector<std::byte>>& outMeshPayloads,
|
||||||
|
MetaCoreModelImportSettingsDocument importSettings
|
||||||
|
) {
|
||||||
|
MetaCoreModelAssetDocument document;
|
||||||
|
document.AssetType = "model";
|
||||||
|
document.RootDisplayName = absoluteSourcePath.filename().generic_string();
|
||||||
|
document.ImporterId = "GltfModelImporter";
|
||||||
|
document.SourcePath = relativeSourcePath;
|
||||||
|
document.SourceHash = sourceHash;
|
||||||
|
document.ModelKind = MetaCoreModelAssetKind::Gltf;
|
||||||
|
document.SourceFormat = absoluteSourcePath.extension() == ".glb" ? "glb" : "gltf";
|
||||||
|
document.ImportSettings = importSettings;
|
||||||
|
|
||||||
|
const auto addFallbackSkeleton = [&]() {
|
||||||
|
document.ImportReport.UsedFallback = true;
|
||||||
|
MetaCoreAddImportMessage(
|
||||||
|
document.ImportReport,
|
||||||
|
MetaCoreImportMessageSeverity::Warning,
|
||||||
|
"fallback_skeleton",
|
||||||
|
"源模型未能完整解析,已生成最小占位模型骨架"
|
||||||
|
);
|
||||||
|
MetaCoreImportedGltfNodeDocument rootNode;
|
||||||
|
rootNode.Name = absoluteSourcePath.stem().string();
|
||||||
|
rootNode.StableNodePath = rootNode.Name;
|
||||||
|
rootNode.MeshIndex = 0;
|
||||||
|
document.Nodes.push_back(std::move(rootNode));
|
||||||
|
|
||||||
|
MetaCoreImportedGltfMeshDocument meshDocument;
|
||||||
|
meshDocument.Name = absoluteSourcePath.stem().string();
|
||||||
|
meshDocument.PrimitiveCount = 1;
|
||||||
|
meshDocument.MaterialSlots.push_back(0);
|
||||||
|
document.Meshes.push_back(std::move(meshDocument));
|
||||||
|
|
||||||
|
MetaCoreMeshAssetDocument meshAsset;
|
||||||
|
meshAsset.AssetGuid = MetaCoreMakeDeterministicGeneratedAssetGuid(
|
||||||
|
relativeSourcePath,
|
||||||
|
"mesh",
|
||||||
|
absoluteSourcePath.stem().string(),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
meshAsset.Name = absoluteSourcePath.stem().string();
|
||||||
|
meshAsset.StableImportKey = MetaCoreBuildStableImportKey("mesh", meshAsset.Name, 0);
|
||||||
|
document.GeneratedMeshAssets.push_back(std::move(meshAsset));
|
||||||
|
|
||||||
|
MetaCoreImportedGltfMaterialDocument materialDocument;
|
||||||
|
materialDocument.Name = absoluteSourcePath.stem().string() + "_Material";
|
||||||
|
document.Materials.push_back(std::move(materialDocument));
|
||||||
|
|
||||||
|
MetaCoreMaterialAssetDocument materialAsset;
|
||||||
|
materialAsset.AssetGuid = MetaCoreMakeDeterministicGeneratedAssetGuid(
|
||||||
|
relativeSourcePath,
|
||||||
|
"material",
|
||||||
|
absoluteSourcePath.stem().string(),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
materialAsset.Name = absoluteSourcePath.stem().string() + "_Material";
|
||||||
|
materialAsset.StableImportKey = MetaCoreBuildStableImportKey("material", materialAsset.Name, 0);
|
||||||
|
document.GeneratedMaterialAssets.push_back(std::move(materialAsset));
|
||||||
|
};
|
||||||
|
|
||||||
|
cgltf_options options = {};
|
||||||
|
cgltf_data* data = nullptr;
|
||||||
|
|
||||||
|
MetaCoreGltfTrace(("开始解析 GLTF 文件: " + absoluteSourcePath.string()).c_str());
|
||||||
|
cgltf_result result = cgltf_parse_file(&options, absoluteSourcePath.string().c_str(), &data);
|
||||||
|
|
||||||
|
if (result != cgltf_result_success) {
|
||||||
|
MetaCoreGltfTrace("GLTF 解析失败!");
|
||||||
|
addFallbackSkeleton();
|
||||||
|
MetaCoreFinalizeImportReport(document.ImportReport);
|
||||||
|
MetaCoreApplyModelImportSettings(document);
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreGltfTrace("GLTF 解析成功,开始加载 buffers...");
|
||||||
|
result = cgltf_load_buffers(&options, data, absoluteSourcePath.string().c_str());
|
||||||
|
if (result != cgltf_result_success) {
|
||||||
|
MetaCoreGltfTrace("GLTF buffers 加载失败!");
|
||||||
|
cgltf_free(data);
|
||||||
|
addFallbackSkeleton();
|
||||||
|
MetaCoreAddImportMessage(
|
||||||
|
document.ImportReport,
|
||||||
|
MetaCoreImportMessageSeverity::Warning,
|
||||||
|
"buffer_load_failed",
|
||||||
|
"glTF 缓冲区读取失败,已退回占位模型骨架"
|
||||||
|
);
|
||||||
|
MetaCoreFinalizeImportReport(document.ImportReport);
|
||||||
|
MetaCoreApplyModelImportSettings(document);
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreGltfTrace(("GLTF buffers 加载成功. Meshes count: " + std::to_string(data->meshes_count)).c_str());
|
||||||
|
|
||||||
|
if (data->images_count > 0) {
|
||||||
|
for (std::size_t i = 0; i < data->images_count; ++i) {
|
||||||
|
const cgltf_image& image = data->images[i];
|
||||||
|
MetaCoreImportedGltfTextureDocument textureDoc;
|
||||||
|
textureDoc.SourcePath = image.uri ? std::filesystem::path(image.uri).lexically_normal() : std::filesystem::path{};
|
||||||
|
textureDoc.UsageHint = "Unknown";
|
||||||
|
|
||||||
|
MetaCoreTextureAssetDocument genTex;
|
||||||
|
const std::string imageName = MetaCoreGltfDisplayName(image.name, "Texture_" + std::to_string(i));
|
||||||
|
genTex.AssetGuid = MetaCoreMakeDeterministicGeneratedAssetGuid(relativeSourcePath, "texture", imageName, i);
|
||||||
|
genTex.Name = imageName;
|
||||||
|
genTex.StableImportKey = MetaCoreBuildStableImportKey("texture", imageName, i);
|
||||||
|
genTex.SourcePath = textureDoc.SourcePath;
|
||||||
|
genTex.UsageHint = "Unknown";
|
||||||
|
|
||||||
|
document.Textures.push_back(std::move(textureDoc));
|
||||||
|
document.GeneratedTextureAssets.push_back(std::move(genTex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->materials_count > 0) {
|
||||||
|
for (std::size_t i = 0; i < data->materials_count; ++i) {
|
||||||
|
const cgltf_material& mat = data->materials[i];
|
||||||
|
MetaCoreImportedGltfMaterialDocument matDoc;
|
||||||
|
matDoc.Name = MetaCoreGltfDisplayName(mat.name, "Material_" + std::to_string(i));
|
||||||
|
matDoc.DoubleSided = mat.double_sided;
|
||||||
|
|
||||||
|
MetaCoreMaterialAssetDocument genMat;
|
||||||
|
genMat.AssetGuid = MetaCoreMakeDeterministicGeneratedAssetGuid(relativeSourcePath, "material", matDoc.Name, i);
|
||||||
|
genMat.Name = matDoc.Name;
|
||||||
|
genMat.StableImportKey = MetaCoreBuildStableImportKey("material", matDoc.Name, i);
|
||||||
|
genMat.DoubleSided = mat.double_sided;
|
||||||
|
genMat.AlphaCutoff = mat.alpha_cutoff;
|
||||||
|
genMat.AlphaMode = mat.alpha_mode == cgltf_alpha_mode_blend ? MetaCoreMaterialAlphaMode::Blend : (mat.alpha_mode == cgltf_alpha_mode_mask ? MetaCoreMaterialAlphaMode::Mask : MetaCoreMaterialAlphaMode::Opaque);
|
||||||
|
|
||||||
|
if (mat.has_pbr_metallic_roughness) {
|
||||||
|
genMat.BaseColor = glm::vec3(mat.pbr_metallic_roughness.base_color_factor[0], mat.pbr_metallic_roughness.base_color_factor[1], mat.pbr_metallic_roughness.base_color_factor[2]);
|
||||||
|
genMat.Metallic = mat.pbr_metallic_roughness.metallic_factor;
|
||||||
|
genMat.Roughness = mat.pbr_metallic_roughness.roughness_factor;
|
||||||
|
|
||||||
|
if (mat.pbr_metallic_roughness.base_color_texture.texture) {
|
||||||
|
const std::int32_t texIdx = static_cast<std::int32_t>(mat.pbr_metallic_roughness.base_color_texture.texture->image - data->images);
|
||||||
|
if (texIdx >= 0 && static_cast<std::size_t>(texIdx) < document.GeneratedTextureAssets.size()) {
|
||||||
|
matDoc.TextureIndices.push_back(texIdx);
|
||||||
|
genMat.BaseColorTexture = document.GeneratedTextureAssets[static_cast<std::size_t>(texIdx)].AssetGuid;
|
||||||
|
} else {
|
||||||
|
MetaCoreAddImportMessage(
|
||||||
|
document.ImportReport,
|
||||||
|
MetaCoreImportMessageSeverity::Warning,
|
||||||
|
"missing_texture_reference",
|
||||||
|
"材质引用了超出范围的贴图,已跳过该贴图槽位"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matDoc.TextureIndices.empty() && !document.GeneratedTextureAssets.empty()) {
|
||||||
|
matDoc.TextureIndices.push_back(0);
|
||||||
|
genMat.BaseColorTexture = document.GeneratedTextureAssets.front().AssetGuid;
|
||||||
|
MetaCoreAddImportMessage(
|
||||||
|
document.ImportReport,
|
||||||
|
MetaCoreImportMessageSeverity::Info,
|
||||||
|
"default_texture_binding",
|
||||||
|
"材质缺少贴图引用时,已回退到首个可用贴图资源"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.Materials.push_back(std::move(matDoc));
|
||||||
|
document.GeneratedMaterialAssets.push_back(std::move(genMat));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->meshes_count > 0) {
|
||||||
|
for (std::size_t i = 0; i < data->meshes_count; ++i) {
|
||||||
|
const cgltf_mesh& mesh = data->meshes[i];
|
||||||
|
MetaCoreImportedGltfMeshDocument meshDoc;
|
||||||
|
meshDoc.Name = MetaCoreGltfDisplayName(mesh.name, "Mesh_" + std::to_string(i));
|
||||||
|
meshDoc.PrimitiveCount = static_cast<std::int32_t>(mesh.primitives_count);
|
||||||
|
|
||||||
|
MetaCoreGltfTrace(("处理 Mesh [" + std::to_string(i) + "]: " + meshDoc.Name + ", Primitives: " + std::to_string(mesh.primitives_count)).c_str());
|
||||||
|
|
||||||
|
MetaCoreMeshAssetDocument genMesh;
|
||||||
|
genMesh.AssetGuid = MetaCoreMakeDeterministicGeneratedAssetGuid(relativeSourcePath, "mesh", meshDoc.Name, i);
|
||||||
|
genMesh.Name = meshDoc.Name;
|
||||||
|
genMesh.StableImportKey = MetaCoreBuildStableImportKey("mesh", meshDoc.Name, i);
|
||||||
|
|
||||||
|
std::vector<std::byte> meshBinaryData;
|
||||||
|
struct Vertex {
|
||||||
|
float px, py, pz;
|
||||||
|
float nx, ny, nz;
|
||||||
|
float u, v;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Vertex> vertices;
|
||||||
|
std::vector<std::uint32_t> indices;
|
||||||
|
|
||||||
|
glm::vec3 bmin{1e30, 1e30, 1e30};
|
||||||
|
glm::vec3 bmax{-1e30, -1e30, -1e30};
|
||||||
|
|
||||||
|
for (std::size_t p = 0; p < mesh.primitives_count; ++p) {
|
||||||
|
const cgltf_primitive& prim = mesh.primitives[p];
|
||||||
|
|
||||||
|
const std::uint32_t vertexOffset = static_cast<std::uint32_t>(vertices.size());
|
||||||
|
|
||||||
|
const cgltf_accessor* posAcc = nullptr;
|
||||||
|
const cgltf_accessor* normAcc = nullptr;
|
||||||
|
const cgltf_accessor* uvAcc = nullptr;
|
||||||
|
|
||||||
|
for (std::size_t a = 0; a < prim.attributes_count; ++a) {
|
||||||
|
const cgltf_attribute& attr = prim.attributes[a];
|
||||||
|
if (attr.type == cgltf_attribute_type_position) posAcc = attr.data;
|
||||||
|
if (attr.type == cgltf_attribute_type_normal) normAcc = attr.data;
|
||||||
|
if (attr.type == cgltf_attribute_type_texcoord) uvAcc = attr.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!posAcc) continue;
|
||||||
|
|
||||||
|
for (std::size_t v = 0; v < posAcc->count; ++v) {
|
||||||
|
Vertex vertex{};
|
||||||
|
float pos[3];
|
||||||
|
cgltf_accessor_read_float(posAcc, v, pos, 3);
|
||||||
|
vertex.px = pos[0]; vertex.py = pos[1]; vertex.pz = pos[2];
|
||||||
|
|
||||||
|
bmin = glm::min(bmin, glm::vec3(pos[0], pos[1], pos[2]));
|
||||||
|
bmax = glm::max(bmax, glm::vec3(pos[0], pos[1], pos[2]));
|
||||||
|
|
||||||
|
if (normAcc) {
|
||||||
|
float norm[3];
|
||||||
|
cgltf_accessor_read_float(normAcc, v, norm, 3);
|
||||||
|
vertex.nx = norm[0]; vertex.ny = norm[1]; vertex.nz = norm[2];
|
||||||
|
}
|
||||||
|
if (uvAcc) {
|
||||||
|
float uv[2];
|
||||||
|
cgltf_accessor_read_float(uvAcc, v, uv, 2);
|
||||||
|
vertex.u = uv[0]; vertex.v = uv[1];
|
||||||
|
}
|
||||||
|
vertices.push_back(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::uint32_t firstIndex = static_cast<std::uint32_t>(indices.size());
|
||||||
|
|
||||||
|
if (prim.indices) {
|
||||||
|
for (std::size_t idx = 0; idx < prim.indices->count; ++idx) {
|
||||||
|
indices.push_back(vertexOffset + static_cast<std::uint32_t>(cgltf_accessor_read_index(prim.indices, idx)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (std::size_t idx = 0; idx < posAcc->count; ++idx) {
|
||||||
|
indices.push_back(vertexOffset + static_cast<std::uint32_t>(idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::int32_t matIdx = -1;
|
||||||
|
if (prim.material) {
|
||||||
|
matIdx = static_cast<std::int32_t>(prim.material - data->materials);
|
||||||
|
meshDoc.MaterialSlots.push_back(matIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreMeshSubMeshDocument subMesh;
|
||||||
|
subMesh.Name = "Primitive_" + std::to_string(p);
|
||||||
|
subMesh.MaterialSlotIndex = matIdx;
|
||||||
|
subMesh.FirstIndex = firstIndex;
|
||||||
|
subMesh.IndexCount = static_cast<std::uint32_t>(indices.size()) - firstIndex;
|
||||||
|
genMesh.SubMeshes.push_back(std::move(subMesh));
|
||||||
|
}
|
||||||
|
if (meshDoc.MaterialSlots.empty() && !document.GeneratedMaterialAssets.empty()) {
|
||||||
|
meshDoc.MaterialSlots.push_back(0);
|
||||||
|
MetaCoreMeshSubMeshDocument defaultSubMesh;
|
||||||
|
defaultSubMesh.Name = "DefaultSubMesh";
|
||||||
|
defaultSubMesh.MaterialSlotIndex = 0;
|
||||||
|
defaultSubMesh.FirstIndex = 0;
|
||||||
|
defaultSubMesh.IndexCount = static_cast<std::uint32_t>(indices.size());
|
||||||
|
genMesh.SubMeshes.push_back(std::move(defaultSubMesh));
|
||||||
|
}
|
||||||
|
|
||||||
|
genMesh.VertexCount = static_cast<std::int32_t>(vertices.size());
|
||||||
|
genMesh.IndexCount = static_cast<std::int32_t>(indices.size());
|
||||||
|
genMesh.BoundingBoxMin = bmin;
|
||||||
|
genMesh.BoundingBoxMax = bmax;
|
||||||
|
|
||||||
|
MetaCoreGltfTrace((" Mesh [" + std::to_string(i) + "] 顶点数: " + std::to_string(genMesh.VertexCount) + ", 索引数: " + std::to_string(genMesh.IndexCount)).c_str());
|
||||||
|
|
||||||
|
const std::size_t payloadSize = sizeof(std::uint32_t) * 2 + vertices.size() * sizeof(Vertex) + indices.size() * sizeof(std::uint32_t);
|
||||||
|
meshBinaryData.resize(payloadSize);
|
||||||
|
std::byte* ptr = meshBinaryData.data();
|
||||||
|
|
||||||
|
std::uint32_t vCount = static_cast<std::uint32_t>(vertices.size());
|
||||||
|
std::uint32_t iCount = static_cast<std::uint32_t>(indices.size());
|
||||||
|
|
||||||
|
std::memcpy(ptr, &vCount, sizeof(std::uint32_t)); ptr += sizeof(std::uint32_t);
|
||||||
|
std::memcpy(ptr, &iCount, sizeof(std::uint32_t)); ptr += sizeof(std::uint32_t);
|
||||||
|
std::memcpy(ptr, vertices.data(), vertices.size() * sizeof(Vertex)); ptr += vertices.size() * sizeof(Vertex);
|
||||||
|
std::memcpy(ptr, indices.data(), indices.size() * sizeof(std::uint32_t));
|
||||||
|
|
||||||
|
genMesh.PayloadIndex = 1 + outMeshPayloads.size();
|
||||||
|
genMesh.PayloadSize = payloadSize;
|
||||||
|
outMeshPayloads.push_back(std::move(meshBinaryData));
|
||||||
|
|
||||||
|
document.Meshes.push_back(std::move(meshDoc));
|
||||||
|
document.GeneratedMeshAssets.push_back(std::move(genMesh));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->nodes_count > 0) {
|
||||||
|
for (std::size_t i = 0; i < data->nodes_count; ++i) {
|
||||||
|
const cgltf_node& node = data->nodes[i];
|
||||||
|
MetaCoreImportedGltfNodeDocument nodeDoc;
|
||||||
|
if (node.name && strlen(node.name) > 0) {
|
||||||
|
nodeDoc.Name = MetaCoreGltfDisplayName(node.name, "Node_" + std::to_string(i));
|
||||||
|
} else if (node.mesh && node.mesh->name && strlen(node.mesh->name) > 0) {
|
||||||
|
nodeDoc.Name = MetaCoreGltfDisplayName(node.mesh->name, "Mesh_" + std::to_string(i)) + "_Node";
|
||||||
|
} else {
|
||||||
|
nodeDoc.Name = "Node_" + std::to_string(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeDoc.ParentIndex = node.parent ? static_cast<std::int32_t>(node.parent - data->nodes) : -1;
|
||||||
|
nodeDoc.MeshIndex = node.mesh ? static_cast<std::int32_t>(node.mesh - data->meshes) : -1;
|
||||||
|
|
||||||
|
if (node.has_translation) {
|
||||||
|
nodeDoc.Position = glm::vec3(node.translation[0], node.translation[1], node.translation[2]);
|
||||||
|
}
|
||||||
|
if (node.has_rotation) {
|
||||||
|
const glm::quat q(node.rotation[3], node.rotation[0], node.rotation[1], node.rotation[2]);
|
||||||
|
nodeDoc.RotationEulerDegrees = glm::degrees(glm::eulerAngles(q));
|
||||||
|
}
|
||||||
|
if (node.has_scale) {
|
||||||
|
nodeDoc.Scale = glm::vec3(node.scale[0], node.scale[1], node.scale[2]);
|
||||||
|
}
|
||||||
|
document.Nodes.push_back(std::move(nodeDoc));
|
||||||
|
}
|
||||||
|
for (std::size_t nodeIndex = 0; nodeIndex < document.Nodes.size(); ++nodeIndex) {
|
||||||
|
document.Nodes[nodeIndex].StableNodePath = MetaCoreBuildNodeStablePath(document.Nodes, nodeIndex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MetaCoreImportedGltfNodeDocument rootNode;
|
||||||
|
rootNode.Name = absoluteSourcePath.stem().string();
|
||||||
|
rootNode.StableNodePath = rootNode.Name;
|
||||||
|
rootNode.MeshIndex = document.Meshes.empty() ? -1 : 0;
|
||||||
|
document.Nodes.push_back(std::move(rootNode));
|
||||||
|
MetaCoreAddImportMessage(
|
||||||
|
document.ImportReport,
|
||||||
|
MetaCoreImportMessageSeverity::Info,
|
||||||
|
"generated_root_node",
|
||||||
|
"源模型未提供节点层级,已生成默认根节点"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cgltf_free(data);
|
||||||
|
MetaCoreFinalizeImportReport(document.ImportReport);
|
||||||
|
MetaCoreApplyModelImportSettings(document);
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace MetaCore
|
||||||
18
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.h
Normal file
18
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MetaCoreFoundation/MetaCoreAssetTypes.h"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace MetaCore {
|
||||||
|
|
||||||
|
[[nodiscard]] MetaCoreModelAssetDocument MetaCoreBuildImportedGltfAssetDocument(
|
||||||
|
const std::filesystem::path& absoluteSourcePath,
|
||||||
|
const std::filesystem::path& relativeSourcePath,
|
||||||
|
std::uint64_t sourceHash,
|
||||||
|
std::vector<std::vector<std::byte>>& outMeshPayloads,
|
||||||
|
MetaCoreModelImportSettingsDocument importSettings = {}
|
||||||
|
);
|
||||||
|
|
||||||
|
} // namespace MetaCore
|
||||||
41
Source/MetaCoreEditor/Private/MetaCoreImportHelpers.h
Normal file
41
Source/MetaCoreEditor/Private/MetaCoreImportHelpers.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MetaCoreFoundation/MetaCoreAssetTypes.h"
|
||||||
|
#include <filesystem>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace MetaCore {
|
||||||
|
|
||||||
|
void MetaCoreAddImportMessage(
|
||||||
|
MetaCoreModelImportReportDocument& report,
|
||||||
|
MetaCoreImportMessageSeverity severity,
|
||||||
|
std::string code,
|
||||||
|
std::string message
|
||||||
|
);
|
||||||
|
|
||||||
|
void MetaCoreFinalizeImportReport(MetaCoreModelImportReportDocument& report);
|
||||||
|
|
||||||
|
[[nodiscard]] MetaCoreAssetGuid MetaCoreMakeDeterministicGeneratedAssetGuid(
|
||||||
|
const std::filesystem::path& relativeSourcePath,
|
||||||
|
std::string_view generatedKind,
|
||||||
|
std::string_view stableName,
|
||||||
|
std::size_t stableIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
[[nodiscard]] std::string MetaCoreBuildStableImportKey(
|
||||||
|
std::string_view kind,
|
||||||
|
std::string_view stableName,
|
||||||
|
std::size_t stableIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
[[nodiscard]] std::string MetaCoreBuildNodeStablePath(
|
||||||
|
const std::vector<MetaCoreImportedGltfNodeDocument>& nodes,
|
||||||
|
std::size_t nodeIndex
|
||||||
|
);
|
||||||
|
|
||||||
|
void MetaCoreApplyModelImportSettings(MetaCoreModelAssetDocument& document);
|
||||||
|
|
||||||
|
} // namespace MetaCore
|
||||||
@ -287,7 +287,7 @@ void MetaCoreSceneInteractionService::FocusSelectedObject(MetaCoreEditorContext&
|
|||||||
if (selectedObjectIds.size() == 1) {
|
if (selectedObjectIds.size() == 1) {
|
||||||
MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
|
MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
|
||||||
if (selectedObject) {
|
if (selectedObject) {
|
||||||
editorContext.GetCameraController().FocusGameObject(*selectedObject);
|
editorContext.GetCameraController().FocusGameObject(selectedObject);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -414,7 +414,7 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
|
|||||||
effectiveMode = ImGuizmo::LOCAL;
|
effectiveMode = ImGuizmo::LOCAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuizmo::PushID(reinterpret_cast<void*>(selectedObject));
|
ImGuizmo::PushID(reinterpret_cast<void*>(selectedObject.GetId()));
|
||||||
glm::mat4 gizmoMatrix = worldMatrixMetaCore;
|
glm::mat4 gizmoMatrix = worldMatrixMetaCore;
|
||||||
ImGuizmo::Manipulate(
|
ImGuizmo::Manipulate(
|
||||||
glm::value_ptr(gizmoViewMatrix),
|
glm::value_ptr(gizmoViewMatrix),
|
||||||
@ -505,7 +505,7 @@ void MetaCoreSceneInteractionService::DrawViewportToolbar(MetaCoreEditorContext&
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine(0, 12);
|
ImGui::SameLine(0, 12);
|
||||||
const bool hasSelection = editorContext.GetSelectedGameObject() != nullptr;
|
const bool hasSelection = (bool)editorContext.GetSelectedGameObject();
|
||||||
if (!hasSelection) {
|
if (!hasSelection) {
|
||||||
ImGui::BeginDisabled();
|
ImGui::BeginDisabled();
|
||||||
}
|
}
|
||||||
@ -548,14 +548,14 @@ void MetaCoreSceneInteractionService::DrawViewportToolbar(MetaCoreEditorContext&
|
|||||||
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
|
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
|
||||||
ImGui::TextDisabled(
|
ImGui::TextDisabled(
|
||||||
"Selection: %s",
|
"Selection: %s",
|
||||||
activeObject != nullptr ? activeObject->Name.c_str() : "<Missing>"
|
activeObject ? activeObject.GetName().c_str() : "<Missing>"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
|
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
|
||||||
ImGui::TextDisabled(
|
ImGui::TextDisabled(
|
||||||
"Selection: %zu objects | Active: %s",
|
"Selection: %zu objects | Active: %s",
|
||||||
selectedObjectIds.size(),
|
selectedObjectIds.size(),
|
||||||
activeObject != nullptr ? activeObject->Name.c_str() : "<Missing>"
|
activeObject ? activeObject.GetName().c_str() : "<Missing>"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,7 +589,7 @@ void MetaCoreSceneInteractionService::HandleShortcuts(MetaCoreEditorContext& edi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_F) && editorContext.GetSelectedGameObject() != nullptr) {
|
if (ImGui::IsKeyPressed(ImGuiKey_F) && (bool)editorContext.GetSelectedGameObject()) {
|
||||||
FocusSelectedObject(editorContext);
|
FocusSelectedObject(editorContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -89,7 +89,7 @@ struct MetaCorePrefabDocument {
|
|||||||
std::string Name{};
|
std::string Name{};
|
||||||
|
|
||||||
MC_PROPERTY()
|
MC_PROPERTY()
|
||||||
std::vector<MetaCoreGameObject> GameObjects{};
|
std::vector<MetaCoreGameObjectData> GameObjects{};
|
||||||
};
|
};
|
||||||
|
|
||||||
MC_STRUCT()
|
MC_STRUCT()
|
||||||
|
|||||||
@ -134,8 +134,8 @@ public:
|
|||||||
void SelectRangeByOrderedIds(const std::vector<MetaCoreId>& orderedIds, MetaCoreId endId, bool additive);
|
void SelectRangeByOrderedIds(const std::vector<MetaCoreId>& orderedIds, MetaCoreId endId, bool additive);
|
||||||
[[nodiscard]] MetaCoreId GetSelectedObjectId() const;
|
[[nodiscard]] MetaCoreId GetSelectedObjectId() const;
|
||||||
void SetSelectedObjectId(MetaCoreId objectId);
|
void SetSelectedObjectId(MetaCoreId objectId);
|
||||||
[[nodiscard]] MetaCoreGameObject* GetSelectedGameObject();
|
[[nodiscard]] MetaCoreGameObject GetSelectedGameObject();
|
||||||
[[nodiscard]] const MetaCoreGameObject* GetSelectedGameObject() const;
|
[[nodiscard]] MetaCoreGameObject GetSelectedGameObject() const;
|
||||||
[[nodiscard]] MetaCoreGizmoOperation GetGizmoOperation() const;
|
[[nodiscard]] MetaCoreGizmoOperation GetGizmoOperation() const;
|
||||||
void SetGizmoOperation(MetaCoreGizmoOperation operation);
|
void SetGizmoOperation(MetaCoreGizmoOperation operation);
|
||||||
[[nodiscard]] MetaCoreGizmoMode GetGizmoMode() const;
|
[[nodiscard]] MetaCoreGizmoMode GetGizmoMode() const;
|
||||||
|
|||||||
@ -148,3 +148,173 @@ std::optional<MetaCorePackageDocument> MetaCoreReadPackageFile(
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace MetaCore
|
} // namespace MetaCore
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace MetaCore {
|
||||||
|
|
||||||
|
MetaCoreMappedPackage::~MetaCoreMappedPackage() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (MappedData_ != nullptr) {
|
||||||
|
UnmapViewOfFile(MappedData_);
|
||||||
|
MappedData_ = nullptr;
|
||||||
|
}
|
||||||
|
if (MapHandle_ != nullptr) {
|
||||||
|
CloseHandle(MapHandle_);
|
||||||
|
MapHandle_ = nullptr;
|
||||||
|
}
|
||||||
|
if (FileHandle_ != INVALID_HANDLE_VALUE && FileHandle_ != nullptr) {
|
||||||
|
CloseHandle(FileHandle_);
|
||||||
|
FileHandle_ = nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreMappedPackage::MetaCoreMappedPackage(MetaCoreMappedPackage&& other) noexcept
|
||||||
|
: FileHandle_(other.FileHandle_),
|
||||||
|
MapHandle_(other.MapHandle_),
|
||||||
|
MappedData_(other.MappedData_),
|
||||||
|
MappedSize_(other.MappedSize_),
|
||||||
|
Document_(std::move(other.Document_)) {
|
||||||
|
other.FileHandle_ = nullptr;
|
||||||
|
other.MapHandle_ = nullptr;
|
||||||
|
other.MappedData_ = nullptr;
|
||||||
|
other.MappedSize_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreMappedPackage& MetaCoreMappedPackage::operator=(MetaCoreMappedPackage&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
this->~MetaCoreMappedPackage();
|
||||||
|
FileHandle_ = other.FileHandle_;
|
||||||
|
MapHandle_ = other.MapHandle_;
|
||||||
|
MappedData_ = other.MappedData_;
|
||||||
|
MappedSize_ = other.MappedSize_;
|
||||||
|
Document_ = std::move(other.Document_);
|
||||||
|
|
||||||
|
other.FileHandle_ = nullptr;
|
||||||
|
other.MapHandle_ = nullptr;
|
||||||
|
other.MappedData_ = nullptr;
|
||||||
|
other.MappedSize_ = 0;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<MetaCoreMappedPackage> MetaCoreMappedPackage::Open(
|
||||||
|
const std::filesystem::path& path,
|
||||||
|
const MetaCoreTypeRegistry& registry
|
||||||
|
) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE hFile = CreateFileW(
|
||||||
|
path.c_str(),
|
||||||
|
GENERIC_READ,
|
||||||
|
FILE_SHARE_READ,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE) return std::nullopt;
|
||||||
|
|
||||||
|
LARGE_INTEGER fileSize;
|
||||||
|
if (!GetFileSizeEx(hFile, &fileSize) || fileSize.QuadPart == 0) {
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE hMap = CreateFileMappingW(
|
||||||
|
hFile,
|
||||||
|
NULL,
|
||||||
|
PAGE_READONLY,
|
||||||
|
0, 0,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
if (hMap == NULL) {
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* pData = MapViewOfFile(
|
||||||
|
hMap,
|
||||||
|
FILE_MAP_READ,
|
||||||
|
0, 0, 0
|
||||||
|
);
|
||||||
|
if (pData == nullptr) {
|
||||||
|
CloseHandle(hMap);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaCoreMappedPackage mappedPackage;
|
||||||
|
mappedPackage.FileHandle_ = hFile;
|
||||||
|
mappedPackage.MapHandle_ = hMap;
|
||||||
|
mappedPackage.MappedData_ = static_cast<const std::byte*>(pData);
|
||||||
|
mappedPackage.MappedSize_ = static_cast<std::size_t>(fileSize.QuadPart);
|
||||||
|
|
||||||
|
const std::span<const std::byte> byteSpan(mappedPackage.MappedData_, mappedPackage.MappedSize_);
|
||||||
|
MetaCoreArchiveReader headerReader(byteSpan);
|
||||||
|
|
||||||
|
if (!MetaCoreDeserializeValue(headerReader, mappedPackage.Document_.Header, registry)) {
|
||||||
|
return std::nullopt; // destructor handles cleanup
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& header = mappedPackage.Document_.Header;
|
||||||
|
if (header.Magic != GMetaCorePackageMagic ||
|
||||||
|
header.FormatVersion != GMetaCorePackageFormatVersion ||
|
||||||
|
header.Endianness != MetaCorePackageEndianness::Little ||
|
||||||
|
header.FileSize != mappedPackage.MappedSize_) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MetaCoreDeserializeSection(header.NameTable, byteSpan, mappedPackage.Document_.NameTable, registry) ||
|
||||||
|
!MetaCoreDeserializeSection(header.ImportTable, byteSpan, mappedPackage.Document_.Imports, registry) ||
|
||||||
|
!MetaCoreDeserializeSection(header.ExportTable, byteSpan, mappedPackage.Document_.Exports, registry) ||
|
||||||
|
!MetaCoreDeserializeSection(header.DependencyTable, byteSpan, mappedPackage.Document_.Dependencies, registry) ||
|
||||||
|
!MetaCoreDeserializeSection(header.CustomVersionTable, byteSpan, mappedPackage.Document_.CustomVersions, registry)) {
|
||||||
|
// Deliberately skipping PayloadSections to avoid memory copying of large binary data
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedPackage;
|
||||||
|
#else
|
||||||
|
// Non-Windows fallback not implemented
|
||||||
|
return std::nullopt;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t MetaCoreMappedPackage::GetPayloadSectionCount() const {
|
||||||
|
if (Document_.Header.PayloadSections.Size < sizeof(std::uint64_t)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::byte* ptr = MappedData_ + Document_.Header.PayloadSections.Offset;
|
||||||
|
std::uint64_t count = 0;
|
||||||
|
std::memcpy(&count, ptr, sizeof(count));
|
||||||
|
return static_cast<std::size_t>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::span<const std::byte> MetaCoreMappedPackage::GetPayloadSection(std::size_t index) const {
|
||||||
|
const std::size_t count = GetPayloadSectionCount();
|
||||||
|
if (index >= count) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::byte* ptr = MappedData_ + Document_.Header.PayloadSections.Offset;
|
||||||
|
ptr += sizeof(std::uint64_t); // skip count
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < count; ++i) {
|
||||||
|
std::uint64_t size = 0;
|
||||||
|
std::memcpy(&size, ptr, sizeof(size));
|
||||||
|
ptr += sizeof(size);
|
||||||
|
|
||||||
|
if (i == index) {
|
||||||
|
return {ptr, static_cast<std::size_t>(size)};
|
||||||
|
}
|
||||||
|
ptr += size;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace MetaCore
|
||||||
|
|||||||
@ -58,6 +58,9 @@ struct MetaCoreTextureAssetDocument {
|
|||||||
|
|
||||||
MC_PROPERTY()
|
MC_PROPERTY()
|
||||||
std::uint64_t PayloadSize = 0;
|
std::uint64_t PayloadSize = 0;
|
||||||
|
|
||||||
|
MC_PROPERTY()
|
||||||
|
std::string Format = "raw";
|
||||||
};
|
};
|
||||||
|
|
||||||
MC_STRUCT()
|
MC_STRUCT()
|
||||||
@ -310,53 +313,6 @@ struct MetaCoreImportedGltfNodeDocument {
|
|||||||
glm::vec3 Scale{1.0F, 1.0F, 1.0F};
|
glm::vec3 Scale{1.0F, 1.0F, 1.0F};
|
||||||
};
|
};
|
||||||
|
|
||||||
MC_STRUCT()
|
|
||||||
struct MetaCoreImportedGltfAssetDocument {
|
|
||||||
MC_GENERATED_BODY()
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::string AssetType{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::string ImporterId{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::filesystem::path SourcePath{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::uint64_t SourceHash = 0;
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::string SourceFormat{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
MetaCoreModelImportSettingsDocument ImportSettings{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
MetaCoreModelImportReportDocument ImportReport{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::vector<MetaCoreImportedGltfMeshDocument> Meshes{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::vector<MetaCoreImportedGltfMaterialDocument> Materials{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::vector<MetaCoreImportedGltfTextureDocument> Textures{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::vector<MetaCoreMeshAssetDocument> GeneratedMeshAssets{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::vector<MetaCoreMaterialAssetDocument> GeneratedMaterialAssets{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::vector<MetaCoreTextureAssetDocument> GeneratedTextureAssets{};
|
|
||||||
|
|
||||||
MC_PROPERTY()
|
|
||||||
std::vector<MetaCoreImportedGltfNodeDocument> Nodes{};
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
MC_STRUCT()
|
MC_STRUCT()
|
||||||
struct MetaCoreModelAssetDocument {
|
struct MetaCoreModelAssetDocument {
|
||||||
|
|||||||
@ -163,5 +163,34 @@ struct MetaCorePackageDocument {
|
|||||||
const MetaCoreTypeRegistry& registry
|
const MetaCoreTypeRegistry& registry
|
||||||
);
|
);
|
||||||
|
|
||||||
|
class MetaCoreMappedPackage {
|
||||||
|
public:
|
||||||
|
static std::optional<MetaCoreMappedPackage> Open(
|
||||||
|
const std::filesystem::path& path,
|
||||||
|
const MetaCoreTypeRegistry& registry
|
||||||
|
);
|
||||||
|
|
||||||
|
MetaCoreMappedPackage() = default;
|
||||||
|
~MetaCoreMappedPackage();
|
||||||
|
|
||||||
|
MetaCoreMappedPackage(const MetaCoreMappedPackage&) = delete;
|
||||||
|
MetaCoreMappedPackage& operator=(const MetaCoreMappedPackage&) = delete;
|
||||||
|
|
||||||
|
MetaCoreMappedPackage(MetaCoreMappedPackage&& other) noexcept;
|
||||||
|
MetaCoreMappedPackage& operator=(MetaCoreMappedPackage&& other) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] const MetaCorePackageDocument& GetDocument() const { return Document_; }
|
||||||
|
[[nodiscard]] std::span<const std::byte> GetPayloadSection(std::size_t index) const;
|
||||||
|
[[nodiscard]] std::size_t GetPayloadSectionCount() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* FileHandle_ = nullptr;
|
||||||
|
void* MapHandle_ = nullptr;
|
||||||
|
const std::byte* MappedData_ = nullptr;
|
||||||
|
std::size_t MappedSize_ = 0;
|
||||||
|
|
||||||
|
MetaCorePackageDocument Document_{};
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace MetaCore
|
} // namespace MetaCore
|
||||||
|
|
||||||
|
|||||||
@ -37,26 +37,6 @@ void MetaCoreTrace(const char* message) {
|
|||||||
|
|
||||||
std::printf("%s%s\n", timeBuffer, message);
|
std::printf("%s%s\n", timeBuffer, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetaCoreReadDebugSceneSyncOverride() {
|
|
||||||
#if defined(_DEBUG)
|
|
||||||
char* rawValue = nullptr;
|
|
||||||
std::size_t rawValueLength = 0;
|
|
||||||
if (_dupenv_s(&rawValue, &rawValueLength, "METACORE_ENABLE_DEBUG_SCENE_SYNC") != 0 || rawValue == nullptr) {
|
|
||||||
if (rawValue != nullptr) {
|
|
||||||
std::free(rawValue);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string value(rawValue);
|
|
||||||
std::free(rawValue);
|
|
||||||
return value == "1" || value == "true" || value == "TRUE" || value == "on" || value == "ON";
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevice) {
|
bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevice) {
|
||||||
@ -72,13 +52,6 @@ bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevi
|
|||||||
|
|
||||||
RenderDevice_ = &renderDevice;
|
RenderDevice_ = &renderDevice;
|
||||||
ViewportRect_ = MetaCoreViewportRect{};
|
ViewportRect_ = MetaCoreViewportRect{};
|
||||||
SceneSyncDegraded_ = false;
|
|
||||||
LastSceneSyncFailure_.clear();
|
|
||||||
HasLoggedSceneSyncFailure_ = false;
|
|
||||||
DebugSceneSyncOverrideEnabled_ = MetaCoreReadDebugSceneSyncOverride();
|
|
||||||
if (DebugSceneSyncOverrideEnabled_) {
|
|
||||||
MetaCoreTrace("metacore.render: debug scene sync override enabled");
|
|
||||||
}
|
|
||||||
MetaCoreTrace("metacore.render: viewport init success");
|
MetaCoreTrace("metacore.render: viewport init success");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -87,10 +60,6 @@ void MetaCoreEditorViewportRenderer::Shutdown() {
|
|||||||
SceneBridge_.Shutdown();
|
SceneBridge_.Shutdown();
|
||||||
RenderDevice_ = nullptr;
|
RenderDevice_ = nullptr;
|
||||||
ViewportRect_ = MetaCoreViewportRect{};
|
ViewportRect_ = MetaCoreViewportRect{};
|
||||||
SceneSyncDegraded_ = false;
|
|
||||||
DebugSceneSyncOverrideEnabled_ = false;
|
|
||||||
LastSceneSyncFailure_.clear();
|
|
||||||
HasLoggedSceneSyncFailure_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreEditorViewportRenderer::SetViewportRect(const MetaCoreViewportRect& viewportRect) {
|
void MetaCoreEditorViewportRenderer::SetViewportRect(const MetaCoreViewportRect& viewportRect) {
|
||||||
@ -107,41 +76,7 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
|
|||||||
|
|
||||||
RenderDevice_->SetSceneViewportRect(ViewportRect_);
|
RenderDevice_->SetSceneViewportRect(ViewportRect_);
|
||||||
SceneBridge_.ApplySceneView(sceneView);
|
SceneBridge_.ApplySceneView(sceneView);
|
||||||
#if defined(_DEBUG)
|
|
||||||
static bool loggedDebugRenderableSync = false;
|
|
||||||
SceneSyncDegraded_ = false;
|
|
||||||
if (!loggedDebugRenderableSync) {
|
|
||||||
MetaCoreTrace("metacore.render: debug scene sync active; syncing renderable objects to the MetaCore viewport");
|
|
||||||
loggedDebugRenderableSync = true;
|
|
||||||
}
|
|
||||||
SceneBridge_.SyncScene(scene, true);
|
|
||||||
if (SceneBridge_.HasRuntimeSyncFailure()) {
|
|
||||||
SceneSyncDegraded_ = true;
|
|
||||||
LastSceneSyncFailure_ = SceneBridge_.GetLastRuntimeSyncFailure();
|
|
||||||
if (!HasLoggedSceneSyncFailure_) {
|
|
||||||
MetaCoreTrace(("metacore.render: scene sync degraded after runtime failure: " + LastSceneSyncFailure_).c_str());
|
|
||||||
HasLoggedSceneSyncFailure_ = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LastSceneSyncFailure_.clear();
|
|
||||||
HasLoggedSceneSyncFailure_ = false;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
#else
|
|
||||||
SceneSyncDegraded_ = false;
|
|
||||||
SceneBridge_.SyncScene(scene, false);
|
SceneBridge_.SyncScene(scene, false);
|
||||||
if (SceneBridge_.HasRuntimeSyncFailure()) {
|
|
||||||
SceneSyncDegraded_ = true;
|
|
||||||
LastSceneSyncFailure_ = SceneBridge_.GetLastRuntimeSyncFailure();
|
|
||||||
if (!HasLoggedSceneSyncFailure_) {
|
|
||||||
MetaCoreTrace(("metacore.render: scene sync degraded after runtime failure: " + LastSceneSyncFailure_).c_str());
|
|
||||||
HasLoggedSceneSyncFailure_ = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LastSceneSyncFailure_.clear();
|
|
||||||
HasLoggedSceneSyncFailure_ = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreEditorViewportRenderer::SetProjectRootPath(const std::filesystem::path& projectRootPath) {
|
void MetaCoreEditorViewportRenderer::SetProjectRootPath(const std::filesystem::path& projectRootPath) {
|
||||||
@ -152,16 +87,4 @@ bool MetaCoreEditorViewportRenderer::TryGetObjectWorldMatrix(MetaCoreId objectId
|
|||||||
return SceneBridge_.TryGetObjectWorldMatrix(objectId, worldMatrix);
|
return SceneBridge_.TryGetObjectWorldMatrix(objectId, worldMatrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MetaCoreEditorViewportRenderer::IsSceneSyncDegraded() const {
|
|
||||||
return SceneSyncDegraded_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MetaCoreEditorViewportRenderer::IsDebugSceneSyncOverrideEnabled() const {
|
|
||||||
return DebugSceneSyncOverrideEnabled_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& MetaCoreEditorViewportRenderer::GetLastSceneSyncFailure() const {
|
|
||||||
return LastSceneSyncFailure_;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace MetaCore
|
} // namespace MetaCore
|
||||||
|
|||||||
@ -431,13 +431,13 @@ struct MetaCoreRuntimeModelLoadRequest {
|
|||||||
MetaCoreTypeRegistry registry;
|
MetaCoreTypeRegistry registry;
|
||||||
MetaCoreRegisterFoundationGeneratedTypes(registry);
|
MetaCoreRegisterFoundationGeneratedTypes(registry);
|
||||||
|
|
||||||
const auto package = MetaCoreReadPackageFile(binaryPackagePath, registry);
|
auto mappedPackage = MetaCoreMappedPackage::Open(binaryPackagePath, registry);
|
||||||
if (!package.has_value() || package->PayloadSections.empty()) {
|
if (!mappedPackage.has_value() || mappedPackage->GetPayloadSectionCount() == 0) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaCoreModelAssetDocument modelDoc;
|
MetaCoreModelAssetDocument modelDoc;
|
||||||
if (!MetaCoreDeserializeFromBytes(package->PayloadSections[0], modelDoc, registry)) {
|
if (!MetaCoreDeserializeFromBytes(mappedPackage->GetPayloadSection(0), modelDoc, registry)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,25 +445,33 @@ struct MetaCoreRuntimeModelLoadRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] NodePath MetaCoreTryLoadBinaryModelPackage(const std::filesystem::path& binaryPackagePath) {
|
[[nodiscard]] NodePath MetaCoreTryLoadBinaryModelPackage(const std::filesystem::path& binaryPackagePath) {
|
||||||
|
MetaCoreTrace(("metacore.render: MetaCoreTryLoadBinaryModelPackage path=" + binaryPackagePath.string()).c_str());
|
||||||
if (binaryPackagePath.empty() || !std::filesystem::exists(binaryPackagePath)) {
|
if (binaryPackagePath.empty() || !std::filesystem::exists(binaryPackagePath)) {
|
||||||
|
MetaCoreTrace("metacore.render: binary package path empty or does not exist");
|
||||||
return NodePath();
|
return NodePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaCoreTypeRegistry registry;
|
MetaCoreTypeRegistry registry;
|
||||||
MetaCoreRegisterFoundationGeneratedTypes(registry);
|
MetaCoreRegisterFoundationGeneratedTypes(registry);
|
||||||
|
|
||||||
const auto package = MetaCoreReadPackageFile(binaryPackagePath, registry);
|
auto mappedPackage = MetaCoreMappedPackage::Open(binaryPackagePath, registry);
|
||||||
if (!package.has_value() || package->PayloadSections.empty()) {
|
if (!mappedPackage.has_value() || mappedPackage->GetPayloadSectionCount() == 0) {
|
||||||
MetaCoreTrace(("metacore.render: model binary package missing payload: " + binaryPackagePath.string()).c_str());
|
MetaCoreTrace(("metacore.render: model binary package missing payload: " + binaryPackagePath.string()).c_str());
|
||||||
return NodePath();
|
return NodePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetaCoreTrace(("metacore.render: package has " + std::to_string(mappedPackage->GetPayloadSectionCount()) + " payload sections").c_str());
|
||||||
|
|
||||||
MetaCoreModelAssetDocument modelDoc;
|
MetaCoreModelAssetDocument modelDoc;
|
||||||
if (!MetaCoreDeserializeFromBytes(package->PayloadSections[0], modelDoc, registry)) {
|
if (!MetaCoreDeserializeFromBytes(mappedPackage->GetPayloadSection(0), modelDoc, registry)) {
|
||||||
MetaCoreTrace(("metacore.render: model binary metadata failed: " + binaryPackagePath.string()).c_str());
|
MetaCoreTrace(("metacore.render: model binary metadata failed: " + binaryPackagePath.string()).c_str());
|
||||||
return NodePath();
|
return NodePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetaCoreTrace(("metacore.render: modelDoc.Nodes.size()=" + std::to_string(modelDoc.Nodes.size()) +
|
||||||
|
" Meshes.size()=" + std::to_string(modelDoc.Meshes.size()) +
|
||||||
|
" GeneratedMeshAssets.size()=" + std::to_string(modelDoc.GeneratedMeshAssets.size())).c_str());
|
||||||
|
|
||||||
NodePath rootNode("ModelRoot");
|
NodePath rootNode("ModelRoot");
|
||||||
std::vector<NodePath> pandaNodes;
|
std::vector<NodePath> pandaNodes;
|
||||||
pandaNodes.resize(modelDoc.Nodes.size());
|
pandaNodes.resize(modelDoc.Nodes.size());
|
||||||
@ -475,19 +483,35 @@ struct MetaCoreRuntimeModelLoadRequest {
|
|||||||
|
|
||||||
const std::size_t meshPayloadIndex =
|
const std::size_t meshPayloadIndex =
|
||||||
nodeDoc.MeshIndex >= 0 ? static_cast<std::size_t>(nodeDoc.MeshIndex) + 1U : 0U;
|
nodeDoc.MeshIndex >= 0 ? static_cast<std::size_t>(nodeDoc.MeshIndex) + 1U : 0U;
|
||||||
if (nodeDoc.MeshIndex >= 0 && meshPayloadIndex < package->PayloadSections.size()) {
|
|
||||||
|
MetaCoreTrace(("metacore.render: Node[" + std::to_string(i) + "] name=" + nodeDoc.Name +
|
||||||
|
" MeshIndex=" + std::to_string(nodeDoc.MeshIndex) +
|
||||||
|
" meshPayloadIndex=" + std::to_string(meshPayloadIndex) +
|
||||||
|
" payloadCount=" + std::to_string(mappedPackage->GetPayloadSectionCount())).c_str());
|
||||||
|
|
||||||
|
if (nodeDoc.MeshIndex >= 0 && meshPayloadIndex < mappedPackage->GetPayloadSectionCount()) {
|
||||||
std::span<const MetaCoreMeshSubMeshDocument> subMeshes;
|
std::span<const MetaCoreMeshSubMeshDocument> subMeshes;
|
||||||
if (static_cast<std::size_t>(nodeDoc.MeshIndex) < modelDoc.GeneratedMeshAssets.size()) {
|
if (static_cast<std::size_t>(nodeDoc.MeshIndex) < modelDoc.GeneratedMeshAssets.size()) {
|
||||||
subMeshes = modelDoc.GeneratedMeshAssets[static_cast<std::size_t>(nodeDoc.MeshIndex)].SubMeshes;
|
subMeshes = modelDoc.GeneratedMeshAssets[nodeDoc.MeshIndex].SubMeshes;
|
||||||
|
MetaCoreTrace(("metacore.render: subMeshes.size()=" + std::to_string(subMeshes.size())).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto payloadSpan = mappedPackage->GetPayloadSection(meshPayloadIndex);
|
||||||
|
MetaCoreTrace(("metacore.render: payload size=" + std::to_string(payloadSpan.size()) + " bytes").c_str());
|
||||||
|
|
||||||
NodePath meshGeom = MetaCoreBuildMeshFromBinary(
|
NodePath meshGeom = MetaCoreBuildMeshFromBinary(
|
||||||
nodeDoc.Name + "_mesh",
|
nodeDoc.Name,
|
||||||
package->PayloadSections[meshPayloadIndex],
|
payloadSpan,
|
||||||
subMeshes
|
subMeshes
|
||||||
);
|
);
|
||||||
if (!meshGeom.is_empty()) {
|
if (!meshGeom.is_empty()) {
|
||||||
meshGeom.reparent_to(pandaNodes[i]);
|
meshGeom.reparent_to(pandaNodes[i]);
|
||||||
|
MetaCoreTrace(("metacore.render: mesh geom attached OK for node " + std::to_string(i)).c_str());
|
||||||
|
} else {
|
||||||
|
MetaCoreTrace(("metacore.render: mesh geom EMPTY for node " + std::to_string(i)).c_str());
|
||||||
}
|
}
|
||||||
|
} else if (nodeDoc.MeshIndex >= 0) {
|
||||||
|
MetaCoreTrace(("metacore.render: SKIP node " + std::to_string(i) + " meshPayloadIndex=" + std::to_string(meshPayloadIndex) + " >= payloadCount=" + std::to_string(mappedPackage->GetPayloadSectionCount())).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,6 +600,8 @@ struct MetaCoreRuntimeModelLoadRequest {
|
|||||||
std::memcpy(&vCount, payload.data(), sizeof(std::uint32_t));
|
std::memcpy(&vCount, payload.data(), sizeof(std::uint32_t));
|
||||||
std::memcpy(&iCount, payload.data() + sizeof(std::uint32_t), sizeof(std::uint32_t));
|
std::memcpy(&iCount, payload.data() + sizeof(std::uint32_t), sizeof(std::uint32_t));
|
||||||
|
|
||||||
|
MetaCoreTrace(("metacore.render: MetaCoreBuildMeshFromBinary name=" + name + " vCount=" + std::to_string(vCount) + " iCount=" + std::to_string(iCount)).c_str());
|
||||||
|
|
||||||
const std::size_t expectedSize = sizeof(std::uint32_t) * 2 +
|
const std::size_t expectedSize = sizeof(std::uint32_t) * 2 +
|
||||||
vCount * sizeof(MetaCoreMeshVertex) +
|
vCount * sizeof(MetaCoreMeshVertex) +
|
||||||
iCount * sizeof(std::uint32_t);
|
iCount * sizeof(std::uint32_t);
|
||||||
@ -674,21 +700,26 @@ struct MetaCoreRuntimeModelLoadRequest {
|
|||||||
|
|
||||||
PT(Texture) texture = new Texture(name);
|
PT(Texture) texture = new Texture(name);
|
||||||
|
|
||||||
// Choose the right format based on channel count
|
|
||||||
Texture::Format format = Texture::F_rgba8;
|
Texture::Format format = Texture::F_rgba8;
|
||||||
if (doc.Channels == 3) format = Texture::F_rgb8;
|
if (doc.Channels == 3) format = Texture::F_rgb8;
|
||||||
else if (doc.Channels == 1) format = Texture::F_luminance;
|
else if (doc.Channels == 1) format = Texture::F_luminance;
|
||||||
|
|
||||||
texture->setup_2d_texture(doc.Width, doc.Height, Texture::T_unsigned_byte, format);
|
texture->setup_2d_texture(doc.Width, doc.Height, Texture::T_unsigned_byte, format);
|
||||||
|
|
||||||
// Copy payload data directly to Panda3D's RAM image buffer
|
if (doc.Format == "dxt5") {
|
||||||
PTA_uchar ramImage = texture->modify_ram_image();
|
PTA_uchar ramImage = PTA_uchar::empty_array(payload.size());
|
||||||
if (ramImage.size() == payload.size()) {
|
std::memcpy(ramImage.p(), payload.data(), payload.size());
|
||||||
std::memcpy(&ramImage[0], reinterpret_cast<const void*>(payload.data()), payload.size());
|
texture->set_ram_image(ramImage, Texture::CM_dxt5);
|
||||||
} else {
|
} else {
|
||||||
MetaCoreTrace(("metacore.render: texture payload size mismatch. expected=" +
|
// Copy payload data directly to Panda3D's RAM image buffer
|
||||||
std::to_string(ramImage.size()) + " actual=" + std::to_string(payload.size())).c_str());
|
PTA_uchar ramImage = texture->modify_ram_image();
|
||||||
return nullptr;
|
if (ramImage.size() == payload.size()) {
|
||||||
|
std::memcpy(&ramImage[0], reinterpret_cast<const void*>(payload.data()), payload.size());
|
||||||
|
} else {
|
||||||
|
MetaCoreTrace(("metacore.render: texture payload size mismatch. expected=" +
|
||||||
|
std::to_string(ramImage.size()) + " actual=" + std::to_string(payload.size())).c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable linear filtering by default for smoother look
|
// Enable linear filtering by default for smoother look
|
||||||
@ -854,15 +885,15 @@ struct MetaCoreRuntimeModelLoadRequest {
|
|||||||
// We only need basic types for the asset document
|
// We only need basic types for the asset document
|
||||||
MetaCoreRegisterFoundationGeneratedTypes(registry);
|
MetaCoreRegisterFoundationGeneratedTypes(registry);
|
||||||
|
|
||||||
const auto package = MetaCoreReadPackageFile(binaryPackagePath, registry);
|
auto mappedPackage = MetaCoreMappedPackage::Open(binaryPackagePath, registry);
|
||||||
if (package.has_value() && package->PayloadSections.size() > 1) {
|
if (mappedPackage.has_value() && mappedPackage->GetPayloadSectionCount() > 1) {
|
||||||
// First payload is the metadata (MetaCoreTextureAssetDocument)
|
// First payload is the metadata (MetaCoreTextureAssetDocument)
|
||||||
MetaCoreTextureAssetDocument texDoc;
|
MetaCoreTextureAssetDocument texDoc;
|
||||||
if (MetaCoreDeserializeFromBytes(package->PayloadSections[0], texDoc, registry)) {
|
if (MetaCoreDeserializeFromBytes(mappedPackage->GetPayloadSection(0), texDoc, registry)) {
|
||||||
PT(Texture) binaryTex = MetaCoreBuildTextureFromBinary(
|
PT(Texture) binaryTex = MetaCoreBuildTextureFromBinary(
|
||||||
relativeTexturePath,
|
relativeTexturePath,
|
||||||
texDoc,
|
texDoc,
|
||||||
package->PayloadSections[1]
|
mappedPackage->GetPayloadSection(1)
|
||||||
);
|
);
|
||||||
if (binaryTex != nullptr) {
|
if (binaryTex != nullptr) {
|
||||||
MetaCoreTrace(("metacore.render: texture binary load success: " + binaryPackagePath.string()).c_str());
|
MetaCoreTrace(("metacore.render: texture binary load success: " + binaryPackagePath.string()).c_str());
|
||||||
@ -1615,18 +1646,33 @@ void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene, bool compat
|
|||||||
if (materialSlotsChanged) {
|
if (materialSlotsChanged) {
|
||||||
syncStage = "refresh_material_slots";
|
syncStage = "refresh_material_slots";
|
||||||
}
|
}
|
||||||
const bool useAssetPreviewPath =
|
|
||||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().MeshSource == MetaCoreMeshSourceKind::Asset &&
|
|
||||||
!objectState.IsUsingPlaceholder;
|
|
||||||
objectState.MeshNode.set_material_off(1);
|
objectState.MeshNode.set_material_off(1);
|
||||||
objectState.MeshNode.set_light_off(1);
|
objectState.MeshNode.set_light_off(1);
|
||||||
objectState.MeshNode.set_two_sided(gameObject.GetComponent<MetaCoreMeshRendererComponent>().DoubleSided);
|
objectState.MeshNode.set_two_sided(gameObject.GetComponent<MetaCoreMeshRendererComponent>().DoubleSided);
|
||||||
objectState.MeshNode.set_color(
|
|
||||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.r,
|
if (gameObject.GetComponent<MetaCoreMeshRendererComponent>().MeshSource == MetaCoreMeshSourceKind::Asset) {
|
||||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.g,
|
if (objectState.IsUsingPlaceholder) {
|
||||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.b,
|
// 如果是占位符立方体,用红色显示
|
||||||
1.0F
|
objectState.MeshNode.set_color(1.0F, 0.0F, 0.0F, 1.0F);
|
||||||
);
|
} else {
|
||||||
|
// 如果成功加载了资产网格,用亮绿色显示,确保能看清几何体
|
||||||
|
objectState.MeshNode.set_color(0.0F, 1.0F, 0.0F, 1.0F);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 内置网格(如 Cube)使用原本的 BaseColor
|
||||||
|
objectState.MeshNode.set_color(
|
||||||
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.r,
|
||||||
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.g,
|
||||||
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.b,
|
||||||
|
1.0F
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按照敏捷开发基础版思路,暂时跳过复杂的贴图和 PBR 材质应用,只看纯色几何体
|
||||||
|
/*
|
||||||
|
const bool useAssetPreviewPath =
|
||||||
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>().MeshSource == MetaCoreMeshSourceKind::Asset &&
|
||||||
|
!objectState.IsUsingPlaceholder;
|
||||||
if (useAssetPreviewPath) {
|
if (useAssetPreviewPath) {
|
||||||
MetaCoreApplyBaseColorTexture(
|
MetaCoreApplyBaseColorTexture(
|
||||||
objectState.MeshNode,
|
objectState.MeshNode,
|
||||||
@ -1664,32 +1710,36 @@ void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene, bool compat
|
|||||||
if (editorCameraHandle != nullptr) {
|
if (editorCameraHandle != nullptr) {
|
||||||
MetaCoreApplySimplePbrViewInputs(objectState.MeshNode, *sceneRootHandle, *editorCameraHandle);
|
MetaCoreApplySimplePbrViewInputs(objectState.MeshNode, *sceneRootHandle, *editorCameraHandle);
|
||||||
}
|
}
|
||||||
const std::filesystem::path& projectRoot =
|
}
|
||||||
|
// 同样注释掉后续的材质槽应用和 else 分支,避免括号不匹配
|
||||||
|
/*
|
||||||
|
const std::filesystem::path& projectRoot =
|
||||||
!Impl_->ProjectRootPath.empty()
|
!Impl_->ProjectRootPath.empty()
|
||||||
? Impl_->ProjectRootPath
|
? Impl_->ProjectRootPath
|
||||||
: (Impl_->ProjectRootPath = MetaCoreResolveProjectRootFromEnvironment());
|
: (Impl_->ProjectRootPath = MetaCoreResolveProjectRootFromEnvironment());
|
||||||
const auto modelDoc = MetaCoreReadRuntimeModelDocument(MetaCoreRuntimeModelLoadRequest{
|
const auto modelDoc = MetaCoreReadRuntimeModelDocument(MetaCoreRuntimeModelLoadRequest{
|
||||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelAssetGuid,
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelAssetGuid,
|
||||||
|
projectRoot,
|
||||||
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelPath,
|
||||||
|
false
|
||||||
|
});
|
||||||
|
if (modelDoc.has_value()) {
|
||||||
|
MetaCoreApplyMaterialSlotsToSubMeshes(
|
||||||
|
objectState.MeshNode,
|
||||||
projectRoot,
|
projectRoot,
|
||||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelPath,
|
gameObject.GetComponent<MetaCoreMeshRendererComponent>(),
|
||||||
false
|
*modelDoc,
|
||||||
});
|
*sceneRootHandle,
|
||||||
if (modelDoc.has_value()) {
|
editorCameraHandle
|
||||||
MetaCoreApplyMaterialSlotsToSubMeshes(
|
);
|
||||||
objectState.MeshNode,
|
}
|
||||||
projectRoot,
|
objectState.MaterialAssetGuids = gameObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids;
|
||||||
gameObject.GetComponent<MetaCoreMeshRendererComponent>(),
|
|
||||||
*modelDoc,
|
|
||||||
*sceneRootHandle,
|
|
||||||
editorCameraHandle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
objectState.MaterialAssetGuids = gameObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids;
|
|
||||||
} else {
|
} else {
|
||||||
objectState.MeshNode.set_texture_off(1);
|
objectState.MeshNode.set_texture_off(1);
|
||||||
objectState.MeshNode.set_shader_off(1);
|
objectState.MeshNode.set_shader_off(1);
|
||||||
objectState.MaterialAssetGuids.clear();
|
objectState.MaterialAssetGuids.clear();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
if (gameObject.GetComponent<MetaCoreMeshRendererComponent>().AlphaMode == MetaCoreMeshAlphaMode::Blend) {
|
if (gameObject.GetComponent<MetaCoreMeshRendererComponent>().AlphaMode == MetaCoreMeshAlphaMode::Blend) {
|
||||||
objectState.MeshNode.set_transparency(TransparencyAttrib::M_alpha);
|
objectState.MeshNode.set_transparency(TransparencyAttrib::M_alpha);
|
||||||
|
|||||||
@ -20,18 +20,11 @@ public:
|
|||||||
void SetProjectRootPath(const std::filesystem::path& projectRootPath);
|
void SetProjectRootPath(const std::filesystem::path& projectRootPath);
|
||||||
|
|
||||||
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
|
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
|
||||||
[[nodiscard]] bool IsSceneSyncDegraded() const;
|
|
||||||
[[nodiscard]] bool IsDebugSceneSyncOverrideEnabled() const;
|
|
||||||
[[nodiscard]] const std::string& GetLastSceneSyncFailure() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MetaCoreRenderDevice* RenderDevice_ = nullptr;
|
MetaCoreRenderDevice* RenderDevice_ = nullptr;
|
||||||
MetaCorePandaSceneBridge SceneBridge_{};
|
MetaCorePandaSceneBridge SceneBridge_{};
|
||||||
MetaCoreViewportRect ViewportRect_{};
|
MetaCoreViewportRect ViewportRect_{};
|
||||||
bool SceneSyncDegraded_ = false;
|
|
||||||
bool DebugSceneSyncOverrideEnabled_ = false;
|
|
||||||
std::string LastSceneSyncFailure_{};
|
|
||||||
bool HasLoggedSceneSyncFailure_ = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace MetaCore
|
} // namespace MetaCore
|
||||||
|
|||||||
@ -77,14 +77,14 @@ void MetaCoreTestSceneEditApi() {
|
|||||||
MetaCoreExpect(scene.GetGameObjects().size() > originalCount, "复制后对象数量应增加");
|
MetaCoreExpect(scene.GetGameObjects().size() > originalCount, "复制后对象数量应增加");
|
||||||
|
|
||||||
const MetaCore::MetaCoreId duplicatedRootId = duplicateRoots.front();
|
const MetaCore::MetaCoreId duplicatedRootId = duplicateRoots.front();
|
||||||
MetaCoreExpect(scene.FindGameObject(duplicatedRootId) != nullptr, "复制后的对象应可被查找到");
|
MetaCoreExpect(scene.FindGameObject(duplicatedRootId), "复制后的对象应可被查找到");
|
||||||
|
|
||||||
const bool reparented = scene.ReparentGameObjects({duplicatedRootId}, 0, true);
|
const bool reparented = scene.ReparentGameObjects({duplicatedRootId}, 0, true);
|
||||||
MetaCoreExpect(reparented, "重挂接操作应成功");
|
MetaCoreExpect(reparented, "重挂接操作应成功");
|
||||||
|
|
||||||
const std::vector<MetaCore::MetaCoreId> deletedIds = scene.DeleteGameObjects({duplicatedRootId});
|
const std::vector<MetaCore::MetaCoreId> deletedIds = scene.DeleteGameObjects({duplicatedRootId});
|
||||||
MetaCoreExpect(!deletedIds.empty(), "删除应返回被删除对象");
|
MetaCoreExpect(!deletedIds.empty(), "删除应返回被删除对象");
|
||||||
MetaCoreExpect(scene.FindGameObject(duplicatedRootId) == nullptr, "删除后对象不应存在");
|
MetaCoreExpect(!scene.FindGameObject(duplicatedRootId), "删除后对象不应存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreTestSelectionStateMachine() {
|
void MetaCoreTestSelectionStateMachine() {
|
||||||
@ -95,9 +95,9 @@ void MetaCoreTestSelectionStateMachine() {
|
|||||||
MetaCore::MetaCoreLogService logService;
|
MetaCore::MetaCoreLogService logService;
|
||||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||||
|
|
||||||
const MetaCore::MetaCoreId objectAId = scene.CreateGameObject("A").Id;
|
const MetaCore::MetaCoreId objectAId = scene.CreateGameObject("A").GetId();
|
||||||
const MetaCore::MetaCoreId objectBId = scene.CreateGameObject("B").Id;
|
const MetaCore::MetaCoreId objectBId = scene.CreateGameObject("B").GetId();
|
||||||
const MetaCore::MetaCoreId objectCId = scene.CreateGameObject("C").Id;
|
const MetaCore::MetaCoreId objectCId = scene.CreateGameObject("C").GetId();
|
||||||
|
|
||||||
MetaCore::MetaCoreEditorContext editorContext(
|
MetaCore::MetaCoreEditorContext editorContext(
|
||||||
window,
|
window,
|
||||||
@ -144,8 +144,8 @@ void MetaCoreTestUndoRedo() {
|
|||||||
|
|
||||||
const std::size_t beforeCreateCount = scene.GetGameObjects().size();
|
const std::size_t beforeCreateCount = scene.GetGameObjects().size();
|
||||||
const bool created = editorContext.ExecuteSnapshotCommand("创建对象", [&]() {
|
const bool created = editorContext.ExecuteSnapshotCommand("创建对象", [&]() {
|
||||||
MetaCore::MetaCoreGameObject& object = scene.CreateGameObject("UndoRedoObject");
|
MetaCore::MetaCoreGameObject object = scene.CreateGameObject("UndoRedoObject");
|
||||||
editorContext.SelectOnly(object.Id);
|
editorContext.SelectOnly(object.GetId());
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
MetaCoreExpect(created, "创建快照命令应执行成功");
|
MetaCoreExpect(created, "创建快照命令应执行成功");
|
||||||
@ -510,8 +510,8 @@ void MetaCoreTestScenePersistenceRoundTrip() {
|
|||||||
|
|
||||||
MetaCore::MetaCoreId cubeId = 0;
|
MetaCore::MetaCoreId cubeId = 0;
|
||||||
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
|
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
|
||||||
if (object.Name == "Cube") {
|
if (object.GetName() == "Cube") {
|
||||||
cubeId = object.Id;
|
cubeId = object.GetId();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -540,7 +540,7 @@ void MetaCoreTestScenePersistenceRoundTrip() {
|
|||||||
|
|
||||||
bool foundRenamedCube = false;
|
bool foundRenamedCube = false;
|
||||||
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
|
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
|
||||||
if (object.Name == "RoundTripCube") {
|
if (object.GetName() == "RoundTripCube") {
|
||||||
foundRenamedCube = true;
|
foundRenamedCube = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -942,27 +942,27 @@ void MetaCoreTestInstantiateImportedModelAssetIntoScene() {
|
|||||||
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
|
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
|
||||||
MetaCoreExpect(instantiatedRootId.has_value(), "应能将导入模型实例化到场景");
|
MetaCoreExpect(instantiatedRootId.has_value(), "应能将导入模型实例化到场景");
|
||||||
|
|
||||||
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
||||||
MetaCoreExpect(instantiatedRoot != nullptr, "实例化后应能找到根对象");
|
MetaCoreExpect(instantiatedRoot, "实例化后应能找到根对象");
|
||||||
MetaCoreExpect(instantiatedRoot->Name == "Pump.gltf", "model root should use source file name");
|
MetaCoreExpect(instantiatedRoot.GetName() == "Pump.gltf", "model root should use source file name");
|
||||||
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
|
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
|
||||||
MetaCoreExpect(instantiatedChildren.size() == 1, "model root should contain imported node");
|
MetaCoreExpect(instantiatedChildren.size() == 1, "model root should contain imported node");
|
||||||
const MetaCore::MetaCoreGameObject* instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
|
MetaCore::MetaCoreGameObject instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
|
||||||
MetaCoreExpect(instantiatedRenderable != nullptr, "instantiated renderable node should exist");
|
MetaCoreExpect(instantiatedRenderable, "instantiated renderable node should exist");
|
||||||
MetaCoreExpect(instantiatedRenderable->Name == "Pump", "renderable child name should come from imported node");
|
MetaCoreExpect(instantiatedRenderable.GetName() == "Pump", "renderable child name should come from imported node");
|
||||||
MetaCoreExpect(instantiatedRenderable->MeshRenderer.has_value(), "renderable node should have MeshRenderer");
|
MetaCoreExpect(instantiatedRenderable.HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "renderable node should have MeshRenderer");
|
||||||
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MeshSource == MetaCore::MetaCoreMeshSourceKind::Asset, "renderable node should use asset mesh source");
|
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshSource == MetaCore::MetaCoreMeshSourceKind::Asset, "renderable node should use asset mesh source");
|
||||||
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MeshAssetGuid.IsValid(), "renderable node should bind mesh asset guid");
|
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid.IsValid(), "renderable node should bind mesh asset guid");
|
||||||
MetaCoreExpect(!instantiatedRenderable->MeshRenderer->MaterialAssetGuids.empty(), "renderable node should bind material assets");
|
MetaCoreExpect(!instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.empty(), "renderable node should bind material assets");
|
||||||
MetaCoreExpect(
|
MetaCoreExpect(
|
||||||
instantiatedRenderable->MeshRenderer->SourceModelPath == (std::filesystem::path("Assets") / "Pump.gltf").generic_string(),
|
instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().SourceModelPath == (std::filesystem::path("Assets") / "Pump.gltf").generic_string(),
|
||||||
"renderable node should record source model path"
|
"renderable node should record source model path"
|
||||||
);
|
);
|
||||||
MetaCoreExpect(
|
MetaCoreExpect(
|
||||||
instantiatedRenderable->MeshRenderer->BaseColorTexturePath == (std::filesystem::path("Textures") / "PumpBaseColor.png").generic_string(),
|
instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().BaseColorTexturePath == (std::filesystem::path("Textures") / "PumpBaseColor.png").generic_string(),
|
||||||
"renderable node should record base color texture path"
|
"renderable node should record base color texture path"
|
||||||
);
|
);
|
||||||
MetaCoreExpect(instantiatedRenderable->MeshRenderer->BaseColor.x == 1.0F, "renderable node should carry base color");
|
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().BaseColor.x == 1.0F, "renderable node should carry base color");
|
||||||
MetaCoreExpect(editorContext.GetActiveObjectId() == *instantiatedRootId, "实例化后应选中新对象");
|
MetaCoreExpect(editorContext.GetActiveObjectId() == *instantiatedRootId, "实例化后应选中新对象");
|
||||||
|
|
||||||
coreServicesModule->Shutdown(moduleRegistry);
|
coreServicesModule->Shutdown(moduleRegistry);
|
||||||
@ -1104,29 +1104,29 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
|
|||||||
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
|
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
|
||||||
MetaCoreExpect(instantiatedRootId.has_value(), "应能将多节点模型实例化到场景");
|
MetaCoreExpect(instantiatedRootId.has_value(), "应能将多节点模型实例化到场景");
|
||||||
|
|
||||||
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
||||||
MetaCoreExpect(instantiatedRoot != nullptr, "多节点实例化后应能找到根对象");
|
MetaCoreExpect(instantiatedRoot, "多节点实例化后应能找到根对象");
|
||||||
MetaCoreExpect(instantiatedRoot->Name == "Assembly.gltf", "multi-node model root should use source file name");
|
MetaCoreExpect(instantiatedRoot.GetName() == "Assembly.gltf", "multi-node model root should use source file name");
|
||||||
MetaCoreExpect(!instantiatedRoot->MeshRenderer.has_value(), "asset root should not force MeshRenderer");
|
MetaCoreExpect(!instantiatedRoot.HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "asset root should not force MeshRenderer");
|
||||||
|
|
||||||
const auto importedRootIterator = std::find_if(scene.GetGameObjects().begin(), scene.GetGameObjects().end(), [&](const MetaCore::MetaCoreGameObject& object) {
|
const auto importedRootIterator = std::find_if(scene.GetGameObjects().begin(), scene.GetGameObjects().end(), [&](const MetaCore::MetaCoreGameObject& object) {
|
||||||
return object.Name == "AssemblyRoot";
|
return object.GetName() == "AssemblyRoot";
|
||||||
});
|
});
|
||||||
MetaCoreExpect(importedRootIterator != scene.GetGameObjects().end(), "multi-node instantiation should create imported root node");
|
MetaCoreExpect(importedRootIterator != scene.GetGameObjects().end(), "multi-node instantiation should create imported root node");
|
||||||
MetaCoreExpect(importedRootIterator->ParentId == instantiatedRoot->Id, "imported root node should be parented under asset root");
|
MetaCoreExpect(importedRootIterator->GetParentId() == instantiatedRoot.GetId(), "imported root node should be parented under asset root");
|
||||||
MetaCoreExpect(!importedRootIterator->MeshRenderer.has_value(), "imported empty root should not force MeshRenderer");
|
MetaCoreExpect(!importedRootIterator->HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "imported empty root should not force MeshRenderer");
|
||||||
|
|
||||||
const auto childIterator = std::find_if(scene.GetGameObjects().begin(), scene.GetGameObjects().end(), [&](const MetaCore::MetaCoreGameObject& object) {
|
const auto childIterator = std::find_if(scene.GetGameObjects().begin(), scene.GetGameObjects().end(), [&](const MetaCore::MetaCoreGameObject& object) {
|
||||||
return object.Name == "Valve_A";
|
return object.GetName() == "Valve_A";
|
||||||
});
|
});
|
||||||
MetaCoreExpect(childIterator != scene.GetGameObjects().end(), "multi-node instantiation should create child node object");
|
MetaCoreExpect(childIterator != scene.GetGameObjects().end(), "multi-node instantiation should create child node object");
|
||||||
MetaCoreExpect(childIterator->ParentId == importedRootIterator->Id, "child node should be parented under imported root node");
|
MetaCoreExpect(childIterator->GetParentId() == importedRootIterator->GetId(), "child node should be parented under imported root node");
|
||||||
MetaCoreExpect(childIterator->MeshRenderer.has_value(), "mesh child should have MeshRenderer");
|
MetaCoreExpect(childIterator->HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "mesh child should have MeshRenderer");
|
||||||
MetaCoreExpect(childIterator->MeshRenderer->MeshAssetGuid == meshAsset.AssetGuid, "child node should bind generated mesh asset");
|
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid == meshAsset.AssetGuid, "child node should bind generated mesh asset");
|
||||||
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.size() == 2, "child node should bind all material slots");
|
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.size() == 2, "child node should bind all material slots");
|
||||||
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids.front() == materialAsset.AssetGuid, "child node should bind generated material asset");
|
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.front() == materialAsset.AssetGuid, "child node should bind generated material asset");
|
||||||
MetaCoreExpect(childIterator->MeshRenderer->MaterialAssetGuids[1] == secondaryMaterialAsset.AssetGuid, "child node should bind secondary material asset");
|
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids[1] == secondaryMaterialAsset.AssetGuid, "child node should bind secondary material asset");
|
||||||
MetaCoreExpect(editorContext.GetActiveObjectId() == instantiatedRoot->Id, "multi-node instantiation should select asset root");
|
MetaCoreExpect(editorContext.GetActiveObjectId() == instantiatedRoot.GetId(), "multi-node instantiation should select asset root");
|
||||||
coreServicesModule->Shutdown(moduleRegistry);
|
coreServicesModule->Shutdown(moduleRegistry);
|
||||||
moduleRegistry.ShutdownServices();
|
moduleRegistry.ShutdownServices();
|
||||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||||
@ -1196,15 +1196,15 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
|
|||||||
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
|
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
|
||||||
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化模型资源");
|
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化模型资源");
|
||||||
|
|
||||||
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
||||||
MetaCoreExpect(instantiatedRoot != nullptr, "instantiated model root should exist");
|
MetaCoreExpect(instantiatedRoot, "instantiated model root should exist");
|
||||||
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
|
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
|
||||||
MetaCoreExpect(instantiatedChildren.size() == 1, "instantiated model root should contain renderable child");
|
MetaCoreExpect(instantiatedChildren.size() == 1, "instantiated model root should contain renderable child");
|
||||||
const MetaCore::MetaCoreGameObject* instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
|
MetaCore::MetaCoreGameObject instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
|
||||||
MetaCoreExpect(instantiatedRenderable != nullptr && instantiatedRenderable->MeshRenderer.has_value(), "instantiated renderable model object should exist");
|
MetaCoreExpect(instantiatedRenderable && instantiatedRenderable.HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "instantiated renderable model object should exist");
|
||||||
|
|
||||||
const MetaCore::MetaCoreAssetGuid originalMeshGuid = instantiatedRenderable->MeshRenderer->MeshAssetGuid;
|
const MetaCore::MetaCoreAssetGuid originalMeshGuid = instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid;
|
||||||
const MetaCore::MetaCoreAssetGuid originalMaterialGuid = instantiatedRenderable->MeshRenderer->MaterialAssetGuids.front();
|
const MetaCore::MetaCoreAssetGuid originalMaterialGuid = instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.front();
|
||||||
{
|
{
|
||||||
std::ofstream gltfFile(modelPath, std::ios::trunc);
|
std::ofstream gltfFile(modelPath, std::ios::trunc);
|
||||||
gltfFile
|
gltfFile
|
||||||
@ -1235,17 +1235,26 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
MetaCoreExpect(!refreshedDocument.GeneratedMeshAssets.empty(), "重导入后应保留生成 mesh");
|
MetaCoreExpect(!refreshedDocument.GeneratedMeshAssets.empty(), "重导入后应保留生成 mesh");
|
||||||
|
MetaCoreExpect(modelPackage->PayloadSections.size() >= 2, "模型资源包应包含 mesh payload");
|
||||||
|
const auto& meshAsset = refreshedDocument.GeneratedMeshAssets.front();
|
||||||
|
MetaCoreExpect(meshAsset.PayloadIndex > 0 && static_cast<std::size_t>(meshAsset.PayloadIndex) < modelPackage->PayloadSections.size(), "mesh payload index 应有效");
|
||||||
|
const auto& meshPayload = modelPackage->PayloadSections[meshAsset.PayloadIndex];
|
||||||
|
MetaCoreExpect(meshPayload.size() >= sizeof(std::uint32_t) * 2, "mesh payload 至少应包含 header");
|
||||||
|
std::uint32_t vCount = 0;
|
||||||
|
std::memcpy(&vCount, meshPayload.data(), sizeof(std::uint32_t));
|
||||||
|
MetaCoreExpect(vCount > 0, "mesh payload 应包含有效的顶点数量");
|
||||||
|
|
||||||
MetaCoreExpect(!refreshedDocument.GeneratedMaterialAssets.empty(), "重导入后应保留生成材质");
|
MetaCoreExpect(!refreshedDocument.GeneratedMaterialAssets.empty(), "重导入后应保留生成材质");
|
||||||
MetaCoreExpect(refreshedDocument.GeneratedMeshAssets.front().AssetGuid == originalMeshGuid, "重导入后 mesh 子资源 GUID 应保持稳定");
|
MetaCoreExpect(refreshedDocument.GeneratedMeshAssets.front().AssetGuid == originalMeshGuid, "重导入后 mesh 子资源 GUID 应保持稳定");
|
||||||
MetaCoreExpect(refreshedDocument.GeneratedMaterialAssets.front().AssetGuid == originalMaterialGuid, "重导入后材质子资源 GUID 应保持稳定");
|
MetaCoreExpect(refreshedDocument.GeneratedMaterialAssets.front().AssetGuid == originalMaterialGuid, "重导入后材质子资源 GUID 应保持稳定");
|
||||||
|
|
||||||
instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
||||||
MetaCoreExpect(instantiatedRoot != nullptr, "reimport should keep model root alive");
|
MetaCoreExpect(instantiatedRoot, "reimport should keep model root alive");
|
||||||
instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
|
instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
|
||||||
MetaCoreExpect(instantiatedRenderable != nullptr && instantiatedRenderable->MeshRenderer.has_value(), "reimport should keep renderable instance alive");
|
MetaCoreExpect(instantiatedRenderable && instantiatedRenderable.HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "reimport should keep renderable instance alive");
|
||||||
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MeshAssetGuid == originalMeshGuid, "reimport should keep mesh reference stable");
|
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid == originalMeshGuid, "reimport should keep mesh reference stable");
|
||||||
MetaCoreExpect(!instantiatedRenderable->MeshRenderer->MaterialAssetGuids.empty(), "reimport should keep material references");
|
MetaCoreExpect(!instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.empty(), "reimport should keep material references");
|
||||||
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MaterialAssetGuids.front() == originalMaterialGuid, "reimport should keep material reference stable");
|
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.front() == originalMaterialGuid, "reimport should keep material reference stable");
|
||||||
coreServicesModule->Shutdown(moduleRegistry);
|
coreServicesModule->Shutdown(moduleRegistry);
|
||||||
moduleRegistry.ShutdownServices();
|
moduleRegistry.ShutdownServices();
|
||||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||||
@ -1352,12 +1361,12 @@ void MetaCoreTestPrefabWorkflow() {
|
|||||||
moduleRegistry
|
moduleRegistry
|
||||||
);
|
);
|
||||||
|
|
||||||
MetaCore::MetaCoreGameObject& root = scene.CreateGameObject("PrefabRoot");
|
MetaCore::MetaCoreGameObject root = scene.CreateGameObject("PrefabRoot");
|
||||||
root.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
|
root.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
||||||
root.Transform.Position = glm::vec3(1.0F, 2.0F, 3.0F);
|
root.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(1.0F, 2.0F, 3.0F);
|
||||||
const MetaCore::MetaCoreId rootId = root.Id;
|
const MetaCore::MetaCoreId rootId = root.GetId();
|
||||||
MetaCore::MetaCoreGameObject& child = scene.CreateGameObject("PrefabChild", rootId);
|
MetaCore::MetaCoreGameObject child = scene.CreateGameObject("PrefabChild", rootId);
|
||||||
child.Light = MetaCore::MetaCoreLightComponent{};
|
child.AddComponent<MetaCore::MetaCoreLightComponent>();
|
||||||
|
|
||||||
editorContext.SelectOnly(rootId);
|
editorContext.SelectOnly(rootId);
|
||||||
|
|
||||||
@ -1383,12 +1392,12 @@ void MetaCoreTestPrefabWorkflow() {
|
|||||||
|
|
||||||
const auto instantiatedRootId = prefabService->InstantiatePrefab(editorContext, prefabRecord->Guid, std::nullopt);
|
const auto instantiatedRootId = prefabService->InstantiatePrefab(editorContext, prefabRecord->Guid, std::nullopt);
|
||||||
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化 prefab");
|
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化 prefab");
|
||||||
MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
|
||||||
MetaCoreExpect(instantiatedRoot != nullptr, "实例化后应能找到根对象");
|
MetaCoreExpect(instantiatedRoot, "实例化后应能找到根对象");
|
||||||
MetaCoreExpect(instantiatedRoot->PrefabInstance.has_value(), "实例根应带有 prefab 元数据");
|
MetaCoreExpect(instantiatedRoot.HasComponent<MetaCore::MetaCorePrefabInstanceMetadata>(), "实例根应带有 prefab 元数据");
|
||||||
|
|
||||||
instantiatedRoot->Transform.Position = glm::vec3(8.0F, 9.0F, 10.0F);
|
instantiatedRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(8.0F, 9.0F, 10.0F);
|
||||||
editorContext.SelectOnly(instantiatedRoot->Id);
|
editorContext.SelectOnly(instantiatedRoot.GetId());
|
||||||
MetaCoreExpect(prefabService->ApplySelectedPrefabInstance(editorContext), "应能应用 prefab 实例");
|
MetaCoreExpect(prefabService->ApplySelectedPrefabInstance(editorContext), "应能应用 prefab 实例");
|
||||||
|
|
||||||
const auto prefabPackage = packageService->ReadPackage(tempProjectRoot / prefabPath);
|
const auto prefabPackage = packageService->ReadPackage(tempProjectRoot / prefabPath);
|
||||||
@ -1412,17 +1421,17 @@ void MetaCoreTestPrefabWorkflow() {
|
|||||||
|
|
||||||
const auto secondInstanceRootId = prefabService->InstantiatePrefab(editorContext, prefabRecord->Guid, std::nullopt);
|
const auto secondInstanceRootId = prefabService->InstantiatePrefab(editorContext, prefabRecord->Guid, std::nullopt);
|
||||||
MetaCoreExpect(secondInstanceRootId.has_value(), "应用后应仍能再次实例化 prefab");
|
MetaCoreExpect(secondInstanceRootId.has_value(), "应用后应仍能再次实例化 prefab");
|
||||||
MetaCore::MetaCoreGameObject* secondInstanceRoot = scene.FindGameObject(*secondInstanceRootId);
|
MetaCore::MetaCoreGameObject secondInstanceRoot = scene.FindGameObject(*secondInstanceRootId);
|
||||||
MetaCoreExpect(secondInstanceRoot != nullptr, "第二个实例应存在");
|
MetaCoreExpect(secondInstanceRoot, "第二个实例应存在");
|
||||||
MetaCoreExpectVec3Near(secondInstanceRoot->Transform.Position, glm::vec3(8.0F, 9.0F, 10.0F), "Apply 后新实例应继承修改过的 transform");
|
MetaCoreExpectVec3Near(secondInstanceRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position, glm::vec3(8.0F, 9.0F, 10.0F), "Apply 后新实例应继承修改过的 transform");
|
||||||
|
|
||||||
secondInstanceRoot->Transform.Position = glm::vec3(-1.0F, -2.0F, -3.0F);
|
secondInstanceRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(-1.0F, -2.0F, -3.0F);
|
||||||
editorContext.SelectOnly(secondInstanceRoot->Id);
|
editorContext.SelectOnly(secondInstanceRoot.GetId());
|
||||||
MetaCoreExpect(prefabService->RevertSelectedPrefabInstance(editorContext), "应能还原 prefab 实例");
|
MetaCoreExpect(prefabService->RevertSelectedPrefabInstance(editorContext), "应能还原 prefab 实例");
|
||||||
|
|
||||||
MetaCore::MetaCoreGameObject* revertedRoot = scene.FindGameObject(editorContext.GetActiveObjectId());
|
MetaCore::MetaCoreGameObject revertedRoot = scene.FindGameObject(editorContext.GetActiveObjectId());
|
||||||
MetaCoreExpect(revertedRoot != nullptr, "还原后应重新选中实例根");
|
MetaCoreExpect(revertedRoot, "还原后应重新选中实例根");
|
||||||
MetaCoreExpectVec3Near(revertedRoot->Transform.Position, glm::vec3(8.0F, 9.0F, 10.0F), "Revert 后实例应恢复为 prefab 内容");
|
MetaCoreExpectVec3Near(revertedRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position, glm::vec3(8.0F, 9.0F, 10.0F), "Revert 后实例应恢复为 prefab 内容");
|
||||||
|
|
||||||
coreServicesModule->Shutdown(moduleRegistry);
|
coreServicesModule->Shutdown(moduleRegistry);
|
||||||
moduleRegistry.ShutdownServices();
|
moduleRegistry.ShutdownServices();
|
||||||
@ -1450,8 +1459,8 @@ void MetaCoreTestComponentRegistryOperations() {
|
|||||||
moduleRegistry
|
moduleRegistry
|
||||||
);
|
);
|
||||||
|
|
||||||
MetaCore::MetaCoreGameObject& object = scene.CreateGameObject("RegistryObject");
|
MetaCore::MetaCoreGameObject object = scene.CreateGameObject("RegistryObject");
|
||||||
editorContext.SelectOnly(object.Id);
|
editorContext.SelectOnly(object.GetId());
|
||||||
|
|
||||||
const auto componentRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIComponentTypeRegistry>();
|
const auto componentRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIComponentTypeRegistry>();
|
||||||
MetaCoreExpect(componentRegistry != nullptr, "应解析到 ComponentTypeRegistry");
|
MetaCoreExpect(componentRegistry != nullptr, "应解析到 ComponentTypeRegistry");
|
||||||
@ -1466,49 +1475,49 @@ void MetaCoreTestComponentRegistryOperations() {
|
|||||||
return cameraDescriptor->AddComponent(object);
|
return cameraDescriptor->AddComponent(object);
|
||||||
});
|
});
|
||||||
MetaCoreExpect(addedCamera, "应能通过注册表添加 Camera");
|
MetaCoreExpect(addedCamera, "应能通过注册表添加 Camera");
|
||||||
MetaCoreExpect(object.Camera.has_value(), "添加后对象应拥有 Camera");
|
MetaCoreExpect(object.HasComponent<MetaCore::MetaCoreCameraComponent>(), "添加后对象应拥有 Camera");
|
||||||
|
|
||||||
const bool addedLight = editorContext.ExecuteSnapshotCommand("添加 Light", [&]() {
|
const bool addedLight = editorContext.ExecuteSnapshotCommand("添加 Light", [&]() {
|
||||||
return lightDescriptor->AddComponent(object);
|
return lightDescriptor->AddComponent(object);
|
||||||
});
|
});
|
||||||
MetaCoreExpect(addedLight, "应能通过注册表添加 Light");
|
MetaCoreExpect(addedLight, "应能通过注册表添加 Light");
|
||||||
MetaCoreExpect(object.Light.has_value(), "添加后对象应拥有 Light");
|
MetaCoreExpect(object.HasComponent<MetaCore::MetaCoreLightComponent>(), "添加后对象应拥有 Light");
|
||||||
|
|
||||||
const bool removedCamera = editorContext.ExecuteSnapshotCommand("移除 Camera", [&]() {
|
const bool removedCamera = editorContext.ExecuteSnapshotCommand("移除 Camera", [&]() {
|
||||||
return cameraDescriptor->RemoveComponent(object);
|
return cameraDescriptor->RemoveComponent(object);
|
||||||
});
|
});
|
||||||
MetaCoreExpect(removedCamera, "应能通过注册表移除 Camera");
|
MetaCoreExpect(removedCamera, "应能通过注册表移除 Camera");
|
||||||
MetaCoreExpect(!object.Camera.has_value(), "移除后对象不应再拥有 Camera");
|
MetaCoreExpect(!object.HasComponent<MetaCore::MetaCoreCameraComponent>(), "移除后对象不应再拥有 Camera");
|
||||||
|
|
||||||
const bool readdedCamera = editorContext.ExecuteSnapshotCommand("重新添加 Camera", [&]() {
|
const bool readdedCamera = editorContext.ExecuteSnapshotCommand("重新添加 Camera", [&]() {
|
||||||
return cameraDescriptor->AddComponent(object);
|
return cameraDescriptor->AddComponent(object);
|
||||||
});
|
});
|
||||||
MetaCoreExpect(readdedCamera, "应能重新添加 Camera");
|
MetaCoreExpect(readdedCamera, "应能重新添加 Camera");
|
||||||
object.Camera->FieldOfViewDegrees = 77.0F;
|
object.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees = 77.0F;
|
||||||
object.Camera->NearClip = 0.25F;
|
object.GetComponent<MetaCore::MetaCoreCameraComponent>().NearClip = 0.25F;
|
||||||
object.Camera->FarClip = 250.0F;
|
object.GetComponent<MetaCore::MetaCoreCameraComponent>().FarClip = 250.0F;
|
||||||
object.Camera->IsPrimary = true;
|
object.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary = true;
|
||||||
MetaCoreExpect(componentRegistry->CopyComponent("Camera", object), "应能复制 Camera 组件值");
|
MetaCoreExpect(componentRegistry->CopyComponent("Camera", object), "应能复制 Camera 组件值");
|
||||||
|
|
||||||
MetaCore::MetaCoreGameObject pasteTarget = scene.CreateGameObject("PasteTarget");
|
MetaCore::MetaCoreGameObject pasteTarget = scene.CreateGameObject("PasteTarget");
|
||||||
MetaCore::MetaCoreGameObject secondPasteTarget = scene.CreateGameObject("SecondPasteTarget");
|
MetaCore::MetaCoreGameObject secondPasteTarget = scene.CreateGameObject("SecondPasteTarget");
|
||||||
MetaCoreExpect(!pasteTarget.Camera.has_value(), "新对象初始不应拥有 Camera");
|
MetaCoreExpect(!pasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "新对象初始不应拥有 Camera");
|
||||||
MetaCoreExpect(!secondPasteTarget.Camera.has_value(), "第二个新对象初始不应拥有 Camera");
|
MetaCoreExpect(!secondPasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "第二个新对象初始不应拥有 Camera");
|
||||||
MetaCoreExpect(componentRegistry->CanPasteComponent("Camera"), "复制后应可粘贴 Camera");
|
MetaCoreExpect(componentRegistry->CanPasteComponent("Camera"), "复制后应可粘贴 Camera");
|
||||||
MetaCoreExpect(componentRegistry->PasteComponent("Camera", pasteTarget), "应能粘贴 Camera 组件值");
|
MetaCoreExpect(componentRegistry->PasteComponent("Camera", pasteTarget), "应能粘贴 Camera 组件值");
|
||||||
MetaCoreExpect(componentRegistry->PasteComponent("Camera", secondPasteTarget), "应能向第二个对象粘贴 Camera 组件值");
|
MetaCoreExpect(componentRegistry->PasteComponent("Camera", secondPasteTarget), "应能向第二个对象粘贴 Camera 组件值");
|
||||||
MetaCoreExpect(pasteTarget.Camera.has_value(), "粘贴后目标对象应拥有 Camera");
|
MetaCoreExpect(pasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "粘贴后目标对象应拥有 Camera");
|
||||||
MetaCoreExpect(secondPasteTarget.Camera.has_value(), "第二个目标对象粘贴后应拥有 Camera");
|
MetaCoreExpect(secondPasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "第二个目标对象粘贴后应拥有 Camera");
|
||||||
MetaCoreExpect(std::abs(pasteTarget.Camera->FieldOfViewDegrees - 77.0F) <= 0.0001F, "粘贴后 FOV 应一致");
|
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees - 77.0F) <= 0.0001F, "粘贴后 FOV 应一致");
|
||||||
MetaCoreExpect(std::abs(pasteTarget.Camera->NearClip - 0.25F) <= 0.0001F, "粘贴后 NearClip 应一致");
|
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().NearClip - 0.25F) <= 0.0001F, "粘贴后 NearClip 应一致");
|
||||||
MetaCoreExpect(std::abs(pasteTarget.Camera->FarClip - 250.0F) <= 0.0001F, "粘贴后 FarClip 应一致");
|
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FarClip - 250.0F) <= 0.0001F, "粘贴后 FarClip 应一致");
|
||||||
MetaCoreExpect(pasteTarget.Camera->IsPrimary, "粘贴后 IsPrimary 应一致");
|
MetaCoreExpect(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary, "粘贴后 IsPrimary 应一致");
|
||||||
MetaCoreExpect(std::abs(secondPasteTarget.Camera->FieldOfViewDegrees - 77.0F) <= 0.0001F, "第二个对象粘贴后 FOV 应一致");
|
MetaCoreExpect(std::abs(secondPasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees - 77.0F) <= 0.0001F, "第二个对象粘贴后 FOV 应一致");
|
||||||
|
|
||||||
MetaCoreExpect(cameraDescriptor->ResetComponent != nullptr, "Camera descriptor 应提供 Reset");
|
MetaCoreExpect(cameraDescriptor->ResetComponent != nullptr, "Camera descriptor 应提供 Reset");
|
||||||
pasteTarget.Camera->FieldOfViewDegrees = 10.0F;
|
pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees = 10.0F;
|
||||||
MetaCoreExpect(cameraDescriptor->ResetComponent(pasteTarget), "应能重置 Camera");
|
MetaCoreExpect(cameraDescriptor->ResetComponent(pasteTarget), "应能重置 Camera");
|
||||||
MetaCoreExpect(std::abs(pasteTarget.Camera->FieldOfViewDegrees - 60.0F) <= 0.0001F, "重置后 Camera 应恢复默认值");
|
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees - 60.0F) <= 0.0001F, "重置后 Camera 应恢复默认值");
|
||||||
|
|
||||||
coreServicesModule->Shutdown(moduleRegistry);
|
coreServicesModule->Shutdown(moduleRegistry);
|
||||||
moduleRegistry.ShutdownServices();
|
moduleRegistry.ShutdownServices();
|
||||||
@ -1722,7 +1731,7 @@ void MetaCoreTestRuntimeDataDispatcherConstruction() {
|
|||||||
MetaCore::MetaCoreSceneBindingDefinition{
|
MetaCore::MetaCoreSceneBindingDefinition{
|
||||||
"binding.pump.position",
|
"binding.pump.position",
|
||||||
"pump.position",
|
"pump.position",
|
||||||
scene.GetGameObjects().front().Id,
|
scene.GetGameObjects().front().GetId(),
|
||||||
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
|
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
|
||||||
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
||||||
}
|
}
|
||||||
@ -1734,8 +1743,8 @@ void MetaCoreTestRuntimeDataDispatcherConstruction() {
|
|||||||
|
|
||||||
void MetaCoreTestRuntimeDataDispatcherAppliesUpdates() {
|
void MetaCoreTestRuntimeDataDispatcherAppliesUpdates() {
|
||||||
MetaCore::MetaCoreScene scene;
|
MetaCore::MetaCoreScene scene;
|
||||||
MetaCore::MetaCoreGameObject& cube = scene.CreateGameObject("Cube");
|
MetaCore::MetaCoreGameObject cube = scene.CreateGameObject("Cube");
|
||||||
cube.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
|
cube.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
||||||
|
|
||||||
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
|
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
|
||||||
dispatcher.SetDataPointDefinitions({
|
dispatcher.SetDataPointDefinitions({
|
||||||
@ -1756,14 +1765,14 @@ void MetaCoreTestRuntimeDataDispatcherAppliesUpdates() {
|
|||||||
MetaCore::MetaCoreSceneBindingDefinition{
|
MetaCore::MetaCoreSceneBindingDefinition{
|
||||||
"binding.position",
|
"binding.position",
|
||||||
"cube.position",
|
"cube.position",
|
||||||
cube.Id,
|
cube.GetId(),
|
||||||
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
|
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
|
||||||
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
||||||
},
|
},
|
||||||
MetaCore::MetaCoreSceneBindingDefinition{
|
MetaCore::MetaCoreSceneBindingDefinition{
|
||||||
"binding.visible",
|
"binding.visible",
|
||||||
"cube.visible",
|
"cube.visible",
|
||||||
cube.Id,
|
cube.GetId(),
|
||||||
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
||||||
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
||||||
}
|
}
|
||||||
@ -1800,8 +1809,8 @@ void MetaCoreTestRuntimeDataDispatcherAppliesUpdates() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
MetaCoreExpectVec3Near(cube.Transform.Position, glm::vec3(4.0F, 5.0F, 6.0F), "Dispatcher 应更新 Transform.Position");
|
MetaCoreExpectVec3Near(cube.GetComponent<MetaCore::MetaCoreTransformComponent>().Position, glm::vec3(4.0F, 5.0F, 6.0F), "Dispatcher 应更新 Transform.Position");
|
||||||
MetaCoreExpect(!cube.MeshRenderer->Visible, "Dispatcher 应更新 MeshRenderer.Visible");
|
MetaCoreExpect(!cube.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().Visible, "Dispatcher 应更新 MeshRenderer.Visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetaCoreTestMockRuntimeDataSourceAdapterEmitsUpdates() {
|
void MetaCoreTestMockRuntimeDataSourceAdapterEmitsUpdates() {
|
||||||
@ -1824,8 +1833,8 @@ void MetaCoreTestMockRuntimeDataSourceAdapterEmitsUpdates() {
|
|||||||
|
|
||||||
void MetaCoreTestRuntimeDataDispatcherMarksStaleBindings() {
|
void MetaCoreTestRuntimeDataDispatcherMarksStaleBindings() {
|
||||||
MetaCore::MetaCoreScene scene;
|
MetaCore::MetaCoreScene scene;
|
||||||
MetaCore::MetaCoreGameObject& cube = scene.CreateGameObject("Cube");
|
MetaCore::MetaCoreGameObject cube = scene.CreateGameObject("Cube");
|
||||||
cube.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
|
cube.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
||||||
|
|
||||||
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
|
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
|
||||||
dispatcher.SetDataPointDefinitions({
|
dispatcher.SetDataPointDefinitions({
|
||||||
@ -1840,7 +1849,7 @@ void MetaCoreTestRuntimeDataDispatcherMarksStaleBindings() {
|
|||||||
MetaCore::MetaCoreSceneBindingDefinition{
|
MetaCore::MetaCoreSceneBindingDefinition{
|
||||||
"binding.visible",
|
"binding.visible",
|
||||||
"cube.visible",
|
"cube.visible",
|
||||||
cube.Id,
|
cube.GetId(),
|
||||||
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
||||||
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
||||||
}
|
}
|
||||||
@ -1938,8 +1947,8 @@ void MetaCoreTestFileReplayRuntimeDataSourceAdapterEmitsReplayFrames() {
|
|||||||
|
|
||||||
void MetaCoreTestRuntimeDiagnosticsSnapshotReportsFaults() {
|
void MetaCoreTestRuntimeDiagnosticsSnapshotReportsFaults() {
|
||||||
MetaCore::MetaCoreScene scene;
|
MetaCore::MetaCoreScene scene;
|
||||||
MetaCore::MetaCoreGameObject& cube = scene.CreateGameObject("Cube");
|
MetaCore::MetaCoreGameObject cube = scene.CreateGameObject("Cube");
|
||||||
cube.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
|
cube.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
||||||
|
|
||||||
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
|
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
|
||||||
dispatcher.SetDataPointDefinitions({
|
dispatcher.SetDataPointDefinitions({
|
||||||
@ -1954,7 +1963,7 @@ void MetaCoreTestRuntimeDiagnosticsSnapshotReportsFaults() {
|
|||||||
MetaCore::MetaCoreSceneBindingDefinition{
|
MetaCore::MetaCoreSceneBindingDefinition{
|
||||||
"binding.visible",
|
"binding.visible",
|
||||||
"cube.visible",
|
"cube.visible",
|
||||||
cube.Id,
|
cube.GetId(),
|
||||||
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
||||||
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
||||||
}
|
}
|
||||||
@ -2370,9 +2379,9 @@ int main() {
|
|||||||
bool hasLight = false;
|
bool hasLight = false;
|
||||||
bool hasCube = false;
|
bool hasCube = false;
|
||||||
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
|
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
|
||||||
hasCamera = hasCamera || object.Camera.has_value();
|
hasCamera = hasCamera || object.HasComponent<MetaCore::MetaCoreCameraComponent>();
|
||||||
hasLight = hasLight || object.Light.has_value();
|
hasLight = hasLight || object.HasComponent<MetaCore::MetaCoreLightComponent>();
|
||||||
hasCube = hasCube || object.MeshRenderer.has_value();
|
hasCube = hasCube || object.HasComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaCoreExpect(hasCamera, "默认场景应包含摄像机");
|
MetaCoreExpect(hasCamera, "默认场景应包含摄像机");
|
||||||
|
|||||||
719
third_party/stb/stb_dxt.h
vendored
Normal file
719
third_party/stb/stb_dxt.h
vendored
Normal file
@ -0,0 +1,719 @@
|
|||||||
|
// stb_dxt.h - v1.12 - DXT1/DXT5 compressor - public domain
|
||||||
|
// original by fabian "ryg" giesen - ported to C by stb
|
||||||
|
// use '#define STB_DXT_IMPLEMENTATION' before including to create the implementation
|
||||||
|
//
|
||||||
|
// USAGE:
|
||||||
|
// call stb_compress_dxt_block() for every block (you must pad)
|
||||||
|
// source should be a 4x4 block of RGBA data in row-major order;
|
||||||
|
// Alpha channel is not stored if you specify alpha=0 (but you
|
||||||
|
// must supply some constant alpha in the alpha channel).
|
||||||
|
// You can turn on dithering and "high quality" using mode.
|
||||||
|
//
|
||||||
|
// version history:
|
||||||
|
// v1.12 - (ryg) fix bug in single-color table generator
|
||||||
|
// v1.11 - (ryg) avoid racy global init, better single-color tables, remove dither
|
||||||
|
// v1.10 - (i.c) various small quality improvements
|
||||||
|
// v1.09 - (stb) update documentation re: surprising alpha channel requirement
|
||||||
|
// v1.08 - (stb) fix bug in dxt-with-alpha block
|
||||||
|
// v1.07 - (stb) bc4; allow not using libc; add STB_DXT_STATIC
|
||||||
|
// v1.06 - (stb) fix to known-broken 1.05
|
||||||
|
// v1.05 - (stb) support bc5/3dc (Arvids Kokins), use extern "C" in C++ (Pavel Krajcevski)
|
||||||
|
// v1.04 - (ryg) default to no rounding bias for lerped colors (as per S3TC/DX10 spec);
|
||||||
|
// single color match fix (allow for inexact color interpolation);
|
||||||
|
// optimal DXT5 index finder; "high quality" mode that runs multiple refinement steps.
|
||||||
|
// v1.03 - (stb) endianness support
|
||||||
|
// v1.02 - (stb) fix alpha encoding bug
|
||||||
|
// v1.01 - (stb) fix bug converting to RGB that messed up quality, thanks ryg & cbloom
|
||||||
|
// v1.00 - (stb) first release
|
||||||
|
//
|
||||||
|
// contributors:
|
||||||
|
// Rich Geldreich (more accurate index selection)
|
||||||
|
// Kevin Schmidt (#defines for "freestanding" compilation)
|
||||||
|
// github:ppiastucki (BC4 support)
|
||||||
|
// Ignacio Castano - improve DXT endpoint quantization
|
||||||
|
// Alan Hickman - static table initialization
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
//
|
||||||
|
// See end of file for license information.
|
||||||
|
|
||||||
|
#ifndef STB_INCLUDE_STB_DXT_H
|
||||||
|
#define STB_INCLUDE_STB_DXT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef STB_DXT_STATIC
|
||||||
|
#define STBDDEF static
|
||||||
|
#else
|
||||||
|
#define STBDDEF extern
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// compression mode (bitflags)
|
||||||
|
#define STB_DXT_NORMAL 0
|
||||||
|
#define STB_DXT_DITHER 1 // use dithering. was always dubious, now deprecated. does nothing!
|
||||||
|
#define STB_DXT_HIGHQUAL 2 // high quality mode, does two refinement steps instead of 1. ~30-40% slower.
|
||||||
|
|
||||||
|
STBDDEF void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src_rgba_four_bytes_per_pixel, int alpha, int mode);
|
||||||
|
STBDDEF void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src_r_one_byte_per_pixel);
|
||||||
|
STBDDEF void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src_rg_two_byte_per_pixel);
|
||||||
|
|
||||||
|
#define STB_COMPRESS_DXT_BLOCK
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // STB_INCLUDE_STB_DXT_H
|
||||||
|
|
||||||
|
#ifdef STB_DXT_IMPLEMENTATION
|
||||||
|
|
||||||
|
// configuration options for DXT encoder. set them in the project/makefile or just define
|
||||||
|
// them at the top.
|
||||||
|
|
||||||
|
// STB_DXT_USE_ROUNDING_BIAS
|
||||||
|
// use a rounding bias during color interpolation. this is closer to what "ideal"
|
||||||
|
// interpolation would do but doesn't match the S3TC/DX10 spec. old versions (pre-1.03)
|
||||||
|
// implicitly had this turned on.
|
||||||
|
//
|
||||||
|
// in case you're targeting a specific type of hardware (e.g. console programmers):
|
||||||
|
// NVidia and Intel GPUs (as of 2010) as well as DX9 ref use DXT decoders that are closer
|
||||||
|
// to STB_DXT_USE_ROUNDING_BIAS. AMD/ATI, S3 and DX10 ref are closer to rounding with no bias.
|
||||||
|
// you also see "(a*5 + b*3) / 8" on some old GPU designs.
|
||||||
|
// #define STB_DXT_USE_ROUNDING_BIAS
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if !defined(STBD_FABS)
|
||||||
|
#include <math.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBD_FABS
|
||||||
|
#define STBD_FABS(x) fabs(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const unsigned char stb__OMatch5[256][2] = {
|
||||||
|
{ 0, 0 }, { 0, 0 }, { 0, 1 }, { 0, 1 }, { 1, 0 }, { 1, 0 }, { 1, 0 }, { 1, 1 },
|
||||||
|
{ 1, 1 }, { 1, 1 }, { 1, 2 }, { 0, 4 }, { 2, 1 }, { 2, 1 }, { 2, 1 }, { 2, 2 },
|
||||||
|
{ 2, 2 }, { 2, 2 }, { 2, 3 }, { 1, 5 }, { 3, 2 }, { 3, 2 }, { 4, 0 }, { 3, 3 },
|
||||||
|
{ 3, 3 }, { 3, 3 }, { 3, 4 }, { 3, 4 }, { 3, 4 }, { 3, 5 }, { 4, 3 }, { 4, 3 },
|
||||||
|
{ 5, 2 }, { 4, 4 }, { 4, 4 }, { 4, 5 }, { 4, 5 }, { 5, 4 }, { 5, 4 }, { 5, 4 },
|
||||||
|
{ 6, 3 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 4, 8 }, { 6, 5 }, { 6, 5 }, { 6, 5 },
|
||||||
|
{ 6, 6 }, { 6, 6 }, { 6, 6 }, { 6, 7 }, { 5, 9 }, { 7, 6 }, { 7, 6 }, { 8, 4 },
|
||||||
|
{ 7, 7 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 7, 8 }, { 7, 8 }, { 7, 9 }, { 8, 7 },
|
||||||
|
{ 8, 7 }, { 9, 6 }, { 8, 8 }, { 8, 8 }, { 8, 9 }, { 8, 9 }, { 9, 8 }, { 9, 8 },
|
||||||
|
{ 9, 8 }, { 10, 7 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 8, 12 }, { 10, 9 }, { 10, 9 },
|
||||||
|
{ 10, 9 }, { 10, 10 }, { 10, 10 }, { 10, 10 }, { 10, 11 }, { 9, 13 }, { 11, 10 }, { 11, 10 },
|
||||||
|
{ 12, 8 }, { 11, 11 }, { 11, 11 }, { 11, 11 }, { 11, 12 }, { 11, 12 }, { 11, 12 }, { 11, 13 },
|
||||||
|
{ 12, 11 }, { 12, 11 }, { 13, 10 }, { 12, 12 }, { 12, 12 }, { 12, 13 }, { 12, 13 }, { 13, 12 },
|
||||||
|
{ 13, 12 }, { 13, 12 }, { 14, 11 }, { 13, 13 }, { 13, 13 }, { 13, 14 }, { 12, 16 }, { 14, 13 },
|
||||||
|
{ 14, 13 }, { 14, 13 }, { 14, 14 }, { 14, 14 }, { 14, 14 }, { 14, 15 }, { 13, 17 }, { 15, 14 },
|
||||||
|
{ 15, 14 }, { 16, 12 }, { 15, 15 }, { 15, 15 }, { 15, 15 }, { 15, 16 }, { 15, 16 }, { 15, 16 },
|
||||||
|
{ 15, 17 }, { 16, 15 }, { 16, 15 }, { 17, 14 }, { 16, 16 }, { 16, 16 }, { 16, 17 }, { 16, 17 },
|
||||||
|
{ 17, 16 }, { 17, 16 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 17 }, { 17, 18 }, { 16, 20 },
|
||||||
|
{ 18, 17 }, { 18, 17 }, { 18, 17 }, { 18, 18 }, { 18, 18 }, { 18, 18 }, { 18, 19 }, { 17, 21 },
|
||||||
|
{ 19, 18 }, { 19, 18 }, { 20, 16 }, { 19, 19 }, { 19, 19 }, { 19, 19 }, { 19, 20 }, { 19, 20 },
|
||||||
|
{ 19, 20 }, { 19, 21 }, { 20, 19 }, { 20, 19 }, { 21, 18 }, { 20, 20 }, { 20, 20 }, { 20, 21 },
|
||||||
|
{ 20, 21 }, { 21, 20 }, { 21, 20 }, { 21, 20 }, { 22, 19 }, { 21, 21 }, { 21, 21 }, { 21, 22 },
|
||||||
|
{ 20, 24 }, { 22, 21 }, { 22, 21 }, { 22, 21 }, { 22, 22 }, { 22, 22 }, { 22, 22 }, { 22, 23 },
|
||||||
|
{ 21, 25 }, { 23, 22 }, { 23, 22 }, { 24, 20 }, { 23, 23 }, { 23, 23 }, { 23, 23 }, { 23, 24 },
|
||||||
|
{ 23, 24 }, { 23, 24 }, { 23, 25 }, { 24, 23 }, { 24, 23 }, { 25, 22 }, { 24, 24 }, { 24, 24 },
|
||||||
|
{ 24, 25 }, { 24, 25 }, { 25, 24 }, { 25, 24 }, { 25, 24 }, { 26, 23 }, { 25, 25 }, { 25, 25 },
|
||||||
|
{ 25, 26 }, { 24, 28 }, { 26, 25 }, { 26, 25 }, { 26, 25 }, { 26, 26 }, { 26, 26 }, { 26, 26 },
|
||||||
|
{ 26, 27 }, { 25, 29 }, { 27, 26 }, { 27, 26 }, { 28, 24 }, { 27, 27 }, { 27, 27 }, { 27, 27 },
|
||||||
|
{ 27, 28 }, { 27, 28 }, { 27, 28 }, { 27, 29 }, { 28, 27 }, { 28, 27 }, { 29, 26 }, { 28, 28 },
|
||||||
|
{ 28, 28 }, { 28, 29 }, { 28, 29 }, { 29, 28 }, { 29, 28 }, { 29, 28 }, { 30, 27 }, { 29, 29 },
|
||||||
|
{ 29, 29 }, { 29, 30 }, { 29, 30 }, { 30, 29 }, { 30, 29 }, { 30, 29 }, { 30, 30 }, { 30, 30 },
|
||||||
|
{ 30, 30 }, { 30, 31 }, { 30, 31 }, { 31, 30 }, { 31, 30 }, { 31, 30 }, { 31, 31 }, { 31, 31 },
|
||||||
|
};
|
||||||
|
static const unsigned char stb__OMatch6[256][2] = {
|
||||||
|
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 1, 1 }, { 1, 1 }, { 1, 2 }, { 2, 1 }, { 2, 2 },
|
||||||
|
{ 2, 2 }, { 2, 3 }, { 3, 2 }, { 3, 3 }, { 3, 3 }, { 3, 4 }, { 4, 3 }, { 4, 4 },
|
||||||
|
{ 4, 4 }, { 4, 5 }, { 5, 4 }, { 5, 5 }, { 5, 5 }, { 5, 6 }, { 6, 5 }, { 6, 6 },
|
||||||
|
{ 6, 6 }, { 6, 7 }, { 7, 6 }, { 7, 7 }, { 7, 7 }, { 7, 8 }, { 8, 7 }, { 8, 8 },
|
||||||
|
{ 8, 8 }, { 8, 9 }, { 9, 8 }, { 9, 9 }, { 9, 9 }, { 9, 10 }, { 10, 9 }, { 10, 10 },
|
||||||
|
{ 10, 10 }, { 10, 11 }, { 11, 10 }, { 8, 16 }, { 11, 11 }, { 11, 12 }, { 12, 11 }, { 9, 17 },
|
||||||
|
{ 12, 12 }, { 12, 13 }, { 13, 12 }, { 11, 16 }, { 13, 13 }, { 13, 14 }, { 14, 13 }, { 12, 17 },
|
||||||
|
{ 14, 14 }, { 14, 15 }, { 15, 14 }, { 14, 16 }, { 15, 15 }, { 15, 16 }, { 16, 14 }, { 16, 15 },
|
||||||
|
{ 17, 14 }, { 16, 16 }, { 16, 17 }, { 17, 16 }, { 18, 15 }, { 17, 17 }, { 17, 18 }, { 18, 17 },
|
||||||
|
{ 20, 14 }, { 18, 18 }, { 18, 19 }, { 19, 18 }, { 21, 15 }, { 19, 19 }, { 19, 20 }, { 20, 19 },
|
||||||
|
{ 20, 20 }, { 20, 20 }, { 20, 21 }, { 21, 20 }, { 21, 21 }, { 21, 21 }, { 21, 22 }, { 22, 21 },
|
||||||
|
{ 22, 22 }, { 22, 22 }, { 22, 23 }, { 23, 22 }, { 23, 23 }, { 23, 23 }, { 23, 24 }, { 24, 23 },
|
||||||
|
{ 24, 24 }, { 24, 24 }, { 24, 25 }, { 25, 24 }, { 25, 25 }, { 25, 25 }, { 25, 26 }, { 26, 25 },
|
||||||
|
{ 26, 26 }, { 26, 26 }, { 26, 27 }, { 27, 26 }, { 24, 32 }, { 27, 27 }, { 27, 28 }, { 28, 27 },
|
||||||
|
{ 25, 33 }, { 28, 28 }, { 28, 29 }, { 29, 28 }, { 27, 32 }, { 29, 29 }, { 29, 30 }, { 30, 29 },
|
||||||
|
{ 28, 33 }, { 30, 30 }, { 30, 31 }, { 31, 30 }, { 30, 32 }, { 31, 31 }, { 31, 32 }, { 32, 30 },
|
||||||
|
{ 32, 31 }, { 33, 30 }, { 32, 32 }, { 32, 33 }, { 33, 32 }, { 34, 31 }, { 33, 33 }, { 33, 34 },
|
||||||
|
{ 34, 33 }, { 36, 30 }, { 34, 34 }, { 34, 35 }, { 35, 34 }, { 37, 31 }, { 35, 35 }, { 35, 36 },
|
||||||
|
{ 36, 35 }, { 36, 36 }, { 36, 36 }, { 36, 37 }, { 37, 36 }, { 37, 37 }, { 37, 37 }, { 37, 38 },
|
||||||
|
{ 38, 37 }, { 38, 38 }, { 38, 38 }, { 38, 39 }, { 39, 38 }, { 39, 39 }, { 39, 39 }, { 39, 40 },
|
||||||
|
{ 40, 39 }, { 40, 40 }, { 40, 40 }, { 40, 41 }, { 41, 40 }, { 41, 41 }, { 41, 41 }, { 41, 42 },
|
||||||
|
{ 42, 41 }, { 42, 42 }, { 42, 42 }, { 42, 43 }, { 43, 42 }, { 40, 48 }, { 43, 43 }, { 43, 44 },
|
||||||
|
{ 44, 43 }, { 41, 49 }, { 44, 44 }, { 44, 45 }, { 45, 44 }, { 43, 48 }, { 45, 45 }, { 45, 46 },
|
||||||
|
{ 46, 45 }, { 44, 49 }, { 46, 46 }, { 46, 47 }, { 47, 46 }, { 46, 48 }, { 47, 47 }, { 47, 48 },
|
||||||
|
{ 48, 46 }, { 48, 47 }, { 49, 46 }, { 48, 48 }, { 48, 49 }, { 49, 48 }, { 50, 47 }, { 49, 49 },
|
||||||
|
{ 49, 50 }, { 50, 49 }, { 52, 46 }, { 50, 50 }, { 50, 51 }, { 51, 50 }, { 53, 47 }, { 51, 51 },
|
||||||
|
{ 51, 52 }, { 52, 51 }, { 52, 52 }, { 52, 52 }, { 52, 53 }, { 53, 52 }, { 53, 53 }, { 53, 53 },
|
||||||
|
{ 53, 54 }, { 54, 53 }, { 54, 54 }, { 54, 54 }, { 54, 55 }, { 55, 54 }, { 55, 55 }, { 55, 55 },
|
||||||
|
{ 55, 56 }, { 56, 55 }, { 56, 56 }, { 56, 56 }, { 56, 57 }, { 57, 56 }, { 57, 57 }, { 57, 57 },
|
||||||
|
{ 57, 58 }, { 58, 57 }, { 58, 58 }, { 58, 58 }, { 58, 59 }, { 59, 58 }, { 59, 59 }, { 59, 59 },
|
||||||
|
{ 59, 60 }, { 60, 59 }, { 60, 60 }, { 60, 60 }, { 60, 61 }, { 61, 60 }, { 61, 61 }, { 61, 61 },
|
||||||
|
{ 61, 62 }, { 62, 61 }, { 62, 62 }, { 62, 62 }, { 62, 63 }, { 63, 62 }, { 63, 63 }, { 63, 63 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int stb__Mul8Bit(int a, int b)
|
||||||
|
{
|
||||||
|
int t = a*b + 128;
|
||||||
|
return (t + (t >> 8)) >> 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stb__From16Bit(unsigned char *out, unsigned short v)
|
||||||
|
{
|
||||||
|
int rv = (v & 0xf800) >> 11;
|
||||||
|
int gv = (v & 0x07e0) >> 5;
|
||||||
|
int bv = (v & 0x001f) >> 0;
|
||||||
|
|
||||||
|
// expand to 8 bits via bit replication
|
||||||
|
out[0] = (rv * 33) >> 2;
|
||||||
|
out[1] = (gv * 65) >> 4;
|
||||||
|
out[2] = (bv * 33) >> 2;
|
||||||
|
out[3] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned short stb__As16Bit(int r, int g, int b)
|
||||||
|
{
|
||||||
|
return (unsigned short)((stb__Mul8Bit(r,31) << 11) + (stb__Mul8Bit(g,63) << 5) + stb__Mul8Bit(b,31));
|
||||||
|
}
|
||||||
|
|
||||||
|
// linear interpolation at 1/3 point between a and b, using desired rounding type
|
||||||
|
static int stb__Lerp13(int a, int b)
|
||||||
|
{
|
||||||
|
#ifdef STB_DXT_USE_ROUNDING_BIAS
|
||||||
|
// with rounding bias
|
||||||
|
return a + stb__Mul8Bit(b-a, 0x55);
|
||||||
|
#else
|
||||||
|
// without rounding bias
|
||||||
|
// replace "/ 3" by "* 0xaaab) >> 17" if your compiler sucks or you really need every ounce of speed.
|
||||||
|
return (2*a + b) / 3;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// lerp RGB color
|
||||||
|
static void stb__Lerp13RGB(unsigned char *out, unsigned char *p1, unsigned char *p2)
|
||||||
|
{
|
||||||
|
out[0] = (unsigned char)stb__Lerp13(p1[0], p2[0]);
|
||||||
|
out[1] = (unsigned char)stb__Lerp13(p1[1], p2[1]);
|
||||||
|
out[2] = (unsigned char)stb__Lerp13(p1[2], p2[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
|
static void stb__EvalColors(unsigned char *color,unsigned short c0,unsigned short c1)
|
||||||
|
{
|
||||||
|
stb__From16Bit(color+ 0, c0);
|
||||||
|
stb__From16Bit(color+ 4, c1);
|
||||||
|
stb__Lerp13RGB(color+ 8, color+0, color+4);
|
||||||
|
stb__Lerp13RGB(color+12, color+4, color+0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The color matching function
|
||||||
|
static unsigned int stb__MatchColorsBlock(unsigned char *block, unsigned char *color)
|
||||||
|
{
|
||||||
|
unsigned int mask = 0;
|
||||||
|
int dirr = color[0*4+0] - color[1*4+0];
|
||||||
|
int dirg = color[0*4+1] - color[1*4+1];
|
||||||
|
int dirb = color[0*4+2] - color[1*4+2];
|
||||||
|
int dots[16];
|
||||||
|
int stops[4];
|
||||||
|
int i;
|
||||||
|
int c0Point, halfPoint, c3Point;
|
||||||
|
|
||||||
|
for(i=0;i<16;i++)
|
||||||
|
dots[i] = block[i*4+0]*dirr + block[i*4+1]*dirg + block[i*4+2]*dirb;
|
||||||
|
|
||||||
|
for(i=0;i<4;i++)
|
||||||
|
stops[i] = color[i*4+0]*dirr + color[i*4+1]*dirg + color[i*4+2]*dirb;
|
||||||
|
|
||||||
|
// think of the colors as arranged on a line; project point onto that line, then choose
|
||||||
|
// next color out of available ones. we compute the crossover points for "best color in top
|
||||||
|
// half"/"best in bottom half" and then the same inside that subinterval.
|
||||||
|
//
|
||||||
|
// relying on this 1d approximation isn't always optimal in terms of euclidean distance,
|
||||||
|
// but it's very close and a lot faster.
|
||||||
|
// http://cbloomrants.blogspot.com/2008/12/12-08-08-dxtc-summary.html
|
||||||
|
|
||||||
|
c0Point = (stops[1] + stops[3]);
|
||||||
|
halfPoint = (stops[3] + stops[2]);
|
||||||
|
c3Point = (stops[2] + stops[0]);
|
||||||
|
|
||||||
|
for (i=15;i>=0;i--) {
|
||||||
|
int dot = dots[i]*2;
|
||||||
|
mask <<= 2;
|
||||||
|
|
||||||
|
if(dot < halfPoint)
|
||||||
|
mask |= (dot < c0Point) ? 1 : 3;
|
||||||
|
else
|
||||||
|
mask |= (dot < c3Point) ? 2 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The color optimization function. (Clever code, part 1)
|
||||||
|
static void stb__OptimizeColorsBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16)
|
||||||
|
{
|
||||||
|
int mind,maxd;
|
||||||
|
unsigned char *minp, *maxp;
|
||||||
|
double magn;
|
||||||
|
int v_r,v_g,v_b;
|
||||||
|
static const int nIterPower = 4;
|
||||||
|
float covf[6],vfr,vfg,vfb;
|
||||||
|
|
||||||
|
// determine color distribution
|
||||||
|
int cov[6];
|
||||||
|
int mu[3],min[3],max[3];
|
||||||
|
int ch,i,iter;
|
||||||
|
|
||||||
|
for(ch=0;ch<3;ch++)
|
||||||
|
{
|
||||||
|
const unsigned char *bp = ((const unsigned char *) block) + ch;
|
||||||
|
int muv,minv,maxv;
|
||||||
|
|
||||||
|
muv = minv = maxv = bp[0];
|
||||||
|
for(i=4;i<64;i+=4)
|
||||||
|
{
|
||||||
|
muv += bp[i];
|
||||||
|
if (bp[i] < minv) minv = bp[i];
|
||||||
|
else if (bp[i] > maxv) maxv = bp[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
mu[ch] = (muv + 8) >> 4;
|
||||||
|
min[ch] = minv;
|
||||||
|
max[ch] = maxv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine covariance matrix
|
||||||
|
for (i=0;i<6;i++)
|
||||||
|
cov[i] = 0;
|
||||||
|
|
||||||
|
for (i=0;i<16;i++)
|
||||||
|
{
|
||||||
|
int r = block[i*4+0] - mu[0];
|
||||||
|
int g = block[i*4+1] - mu[1];
|
||||||
|
int b = block[i*4+2] - mu[2];
|
||||||
|
|
||||||
|
cov[0] += r*r;
|
||||||
|
cov[1] += r*g;
|
||||||
|
cov[2] += r*b;
|
||||||
|
cov[3] += g*g;
|
||||||
|
cov[4] += g*b;
|
||||||
|
cov[5] += b*b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert covariance matrix to float, find principal axis via power iter
|
||||||
|
for(i=0;i<6;i++)
|
||||||
|
covf[i] = cov[i] / 255.0f;
|
||||||
|
|
||||||
|
vfr = (float) (max[0] - min[0]);
|
||||||
|
vfg = (float) (max[1] - min[1]);
|
||||||
|
vfb = (float) (max[2] - min[2]);
|
||||||
|
|
||||||
|
for(iter=0;iter<nIterPower;iter++)
|
||||||
|
{
|
||||||
|
float r = vfr*covf[0] + vfg*covf[1] + vfb*covf[2];
|
||||||
|
float g = vfr*covf[1] + vfg*covf[3] + vfb*covf[4];
|
||||||
|
float b = vfr*covf[2] + vfg*covf[4] + vfb*covf[5];
|
||||||
|
|
||||||
|
vfr = r;
|
||||||
|
vfg = g;
|
||||||
|
vfb = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
magn = STBD_FABS(vfr);
|
||||||
|
if (STBD_FABS(vfg) > magn) magn = STBD_FABS(vfg);
|
||||||
|
if (STBD_FABS(vfb) > magn) magn = STBD_FABS(vfb);
|
||||||
|
|
||||||
|
if(magn < 4.0f) { // too small, default to luminance
|
||||||
|
v_r = 299; // JPEG YCbCr luma coefs, scaled by 1000.
|
||||||
|
v_g = 587;
|
||||||
|
v_b = 114;
|
||||||
|
} else {
|
||||||
|
magn = 512.0 / magn;
|
||||||
|
v_r = (int) (vfr * magn);
|
||||||
|
v_g = (int) (vfg * magn);
|
||||||
|
v_b = (int) (vfb * magn);
|
||||||
|
}
|
||||||
|
|
||||||
|
minp = maxp = block;
|
||||||
|
mind = maxd = block[0]*v_r + block[1]*v_g + block[2]*v_b;
|
||||||
|
// Pick colors at extreme points
|
||||||
|
for(i=1;i<16;i++)
|
||||||
|
{
|
||||||
|
int dot = block[i*4+0]*v_r + block[i*4+1]*v_g + block[i*4+2]*v_b;
|
||||||
|
|
||||||
|
if (dot < mind) {
|
||||||
|
mind = dot;
|
||||||
|
minp = block+i*4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dot > maxd) {
|
||||||
|
maxd = dot;
|
||||||
|
maxp = block+i*4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*pmax16 = stb__As16Bit(maxp[0],maxp[1],maxp[2]);
|
||||||
|
*pmin16 = stb__As16Bit(minp[0],minp[1],minp[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const float stb__midpoints5[32] = {
|
||||||
|
0.015686f, 0.047059f, 0.078431f, 0.111765f, 0.145098f, 0.176471f, 0.207843f, 0.241176f, 0.274510f, 0.305882f, 0.337255f, 0.370588f, 0.403922f, 0.435294f, 0.466667f, 0.5f,
|
||||||
|
0.533333f, 0.564706f, 0.596078f, 0.629412f, 0.662745f, 0.694118f, 0.725490f, 0.758824f, 0.792157f, 0.823529f, 0.854902f, 0.888235f, 0.921569f, 0.952941f, 0.984314f, 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
static const float stb__midpoints6[64] = {
|
||||||
|
0.007843f, 0.023529f, 0.039216f, 0.054902f, 0.070588f, 0.086275f, 0.101961f, 0.117647f, 0.133333f, 0.149020f, 0.164706f, 0.180392f, 0.196078f, 0.211765f, 0.227451f, 0.245098f,
|
||||||
|
0.262745f, 0.278431f, 0.294118f, 0.309804f, 0.325490f, 0.341176f, 0.356863f, 0.372549f, 0.388235f, 0.403922f, 0.419608f, 0.435294f, 0.450980f, 0.466667f, 0.482353f, 0.500000f,
|
||||||
|
0.517647f, 0.533333f, 0.549020f, 0.564706f, 0.580392f, 0.596078f, 0.611765f, 0.627451f, 0.643137f, 0.658824f, 0.674510f, 0.690196f, 0.705882f, 0.721569f, 0.737255f, 0.754902f,
|
||||||
|
0.772549f, 0.788235f, 0.803922f, 0.819608f, 0.835294f, 0.850980f, 0.866667f, 0.882353f, 0.898039f, 0.913725f, 0.929412f, 0.945098f, 0.960784f, 0.976471f, 0.992157f, 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned short stb__Quantize5(float x)
|
||||||
|
{
|
||||||
|
unsigned short q;
|
||||||
|
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
|
||||||
|
q = (unsigned short)(x * 31);
|
||||||
|
q += (x > stb__midpoints5[q]);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned short stb__Quantize6(float x)
|
||||||
|
{
|
||||||
|
unsigned short q;
|
||||||
|
x = x < 0 ? 0 : x > 1 ? 1 : x; // saturate
|
||||||
|
q = (unsigned short)(x * 63);
|
||||||
|
q += (x > stb__midpoints6[q]);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The refinement function. (Clever code, part 2)
|
||||||
|
// Tries to optimize colors to suit block contents better.
|
||||||
|
// (By solving a least squares system via normal equations+Cramer's rule)
|
||||||
|
static int stb__RefineBlock(unsigned char *block, unsigned short *pmax16, unsigned short *pmin16, unsigned int mask)
|
||||||
|
{
|
||||||
|
static const int w1Tab[4] = { 3,0,2,1 };
|
||||||
|
static const int prods[4] = { 0x090000,0x000900,0x040102,0x010402 };
|
||||||
|
// ^some magic to save a lot of multiplies in the accumulating loop...
|
||||||
|
// (precomputed products of weights for least squares system, accumulated inside one 32-bit register)
|
||||||
|
|
||||||
|
float f;
|
||||||
|
unsigned short oldMin, oldMax, min16, max16;
|
||||||
|
int i, akku = 0, xx,xy,yy;
|
||||||
|
int At1_r,At1_g,At1_b;
|
||||||
|
int At2_r,At2_g,At2_b;
|
||||||
|
unsigned int cm = mask;
|
||||||
|
|
||||||
|
oldMin = *pmin16;
|
||||||
|
oldMax = *pmax16;
|
||||||
|
|
||||||
|
if((mask ^ (mask<<2)) < 4) // all pixels have the same index?
|
||||||
|
{
|
||||||
|
// yes, linear system would be singular; solve using optimal
|
||||||
|
// single-color match on average color
|
||||||
|
int r = 8, g = 8, b = 8;
|
||||||
|
for (i=0;i<16;++i) {
|
||||||
|
r += block[i*4+0];
|
||||||
|
g += block[i*4+1];
|
||||||
|
b += block[i*4+2];
|
||||||
|
}
|
||||||
|
|
||||||
|
r >>= 4; g >>= 4; b >>= 4;
|
||||||
|
|
||||||
|
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
|
||||||
|
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
|
||||||
|
} else {
|
||||||
|
At1_r = At1_g = At1_b = 0;
|
||||||
|
At2_r = At2_g = At2_b = 0;
|
||||||
|
for (i=0;i<16;++i,cm>>=2) {
|
||||||
|
int step = cm&3;
|
||||||
|
int w1 = w1Tab[step];
|
||||||
|
int r = block[i*4+0];
|
||||||
|
int g = block[i*4+1];
|
||||||
|
int b = block[i*4+2];
|
||||||
|
|
||||||
|
akku += prods[step];
|
||||||
|
At1_r += w1*r;
|
||||||
|
At1_g += w1*g;
|
||||||
|
At1_b += w1*b;
|
||||||
|
At2_r += r;
|
||||||
|
At2_g += g;
|
||||||
|
At2_b += b;
|
||||||
|
}
|
||||||
|
|
||||||
|
At2_r = 3*At2_r - At1_r;
|
||||||
|
At2_g = 3*At2_g - At1_g;
|
||||||
|
At2_b = 3*At2_b - At1_b;
|
||||||
|
|
||||||
|
// extract solutions and decide solvability
|
||||||
|
xx = akku >> 16;
|
||||||
|
yy = (akku >> 8) & 0xff;
|
||||||
|
xy = (akku >> 0) & 0xff;
|
||||||
|
|
||||||
|
f = 3.0f / 255.0f / (xx*yy - xy*xy);
|
||||||
|
|
||||||
|
max16 = stb__Quantize5((At1_r*yy - At2_r * xy) * f) << 11;
|
||||||
|
max16 |= stb__Quantize6((At1_g*yy - At2_g * xy) * f) << 5;
|
||||||
|
max16 |= stb__Quantize5((At1_b*yy - At2_b * xy) * f) << 0;
|
||||||
|
|
||||||
|
min16 = stb__Quantize5((At2_r*xx - At1_r * xy) * f) << 11;
|
||||||
|
min16 |= stb__Quantize6((At2_g*xx - At1_g * xy) * f) << 5;
|
||||||
|
min16 |= stb__Quantize5((At2_b*xx - At1_b * xy) * f) << 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pmin16 = min16;
|
||||||
|
*pmax16 = max16;
|
||||||
|
return oldMin != min16 || oldMax != max16;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Color block compression
|
||||||
|
static void stb__CompressColorBlock(unsigned char *dest, unsigned char *block, int mode)
|
||||||
|
{
|
||||||
|
unsigned int mask;
|
||||||
|
int i;
|
||||||
|
int refinecount;
|
||||||
|
unsigned short max16, min16;
|
||||||
|
unsigned char color[4*4];
|
||||||
|
|
||||||
|
refinecount = (mode & STB_DXT_HIGHQUAL) ? 2 : 1;
|
||||||
|
|
||||||
|
// check if block is constant
|
||||||
|
for (i=1;i<16;i++)
|
||||||
|
if (((unsigned int *) block)[i] != ((unsigned int *) block)[0])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(i == 16) { // constant color
|
||||||
|
int r = block[0], g = block[1], b = block[2];
|
||||||
|
mask = 0xaaaaaaaa;
|
||||||
|
max16 = (stb__OMatch5[r][0]<<11) | (stb__OMatch6[g][0]<<5) | stb__OMatch5[b][0];
|
||||||
|
min16 = (stb__OMatch5[r][1]<<11) | (stb__OMatch6[g][1]<<5) | stb__OMatch5[b][1];
|
||||||
|
} else {
|
||||||
|
// first step: PCA+map along principal axis
|
||||||
|
stb__OptimizeColorsBlock(block,&max16,&min16);
|
||||||
|
if (max16 != min16) {
|
||||||
|
stb__EvalColors(color,max16,min16);
|
||||||
|
mask = stb__MatchColorsBlock(block,color);
|
||||||
|
} else
|
||||||
|
mask = 0;
|
||||||
|
|
||||||
|
// third step: refine (multiple times if requested)
|
||||||
|
for (i=0;i<refinecount;i++) {
|
||||||
|
unsigned int lastmask = mask;
|
||||||
|
|
||||||
|
if (stb__RefineBlock(block,&max16,&min16,mask)) {
|
||||||
|
if (max16 != min16) {
|
||||||
|
stb__EvalColors(color,max16,min16);
|
||||||
|
mask = stb__MatchColorsBlock(block,color);
|
||||||
|
} else {
|
||||||
|
mask = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mask == lastmask)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the color block
|
||||||
|
if(max16 < min16)
|
||||||
|
{
|
||||||
|
unsigned short t = min16;
|
||||||
|
min16 = max16;
|
||||||
|
max16 = t;
|
||||||
|
mask ^= 0x55555555;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0] = (unsigned char) (max16);
|
||||||
|
dest[1] = (unsigned char) (max16 >> 8);
|
||||||
|
dest[2] = (unsigned char) (min16);
|
||||||
|
dest[3] = (unsigned char) (min16 >> 8);
|
||||||
|
dest[4] = (unsigned char) (mask);
|
||||||
|
dest[5] = (unsigned char) (mask >> 8);
|
||||||
|
dest[6] = (unsigned char) (mask >> 16);
|
||||||
|
dest[7] = (unsigned char) (mask >> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alpha block compression (this is easy for a change)
|
||||||
|
static void stb__CompressAlphaBlock(unsigned char *dest,unsigned char *src, int stride)
|
||||||
|
{
|
||||||
|
int i,dist,bias,dist4,dist2,bits,mask;
|
||||||
|
|
||||||
|
// find min/max color
|
||||||
|
int mn,mx;
|
||||||
|
mn = mx = src[0];
|
||||||
|
|
||||||
|
for (i=1;i<16;i++)
|
||||||
|
{
|
||||||
|
if (src[i*stride] < mn) mn = src[i*stride];
|
||||||
|
else if (src[i*stride] > mx) mx = src[i*stride];
|
||||||
|
}
|
||||||
|
|
||||||
|
// encode them
|
||||||
|
dest[0] = (unsigned char)mx;
|
||||||
|
dest[1] = (unsigned char)mn;
|
||||||
|
dest += 2;
|
||||||
|
|
||||||
|
// determine bias and emit color indices
|
||||||
|
// given the choice of mx/mn, these indices are optimal:
|
||||||
|
// http://fgiesen.wordpress.com/2009/12/15/dxt5-alpha-block-index-determination/
|
||||||
|
dist = mx-mn;
|
||||||
|
dist4 = dist*4;
|
||||||
|
dist2 = dist*2;
|
||||||
|
bias = (dist < 8) ? (dist - 1) : (dist/2 + 2);
|
||||||
|
bias -= mn * 7;
|
||||||
|
bits = 0,mask=0;
|
||||||
|
|
||||||
|
for (i=0;i<16;i++) {
|
||||||
|
int a = src[i*stride]*7 + bias;
|
||||||
|
int ind,t;
|
||||||
|
|
||||||
|
// select index. this is a "linear scale" lerp factor between 0 (val=min) and 7 (val=max).
|
||||||
|
t = (a >= dist4) ? -1 : 0; ind = t & 4; a -= dist4 & t;
|
||||||
|
t = (a >= dist2) ? -1 : 0; ind += t & 2; a -= dist2 & t;
|
||||||
|
ind += (a >= dist);
|
||||||
|
|
||||||
|
// turn linear scale into DXT index (0/1 are extremal pts)
|
||||||
|
ind = -ind & 7;
|
||||||
|
ind ^= (2 > ind);
|
||||||
|
|
||||||
|
// write index
|
||||||
|
mask |= ind << bits;
|
||||||
|
if((bits += 3) >= 8) {
|
||||||
|
*dest++ = (unsigned char)mask;
|
||||||
|
mask >>= 8;
|
||||||
|
bits -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_compress_dxt_block(unsigned char *dest, const unsigned char *src, int alpha, int mode)
|
||||||
|
{
|
||||||
|
unsigned char data[16][4];
|
||||||
|
if (alpha) {
|
||||||
|
int i;
|
||||||
|
stb__CompressAlphaBlock(dest,(unsigned char*) src+3, 4);
|
||||||
|
dest += 8;
|
||||||
|
// make a new copy of the data in which alpha is opaque,
|
||||||
|
// because code uses a fast test for color constancy
|
||||||
|
memcpy(data, src, 4*16);
|
||||||
|
for (i=0; i < 16; ++i)
|
||||||
|
data[i][3] = 255;
|
||||||
|
src = &data[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
stb__CompressColorBlock(dest,(unsigned char*) src,mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_compress_bc4_block(unsigned char *dest, const unsigned char *src)
|
||||||
|
{
|
||||||
|
stb__CompressAlphaBlock(dest,(unsigned char*) src, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stb_compress_bc5_block(unsigned char *dest, const unsigned char *src)
|
||||||
|
{
|
||||||
|
stb__CompressAlphaBlock(dest,(unsigned char*) src,2);
|
||||||
|
stb__CompressAlphaBlock(dest + 8,(unsigned char*) src+1,2);
|
||||||
|
}
|
||||||
|
#endif // STB_DXT_IMPLEMENTATION
|
||||||
|
|
||||||
|
// Compile with STB_DXT_IMPLEMENTATION and STB_DXT_GENERATE_TABLES
|
||||||
|
// defined to generate the tables above.
|
||||||
|
#ifdef STB_DXT_GENERATE_TABLES
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
const char *omatch_names[] = { "stb__OMatch5", "stb__OMatch6" };
|
||||||
|
int dequant_mults[2] = { 33*4, 65 }; // .4 fixed-point dequant multipliers
|
||||||
|
|
||||||
|
// optimal endpoint tables
|
||||||
|
for (i = 0; i < 2; ++i) {
|
||||||
|
int dequant = dequant_mults[i];
|
||||||
|
int size = i ? 64 : 32;
|
||||||
|
printf("static const unsigned char %s[256][2] = {\n", omatch_names[i]);
|
||||||
|
for (int j = 0; j < 256; ++j) {
|
||||||
|
int mn, mx;
|
||||||
|
int best_mn = 0, best_mx = 0;
|
||||||
|
int best_err = 256 * 100;
|
||||||
|
for (mn=0;mn<size;mn++) {
|
||||||
|
for (mx=0;mx<size;mx++) {
|
||||||
|
int mine = (mn * dequant) >> 4;
|
||||||
|
int maxe = (mx * dequant) >> 4;
|
||||||
|
int err = abs(stb__Lerp13(maxe, mine) - j) * 100;
|
||||||
|
|
||||||
|
// DX10 spec says that interpolation must be within 3% of "correct" result,
|
||||||
|
// add this as error term. Normally we'd expect a random distribution of
|
||||||
|
// +-1.5% error, but nowhere in the spec does it say that the error has to be
|
||||||
|
// unbiased - better safe than sorry.
|
||||||
|
err += abs(maxe - mine) * 3;
|
||||||
|
|
||||||
|
if(err < best_err) {
|
||||||
|
best_mn = mn;
|
||||||
|
best_mx = mx;
|
||||||
|
best_err = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((j % 8) == 0) printf(" "); // 2 spaces, third is done below
|
||||||
|
printf(" { %2d, %2d },", best_mx, best_mn);
|
||||||
|
if ((j % 8) == 7) printf("\n");
|
||||||
|
}
|
||||||
|
printf("};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
This software is available under 2 licenses -- choose whichever you prefer.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE A - MIT License
|
||||||
|
Copyright (c) 2017 Sean Barrett
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||||
|
software, either in source code form or as a compiled binary, for any purpose,
|
||||||
|
commercial or non-commercial, and by any means.
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||||
|
software dedicate any and all copyright interest in the software to the public
|
||||||
|
domain. We make this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of our heirs and successors. We intend this dedication to be an
|
||||||
|
overt act of relinquishment in perpetuity of all present and future rights to
|
||||||
|
this software under copyright law.
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
7988
third_party/stb/stb_image.h
vendored
Normal file
7988
third_party/stb/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
third_party/stb/stb_image_impl.cpp
vendored
Normal file
7
third_party/stb/stb_image_impl.cpp
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
// stb_image 实现编译单元
|
||||||
|
// 仅此一个 .cpp 文件中定义 STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#define STB_DXT_IMPLEMENTATION
|
||||||
|
#include "stb_dxt.h"
|
||||||
Loading…
Reference in New Issue
Block a user