feat: 实现模型导入基础版,支持纯色渲染排查,修复UI卡顿

This commit is contained in:
ayuan9957 2026-05-14 11:14:51 +08:00
parent c8021a9206
commit 2799ddc0e3
22 changed files with 10256 additions and 1613 deletions

View File

@ -32,7 +32,7 @@ find_package(imgui CONFIG REQUIRED)
set(METACORE_COMMON_WARNINGS)
if(MSVC)
set(METACORE_COMMON_WARNINGS /W4 /permissive- /EHsc)
set(METACORE_COMMON_WARNINGS /W4 /permissive- /EHsc /utf-8)
else()
set(METACORE_COMMON_WARNINGS -Wall -Wextra -Wpedantic)
endif()
@ -302,12 +302,15 @@ set(METACORE_EDITOR_HEADERS
set(METACORE_EDITOR_PRIVATE_HEADERS
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.h
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.h
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.h
third_party/ImGuizmo/ImGuizmo.h
third_party/cgltf/cgltf.h
third_party/stb/stb_image.h
)
set(METACORE_EDITOR_SOURCES
Source/MetaCoreEditor/Private/MetaCoreBuiltinCoreServicesModule.cpp
Source/MetaCoreEditor/Private/MetaCoreGltfImporter.cpp
Source/MetaCoreEditor/Private/MetaCoreBuiltinEditorModule.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorApp.cpp
Source/MetaCoreEditor/Private/MetaCoreEditorCameraController.cpp
@ -318,6 +321,7 @@ set(METACORE_EDITOR_SOURCES
Source/MetaCoreEditor/Private/MetaCoreSceneInteractionService.cpp
third_party/ImGuizmo/ImGuizmo.cpp
third_party/cgltf/cgltf.cpp
third_party/stb/stb_image_impl.cpp
)
metacore_generate_reflection(
@ -343,6 +347,7 @@ target_include_directories(MetaCoreEditor
third_party
third_party/ImGuizmo
third_party/cgltf
third_party/stb
)
target_link_libraries(MetaCoreEditor
@ -400,6 +405,7 @@ if(METACORE_BUILD_TESTS)
MetaCoreEditor
MetaCoreRuntimeData
)
target_compile_options(MetaCoreSmokeTests PRIVATE ${METACORE_COMMON_WARNINGS})
metacore_stage_panda3d_runtime(MetaCoreSmokeTests)
metacore_stage_simplepbr_runtime(MetaCoreSmokeTests)

File diff suppressed because it is too large Load Diff

View File

@ -136,11 +136,11 @@ void MetaCoreDrawSceneViewportDropTarget(
ImGui::SetCursorScreenPos(min);
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(
ImVec2(min.x + (viewportSize.x - textSize.x) * 0.5F, min.y + (viewportSize.y - textSize.y) * 0.5F),
IM_COL32(230, 236, 245, 255),
"拖拽 Model / Prefab 到场景"
"\u62D6\u62FD Model / Prefab \u5230\u573A\u666F"
);
if (ImGui::BeginDragDropTarget()) {
@ -728,22 +728,11 @@ bool MetaCoreEditorApp::Initialize() {
}
MetaCoreTraceStartup("metacore.app: add console messages");
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "System", "MetaCore 编辑器已初始化");
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Render", "MetaCore 场景视口已准备完成");
if (ViewportRenderer_.IsSceneSyncDegraded()) {
EditorContext_->AddConsoleMessage(
MetaCoreLogLevel::Warning,
"Render",
"Debug 兼容模式已启用:当前优先保证 MetaCore 编辑器稳定运行,部分 3D 场景内容会暂时简化显示"
);
EditorContext_->AddConsoleMessage(
MetaCoreLogLevel::Info,
"Render",
"如需继续排查场景同步问题,可设置开发环境变量 METACORE_ENABLE_DEBUG_SCENE_SYNC=1 后重启编辑器"
);
}
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", "\u004D\u0065\u0074\u0061\u0043\u006F\u0072\u0065\u0020\u573A\u666F\u89C6\u53E3\u5DF2\u51C6\u5907\u5B8C\u6210");
if (loadedStartupScene) {
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Project", "已加载启动场景");
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Project", "\u5DF2\u52A0\u8F7D\u542F\u52A8\u573A\u666F");
}
Initialized_ = true;
@ -962,8 +951,8 @@ void MetaCoreEditorApp::DrawEditorFrame() {
if (viewportState.Focused && !ImGui::GetIO().WantCaptureKeyboard && EditorContext_->GetInput().WasKeyPressed(MetaCoreInputKey::Focus)) {
if (MetaCoreGameObject selectedObject = EditorContext_->GetSelectedGameObject(); selectedObject) {
EditorContext_->GetCameraController().FocusGameObject(*selectedObject);
EditorContext_->AddConsoleMessage(MetaCoreLogLevel::Info, "Scene", "相机已聚焦到当前对象");
EditorContext_->GetCameraController().FocusGameObject(selectedObject);
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);
if (ViewportRenderer_.IsSceneSyncDegraded()) {
MetaCoreDrawCompatibilityScenePreviewOverlay(Scene_, sceneView, viewportState);
}
MetaCoreDrawWorldOriginOverlay(*EditorContext_, sceneView, viewportState);
MetaCoreDrawSelectionBoundsOverlay(*EditorContext_, Scene_, sceneView, viewportState);
MetaCoreDrawSelectionGroupCenterOverlay(*EditorContext_, Scene_, sceneView, viewportState);
@ -1071,41 +1058,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
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_.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 bottomNodeId = ImGui::DockBuilderSplitNode(mainNodeId, ImGuiDir_Down, 0.28F, nullptr, &mainNodeId);
ImGui::DockBuilderDockWindow("层级", leftNodeId);
ImGui::DockBuilderDockWindow("检查器", rightNodeId);
ImGui::DockBuilderDockWindow("项目", bottomNodeId);
ImGui::DockBuilderDockWindow("控制台", bottomNodeId);
ImGui::DockBuilderDockWindow("\u5C42\u7EA7", leftNodeId);
ImGui::DockBuilderDockWindow("\u68C0\u67E5\u5668", rightNodeId);
ImGui::DockBuilderDockWindow("\u9879\u76EE", bottomNodeId);
ImGui::DockBuilderDockWindow("\u63A7\u5236\u53F0", bottomNodeId);
ImGui::DockBuilderFinish(dockSpaceId);
EditorContext_->SetDockLayoutBuilt(true);

View File

@ -61,8 +61,8 @@ void MetaCoreEditorCameraController::Update(const MetaCoreSceneViewportState& vi
}
void MetaCoreEditorCameraController::FocusGameObject(const MetaCoreGameObject& gameObject) {
Target_ = gameObject.Transform.Position;
const float scaleRadius = std::max({gameObject.Transform.Scale.x, gameObject.Transform.Scale.y, gameObject.Transform.Scale.z});
Target_ = gameObject.GetComponent<MetaCoreTransformComponent>().Position;
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);
}

View File

@ -1,4 +1,7 @@
#include "MetaCoreEditor/MetaCoreEditorContext.h"
#if defined(_MSC_VER)
#pragma execution_character_set("utf-8")
#endif
#include "MetaCoreEditor/MetaCoreEditorModule.h"
#include "MetaCoreEditor/MetaCoreEditorServices.h"
@ -81,40 +84,40 @@ private:
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)) {
return false;
}
if (lhs.HasComponent<MetaCoreCameraComponent>() != rhs.HasComponent<MetaCoreCameraComponent>()) {
if (lhs.Camera.has_value() != rhs.Camera.has_value()) {
return false;
}
if (lhs.HasComponent<MetaCoreCameraComponent>()) {
if (!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees, rhs.GetComponent<MetaCoreCameraComponent>().FieldOfViewDegrees) ||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().NearClip, rhs.GetComponent<MetaCoreCameraComponent>().NearClip) ||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreCameraComponent>().FarClip, rhs.GetComponent<MetaCoreCameraComponent>().FarClip) ||
lhs.GetComponent<MetaCoreCameraComponent>().IsPrimary != rhs.GetComponent<MetaCoreCameraComponent>().IsPrimary) {
if (lhs.Camera.has_value()) {
if (!MetaCoreNearlyEqual(lhs.Camera->FieldOfViewDegrees, rhs.Camera->FieldOfViewDegrees) ||
!MetaCoreNearlyEqual(lhs.Camera->NearClip, rhs.Camera->NearClip) ||
!MetaCoreNearlyEqual(lhs.Camera->FarClip, rhs.Camera->FarClip) ||
lhs.Camera->IsPrimary != rhs.Camera->IsPrimary) {
return false;
}
}
if (lhs.HasComponent<MetaCoreMeshRendererComponent>() != rhs.HasComponent<MetaCoreMeshRendererComponent>()) {
if (lhs.MeshRenderer.has_value() != rhs.MeshRenderer.has_value()) {
return false;
}
if (lhs.HasComponent<MetaCoreMeshRendererComponent>()) {
if (lhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh != rhs.GetComponent<MetaCoreMeshRendererComponent>().BuiltinMesh ||
!MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor, rhs.GetComponent<MetaCoreMeshRendererComponent>().BaseColor) ||
lhs.GetComponent<MetaCoreMeshRendererComponent>().Visible != rhs.GetComponent<MetaCoreMeshRendererComponent>().Visible) {
if (lhs.MeshRenderer.has_value()) {
if (lhs.MeshRenderer->BuiltinMesh != rhs.MeshRenderer->BuiltinMesh ||
!MetaCoreNearlyEqualVec3(lhs.MeshRenderer->BaseColor, rhs.MeshRenderer->BaseColor) ||
lhs.MeshRenderer->Visible != rhs.MeshRenderer->Visible) {
return false;
}
}
if (lhs.HasComponent<MetaCoreLightComponent>() != rhs.HasComponent<MetaCoreLightComponent>()) {
if (lhs.Light.has_value() != rhs.Light.has_value()) {
return false;
}
if (lhs.HasComponent<MetaCoreLightComponent>()) {
if (!MetaCoreNearlyEqualVec3(lhs.GetComponent<MetaCoreLightComponent>().Color, rhs.GetComponent<MetaCoreLightComponent>().Color) ||
!MetaCoreNearlyEqual(lhs.GetComponent<MetaCoreLightComponent>().Intensity, rhs.GetComponent<MetaCoreLightComponent>().Intensity)) {
if (lhs.Light.has_value()) {
if (!MetaCoreNearlyEqualVec3(lhs.Light->Color, rhs.Light->Color) ||
!MetaCoreNearlyEqual(lhs.Light->Intensity, rhs.Light->Intensity)) {
return false;
}
}
@ -221,7 +224,7 @@ void MetaCoreEditorContext::SelectOnly(MetaCoreId objectId) {
}
void MetaCoreEditorContext::ToggleSelection(MetaCoreId objectId) {
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr) {
if (objectId == 0 || !Scene_.FindGameObject(objectId)) {
return;
}
@ -242,7 +245,7 @@ void MetaCoreEditorContext::ToggleSelection(MetaCoreId objectId) {
}
void MetaCoreEditorContext::AddToSelection(MetaCoreId objectId, bool makeActive) {
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr) {
if (objectId == 0 || !Scene_.FindGameObject(objectId)) {
return;
}
@ -263,7 +266,7 @@ void MetaCoreEditorContext::SetSelection(const std::vector<MetaCoreId>& objectId
std::unordered_set<MetaCoreId> deduplicatedIds;
for (MetaCoreId objectId : objectIds) {
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr || deduplicatedIds.contains(objectId)) {
if (objectId == 0 || !Scene_.FindGameObject(objectId) || deduplicatedIds.contains(objectId)) {
continue;
}
deduplicatedIds.insert(objectId);
@ -284,11 +287,11 @@ void MetaCoreEditorContext::SetSelection(const std::vector<MetaCoreId>& objectId
MetaCoreId MetaCoreEditorContext::GetSelectionAnchorId() const { return SelectionAnchorId_; }
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) {
if (endId == 0 || Scene_.FindGameObject(endId) == nullptr) {
if (endId == 0 || !Scene_.FindGameObject(endId)) {
return;
}
@ -331,7 +334,7 @@ void MetaCoreEditorContext::SelectRangeByOrderedIds(const std::vector<MetaCoreId
MetaCoreId MetaCoreEditorContext::GetSelectedObjectId() const { return ActiveObjectId_; }
void MetaCoreEditorContext::SetSelectedObjectId(MetaCoreId objectId) { SelectOnly(objectId); }
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_; }
void MetaCoreEditorContext::SetGizmoOperation(MetaCoreGizmoOperation operation) { GizmoOperation_ = operation; }
MetaCoreGizmoMode MetaCoreEditorContext::GetGizmoMode() const { return GizmoMode_; }
@ -391,7 +394,7 @@ void MetaCoreEditorContext::RestoreStateSnapshot(const MetaCoreEditorStateSnapsh
Scene_.RestoreSnapshot(snapshot.SceneSnapshot);
SetSelection(snapshot.SelectionSnapshot.SelectedObjectIds, snapshot.SelectionSnapshot.ActiveObjectId);
if (snapshot.SelectionSnapshot.SelectionAnchorId != 0 &&
Scene_.FindGameObject(snapshot.SelectionSnapshot.SelectionAnchorId) != nullptr) {
(bool)Scene_.FindGameObject(snapshot.SelectionSnapshot.SelectionAnchorId)) {
SelectionAnchorId_ = snapshot.SelectionSnapshot.SelectionAnchorId;
}
NormalizeSelection();
@ -480,10 +483,10 @@ bool MetaCoreEditorContext::EnsureRuntimeDataConfigLoaded() {
RuntimeDataConfigLoaded_ = true;
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()) {
AddConsoleMessage(MetaCoreLogLevel::Info, "RuntimeData", "未找到 ProjectRuntime 配置,将按默认项目运行路径保存");
AddConsoleMessage(MetaCoreLogLevel::Info, "RuntimeData", "ProjectRuntime config not found, using default path");
}
return true;
}
@ -606,7 +609,7 @@ void MetaCoreEditorContext::NormalizeSelection() {
sanitizedSelection.reserve(SelectedObjectIds_.size());
std::unordered_set<MetaCoreId> deduplicatedIds;
for (MetaCoreId objectId : SelectedObjectIds_) {
if (objectId == 0 || Scene_.FindGameObject(objectId) == nullptr || deduplicatedIds.contains(objectId)) {
if (objectId == 0 || !Scene_.FindGameObject(objectId) || deduplicatedIds.contains(objectId)) {
continue;
}
deduplicatedIds.insert(objectId);

View 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

View 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

View 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

View File

@ -287,7 +287,7 @@ void MetaCoreSceneInteractionService::FocusSelectedObject(MetaCoreEditorContext&
if (selectedObjectIds.size() == 1) {
MetaCoreGameObject selectedObject = editorContext.GetSelectedGameObject();
if (selectedObject) {
editorContext.GetCameraController().FocusGameObject(*selectedObject);
editorContext.GetCameraController().FocusGameObject(selectedObject);
}
return;
}
@ -414,7 +414,7 @@ void MetaCoreSceneInteractionService::HandleGizmoManipulation(MetaCoreEditorCont
effectiveMode = ImGuizmo::LOCAL;
}
ImGuizmo::PushID(reinterpret_cast<void*>(selectedObject));
ImGuizmo::PushID(reinterpret_cast<void*>(selectedObject.GetId()));
glm::mat4 gizmoMatrix = worldMatrixMetaCore;
ImGuizmo::Manipulate(
glm::value_ptr(gizmoViewMatrix),
@ -505,7 +505,7 @@ void MetaCoreSceneInteractionService::DrawViewportToolbar(MetaCoreEditorContext&
}
ImGui::SameLine(0, 12);
const bool hasSelection = editorContext.GetSelectedGameObject() != nullptr;
const bool hasSelection = (bool)editorContext.GetSelectedGameObject();
if (!hasSelection) {
ImGui::BeginDisabled();
}
@ -548,14 +548,14 @@ void MetaCoreSceneInteractionService::DrawViewportToolbar(MetaCoreEditorContext&
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
ImGui::TextDisabled(
"Selection: %s",
activeObject != nullptr ? activeObject->Name.c_str() : "<Missing>"
activeObject ? activeObject.GetName().c_str() : "<Missing>"
);
} else {
const MetaCoreGameObject activeObject = editorContext.GetSelectedGameObject();
ImGui::TextDisabled(
"Selection: %zu objects | Active: %s",
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);
}

View File

@ -89,7 +89,7 @@ struct MetaCorePrefabDocument {
std::string Name{};
MC_PROPERTY()
std::vector<MetaCoreGameObject> GameObjects{};
std::vector<MetaCoreGameObjectData> GameObjects{};
};
MC_STRUCT()

View File

@ -134,8 +134,8 @@ public:
void SelectRangeByOrderedIds(const std::vector<MetaCoreId>& orderedIds, MetaCoreId endId, bool additive);
[[nodiscard]] MetaCoreId GetSelectedObjectId() const;
void SetSelectedObjectId(MetaCoreId objectId);
[[nodiscard]] MetaCoreGameObject* GetSelectedGameObject();
[[nodiscard]] const MetaCoreGameObject* GetSelectedGameObject() const;
[[nodiscard]] MetaCoreGameObject GetSelectedGameObject();
[[nodiscard]] MetaCoreGameObject GetSelectedGameObject() const;
[[nodiscard]] MetaCoreGizmoOperation GetGizmoOperation() const;
void SetGizmoOperation(MetaCoreGizmoOperation operation);
[[nodiscard]] MetaCoreGizmoMode GetGizmoMode() const;

View File

@ -148,3 +148,173 @@ std::optional<MetaCorePackageDocument> MetaCoreReadPackageFile(
}
} // 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

View File

@ -58,6 +58,9 @@ struct MetaCoreTextureAssetDocument {
MC_PROPERTY()
std::uint64_t PayloadSize = 0;
MC_PROPERTY()
std::string Format = "raw";
};
MC_STRUCT()
@ -310,53 +313,6 @@ struct MetaCoreImportedGltfNodeDocument {
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()
struct MetaCoreModelAssetDocument {

View File

@ -163,5 +163,34 @@ struct MetaCorePackageDocument {
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

View File

@ -37,26 +37,6 @@ void MetaCoreTrace(const char* 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
bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevice) {
@ -72,13 +52,6 @@ bool MetaCoreEditorViewportRenderer::Initialize(MetaCoreRenderDevice& renderDevi
RenderDevice_ = &renderDevice;
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");
return true;
}
@ -87,10 +60,6 @@ void MetaCoreEditorViewportRenderer::Shutdown() {
SceneBridge_.Shutdown();
RenderDevice_ = nullptr;
ViewportRect_ = MetaCoreViewportRect{};
SceneSyncDegraded_ = false;
DebugSceneSyncOverrideEnabled_ = false;
LastSceneSyncFailure_.clear();
HasLoggedSceneSyncFailure_ = false;
}
void MetaCoreEditorViewportRenderer::SetViewportRect(const MetaCoreViewportRect& viewportRect) {
@ -107,41 +76,7 @@ void MetaCoreEditorViewportRenderer::RenderSceneToViewport(const MetaCoreScene&
RenderDevice_->SetSceneViewportRect(ViewportRect_);
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);
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) {
@ -152,16 +87,4 @@ bool MetaCoreEditorViewportRenderer::TryGetObjectWorldMatrix(MetaCoreId objectId
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

View File

@ -431,13 +431,13 @@ struct MetaCoreRuntimeModelLoadRequest {
MetaCoreTypeRegistry registry;
MetaCoreRegisterFoundationGeneratedTypes(registry);
const auto package = MetaCoreReadPackageFile(binaryPackagePath, registry);
if (!package.has_value() || package->PayloadSections.empty()) {
auto mappedPackage = MetaCoreMappedPackage::Open(binaryPackagePath, registry);
if (!mappedPackage.has_value() || mappedPackage->GetPayloadSectionCount() == 0) {
return std::nullopt;
}
MetaCoreModelAssetDocument modelDoc;
if (!MetaCoreDeserializeFromBytes(package->PayloadSections[0], modelDoc, registry)) {
if (!MetaCoreDeserializeFromBytes(mappedPackage->GetPayloadSection(0), modelDoc, registry)) {
return std::nullopt;
}
@ -445,25 +445,33 @@ struct MetaCoreRuntimeModelLoadRequest {
}
[[nodiscard]] NodePath MetaCoreTryLoadBinaryModelPackage(const std::filesystem::path& binaryPackagePath) {
MetaCoreTrace(("metacore.render: MetaCoreTryLoadBinaryModelPackage path=" + binaryPackagePath.string()).c_str());
if (binaryPackagePath.empty() || !std::filesystem::exists(binaryPackagePath)) {
MetaCoreTrace("metacore.render: binary package path empty or does not exist");
return NodePath();
}
MetaCoreTypeRegistry registry;
MetaCoreRegisterFoundationGeneratedTypes(registry);
const auto package = MetaCoreReadPackageFile(binaryPackagePath, registry);
if (!package.has_value() || package->PayloadSections.empty()) {
auto mappedPackage = MetaCoreMappedPackage::Open(binaryPackagePath, registry);
if (!mappedPackage.has_value() || mappedPackage->GetPayloadSectionCount() == 0) {
MetaCoreTrace(("metacore.render: model binary package missing payload: " + binaryPackagePath.string()).c_str());
return NodePath();
}
MetaCoreTrace(("metacore.render: package has " + std::to_string(mappedPackage->GetPayloadSectionCount()) + " payload sections").c_str());
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());
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");
std::vector<NodePath> pandaNodes;
pandaNodes.resize(modelDoc.Nodes.size());
@ -475,19 +483,35 @@ struct MetaCoreRuntimeModelLoadRequest {
const std::size_t meshPayloadIndex =
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;
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(
nodeDoc.Name + "_mesh",
package->PayloadSections[meshPayloadIndex],
nodeDoc.Name,
payloadSpan,
subMeshes
);
if (!meshGeom.is_empty()) {
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(&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 +
vCount * sizeof(MetaCoreMeshVertex) +
iCount * sizeof(std::uint32_t);
@ -674,21 +700,26 @@ struct MetaCoreRuntimeModelLoadRequest {
PT(Texture) texture = new Texture(name);
// Choose the right format based on channel count
Texture::Format format = Texture::F_rgba8;
if (doc.Channels == 3) format = Texture::F_rgb8;
else if (doc.Channels == 1) format = Texture::F_luminance;
texture->setup_2d_texture(doc.Width, doc.Height, Texture::T_unsigned_byte, format);
// Copy payload data directly to Panda3D's RAM image buffer
PTA_uchar ramImage = texture->modify_ram_image();
if (ramImage.size() == payload.size()) {
std::memcpy(&ramImage[0], reinterpret_cast<const void*>(payload.data()), payload.size());
if (doc.Format == "dxt5") {
PTA_uchar ramImage = PTA_uchar::empty_array(payload.size());
std::memcpy(ramImage.p(), payload.data(), payload.size());
texture->set_ram_image(ramImage, Texture::CM_dxt5);
} else {
MetaCoreTrace(("metacore.render: texture payload size mismatch. expected=" +
std::to_string(ramImage.size()) + " actual=" + std::to_string(payload.size())).c_str());
return nullptr;
// Copy payload data directly to Panda3D's RAM image buffer
PTA_uchar ramImage = texture->modify_ram_image();
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
@ -854,15 +885,15 @@ struct MetaCoreRuntimeModelLoadRequest {
// We only need basic types for the asset document
MetaCoreRegisterFoundationGeneratedTypes(registry);
const auto package = MetaCoreReadPackageFile(binaryPackagePath, registry);
if (package.has_value() && package->PayloadSections.size() > 1) {
auto mappedPackage = MetaCoreMappedPackage::Open(binaryPackagePath, registry);
if (mappedPackage.has_value() && mappedPackage->GetPayloadSectionCount() > 1) {
// First payload is the metadata (MetaCoreTextureAssetDocument)
MetaCoreTextureAssetDocument texDoc;
if (MetaCoreDeserializeFromBytes(package->PayloadSections[0], texDoc, registry)) {
if (MetaCoreDeserializeFromBytes(mappedPackage->GetPayloadSection(0), texDoc, registry)) {
PT(Texture) binaryTex = MetaCoreBuildTextureFromBinary(
relativeTexturePath,
texDoc,
package->PayloadSections[1]
mappedPackage->GetPayloadSection(1)
);
if (binaryTex != nullptr) {
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) {
syncStage = "refresh_material_slots";
}
const bool useAssetPreviewPath =
gameObject.GetComponent<MetaCoreMeshRendererComponent>().MeshSource == MetaCoreMeshSourceKind::Asset &&
!objectState.IsUsingPlaceholder;
objectState.MeshNode.set_material_off(1);
objectState.MeshNode.set_light_off(1);
objectState.MeshNode.set_two_sided(gameObject.GetComponent<MetaCoreMeshRendererComponent>().DoubleSided);
objectState.MeshNode.set_color(
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.r,
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.g,
gameObject.GetComponent<MetaCoreMeshRendererComponent>().BaseColor.b,
1.0F
);
if (gameObject.GetComponent<MetaCoreMeshRendererComponent>().MeshSource == MetaCoreMeshSourceKind::Asset) {
if (objectState.IsUsingPlaceholder) {
// 如果是占位符立方体,用红色显示
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) {
MetaCoreApplyBaseColorTexture(
objectState.MeshNode,
@ -1664,32 +1710,36 @@ void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene, bool compat
if (editorCameraHandle != nullptr) {
MetaCoreApplySimplePbrViewInputs(objectState.MeshNode, *sceneRootHandle, *editorCameraHandle);
}
const std::filesystem::path& projectRoot =
}
// 同样注释掉后续的材质槽应用和 else 分支,避免括号不匹配
/*
const std::filesystem::path& projectRoot =
!Impl_->ProjectRootPath.empty()
? Impl_->ProjectRootPath
: (Impl_->ProjectRootPath = MetaCoreResolveProjectRootFromEnvironment());
const auto modelDoc = MetaCoreReadRuntimeModelDocument(MetaCoreRuntimeModelLoadRequest{
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelAssetGuid,
const auto modelDoc = MetaCoreReadRuntimeModelDocument(MetaCoreRuntimeModelLoadRequest{
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelAssetGuid,
projectRoot,
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelPath,
false
});
if (modelDoc.has_value()) {
MetaCoreApplyMaterialSlotsToSubMeshes(
objectState.MeshNode,
projectRoot,
gameObject.GetComponent<MetaCoreMeshRendererComponent>().SourceModelPath,
false
});
if (modelDoc.has_value()) {
MetaCoreApplyMaterialSlotsToSubMeshes(
objectState.MeshNode,
projectRoot,
gameObject.GetComponent<MetaCoreMeshRendererComponent>(),
*modelDoc,
*sceneRootHandle,
editorCameraHandle
);
}
objectState.MaterialAssetGuids = gameObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids;
gameObject.GetComponent<MetaCoreMeshRendererComponent>(),
*modelDoc,
*sceneRootHandle,
editorCameraHandle
);
}
objectState.MaterialAssetGuids = gameObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids;
} else {
objectState.MeshNode.set_texture_off(1);
objectState.MeshNode.set_shader_off(1);
objectState.MaterialAssetGuids.clear();
}
*/
if (gameObject.GetComponent<MetaCoreMeshRendererComponent>().AlphaMode == MetaCoreMeshAlphaMode::Blend) {
objectState.MeshNode.set_transparency(TransparencyAttrib::M_alpha);

View File

@ -20,18 +20,11 @@ public:
void SetProjectRootPath(const std::filesystem::path& projectRootPath);
[[nodiscard]] bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const;
[[nodiscard]] bool IsSceneSyncDegraded() const;
[[nodiscard]] bool IsDebugSceneSyncOverrideEnabled() const;
[[nodiscard]] const std::string& GetLastSceneSyncFailure() const;
private:
MetaCoreRenderDevice* RenderDevice_ = nullptr;
MetaCorePandaSceneBridge SceneBridge_{};
MetaCoreViewportRect ViewportRect_{};
bool SceneSyncDegraded_ = false;
bool DebugSceneSyncOverrideEnabled_ = false;
std::string LastSceneSyncFailure_{};
bool HasLoggedSceneSyncFailure_ = false;
};
} // namespace MetaCore

View File

@ -77,14 +77,14 @@ void MetaCoreTestSceneEditApi() {
MetaCoreExpect(scene.GetGameObjects().size() > originalCount, "复制后对象数量应增加");
const MetaCore::MetaCoreId duplicatedRootId = duplicateRoots.front();
MetaCoreExpect(scene.FindGameObject(duplicatedRootId) != nullptr, "复制后的对象应可被查找到");
MetaCoreExpect(scene.FindGameObject(duplicatedRootId), "复制后的对象应可被查找到");
const bool reparented = scene.ReparentGameObjects({duplicatedRootId}, 0, true);
MetaCoreExpect(reparented, "重挂接操作应成功");
const std::vector<MetaCore::MetaCoreId> deletedIds = scene.DeleteGameObjects({duplicatedRootId});
MetaCoreExpect(!deletedIds.empty(), "删除应返回被删除对象");
MetaCoreExpect(scene.FindGameObject(duplicatedRootId) == nullptr, "删除后对象不应存在");
MetaCoreExpect(!scene.FindGameObject(duplicatedRootId), "删除后对象不应存在");
}
void MetaCoreTestSelectionStateMachine() {
@ -95,9 +95,9 @@ void MetaCoreTestSelectionStateMachine() {
MetaCore::MetaCoreLogService logService;
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
const MetaCore::MetaCoreId objectAId = scene.CreateGameObject("A").Id;
const MetaCore::MetaCoreId objectBId = scene.CreateGameObject("B").Id;
const MetaCore::MetaCoreId objectCId = scene.CreateGameObject("C").Id;
const MetaCore::MetaCoreId objectAId = scene.CreateGameObject("A").GetId();
const MetaCore::MetaCoreId objectBId = scene.CreateGameObject("B").GetId();
const MetaCore::MetaCoreId objectCId = scene.CreateGameObject("C").GetId();
MetaCore::MetaCoreEditorContext editorContext(
window,
@ -144,8 +144,8 @@ void MetaCoreTestUndoRedo() {
const std::size_t beforeCreateCount = scene.GetGameObjects().size();
const bool created = editorContext.ExecuteSnapshotCommand("创建对象", [&]() {
MetaCore::MetaCoreGameObject& object = scene.CreateGameObject("UndoRedoObject");
editorContext.SelectOnly(object.Id);
MetaCore::MetaCoreGameObject object = scene.CreateGameObject("UndoRedoObject");
editorContext.SelectOnly(object.GetId());
return true;
});
MetaCoreExpect(created, "创建快照命令应执行成功");
@ -510,8 +510,8 @@ void MetaCoreTestScenePersistenceRoundTrip() {
MetaCore::MetaCoreId cubeId = 0;
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
if (object.Name == "Cube") {
cubeId = object.Id;
if (object.GetName() == "Cube") {
cubeId = object.GetId();
break;
}
}
@ -540,7 +540,7 @@ void MetaCoreTestScenePersistenceRoundTrip() {
bool foundRenamedCube = false;
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
if (object.Name == "RoundTripCube") {
if (object.GetName() == "RoundTripCube") {
foundRenamedCube = true;
break;
}
@ -942,27 +942,27 @@ void MetaCoreTestInstantiateImportedModelAssetIntoScene() {
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
MetaCoreExpect(instantiatedRootId.has_value(), "应能将导入模型实例化到场景");
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot->Name == "Pump.gltf", "model root should use source file name");
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot, "实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot.GetName() == "Pump.gltf", "model root should use source file name");
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
MetaCoreExpect(instantiatedChildren.size() == 1, "model root should contain imported node");
const MetaCore::MetaCoreGameObject* instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(instantiatedRenderable != nullptr, "instantiated renderable node should exist");
MetaCoreExpect(instantiatedRenderable->Name == "Pump", "renderable child name should come from imported node");
MetaCoreExpect(instantiatedRenderable->MeshRenderer.has_value(), "renderable node should have MeshRenderer");
MetaCoreExpect(instantiatedRenderable->MeshRenderer->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->MeshRenderer->MaterialAssetGuids.empty(), "renderable node should bind material assets");
MetaCore::MetaCoreGameObject instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(instantiatedRenderable, "instantiated renderable node should exist");
MetaCoreExpect(instantiatedRenderable.GetName() == "Pump", "renderable child name should come from imported node");
MetaCoreExpect(instantiatedRenderable.HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "renderable node should have MeshRenderer");
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshSource == MetaCore::MetaCoreMeshSourceKind::Asset, "renderable node should use asset mesh source");
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid.IsValid(), "renderable node should bind mesh asset guid");
MetaCoreExpect(!instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.empty(), "renderable node should bind material assets");
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"
);
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"
);
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, "实例化后应选中新对象");
coreServicesModule->Shutdown(moduleRegistry);
@ -1104,29 +1104,29 @@ void MetaCoreTestInstantiateMultiNodeModelAssetIntoScene() {
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
MetaCoreExpect(instantiatedRootId.has_value(), "应能将多节点模型实例化到场景");
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "多节点实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot->Name == "Assembly.gltf", "multi-node model root should use source file name");
MetaCoreExpect(!instantiatedRoot->MeshRenderer.has_value(), "asset root should not force MeshRenderer");
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot, "多节点实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot.GetName() == "Assembly.gltf", "multi-node model root should use source file name");
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) {
return object.Name == "AssemblyRoot";
return object.GetName() == "AssemblyRoot";
});
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->MeshRenderer.has_value(), "imported empty root should not force MeshRenderer");
MetaCoreExpect(importedRootIterator->GetParentId() == instantiatedRoot.GetId(), "imported root node should be parented under asset root");
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) {
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->ParentId == importedRootIterator->Id, "child node should be parented under imported root node");
MetaCoreExpect(childIterator->MeshRenderer.has_value(), "mesh child should have MeshRenderer");
MetaCoreExpect(childIterator->MeshRenderer->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->MeshRenderer->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(editorContext.GetActiveObjectId() == instantiatedRoot->Id, "multi-node instantiation should select asset root");
MetaCoreExpect(childIterator->GetParentId() == importedRootIterator->GetId(), "child node should be parented under imported root node");
MetaCoreExpect(childIterator->HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "mesh child should have MeshRenderer");
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid == meshAsset.AssetGuid, "child node should bind generated mesh asset");
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.size() == 2, "child node should bind all material slots");
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.front() == materialAsset.AssetGuid, "child node should bind generated material asset");
MetaCoreExpect(childIterator->GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids[1] == secondaryMaterialAsset.AssetGuid, "child node should bind secondary material asset");
MetaCoreExpect(editorContext.GetActiveObjectId() == instantiatedRoot.GetId(), "multi-node instantiation should select asset root");
coreServicesModule->Shutdown(moduleRegistry);
moduleRegistry.ShutdownServices();
_putenv_s("METACORE_PROJECT_PATH", "");
@ -1196,15 +1196,15 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
const auto instantiatedRootId = sceneEditingService->InstantiateModelAsset(editorContext, modelRecord->Guid, std::nullopt);
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化模型资源");
const MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "instantiated model root should exist");
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot, "instantiated model root should exist");
const std::vector<MetaCore::MetaCoreId> instantiatedChildren = scene.GetChildrenOf(*instantiatedRootId);
MetaCoreExpect(instantiatedChildren.size() == 1, "instantiated model root should contain renderable child");
const MetaCore::MetaCoreGameObject* instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(instantiatedRenderable != nullptr && instantiatedRenderable->MeshRenderer.has_value(), "instantiated renderable model object should exist");
MetaCore::MetaCoreGameObject instantiatedRenderable = scene.FindGameObject(instantiatedChildren.front());
MetaCoreExpect(instantiatedRenderable && instantiatedRenderable.HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "instantiated renderable model object should exist");
const MetaCore::MetaCoreAssetGuid originalMeshGuid = instantiatedRenderable->MeshRenderer->MeshAssetGuid;
const MetaCore::MetaCoreAssetGuid originalMaterialGuid = instantiatedRenderable->MeshRenderer->MaterialAssetGuids.front();
const MetaCore::MetaCoreAssetGuid originalMeshGuid = instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid;
const MetaCore::MetaCoreAssetGuid originalMaterialGuid = instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.front();
{
std::ofstream gltfFile(modelPath, std::ios::trunc);
gltfFile
@ -1235,17 +1235,26 @@ void MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable() {
);
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.GeneratedMeshAssets.front().AssetGuid == originalMeshGuid, "重导入后 mesh 子资源 GUID 应保持稳定");
MetaCoreExpect(refreshedDocument.GeneratedMaterialAssets.front().AssetGuid == originalMaterialGuid, "重导入后材质子资源 GUID 应保持稳定");
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());
MetaCoreExpect(instantiatedRenderable != nullptr && instantiatedRenderable->MeshRenderer.has_value(), "reimport should keep renderable instance alive");
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MeshAssetGuid == originalMeshGuid, "reimport should keep mesh reference stable");
MetaCoreExpect(!instantiatedRenderable->MeshRenderer->MaterialAssetGuids.empty(), "reimport should keep material references");
MetaCoreExpect(instantiatedRenderable->MeshRenderer->MaterialAssetGuids.front() == originalMaterialGuid, "reimport should keep material reference stable");
MetaCoreExpect(instantiatedRenderable && instantiatedRenderable.HasComponent<MetaCore::MetaCoreMeshRendererComponent>(), "reimport should keep renderable instance alive");
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MeshAssetGuid == originalMeshGuid, "reimport should keep mesh reference stable");
MetaCoreExpect(!instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.empty(), "reimport should keep material references");
MetaCoreExpect(instantiatedRenderable.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().MaterialAssetGuids.front() == originalMaterialGuid, "reimport should keep material reference stable");
coreServicesModule->Shutdown(moduleRegistry);
moduleRegistry.ShutdownServices();
_putenv_s("METACORE_PROJECT_PATH", "");
@ -1352,12 +1361,12 @@ void MetaCoreTestPrefabWorkflow() {
moduleRegistry
);
MetaCore::MetaCoreGameObject& root = scene.CreateGameObject("PrefabRoot");
root.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
root.Transform.Position = glm::vec3(1.0F, 2.0F, 3.0F);
const MetaCore::MetaCoreId rootId = root.Id;
MetaCore::MetaCoreGameObject& child = scene.CreateGameObject("PrefabChild", rootId);
child.Light = MetaCore::MetaCoreLightComponent{};
MetaCore::MetaCoreGameObject root = scene.CreateGameObject("PrefabRoot");
root.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
root.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(1.0F, 2.0F, 3.0F);
const MetaCore::MetaCoreId rootId = root.GetId();
MetaCore::MetaCoreGameObject child = scene.CreateGameObject("PrefabChild", rootId);
child.AddComponent<MetaCore::MetaCoreLightComponent>();
editorContext.SelectOnly(rootId);
@ -1383,12 +1392,12 @@ void MetaCoreTestPrefabWorkflow() {
const auto instantiatedRootId = prefabService->InstantiatePrefab(editorContext, prefabRecord->Guid, std::nullopt);
MetaCoreExpect(instantiatedRootId.has_value(), "应能实例化 prefab");
MetaCore::MetaCoreGameObject* instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot != nullptr, "实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot->PrefabInstance.has_value(), "实例根应带有 prefab 元数据");
MetaCore::MetaCoreGameObject instantiatedRoot = scene.FindGameObject(*instantiatedRootId);
MetaCoreExpect(instantiatedRoot, "实例化后应能找到根对象");
MetaCoreExpect(instantiatedRoot.HasComponent<MetaCore::MetaCorePrefabInstanceMetadata>(), "实例根应带有 prefab 元数据");
instantiatedRoot->Transform.Position = glm::vec3(8.0F, 9.0F, 10.0F);
editorContext.SelectOnly(instantiatedRoot->Id);
instantiatedRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(8.0F, 9.0F, 10.0F);
editorContext.SelectOnly(instantiatedRoot.GetId());
MetaCoreExpect(prefabService->ApplySelectedPrefabInstance(editorContext), "应能应用 prefab 实例");
const auto prefabPackage = packageService->ReadPackage(tempProjectRoot / prefabPath);
@ -1412,17 +1421,17 @@ void MetaCoreTestPrefabWorkflow() {
const auto secondInstanceRootId = prefabService->InstantiatePrefab(editorContext, prefabRecord->Guid, std::nullopt);
MetaCoreExpect(secondInstanceRootId.has_value(), "应用后应仍能再次实例化 prefab");
MetaCore::MetaCoreGameObject* secondInstanceRoot = scene.FindGameObject(*secondInstanceRootId);
MetaCoreExpect(secondInstanceRoot != nullptr, "第二个实例应存在");
MetaCoreExpectVec3Near(secondInstanceRoot->Transform.Position, glm::vec3(8.0F, 9.0F, 10.0F), "Apply 后新实例应继承修改过的 transform");
MetaCore::MetaCoreGameObject secondInstanceRoot = scene.FindGameObject(*secondInstanceRootId);
MetaCoreExpect(secondInstanceRoot, "第二个实例应存在");
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);
editorContext.SelectOnly(secondInstanceRoot->Id);
secondInstanceRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(-1.0F, -2.0F, -3.0F);
editorContext.SelectOnly(secondInstanceRoot.GetId());
MetaCoreExpect(prefabService->RevertSelectedPrefabInstance(editorContext), "应能还原 prefab 实例");
MetaCore::MetaCoreGameObject* revertedRoot = scene.FindGameObject(editorContext.GetActiveObjectId());
MetaCoreExpect(revertedRoot != nullptr, "还原后应重新选中实例根");
MetaCoreExpectVec3Near(revertedRoot->Transform.Position, glm::vec3(8.0F, 9.0F, 10.0F), "Revert 后实例应恢复为 prefab 内容");
MetaCore::MetaCoreGameObject revertedRoot = scene.FindGameObject(editorContext.GetActiveObjectId());
MetaCoreExpect(revertedRoot, "还原后应重新选中实例根");
MetaCoreExpectVec3Near(revertedRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position, glm::vec3(8.0F, 9.0F, 10.0F), "Revert 后实例应恢复为 prefab 内容");
coreServicesModule->Shutdown(moduleRegistry);
moduleRegistry.ShutdownServices();
@ -1450,8 +1459,8 @@ void MetaCoreTestComponentRegistryOperations() {
moduleRegistry
);
MetaCore::MetaCoreGameObject& object = scene.CreateGameObject("RegistryObject");
editorContext.SelectOnly(object.Id);
MetaCore::MetaCoreGameObject object = scene.CreateGameObject("RegistryObject");
editorContext.SelectOnly(object.GetId());
const auto componentRegistry = moduleRegistry.ResolveService<MetaCore::MetaCoreIComponentTypeRegistry>();
MetaCoreExpect(componentRegistry != nullptr, "应解析到 ComponentTypeRegistry");
@ -1466,49 +1475,49 @@ void MetaCoreTestComponentRegistryOperations() {
return cameraDescriptor->AddComponent(object);
});
MetaCoreExpect(addedCamera, "应能通过注册表添加 Camera");
MetaCoreExpect(object.Camera.has_value(), "添加后对象应拥有 Camera");
MetaCoreExpect(object.HasComponent<MetaCore::MetaCoreCameraComponent>(), "添加后对象应拥有 Camera");
const bool addedLight = editorContext.ExecuteSnapshotCommand("添加 Light", [&]() {
return lightDescriptor->AddComponent(object);
});
MetaCoreExpect(addedLight, "应能通过注册表添加 Light");
MetaCoreExpect(object.Light.has_value(), "添加后对象应拥有 Light");
MetaCoreExpect(object.HasComponent<MetaCore::MetaCoreLightComponent>(), "添加后对象应拥有 Light");
const bool removedCamera = editorContext.ExecuteSnapshotCommand("移除 Camera", [&]() {
return cameraDescriptor->RemoveComponent(object);
});
MetaCoreExpect(removedCamera, "应能通过注册表移除 Camera");
MetaCoreExpect(!object.Camera.has_value(), "移除后对象不应再拥有 Camera");
MetaCoreExpect(!object.HasComponent<MetaCore::MetaCoreCameraComponent>(), "移除后对象不应再拥有 Camera");
const bool readdedCamera = editorContext.ExecuteSnapshotCommand("重新添加 Camera", [&]() {
return cameraDescriptor->AddComponent(object);
});
MetaCoreExpect(readdedCamera, "应能重新添加 Camera");
object.Camera->FieldOfViewDegrees = 77.0F;
object.Camera->NearClip = 0.25F;
object.Camera->FarClip = 250.0F;
object.Camera->IsPrimary = true;
object.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees = 77.0F;
object.GetComponent<MetaCore::MetaCoreCameraComponent>().NearClip = 0.25F;
object.GetComponent<MetaCore::MetaCoreCameraComponent>().FarClip = 250.0F;
object.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary = true;
MetaCoreExpect(componentRegistry->CopyComponent("Camera", object), "应能复制 Camera 组件值");
MetaCore::MetaCoreGameObject pasteTarget = scene.CreateGameObject("PasteTarget");
MetaCore::MetaCoreGameObject secondPasteTarget = scene.CreateGameObject("SecondPasteTarget");
MetaCoreExpect(!pasteTarget.Camera.has_value(), "新对象初始不应拥有 Camera");
MetaCoreExpect(!secondPasteTarget.Camera.has_value(), "第二个新对象初始不应拥有 Camera");
MetaCoreExpect(!pasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "新对象初始不应拥有 Camera");
MetaCoreExpect(!secondPasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "第二个新对象初始不应拥有 Camera");
MetaCoreExpect(componentRegistry->CanPasteComponent("Camera"), "复制后应可粘贴 Camera");
MetaCoreExpect(componentRegistry->PasteComponent("Camera", pasteTarget), "应能粘贴 Camera 组件值");
MetaCoreExpect(componentRegistry->PasteComponent("Camera", secondPasteTarget), "应能向第二个对象粘贴 Camera 组件值");
MetaCoreExpect(pasteTarget.Camera.has_value(), "粘贴后目标对象应拥有 Camera");
MetaCoreExpect(secondPasteTarget.Camera.has_value(), "第二个目标对象粘贴后应拥有 Camera");
MetaCoreExpect(std::abs(pasteTarget.Camera->FieldOfViewDegrees - 77.0F) <= 0.0001F, "粘贴后 FOV 应一致");
MetaCoreExpect(std::abs(pasteTarget.Camera->NearClip - 0.25F) <= 0.0001F, "粘贴后 NearClip 应一致");
MetaCoreExpect(std::abs(pasteTarget.Camera->FarClip - 250.0F) <= 0.0001F, "粘贴后 FarClip 应一致");
MetaCoreExpect(pasteTarget.Camera->IsPrimary, "粘贴后 IsPrimary 应一致");
MetaCoreExpect(std::abs(secondPasteTarget.Camera->FieldOfViewDegrees - 77.0F) <= 0.0001F, "第二个对象粘贴后 FOV 应一致");
MetaCoreExpect(pasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "粘贴后目标对象应拥有 Camera");
MetaCoreExpect(secondPasteTarget.HasComponent<MetaCore::MetaCoreCameraComponent>(), "第二个目标对象粘贴后应拥有 Camera");
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees - 77.0F) <= 0.0001F, "粘贴后 FOV 应一致");
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().NearClip - 0.25F) <= 0.0001F, "粘贴后 NearClip 应一致");
MetaCoreExpect(std::abs(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FarClip - 250.0F) <= 0.0001F, "粘贴后 FarClip 应一致");
MetaCoreExpect(pasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary, "粘贴后 IsPrimary 应一致");
MetaCoreExpect(std::abs(secondPasteTarget.GetComponent<MetaCore::MetaCoreCameraComponent>().FieldOfViewDegrees - 77.0F) <= 0.0001F, "第二个对象粘贴后 FOV 应一致");
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(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);
moduleRegistry.ShutdownServices();
@ -1722,7 +1731,7 @@ void MetaCoreTestRuntimeDataDispatcherConstruction() {
MetaCore::MetaCoreSceneBindingDefinition{
"binding.pump.position",
"pump.position",
scene.GetGameObjects().front().Id,
scene.GetGameObjects().front().GetId(),
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
}
@ -1734,8 +1743,8 @@ void MetaCoreTestRuntimeDataDispatcherConstruction() {
void MetaCoreTestRuntimeDataDispatcherAppliesUpdates() {
MetaCore::MetaCoreScene scene;
MetaCore::MetaCoreGameObject& cube = scene.CreateGameObject("Cube");
cube.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
MetaCore::MetaCoreGameObject cube = scene.CreateGameObject("Cube");
cube.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
dispatcher.SetDataPointDefinitions({
@ -1756,14 +1765,14 @@ void MetaCoreTestRuntimeDataDispatcherAppliesUpdates() {
MetaCore::MetaCoreSceneBindingDefinition{
"binding.position",
"cube.position",
cube.Id,
cube.GetId(),
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
},
MetaCore::MetaCoreSceneBindingDefinition{
"binding.visible",
"cube.visible",
cube.Id,
cube.GetId(),
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
}
@ -1800,8 +1809,8 @@ void MetaCoreTestRuntimeDataDispatcherAppliesUpdates() {
}
});
MetaCoreExpectVec3Near(cube.Transform.Position, glm::vec3(4.0F, 5.0F, 6.0F), "Dispatcher 应更新 Transform.Position");
MetaCoreExpect(!cube.MeshRenderer->Visible, "Dispatcher 应更新 MeshRenderer.Visible");
MetaCoreExpectVec3Near(cube.GetComponent<MetaCore::MetaCoreTransformComponent>().Position, glm::vec3(4.0F, 5.0F, 6.0F), "Dispatcher 应更新 Transform.Position");
MetaCoreExpect(!cube.GetComponent<MetaCore::MetaCoreMeshRendererComponent>().Visible, "Dispatcher 应更新 MeshRenderer.Visible");
}
void MetaCoreTestMockRuntimeDataSourceAdapterEmitsUpdates() {
@ -1824,8 +1833,8 @@ void MetaCoreTestMockRuntimeDataSourceAdapterEmitsUpdates() {
void MetaCoreTestRuntimeDataDispatcherMarksStaleBindings() {
MetaCore::MetaCoreScene scene;
MetaCore::MetaCoreGameObject& cube = scene.CreateGameObject("Cube");
cube.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
MetaCore::MetaCoreGameObject cube = scene.CreateGameObject("Cube");
cube.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
dispatcher.SetDataPointDefinitions({
@ -1840,7 +1849,7 @@ void MetaCoreTestRuntimeDataDispatcherMarksStaleBindings() {
MetaCore::MetaCoreSceneBindingDefinition{
"binding.visible",
"cube.visible",
cube.Id,
cube.GetId(),
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
}
@ -1938,8 +1947,8 @@ void MetaCoreTestFileReplayRuntimeDataSourceAdapterEmitsReplayFrames() {
void MetaCoreTestRuntimeDiagnosticsSnapshotReportsFaults() {
MetaCore::MetaCoreScene scene;
MetaCore::MetaCoreGameObject& cube = scene.CreateGameObject("Cube");
cube.MeshRenderer = MetaCore::MetaCoreMeshRendererComponent{};
MetaCore::MetaCoreGameObject cube = scene.CreateGameObject("Cube");
cube.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
MetaCore::MetaCoreRuntimeDataDispatcher dispatcher(scene);
dispatcher.SetDataPointDefinitions({
@ -1954,7 +1963,7 @@ void MetaCoreTestRuntimeDiagnosticsSnapshotReportsFaults() {
MetaCore::MetaCoreSceneBindingDefinition{
"binding.visible",
"cube.visible",
cube.Id,
cube.GetId(),
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
}
@ -2370,9 +2379,9 @@ int main() {
bool hasLight = false;
bool hasCube = false;
for (const MetaCore::MetaCoreGameObject& object : scene.GetGameObjects()) {
hasCamera = hasCamera || object.Camera.has_value();
hasLight = hasLight || object.Light.has_value();
hasCube = hasCube || object.MeshRenderer.has_value();
hasCamera = hasCamera || object.HasComponent<MetaCore::MetaCoreCameraComponent>();
hasLight = hasLight || object.HasComponent<MetaCore::MetaCoreLightComponent>();
hasCube = hasCube || object.HasComponent<MetaCore::MetaCoreMeshRendererComponent>();
}
MetaCoreExpect(hasCamera, "默认场景应包含摄像机");

719
third_party/stb/stb_dxt.h vendored Normal file
View 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

File diff suppressed because it is too large Load Diff

7
third_party/stb/stb_image_impl.cpp vendored Normal file
View 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"