1760 lines
73 KiB
C++
1760 lines
73 KiB
C++
#include "MetaCoreRender/MetaCoreFilamentSceneBridge.h"
|
||
#include "MetaCorePlatform/MetaCoreWindow.h"
|
||
#include "MetaCoreScene/MetaCoreScene.h"
|
||
#include "MetaCoreScene/MetaCoreComponents.h"
|
||
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
||
#include "MetaCoreRender/MetaCoreImGuiHelper.h"
|
||
#include "MetaCoreRender/MetaCoreSceneRenderSync.h"
|
||
#include "MetaCoreRender/MetaCoreRuntimeUiRenderer.h"
|
||
#include <imgui.h>
|
||
#include "MetaCoreFoundation/MetaCoreAssetRegistry.h"
|
||
|
||
#include <filament/Engine.h>
|
||
#include <filament/Scene.h>
|
||
#include <filament/View.h>
|
||
#include <filament/Camera.h>
|
||
#include <filament/SwapChain.h>
|
||
#include <filament/Renderer.h>
|
||
#include <filament/Viewport.h>
|
||
#include <filament/TransformManager.h>
|
||
#include <filament/Texture.h>
|
||
#include <filament/RenderTarget.h>
|
||
#include <filament/LightManager.h>
|
||
#include <filament/RenderableManager.h>
|
||
#include <filament/Material.h>
|
||
#include <filament/MaterialInstance.h>
|
||
#include <filament/IndirectLight.h>
|
||
|
||
#include <gltfio/AssetLoader.h>
|
||
#include <gltfio/FilamentAsset.h>
|
||
#include <gltfio/ResourceLoader.h>
|
||
#include <gltfio/TextureProvider.h>
|
||
#include <gltfio/MaterialProvider.h>
|
||
|
||
#include <utils/Entity.h>
|
||
#include <utils/EntityManager.h>
|
||
#include <utils/NameComponentManager.h>
|
||
#include "MetaCoreScene/MetaCoreTransformUtils.h"
|
||
|
||
#define GLM_ENABLE_EXPERIMENTAL
|
||
#include <glm/gtc/type_ptr.hpp>
|
||
#include <glm/geometric.hpp>
|
||
#include <glm/gtc/matrix_transform.hpp>
|
||
#include <glm/gtx/matrix_decompose.hpp>
|
||
#include <glm/gtc/quaternion.hpp>
|
||
|
||
#include <iostream>
|
||
#include <fstream>
|
||
#include <algorithm>
|
||
#include <cctype>
|
||
#include <unordered_map>
|
||
#include <unordered_set>
|
||
#include <map>
|
||
#include <vector>
|
||
#include <functional>
|
||
#include <optional>
|
||
#include <string_view>
|
||
#include <cstdint>
|
||
|
||
// 引入 Windows 和 OpenGL 头文件以创建纹理
|
||
#ifndef WIN32_LEAN_AND_MEAN
|
||
#define WIN32_LEAN_AND_MEAN
|
||
#endif
|
||
#ifndef NOMINMAX
|
||
#define NOMINMAX
|
||
#endif
|
||
#include <windows.h>
|
||
#include <GL/gl.h>
|
||
|
||
namespace MetaCore {
|
||
|
||
namespace {
|
||
|
||
[[nodiscard]] filament::backend::Backend MetaCoreToFilamentBackend(MetaCoreRenderBackend backend) {
|
||
switch (backend) {
|
||
case MetaCoreRenderBackend::OpenGL:
|
||
return filament::backend::Backend::OPENGL;
|
||
case MetaCoreRenderBackend::Vulkan:
|
||
return filament::backend::Backend::VULKAN;
|
||
case MetaCoreRenderBackend::WebGPU:
|
||
return filament::backend::Backend::WEBGPU;
|
||
}
|
||
return filament::backend::Backend::OPENGL;
|
||
}
|
||
|
||
[[nodiscard]] const char* MetaCoreRenderBackendToString(MetaCoreRenderBackend backend) {
|
||
switch (backend) {
|
||
case MetaCoreRenderBackend::OpenGL:
|
||
return "OpenGL";
|
||
case MetaCoreRenderBackend::Vulkan:
|
||
return "Vulkan";
|
||
case MetaCoreRenderBackend::WebGPU:
|
||
return "WebGPU";
|
||
}
|
||
return "OpenGL";
|
||
}
|
||
|
||
class MetaCoreViewportMaterialProvider final : public filament::gltfio::MaterialProvider {
|
||
public:
|
||
explicit MetaCoreViewportMaterialProvider(filament::Engine* engine)
|
||
: Inner_(filament::gltfio::createJitShaderProvider(engine)) {}
|
||
|
||
~MetaCoreViewportMaterialProvider() override {
|
||
delete Inner_;
|
||
}
|
||
|
||
filament::MaterialInstance* createMaterialInstance(
|
||
filament::gltfio::MaterialKey* config,
|
||
filament::gltfio::UvMap* uvmap,
|
||
const char* label = "material",
|
||
const char* extras = nullptr
|
||
) override {
|
||
CoerceFoliageAlphaMode(config);
|
||
return Inner_->createMaterialInstance(config, uvmap, label, extras);
|
||
}
|
||
|
||
filament::Material* getMaterial(
|
||
filament::gltfio::MaterialKey* config,
|
||
filament::gltfio::UvMap* uvmap,
|
||
const char* label = "material"
|
||
) override {
|
||
CoerceFoliageAlphaMode(config);
|
||
return Inner_->getMaterial(config, uvmap, label);
|
||
}
|
||
|
||
const filament::Material* const* getMaterials() const noexcept override {
|
||
return Inner_->getMaterials();
|
||
}
|
||
|
||
size_t getMaterialsCount() const noexcept override {
|
||
return Inner_->getMaterialsCount();
|
||
}
|
||
|
||
void destroyMaterials() override {
|
||
Inner_->destroyMaterials();
|
||
}
|
||
|
||
bool needsDummyData(filament::VertexAttribute attrib) const noexcept override {
|
||
return Inner_->needsDummyData(attrib);
|
||
}
|
||
|
||
private:
|
||
static void CoerceFoliageAlphaMode(filament::gltfio::MaterialKey* config) {
|
||
if (config != nullptr && config->alphaMode == filament::gltfio::AlphaMode::BLEND) {
|
||
config->alphaMode = filament::gltfio::AlphaMode::MASK;
|
||
}
|
||
}
|
||
|
||
filament::gltfio::MaterialProvider* Inner_ = nullptr;
|
||
};
|
||
|
||
[[nodiscard]] std::string MetaCoreBuildModelLoadFailureKey(const std::filesystem::path& absolutePath) {
|
||
std::string key = absolutePath.lexically_normal().generic_string();
|
||
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c) {
|
||
return static_cast<char>(std::tolower(c));
|
||
});
|
||
return key;
|
||
}
|
||
|
||
[[nodiscard]] bool MetaCoreIsExternalGltfResourceUri(std::string_view uri) {
|
||
if (uri.empty() || uri.starts_with("data:") || uri.starts_with("//")) {
|
||
return false;
|
||
}
|
||
return uri.find("://") == std::string_view::npos;
|
||
}
|
||
|
||
[[nodiscard]] std::optional<std::filesystem::path> MetaCoreDecodeGltfResourceUri(std::string_view uri) {
|
||
std::string decoded;
|
||
decoded.reserve(uri.size());
|
||
for (std::size_t index = 0; index < uri.size(); ++index) {
|
||
const char character = uri[index];
|
||
if (character == '%' && index + 2 < uri.size()) {
|
||
const auto hexToInt = [](char value) -> int {
|
||
if (value >= '0' && value <= '9') return value - '0';
|
||
if (value >= 'a' && value <= 'f') return 10 + (value - 'a');
|
||
if (value >= 'A' && value <= 'F') return 10 + (value - 'A');
|
||
return -1;
|
||
};
|
||
const int high = hexToInt(uri[index + 1]);
|
||
const int low = hexToInt(uri[index + 2]);
|
||
if (high >= 0 && low >= 0) {
|
||
decoded.push_back(static_cast<char>((high << 4) | low));
|
||
index += 2;
|
||
continue;
|
||
}
|
||
}
|
||
decoded.push_back(character);
|
||
}
|
||
|
||
std::filesystem::path decodedPath(decoded);
|
||
if (decodedPath.empty() || decodedPath.is_absolute()) {
|
||
return std::nullopt;
|
||
}
|
||
for (const auto& part : decodedPath) {
|
||
if (part == "..") {
|
||
return std::nullopt;
|
||
}
|
||
}
|
||
return decodedPath.lexically_normal();
|
||
}
|
||
|
||
void MetaCoreReleaseResourceBuffer(void*, size_t, void* userData) {
|
||
delete static_cast<std::vector<uint8_t>*>(userData);
|
||
}
|
||
|
||
void MetaCoreAddExternalGltfResourceData(
|
||
filament::gltfio::ResourceLoader& resourceLoader,
|
||
const filament::gltfio::FilamentAsset& asset,
|
||
const std::filesystem::path& modelPath
|
||
) {
|
||
const std::filesystem::path resourceRoot = modelPath.parent_path();
|
||
const char* const* resourceUris = asset.getResourceUris();
|
||
const std::size_t resourceUriCount = asset.getResourceUriCount();
|
||
for (std::size_t resourceIndex = 0; resourceIndex < resourceUriCount; ++resourceIndex) {
|
||
const char* resourceUri = resourceUris[resourceIndex];
|
||
if (resourceUri == nullptr || !MetaCoreIsExternalGltfResourceUri(resourceUri) || resourceLoader.hasResourceData(resourceUri)) {
|
||
continue;
|
||
}
|
||
|
||
const auto decodedUri = MetaCoreDecodeGltfResourceUri(resourceUri);
|
||
if (!decodedUri.has_value()) {
|
||
continue;
|
||
}
|
||
|
||
std::ifstream input(resourceRoot / *decodedUri, std::ios::binary);
|
||
if (!input.is_open()) {
|
||
std::cerr << "FilamentSceneBridge: Missing GLTF external resource: "
|
||
<< (resourceRoot / *decodedUri) << " uri=" << resourceUri << std::endl;
|
||
continue;
|
||
}
|
||
|
||
auto* buffer = new std::vector<uint8_t>(
|
||
(std::istreambuf_iterator<char>(input)),
|
||
std::istreambuf_iterator<char>()
|
||
);
|
||
resourceLoader.addResourceData(
|
||
resourceUri,
|
||
filament::gltfio::ResourceLoader::BufferDescriptor(
|
||
buffer->data(),
|
||
buffer->size(),
|
||
MetaCoreReleaseResourceBuffer,
|
||
buffer
|
||
)
|
||
);
|
||
}
|
||
}
|
||
|
||
} // namespace
|
||
|
||
class MetaCoreFilamentSceneBridge::MetaCoreFilamentSceneBridgeImpl {
|
||
public:
|
||
MetaCoreFilamentSceneBridgeImpl() = default;
|
||
|
||
~MetaCoreFilamentSceneBridgeImpl() {
|
||
Shutdown();
|
||
}
|
||
|
||
// 提供公有接口用于构建渲染同步快照
|
||
MetaCoreSceneRenderSyncSnapshot BuildSnapshot(const MetaCoreScene& scene) const {
|
||
return RenderSync_.BuildSnapshot(scene);
|
||
}
|
||
|
||
bool Initialize(MetaCoreWindow& window, bool offscreen = true, MetaCoreRenderBackend backend = MetaCoreRenderBackend::OpenGL) {
|
||
std::cout << "FilamentSceneBridge: Initializing with backend=" << MetaCoreRenderBackendToString(backend) << std::endl;
|
||
|
||
// 创建 Filament Engine (不共享上下文,避免闪退)
|
||
Engine_ = filament::Engine::create(MetaCoreToFilamentBackend(backend));
|
||
if (!Engine_ && backend != MetaCoreRenderBackend::OpenGL) {
|
||
std::cerr << "FilamentSceneBridge: backend " << MetaCoreRenderBackendToString(backend)
|
||
<< " is not available; falling back to OpenGL." << std::endl;
|
||
Engine_ = filament::Engine::create(MetaCoreToFilamentBackend(MetaCoreRenderBackend::OpenGL));
|
||
}
|
||
|
||
if (!Engine_) {
|
||
std::cerr << "Failed to create Filament Engine!" << std::endl;
|
||
return false;
|
||
}
|
||
|
||
// 创建绑定真实窗口的交换链
|
||
SwapChain_ = Engine_->createSwapChain((void*)window.GetNativeWindowHandle());
|
||
if (!SwapChain_) {
|
||
std::cerr << "Failed to create Filament SwapChain with window handle!" << std::endl;
|
||
return false;
|
||
}
|
||
|
||
// 创建渲染器
|
||
Renderer_ = Engine_->createRenderer();
|
||
|
||
// 创建场景
|
||
Scene_ = Engine_->createScene();
|
||
|
||
// 创建默认灯光
|
||
filament::LightManager::ShadowOptions shadowOptions;
|
||
shadowOptions.mapSize = 2048;
|
||
shadowOptions.shadowCascades = 3;
|
||
|
||
// 主光 (Sun)
|
||
Light_ = utils::EntityManager::get().create();
|
||
filament::LightManager::Builder(filament::LightManager::Type::DIRECTIONAL)
|
||
.color(filament::Color::toLinear<filament::ACCURATE>({ 1.0f, 0.95f, 0.9f }))
|
||
.intensity(100000.0f)
|
||
.direction({ 0.5f, 0.5f, -1.0f })
|
||
.castShadows(true)
|
||
.shadowOptions(shadowOptions)
|
||
.build(*Engine_, Light_);
|
||
Scene_->addEntity(Light_);
|
||
EntitiesInScene_.insert(Light_);
|
||
|
||
// 创建相机
|
||
utils::Entity cameraEntity = utils::EntityManager::get().create();
|
||
Camera_ = Engine_->createCamera(cameraEntity);
|
||
|
||
// 创建视图
|
||
View_ = Engine_->createView();
|
||
View_->setScene(Scene_);
|
||
View_->setCamera(Camera_);
|
||
|
||
if (offscreen) {
|
||
// 创建 UI 视图
|
||
UIView_ = Engine_->createView();
|
||
|
||
// 创建 ImGuiHelper
|
||
ImGuiHelper_ = new MetaCoreImGuiHelper(Engine_, UIView_, "", ImGui::GetCurrentContext());
|
||
|
||
// 初始化离屏渲染纹理
|
||
auto [width, height] = window.GetFramebufferSize();
|
||
|
||
// 1. 创建 Filament 颜色纹理
|
||
FilamentTexture_ = filament::Texture::Builder()
|
||
.width(static_cast<uint32_t>(width))
|
||
.height(static_cast<uint32_t>(height))
|
||
.usage(filament::Texture::Usage::COLOR_ATTACHMENT | filament::Texture::Usage::SAMPLEABLE)
|
||
.format(filament::Texture::InternalFormat::RGBA8)
|
||
.build(*Engine_);
|
||
|
||
// 2. 创建深度纹理
|
||
DepthTexture_ = filament::Texture::Builder()
|
||
.width(static_cast<uint32_t>(width))
|
||
.height(static_cast<uint32_t>(height))
|
||
.usage(filament::Texture::Usage::DEPTH_ATTACHMENT)
|
||
.format(filament::Texture::InternalFormat::DEPTH24)
|
||
.build(*Engine_);
|
||
|
||
// 3. 创建 RenderTarget 并绑定
|
||
RenderTarget_ = filament::RenderTarget::Builder()
|
||
.texture(filament::RenderTarget::AttachmentPoint::COLOR, FilamentTexture_)
|
||
.texture(filament::RenderTarget::AttachmentPoint::DEPTH, DepthTexture_)
|
||
.build(*Engine_);
|
||
|
||
// 4. 设置到 View
|
||
View_->setRenderTarget(RenderTarget_);
|
||
View_->setViewport({0, 0, static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
|
||
} else {
|
||
auto [width, height] = window.GetFramebufferSize();
|
||
View_->setViewport({0, 0, static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
|
||
|
||
RuntimeUIView_ = Engine_->createView();
|
||
RuntimeUiHelper_ = new MetaCoreImGuiHelper(Engine_, RuntimeUIView_, "", nullptr);
|
||
RuntimeUiHelper_->setDisplaySize(width, height);
|
||
RuntimeUIView_->setViewport({0, 0, static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
|
||
}
|
||
|
||
// 确保开启后处理(为了HDR)
|
||
View_->setPostProcessingEnabled(true);
|
||
|
||
// 初始化 gltfio
|
||
MaterialProvider_ = new MetaCoreViewportMaterialProvider(Engine_);
|
||
NameManager_ = new utils::NameComponentManager(utils::EntityManager::get());
|
||
AssetLoader_ = filament::gltfio::AssetLoader::create({ Engine_, MaterialProvider_, NameManager_ });
|
||
ResourceLoader_ = new filament::gltfio::ResourceLoader({ Engine_ });
|
||
|
||
// 创建 STB 图像解码器以支持 JPG/PNG 等内部贴图
|
||
StbDecoder_ = filament::gltfio::createStbProvider(Engine_);
|
||
ResourceLoader_->addTextureProvider("image/png", StbDecoder_);
|
||
ResourceLoader_->addTextureProvider("image/jpeg", StbDecoder_);
|
||
|
||
// 构造一个 1x1 的浅灰色 Texture 充当 IBL 反射天空盒
|
||
|
||
|
||
// 生成标准的 256x256 全级 Mipmap IBL 反射贴图
|
||
// Filament 要求 IBL 至少 256 且具有完整的 Mipmap,否则带有 Roughness 的 PBR 材质(尤其是金属)会因为采样不到而死黑!
|
||
// 生成标准的 256x256 全级 Mipmap HDR(RGBA16F) IBL 反射贴图
|
||
// Filament 的 IndirectLight::reflections 严格要求必须是 HDR 格式(如 RGBA16F),如果给 LDR (RGBA8) 则会直接被丢弃!
|
||
// 必须使用 FLOAT 数据类型提交!
|
||
uint32_t texSize = 256;
|
||
IblTexture_ = filament::Texture::Builder()
|
||
.width(texSize).height(texSize).levels(9) // 256 共有 9 级 mipmap
|
||
.usage(filament::Texture::Usage::DEFAULT)
|
||
.format(filament::Texture::InternalFormat::RGBA16F)
|
||
.sampler(filament::Texture::Sampler::SAMPLER_CUBEMAP)
|
||
.build(*Engine_);
|
||
|
||
for (uint8_t level = 0; level < 9; ++level) {
|
||
uint32_t shifted = texSize >> level;
|
||
uint32_t levelSize = shifted > 0 ? shifted : 1;
|
||
size_t facePixels = levelSize * levelSize;
|
||
|
||
float* colors = new float[facePixels * 6 * 4];
|
||
for (size_t i = 0; i < facePixels * 6 * 4; ++i) colors[i] = 0.5f; // HDR 环境光强度 0.5
|
||
|
||
filament::Texture::PixelBufferDescriptor bufferDesc(
|
||
colors, facePixels * 6 * 4 * sizeof(float),
|
||
filament::Texture::Format::RGBA,
|
||
filament::Texture::Type::FLOAT,
|
||
[](void* buffer, size_t size, void* user) { delete[] static_cast<float*>(buffer); }
|
||
);
|
||
|
||
filament::Texture::FaceOffsets faceOffsets;
|
||
for (int i = 0; i < 6; ++i) faceOffsets.offsets[i] = i * facePixels * 4 * sizeof(float);
|
||
|
||
IblTexture_->setImage(*Engine_, level, std::move(bufferDesc), faceOffsets);
|
||
}
|
||
|
||
// 生成默认全局环境漫反射与镜面反射 (IBL)
|
||
filament::math::float3 sh[9] = {};
|
||
sh[0] = { 1.0f, 1.0f, 1.0f }; // 基础环境光亮度
|
||
IndirectLight_ = filament::IndirectLight::Builder()
|
||
.irradiance(3, sh)
|
||
.reflections(IblTexture_) // 恢复物理正确的镜面反射
|
||
.intensity(30000.0f) // 正常物理光强
|
||
.build(*Engine_);
|
||
Scene_->setIndirectLight(IndirectLight_);
|
||
|
||
// 开启高级后处理
|
||
View_->setAntiAliasing(filament::View::AntiAliasing::FXAA);
|
||
View_->setBloomOptions({ .strength = 0.15f, .resolution = 384, .levels = 6, .blendMode = filament::View::BloomOptions::BlendMode::ADD, .threshold = true });
|
||
|
||
std::cout << "FilamentSceneBridge: Initialized with Offscreen=" << (offscreen ? "true" : "false") << std::endl;
|
||
return true;
|
||
}
|
||
|
||
void Shutdown() {
|
||
if (Engine_) {
|
||
std::cout << "FilamentSceneBridge: Shutting down..." << std::endl;
|
||
|
||
// 1. 销毁所有加载的资产,切断其内部 Renderable 对克隆材质实例的占用引用
|
||
std::cout << "[DEBUG] LoadedAssets_ size: " << LoadedAssets_.size() << std::endl;
|
||
for (auto& [id, asset] : LoadedAssets_) {
|
||
std::cout << "[DEBUG] Destroying asset for ID: " << id << ", asset pointer: " << asset << std::endl;
|
||
if (asset) {
|
||
AssetLoader_->destroyAsset(asset);
|
||
}
|
||
std::cout << "[DEBUG] Destroyed asset for ID: " << id << std::endl;
|
||
}
|
||
LoadedAssets_.clear();
|
||
AssetBuffers_.clear();
|
||
FailedModelLoads_.clear();
|
||
FailedModelLoadPaths_.clear();
|
||
|
||
// 2. 销毁所有 pivotEntity 中间辅助坐标变换实体
|
||
for (auto& [objectId, assetEntity] : ObjectToFilamentEntity_) {
|
||
utils::Entity entity = assetEntity.second;
|
||
if (!entity) continue;
|
||
bool isAssetEntity = false;
|
||
if (assetEntity.first) {
|
||
const utils::Entity* entities = assetEntity.first->getEntities();
|
||
size_t entityCount = assetEntity.first->getEntityCount();
|
||
for (size_t i = 0; i < entityCount; ++i) {
|
||
if (entities[i] == entity) {
|
||
isAssetEntity = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (!isAssetEntity) {
|
||
if (Scene_) {
|
||
Scene_->remove(entity);
|
||
EntitiesInScene_.erase(entity);
|
||
}
|
||
Engine_->destroy(entity);
|
||
utils::EntityManager::get().destroy(entity);
|
||
}
|
||
}
|
||
ObjectToFilamentEntity_.clear();
|
||
ObjectWorldMatrices_.clear();
|
||
AssetBuffers_.clear();
|
||
|
||
|
||
|
||
for (auto& [id, entity] : SceneLightEntities_) {
|
||
if (Scene_) {
|
||
Scene_->remove(entity);
|
||
EntitiesInScene_.erase(entity);
|
||
}
|
||
Engine_->destroy(entity);
|
||
utils::EntityManager::get().destroy(entity);
|
||
}
|
||
SceneLightEntities_.clear();
|
||
EntitiesInScene_.clear();
|
||
Engine_->flushAndWait();
|
||
|
||
// 销毁 gltfio 资源
|
||
std::cout << "[DEBUG] Destroying gltfio resources..." << std::endl;
|
||
if (AssetLoader_) {
|
||
filament::gltfio::AssetLoader::destroy(&AssetLoader_);
|
||
AssetLoader_ = nullptr;
|
||
}
|
||
if (ResourceLoader_) {
|
||
delete ResourceLoader_;
|
||
ResourceLoader_ = nullptr;
|
||
}
|
||
if (StbDecoder_) {
|
||
delete StbDecoder_;
|
||
StbDecoder_ = nullptr;
|
||
}
|
||
if (IndirectLight_) {
|
||
Engine_->destroy(IndirectLight_);
|
||
IndirectLight_ = nullptr;
|
||
}
|
||
if (IblTexture_) {
|
||
Engine_->destroy(IblTexture_);
|
||
IblTexture_ = nullptr;
|
||
}
|
||
if (MaterialProvider_) {
|
||
MaterialProvider_->destroyMaterials();
|
||
delete MaterialProvider_;
|
||
MaterialProvider_ = nullptr;
|
||
}
|
||
if (ImGuiHelper_) {
|
||
delete ImGuiHelper_;
|
||
ImGuiHelper_ = nullptr;
|
||
}
|
||
if (RuntimeUiHelper_) {
|
||
delete RuntimeUiHelper_;
|
||
RuntimeUiHelper_ = nullptr;
|
||
}
|
||
Engine_->flushAndWait();
|
||
|
||
// 销毁离屏渲染资源
|
||
if (RenderTarget_) {
|
||
Engine_->destroy(RenderTarget_);
|
||
RenderTarget_ = nullptr;
|
||
}
|
||
if (FilamentTexture_) {
|
||
Engine_->destroy(FilamentTexture_);
|
||
FilamentTexture_ = nullptr;
|
||
}
|
||
if (DepthTexture_) {
|
||
Engine_->destroy(DepthTexture_);
|
||
DepthTexture_ = nullptr;
|
||
}
|
||
if (GLTextureId_) {
|
||
glDeleteTextures(1, &GLTextureId_);
|
||
GLTextureId_ = 0;
|
||
}
|
||
// 销毁 View
|
||
Engine_->destroy(View_);
|
||
if (RuntimeUIView_) {
|
||
Engine_->destroy(RuntimeUIView_);
|
||
RuntimeUIView_ = nullptr;
|
||
}
|
||
if (UIView_) {
|
||
Engine_->destroy(UIView_);
|
||
UIView_ = nullptr;
|
||
}
|
||
|
||
// 销毁相机组件和实体
|
||
if (Camera_) {
|
||
utils::Entity cameraEntity = Camera_->getEntity();
|
||
Engine_->destroyCameraComponent(cameraEntity);
|
||
utils::EntityManager::get().destroy(cameraEntity);
|
||
}
|
||
|
||
// 销毁其他资源
|
||
Engine_->destroy(Scene_);
|
||
Engine_->destroy(Renderer_);
|
||
Engine_->destroy(SwapChain_);
|
||
Engine_->flushAndWait();
|
||
|
||
delete NameManager_;
|
||
NameManager_ = nullptr;
|
||
|
||
// 最后销毁 Engine
|
||
filament::Engine::destroy(&Engine_);
|
||
Engine_ = nullptr;
|
||
}
|
||
}
|
||
|
||
void SetProjectRootPath(const std::filesystem::path& projectRootPath) {
|
||
ProjectRootPath_ = projectRootPath;
|
||
}
|
||
|
||
void ParseModelHierarchyToEcs(MetaCoreScene& scene, MetaCoreGameObject& parentObject, filament::gltfio::FilamentAsset* asset) {
|
||
if (!asset) return;
|
||
|
||
auto& tm = Engine_->getTransformManager();
|
||
|
||
// 绑定顶级父 GameObject 标识到 Filament Asset 根 Entity,彻底打通顶级 Gizmo 的同步!
|
||
ObjectToFilamentEntity_[parentObject.GetId()] = { asset, asset->getRoot() };
|
||
|
||
// 1. 自底向上建立完整的 Filament 父子关系映射,解决隐藏过渡节点导致的层级断裂问题
|
||
std::unordered_map<utils::Entity, std::vector<utils::Entity>, utils::Entity::Hasher> parentToChildren =
|
||
BuildFullParentToChildrenMap(asset);
|
||
|
||
// 2. 建立 ECS 的父子关系映射
|
||
std::unordered_map<MetaCoreId, std::vector<MetaCoreGameObject>> ecsParentToChildren;
|
||
std::vector<MetaCoreId> subtreeIds = scene.GetSubtreeObjectIds(parentObject.GetId());
|
||
for (MetaCoreId subId : subtreeIds) {
|
||
auto subObj = scene.FindGameObject(subId);
|
||
if (subObj) {
|
||
ecsParentToChildren[subObj.GetParentId()].push_back(subObj);
|
||
}
|
||
}
|
||
|
||
// 3. 递归 DFS 精确结构匹配并初始化 Transform 属性,穿透所有过渡节点
|
||
std::function<void(MetaCoreGameObject, utils::Entity)> matchRecursive =
|
||
[&](MetaCoreGameObject currentObj, utils::Entity currentEntity) {
|
||
ObjectToFilamentEntity_[currentObj.GetId()] = { asset, currentEntity };
|
||
|
||
if (currentEntity != asset->getRoot()) {
|
||
auto instance = tm.getInstance(currentEntity);
|
||
if (instance) {
|
||
filament::math::mat4f localTransform = tm.getTransform(instance);
|
||
|
||
glm::mat4 filamentMatrix;
|
||
std::memcpy(glm::value_ptr(filamentMatrix), &localTransform[0][0], sizeof(float) * 16);
|
||
|
||
glm::mat4 R_minus90X = glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), {1, 0, 0});
|
||
glm::mat4 R_plus90X = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), {1, 0, 0});
|
||
glm::mat4 matrix = R_minus90X * filamentMatrix * R_plus90X;
|
||
|
||
// 拆解矩阵
|
||
glm::vec3 scale;
|
||
glm::quat rotation;
|
||
glm::vec3 translation;
|
||
glm::vec3 skew;
|
||
glm::vec4 perspective;
|
||
glm::decompose(matrix, scale, rotation, translation, skew, perspective);
|
||
|
||
auto& transform = currentObj.GetComponent<MetaCoreTransformComponent>();
|
||
transform.Position = translation;
|
||
transform.RotationEulerDegrees = glm::degrees(glm::eulerAngles(rotation));
|
||
transform.Scale = scale;
|
||
}
|
||
}
|
||
|
||
// 穿透过渡节点获取当前实体的有效 Filament 子实体列表
|
||
std::vector<utils::Entity> fChildren;
|
||
GetEffectiveFilamentChildren(currentEntity, asset, parentToChildren, fChildren);
|
||
|
||
auto& eChildren = ecsParentToChildren[currentObj.GetId()];
|
||
|
||
for (utils::Entity fChild : fChildren) {
|
||
const char* nodeName = asset->getName(fChild);
|
||
std::string name = nodeName ? nodeName : "";
|
||
|
||
MetaCoreGameObject matchedEObj;
|
||
for (auto it = eChildren.begin(); it != eChildren.end(); ++it) {
|
||
if (it->GetName() == name) {
|
||
matchedEObj = *it;
|
||
eChildren.erase(it);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 模糊/降级匹配
|
||
if (!matchedEObj && name.empty() && !eChildren.empty()) {
|
||
matchedEObj = eChildren.front();
|
||
eChildren.erase(eChildren.begin());
|
||
}
|
||
|
||
if (matchedEObj) {
|
||
matchRecursive(matchedEObj, fChild);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 从根开始执行递归层级树匹配
|
||
matchRecursive(parentObject, asset->getRoot());
|
||
}
|
||
|
||
static std::string NormalizePath(std::string path) {
|
||
std::replace(path.begin(), path.end(), '\\', '/');
|
||
return path;
|
||
}
|
||
|
||
void SyncScene(const MetaCoreSceneRenderSyncSnapshot& snapshot, MetaCoreScene* scene, bool compatibilityMeshOnly, bool useScenePrimaryCamera) {
|
||
if (!AssetLoader_) return;
|
||
(void)compatibilityMeshOnly;
|
||
|
||
LastSyncSnapshot_ = snapshot;
|
||
ObjectWorldMatrices_ = LastSyncSnapshot_.WorldMatrices;
|
||
|
||
// 构建物体ID到网格快照的快速映射,方便后续查询可见性及材质属性
|
||
std::unordered_map<MetaCoreId, const MetaCoreRenderSyncRenderable*> renderableMap;
|
||
for (const auto& r : snapshot.Renderables) {
|
||
renderableMap[r.ObjectId] = &r;
|
||
}
|
||
|
||
// 定义用于检测场景中某个对象及其所有祖先节点当前在快照里是否应为可见状态的辅助方法
|
||
std::unordered_map<MetaCoreId, bool> visibilityCache;
|
||
std::function<bool(MetaCoreId)> isObjectVisible = [&](MetaCoreId objectId) -> bool {
|
||
if (objectId == 0) {
|
||
return true;
|
||
}
|
||
auto cacheIt = visibilityCache.find(objectId);
|
||
if (cacheIt != visibilityCache.end()) {
|
||
return cacheIt->second;
|
||
}
|
||
|
||
if (!snapshot.WorldMatrices.contains(objectId)) {
|
||
visibilityCache[objectId] = false;
|
||
return false;
|
||
}
|
||
|
||
bool selfVisible = true;
|
||
MetaCoreId parentId = 0;
|
||
auto rIt = renderableMap.find(objectId);
|
||
if (rIt != renderableMap.end()) {
|
||
selfVisible = rIt->second->Visible;
|
||
parentId = rIt->second->ParentId;
|
||
} else if (scene != nullptr) {
|
||
auto obj = scene->FindGameObject(objectId);
|
||
if (obj) {
|
||
parentId = obj.GetParentId();
|
||
}
|
||
}
|
||
|
||
if (!selfVisible) {
|
||
visibilityCache[objectId] = false;
|
||
return false;
|
||
}
|
||
|
||
bool parentVisible = isObjectVisible(parentId);
|
||
visibilityCache[objectId] = parentVisible;
|
||
return parentVisible;
|
||
};
|
||
SyncSceneLights(LastSyncSnapshot_);
|
||
if (useScenePrimaryCamera) {
|
||
MetaCoreSceneView primarySceneView;
|
||
if (MetaCoreSceneRenderSync::TryBuildSceneViewFromPrimaryCamera(LastSyncSnapshot_, primarySceneView)) {
|
||
ApplySceneView(primarySceneView);
|
||
}
|
||
}
|
||
|
||
// 1. 收集当前 snapshot 中所有存在的模型 Root ID
|
||
std::unordered_set<MetaCoreId> activeHostRootIds;
|
||
for (const auto& renderable : snapshot.Renderables) {
|
||
if (IsEngineLoadedModelRenderable(renderable)) {
|
||
if (renderable.HostRootId != 0) {
|
||
activeHostRootIds.insert(renderable.HostRootId);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2. 检查所有已加载的资产,若其 Root ID 不在 activeHostRootIds 中,则说明已被删除,执行清理
|
||
for (auto it = LoadedAssets_.begin(); it != LoadedAssets_.end(); ) {
|
||
MetaCoreId hostRootId = it->first;
|
||
filament::gltfio::FilamentAsset* asset = it->second;
|
||
|
||
if (!activeHostRootIds.contains(hostRootId)) {
|
||
std::cout << "FilamentSceneBridge: Unloading deleted asset: " << hostRootId << std::endl;
|
||
|
||
// 从场景中移除该资产关联的所有实体
|
||
if (Scene_ && asset) {
|
||
const utils::Entity* entities = asset->getEntities();
|
||
size_t entityCount = asset->getEntityCount();
|
||
for (size_t i = 0; i < entityCount; ++i) {
|
||
Scene_->remove(entities[i]);
|
||
EntitiesInScene_.erase(entities[i]);
|
||
}
|
||
}
|
||
|
||
// 查找并清理对应的 pivotEntity
|
||
auto pivotIt = ObjectToFilamentEntity_.find(hostRootId);
|
||
if (pivotIt != ObjectToFilamentEntity_.end()) {
|
||
utils::Entity pivotEntity = pivotIt->second.second;
|
||
bool isPivotNode = (asset == nullptr || pivotEntity != asset->getRoot());
|
||
if (Scene_ && pivotEntity) {
|
||
Scene_->remove(pivotEntity);
|
||
EntitiesInScene_.erase(pivotEntity);
|
||
}
|
||
if (isPivotNode && pivotEntity) {
|
||
utils::EntityManager::get().destroy(pivotEntity);
|
||
}
|
||
}
|
||
|
||
// 销毁资产本身
|
||
if (asset) {
|
||
AssetLoader_->destroyAsset(asset);
|
||
}
|
||
|
||
// 从 ObjectToFilamentEntity_ 和 ObjectWorldMatrices_ 中清除所有指向该 asset 的映射
|
||
if (asset != nullptr) {
|
||
for (auto entIt = ObjectToFilamentEntity_.begin(); entIt != ObjectToFilamentEntity_.end(); ) {
|
||
if (entIt->second.first == asset) {
|
||
ObjectWorldMatrices_.erase(entIt->first);
|
||
entIt = ObjectToFilamentEntity_.erase(entIt);
|
||
} else {
|
||
++entIt;
|
||
}
|
||
}
|
||
}
|
||
// 确保顶级父 ID 的变换缓存也被清除
|
||
ObjectWorldMatrices_.erase(hostRootId);
|
||
|
||
|
||
|
||
// 从 LoadedAssets_ 中移除
|
||
AssetBuffers_.erase(hostRootId);
|
||
FailedModelLoads_.erase(hostRootId);
|
||
it = LoadedAssets_.erase(it);
|
||
} else {
|
||
++it;
|
||
}
|
||
}
|
||
|
||
for (const auto& renderable : snapshot.Renderables) {
|
||
if (!IsEngineLoadedModelRenderable(renderable)) {
|
||
continue;
|
||
}
|
||
|
||
std::string modelPath;
|
||
if (renderable.SourceModelAssetGuid.IsValid()) {
|
||
modelPath = MetaCoreAssetRegistry::Get().ResolveGuidToPath(renderable.SourceModelAssetGuid).generic_string();
|
||
}
|
||
if (modelPath.empty() && !renderable.SourceModelPath.empty()) {
|
||
modelPath = renderable.SourceModelPath;
|
||
}
|
||
if (modelPath.empty() && renderable.MeshSource == MetaCoreMeshSourceKind::Builtin) {
|
||
if (renderable.BuiltinMesh == MetaCoreBuiltinMeshType::Cube) {
|
||
modelPath = "Assets/Models/Cube.glb";
|
||
} else if (renderable.BuiltinMesh == MetaCoreBuiltinMeshType::Plane) {
|
||
modelPath = "Assets/Models/Plane.glb";
|
||
}
|
||
}
|
||
if (modelPath.empty()) {
|
||
continue;
|
||
}
|
||
|
||
std::filesystem::path absolutePath = ProjectRootPath_ / modelPath;
|
||
|
||
MetaCoreId hostRootId = renderable.HostRootId;
|
||
std::string hostRootName = renderable.HostRootName;
|
||
const std::string failureKey = MetaCoreBuildModelLoadFailureKey(absolutePath);
|
||
const bool sourceExists = std::filesystem::exists(absolutePath);
|
||
if (sourceExists) {
|
||
FailedModelLoadPaths_.erase(failureKey);
|
||
}
|
||
|
||
// 如果该模型的顶级根宿主已经被加载过,则不需要重复加载,直接更新变换
|
||
if (LoadedAssets_.contains(hostRootId)) {
|
||
continue;
|
||
}
|
||
if (FailedModelLoads_.contains(hostRootId) || FailedModelLoadPaths_.contains(failureKey)) {
|
||
continue;
|
||
}
|
||
if (!sourceExists) {
|
||
std::cerr << "FilamentSceneBridge: Missing GLTF source: " << absolutePath << " (Host: " << hostRootName << ")" << std::endl;
|
||
FailedModelLoads_.insert(hostRootId);
|
||
FailedModelLoadPaths_.insert(failureKey);
|
||
continue;
|
||
}
|
||
|
||
// 加载新模型,以顶级根宿主 hostRootId 进行注册存储
|
||
std::cout << "FilamentSceneBridge: Loading GLTF: " << absolutePath << " (Host: " << hostRootName << ")" << std::endl;
|
||
|
||
std::ifstream file(absolutePath, std::ios::binary);
|
||
if (!file.is_open()) {
|
||
std::cerr << "Failed to open file: " << absolutePath << std::endl;
|
||
FailedModelLoads_.insert(hostRootId);
|
||
FailedModelLoadPaths_.insert(failureKey);
|
||
continue;
|
||
}
|
||
|
||
std::vector<char> buffer((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
|
||
|
||
filament::gltfio::FilamentAsset* asset = AssetLoader_->createAsset(
|
||
reinterpret_cast<const uint8_t*>(buffer.data()),
|
||
static_cast<uint32_t>(buffer.size())
|
||
);
|
||
|
||
if (!asset) {
|
||
std::cerr << "Failed to create asset!" << std::endl;
|
||
FailedModelLoads_.insert(hostRootId);
|
||
FailedModelLoadPaths_.insert(failureKey);
|
||
continue;
|
||
}
|
||
|
||
// 加载贴图等资源
|
||
if (ResourceLoader_) {
|
||
MetaCoreAddExternalGltfResourceData(*ResourceLoader_, *asset, absolutePath);
|
||
ResourceLoader_->loadResources(asset);
|
||
ResourceLoader_->asyncUpdateLoad(); // 触发底层的纹理同步
|
||
ResourceLoader_->evictResourceData();
|
||
}
|
||
|
||
// 添加到场景
|
||
Scene_->addEntities(asset->getEntities(), asset->getEntityCount());
|
||
{
|
||
const utils::Entity* entities = asset->getEntities();
|
||
size_t entityCount = asset->getEntityCount();
|
||
for (size_t i = 0; i < entityCount; ++i) {
|
||
EntitiesInScene_.insert(entities[i]);
|
||
}
|
||
}
|
||
|
||
LoadedAssets_[hostRootId] = asset;
|
||
AssetBuffers_[hostRootId] = std::move(buffer);
|
||
|
||
|
||
|
||
if (scene != nullptr) {
|
||
// 有 ECS 实例模式下同步层级结构并补挂 Tag
|
||
auto hostRootObj = scene->FindGameObject(hostRootId);
|
||
if (hostRootObj) {
|
||
MetaCoreGameObject nonConstHostRoot = hostRootObj;
|
||
|
||
// 现场补挂 Tag,确保后续帧和拖拽时 100% 命中 Tag 快速通道,绝不走 Fallback
|
||
if (!nonConstHostRoot.HasComponent<MetaCoreModelRootTag>()) {
|
||
nonConstHostRoot.AddComponent<MetaCoreModelRootTag>(MetaCoreModelRootTag{ modelPath });
|
||
}
|
||
|
||
// 如果名字是默认的 "Cube",自动改为模型文件名
|
||
if (nonConstHostRoot.GetName() == "Cube") {
|
||
std::string filename = std::filesystem::path(modelPath).filename().string();
|
||
nonConstHostRoot.GetName() = filename;
|
||
}
|
||
|
||
ParseModelHierarchyToEcs(*scene, nonConstHostRoot, asset);
|
||
}
|
||
} else {
|
||
// 如果 scene 为 nullptr,代表纯快照渲染模式。我们需要直接在 ObjectToFilamentEntity_ 中建立子实体的映射。
|
||
// 顶级父 ID 对应 asset->getRoot()
|
||
ObjectToFilamentEntity_[hostRootId] = { asset, asset->getRoot() };
|
||
|
||
// 1. 自底向上建立完整的 Filament 父子关系映射,解决隐藏过渡节点导致的层级断裂问题
|
||
std::unordered_map<utils::Entity, std::vector<utils::Entity>, utils::Entity::Hasher> parentToChildren =
|
||
BuildFullParentToChildrenMap(asset);
|
||
|
||
// 2. 建立快照中的父子关系映射
|
||
std::unordered_map<MetaCoreId, std::vector<MetaCoreRenderSyncRenderable>> snapParentToChildren;
|
||
for (const auto& r : snapshot.Renderables) {
|
||
if (r.HostRootId == hostRootId && r.ObjectId != hostRootId) {
|
||
snapParentToChildren[r.ParentId].push_back(r);
|
||
}
|
||
}
|
||
|
||
// 3. 递归 DFS 进行精确层级树结构匹配,穿透所有过渡节点
|
||
std::function<void(MetaCoreId, utils::Entity)> matchRecursive =
|
||
[&](MetaCoreId currentId, utils::Entity currentEntity) {
|
||
ObjectToFilamentEntity_[currentId] = { asset, currentEntity };
|
||
|
||
// 穿透过渡节点获取当前实体的有效 Filament 子实体列表
|
||
std::vector<utils::Entity> fChildren;
|
||
GetEffectiveFilamentChildren(currentEntity, asset, parentToChildren, fChildren);
|
||
|
||
auto& sChildren = snapParentToChildren[currentId];
|
||
|
||
for (utils::Entity fChild : fChildren) {
|
||
const char* nodeName = asset->getName(fChild);
|
||
std::string name = nodeName ? nodeName : "";
|
||
|
||
MetaCoreId matchedChildId = 0;
|
||
for (auto it = sChildren.begin(); it != sChildren.end(); ++it) {
|
||
if (it->Name == name) {
|
||
matchedChildId = it->ObjectId;
|
||
sChildren.erase(it);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 兜底匹配
|
||
if (matchedChildId == 0 && name.empty() && !sChildren.empty()) {
|
||
matchedChildId = sChildren.front().ObjectId;
|
||
sChildren.erase(sChildren.begin());
|
||
}
|
||
|
||
if (matchedChildId != 0) {
|
||
matchRecursive(matchedChildId, fChild);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 从根开始执行递归层级树匹配
|
||
matchRecursive(hostRootId, asset->getRoot());
|
||
}
|
||
|
||
// 创建中间 Pivot 实体用于坐标系转换 (glTF Y-up -> MetaCore Z-up)
|
||
utils::Entity pivotEntity = utils::EntityManager::get().create();
|
||
auto& tm = Engine_->getTransformManager();
|
||
tm.create(pivotEntity);
|
||
|
||
// 将资产根节点挂载到 Pivot 下
|
||
utils::Entity assetRoot = asset->getRoot();
|
||
tm.setParent(tm.getInstance(assetRoot), tm.getInstance(pivotEntity));
|
||
|
||
// 获取原始局部变换并叠加旋转补偿
|
||
filament::math::mat4f originalLocalTransform = tm.getTransform(tm.getInstance(assetRoot));
|
||
glm::mat4 originalMatrix;
|
||
std::memcpy(glm::value_ptr(originalMatrix), &originalLocalTransform[0][0], sizeof(float) * 16);
|
||
|
||
glm::mat4 compensationMatrix = glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), {1, 0, 0});
|
||
glm::mat4 finalRootMatrix = compensationMatrix * originalMatrix;
|
||
|
||
tm.setTransform(tm.getInstance(assetRoot), *reinterpret_cast<const filament::math::mat4f*>(glm::value_ptr(finalRootMatrix)));
|
||
|
||
// 【关键一步】:注册 hostRootId(顶级根宿主)的 ID 与 pivotEntity,彻底打通顶级宿主的 Gizmo 坐标变换!
|
||
ObjectToFilamentEntity_[hostRootId] = { asset, pivotEntity };
|
||
|
||
Scene_->addEntity(pivotEntity);
|
||
EntitiesInScene_.insert(pivotEntity);
|
||
|
||
// 更新初始位置
|
||
glm::mat4 initLocalMat{1.0f};
|
||
auto itMat = snapshot.LocalMatrices.find(renderable.ObjectId);
|
||
if (itMat != snapshot.LocalMatrices.end()) {
|
||
initLocalMat = itMat->second;
|
||
}
|
||
glm::mat4 initWorldMat = initLocalMat;
|
||
auto itWorldMat = snapshot.WorldMatrices.find(renderable.ObjectId);
|
||
if (itWorldMat != snapshot.WorldMatrices.end()) {
|
||
initWorldMat = itWorldMat->second;
|
||
}
|
||
UpdateTransform(renderable.ObjectId, initLocalMat, initWorldMat);
|
||
}
|
||
|
||
// 1. 构建反向查找映射,方便反查 entity 对应的 objectId
|
||
std::unordered_map<utils::Entity, MetaCoreId, utils::Entity::Hasher> entityToObjectId;
|
||
for (const auto& [objectId, assetEntity] : ObjectToFilamentEntity_) {
|
||
entityToObjectId[assetEntity.second] = objectId;
|
||
}
|
||
|
||
// 2. 控制所有已加载 glTF 资产内部所有实体(包括无直接映射的内部渲染实体)的可见性
|
||
auto& tm = Engine_->getTransformManager();
|
||
for (const auto& [hostRootId, asset] : LoadedAssets_) {
|
||
if (!asset) continue;
|
||
const utils::Entity* entities = asset->getEntities();
|
||
size_t entityCount = asset->getEntityCount();
|
||
for (size_t i = 0; i < entityCount; ++i) {
|
||
utils::Entity entity = entities[i];
|
||
if (!entity) continue;
|
||
|
||
bool shouldBeInScene = false;
|
||
auto it = entityToObjectId.find(entity);
|
||
if (it != entityToObjectId.end()) {
|
||
// 若是直接映射节点,其可见性由快照决定
|
||
shouldBeInScene = isObjectVisible(it->second);
|
||
} else {
|
||
// 若是没有直接映射关系的内部渲染/辅助实体,上溯其在 Filament 层级树中的父节点链,
|
||
// 找到最近的具有映射关系的祖先节点,并继承其可见性状态。
|
||
utils::Entity current = entity;
|
||
utils::Entity mappedAncestor;
|
||
while (current) {
|
||
auto instance = tm.getInstance(current);
|
||
if (!instance) break;
|
||
utils::Entity parent = tm.getParent(instance);
|
||
if (!parent) break;
|
||
if (entityToObjectId.contains(parent)) {
|
||
mappedAncestor = parent;
|
||
break;
|
||
}
|
||
current = parent;
|
||
}
|
||
|
||
if (mappedAncestor) {
|
||
shouldBeInScene = isObjectVisible(entityToObjectId[mappedAncestor]);
|
||
} else {
|
||
// 回退保护:如果没有找到,则与该资产对应的顶级根宿主保持状态一致
|
||
shouldBeInScene = isObjectVisible(hostRootId);
|
||
}
|
||
}
|
||
|
||
// 同步可见性状态到 Filament Scene
|
||
if (shouldBeInScene) {
|
||
if (Scene_ && !EntitiesInScene_.contains(entity)) {
|
||
Scene_->addEntity(entity);
|
||
EntitiesInScene_.insert(entity);
|
||
}
|
||
} else {
|
||
if (Scene_ && EntitiesInScene_.contains(entity)) {
|
||
Scene_->remove(entity);
|
||
EntitiesInScene_.erase(entity);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3. 控制 ObjectToFilamentEntity_ 中所有非资产实体(如 pivotEntity 辅助实体)的可见性
|
||
for (const auto& [objectId, assetEntity] : ObjectToFilamentEntity_) {
|
||
utils::Entity entity = assetEntity.second;
|
||
if (!entity) continue;
|
||
|
||
// 检查该实体是否为资产内部实体,如果不是(例如 pivotEntity),则直接控制可见性
|
||
bool isAssetEntity = false;
|
||
if (assetEntity.first) {
|
||
const utils::Entity* entities = assetEntity.first->getEntities();
|
||
size_t entityCount = assetEntity.first->getEntityCount();
|
||
for (size_t i = 0; i < entityCount; ++i) {
|
||
if (entities[i] == entity) {
|
||
isAssetEntity = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!isAssetEntity) {
|
||
bool shouldBeInScene = isObjectVisible(objectId);
|
||
if (shouldBeInScene) {
|
||
if (Scene_ && !EntitiesInScene_.contains(entity)) {
|
||
Scene_->addEntity(entity);
|
||
EntitiesInScene_.insert(entity);
|
||
}
|
||
} else {
|
||
if (Scene_ && EntitiesInScene_.contains(entity)) {
|
||
Scene_->remove(entity);
|
||
EntitiesInScene_.erase(entity);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 4. 同步所有存活实体的本地和全局变换矩阵
|
||
for (const auto& [objectId, assetEntity] : ObjectToFilamentEntity_) {
|
||
auto itWorld = snapshot.WorldMatrices.find(objectId);
|
||
if (itWorld != snapshot.WorldMatrices.end()) {
|
||
auto itLocal = snapshot.LocalMatrices.find(objectId);
|
||
if (itLocal != snapshot.LocalMatrices.end()) {
|
||
UpdateTransform(objectId, itLocal->second, itWorld->second);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 5. 同步所有存活网格的材质属性。
|
||
auto& rm = Engine_->getRenderableManager();
|
||
const auto applyMaterialParameters = [&](auto instance, const MetaCoreRenderSyncRenderable& renderable) -> bool {
|
||
if (!instance) {
|
||
return false;
|
||
}
|
||
|
||
size_t primCount = rm.getPrimitiveCount(instance);
|
||
for (size_t primIndex = 0; primIndex < primCount; ++primIndex) {
|
||
filament::MaterialInstance* matInst = rm.getMaterialInstanceAt(instance, primIndex);
|
||
if (matInst) {
|
||
const filament::Material* material = matInst->getMaterial();
|
||
filament::math::float4 baseColorVec{
|
||
renderable.BaseColor.x,
|
||
renderable.BaseColor.y,
|
||
renderable.BaseColor.z,
|
||
1.0f
|
||
};
|
||
if (material != nullptr && material->hasParameter("baseColorFactor")) {
|
||
matInst->setParameter("baseColorFactor", baseColorVec);
|
||
}
|
||
if (material != nullptr && material->hasParameter("metallicFactor")) {
|
||
matInst->setParameter("metallicFactor", renderable.Metallic);
|
||
}
|
||
if (material != nullptr && material->hasParameter("roughnessFactor")) {
|
||
matInst->setParameter("roughnessFactor", renderable.Roughness);
|
||
}
|
||
if (material != nullptr && material->hasParameter("emissiveFactor")) {
|
||
matInst->setParameter(
|
||
"emissiveFactor",
|
||
filament::math::float3{
|
||
renderable.EmissiveColor.x,
|
||
renderable.EmissiveColor.y,
|
||
renderable.EmissiveColor.z
|
||
}
|
||
);
|
||
}
|
||
matInst->setDoubleSided(renderable.DoubleSided);
|
||
}
|
||
}
|
||
return true;
|
||
};
|
||
|
||
for (const auto& renderable : snapshot.Renderables) {
|
||
bool applied = false;
|
||
auto itEntity = ObjectToFilamentEntity_.find(renderable.ObjectId);
|
||
if (itEntity != ObjectToFilamentEntity_.end()) {
|
||
utils::Entity entity = itEntity->second.second;
|
||
if (entity) {
|
||
applied = applyMaterialParameters(rm.getInstance(entity), renderable);
|
||
}
|
||
}
|
||
|
||
if (!applied) {
|
||
const auto loadedAsset = LoadedAssets_.find(renderable.HostRootId);
|
||
if (loadedAsset == LoadedAssets_.end() || loadedAsset->second == nullptr) {
|
||
continue;
|
||
}
|
||
|
||
const utils::Entity* entities = loadedAsset->second->getEntities();
|
||
const size_t entityCount = loadedAsset->second->getEntityCount();
|
||
for (size_t entityIndex = 0; entityIndex < entityCount; ++entityIndex) {
|
||
if (entities[entityIndex] && applyMaterialParameters(rm.getInstance(entities[entityIndex]), renderable)) {
|
||
applied = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void ApplySceneView(const MetaCoreSceneView& sceneView) {
|
||
if (!Camera_ || !View_) return;
|
||
|
||
// 1. 视图矩阵同步
|
||
const glm::mat4 viewMatrix = glm::lookAt(sceneView.CameraPosition, sceneView.CameraTarget, sceneView.CameraUp);
|
||
const glm::mat4 cameraModelMatrix = glm::inverse(viewMatrix);
|
||
Camera_->setModelMatrix(*reinterpret_cast<const filament::math::mat4f*>(glm::value_ptr(cameraModelMatrix)));
|
||
|
||
// 2. 投影矩阵:回归 Filament 原生设置
|
||
const auto& viewport = View_->getViewport();
|
||
float aspect = (viewport.height > 0) ? (static_cast<float>(viewport.width) / static_cast<float>(viewport.height)) : 1.0f;
|
||
|
||
Camera_->setProjection(
|
||
sceneView.VerticalFieldOfViewDegrees,
|
||
aspect,
|
||
sceneView.NearClip,
|
||
sceneView.FarClip,
|
||
filament::Camera::Fov::VERTICAL
|
||
);
|
||
}
|
||
|
||
void SetRuntimeUiOverlayFrame(const MetaCoreRuntimeUiFrame& frame, int width, int height) {
|
||
RuntimeUiFrame_ = frame;
|
||
RuntimeUiWidth_ = width;
|
||
RuntimeUiHeight_ = height;
|
||
}
|
||
|
||
void RenderAll() {
|
||
if (!Renderer_ || !SwapChain_ || !View_) {
|
||
return;
|
||
}
|
||
|
||
// 持续推进后台的纹理解码任务!
|
||
if (ResourceLoader_) {
|
||
ResourceLoader_->asyncUpdateLoad();
|
||
}
|
||
|
||
bool renderRuntimeUi = false;
|
||
if (!RenderTarget_ && RuntimeUIView_ && RuntimeUiHelper_) {
|
||
const auto viewport = View_->getViewport();
|
||
const int overlayWidth = RuntimeUiWidth_ > 0 ? RuntimeUiWidth_ : static_cast<int>(viewport.width);
|
||
const int overlayHeight = RuntimeUiHeight_ > 0 ? RuntimeUiHeight_ : static_cast<int>(viewport.height);
|
||
if (overlayWidth > 0 && overlayHeight > 0) {
|
||
RuntimeUiHelper_->setDisplaySize(overlayWidth, overlayHeight);
|
||
RuntimeUIView_->setViewport({
|
||
0,
|
||
0,
|
||
static_cast<uint32_t>(overlayWidth),
|
||
static_cast<uint32_t>(overlayHeight)
|
||
});
|
||
|
||
const bool hasRuntimeUi = !RuntimeUiFrame_.Commands.empty();
|
||
const bool preparedRuntimeUi = RuntimeUiHelper_->processRuntimeUiFrame(
|
||
RuntimeUiFrame_,
|
||
overlayWidth,
|
||
overlayHeight
|
||
);
|
||
renderRuntimeUi = preparedRuntimeUi && hasRuntimeUi;
|
||
}
|
||
}
|
||
|
||
if (Renderer_->beginFrame(SwapChain_)) {
|
||
if (RenderTarget_) {
|
||
// 1. 渲染 3D 离屏视口 (输出到 RenderTarget)
|
||
filament::Renderer::ClearOptions options;
|
||
options.clearColor = { 0.11f, 0.12f, 0.14f, 1.0f }; // 深灰色
|
||
options.clear = true;
|
||
Renderer_->setClearOptions(options);
|
||
|
||
Renderer_->render(View_);
|
||
|
||
if (UIView_ && ImGuiHelper_) {
|
||
// 2. 准备并渲染 UI 视口 (输出到 SwapChain)
|
||
ImGuiIO& io = ImGui::GetIO();
|
||
ImGuiHelper_->setDisplaySize(io.DisplaySize.x, io.DisplaySize.y);
|
||
UIView_->setViewport({0, 0, static_cast<uint32_t>(io.DisplaySize.x), static_cast<uint32_t>(io.DisplaySize.y)});
|
||
|
||
options.clearColor = { 0.11f, 0.12f, 0.14f, 1.0f }; // 灰色
|
||
options.clear = true;
|
||
Renderer_->setClearOptions(options);
|
||
|
||
ImGuiHelper_->processImGuiCommands(ImGui::GetDrawData(), io);
|
||
|
||
Renderer_->render(UIView_);
|
||
}
|
||
} else {
|
||
// 3. 直接上屏渲染
|
||
filament::Renderer::ClearOptions options;
|
||
options.clearColor = { 0.11f, 0.12f, 0.14f, 1.0f }; // 深灰色
|
||
options.clear = true;
|
||
Renderer_->setClearOptions(options);
|
||
|
||
Renderer_->render(View_);
|
||
|
||
if (renderRuntimeUi) {
|
||
options.clearColor = {0.0f, 0.0f, 0.0f, 0.0f};
|
||
options.clear = false;
|
||
Renderer_->setClearOptions(options);
|
||
Renderer_->render(RuntimeUIView_);
|
||
}
|
||
}
|
||
|
||
Renderer_->endFrame();
|
||
|
||
// 在帧结束、指令提交后,安全地销毁上一帧遗留的旧纹理
|
||
for (auto* tex : OldTextures_) {
|
||
Engine_->destroy(tex);
|
||
}
|
||
OldTextures_.clear();
|
||
}
|
||
}
|
||
|
||
uint32_t GetGLTextureId() const {
|
||
return GLTextureId_;
|
||
}
|
||
|
||
void Resize(int width, int height) {
|
||
if (width <= 0 || height <= 0) return;
|
||
|
||
if (!RenderTarget_) {
|
||
View_->setViewport({0, 0, static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
|
||
return;
|
||
}
|
||
|
||
// 【关键修复】:如果大小没有改变,不要重新创建纹理!
|
||
// 否则会导致上一帧传给 ImGui 的纹理指针在这一帧被提前销毁,引发 Use-after-free 崩溃。
|
||
if (FilamentTexture_ &&
|
||
FilamentTexture_->getWidth() == static_cast<uint32_t>(width) &&
|
||
FilamentTexture_->getHeight() == static_cast<uint32_t>(height)) {
|
||
return;
|
||
}
|
||
|
||
if (RenderTarget_) Engine_->destroy(RenderTarget_);
|
||
if (DepthTexture_) Engine_->destroy(DepthTexture_);
|
||
|
||
// 【关键修复】:推迟销毁旧纹理,避免当前帧 ImGui 的 DrawList 还在引用它导致崩溃。
|
||
if (FilamentTexture_) {
|
||
OldTextures_.push_back(FilamentTexture_);
|
||
}
|
||
|
||
FilamentTexture_ = filament::Texture::Builder()
|
||
.width(static_cast<uint32_t>(width))
|
||
.height(static_cast<uint32_t>(height))
|
||
.usage(filament::Texture::Usage::COLOR_ATTACHMENT | filament::Texture::Usage::SAMPLEABLE)
|
||
.format(filament::Texture::InternalFormat::RGBA8)
|
||
.build(*Engine_);
|
||
|
||
DepthTexture_ = filament::Texture::Builder()
|
||
.width(static_cast<uint32_t>(width))
|
||
.height(static_cast<uint32_t>(height))
|
||
.usage(filament::Texture::Usage::DEPTH_ATTACHMENT)
|
||
.format(filament::Texture::InternalFormat::DEPTH24)
|
||
.build(*Engine_);
|
||
|
||
RenderTarget_ = filament::RenderTarget::Builder()
|
||
.texture(filament::RenderTarget::AttachmentPoint::COLOR, FilamentTexture_)
|
||
.texture(filament::RenderTarget::AttachmentPoint::DEPTH, DepthTexture_)
|
||
.build(*Engine_);
|
||
|
||
View_->setRenderTarget(RenderTarget_);
|
||
View_->setViewport({0, 0, static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
|
||
}
|
||
|
||
void* GetFilamentTexturePointer() const {
|
||
return FilamentTexture_;
|
||
}
|
||
|
||
bool TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const {
|
||
const auto worldIt = ObjectWorldMatrices_.find(objectId);
|
||
if (worldIt == ObjectWorldMatrices_.end()) {
|
||
return false;
|
||
}
|
||
|
||
worldMatrix = worldIt->second;
|
||
return true;
|
||
}
|
||
|
||
bool HasRuntimeSyncFailure() const { return false; }
|
||
const std::string& GetLastRuntimeSyncFailure() const { return EmptyString_; }
|
||
|
||
bool VerifyTransformForTesting(MetaCoreId objectId, const glm::mat4& expectedMatrix) const {
|
||
auto it = ObjectToFilamentEntity_.find(objectId);
|
||
if (it == ObjectToFilamentEntity_.end()) return false;
|
||
|
||
utils::Entity entity = it->second.second;
|
||
auto& tm = Engine_->getTransformManager();
|
||
auto instance = tm.getInstance(entity);
|
||
if (!instance) return false;
|
||
|
||
filament::math::mat4f currentMat = tm.getTransform(instance);
|
||
glm::mat4 actualMatrix;
|
||
std::memcpy(glm::value_ptr(actualMatrix), ¤tMat[0][0], sizeof(float) * 16);
|
||
|
||
glm::mat4 targetMatrix = expectedMatrix;
|
||
if (LoadedAssets_.count(objectId) == 0) {
|
||
glm::mat4 R_plus90X = glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), {1, 0, 0});
|
||
glm::mat4 R_minus90X = glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), {1, 0, 0});
|
||
targetMatrix = R_plus90X * targetMatrix * R_minus90X;
|
||
}
|
||
|
||
// 浮点数比对,精度 0.0001
|
||
for (int c = 0; c < 4; ++c) {
|
||
for (int r = 0; r < 4; ++r) {
|
||
if (std::abs(actualMatrix[c][r] - targetMatrix[c][r]) > 0.0001f) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool VerifyLightExistsForTesting(MetaCoreId objectId) const {
|
||
return SceneLightEntities_.contains(objectId);
|
||
}
|
||
|
||
float GetDefaultLightIntensityForTesting() const {
|
||
if (!Engine_) return 0.0F;
|
||
auto& lightManager = Engine_->getLightManager();
|
||
const auto defaultLightInstance = lightManager.getInstance(Light_);
|
||
if (defaultLightInstance) {
|
||
return lightManager.getIntensity(defaultLightInstance);
|
||
}
|
||
return 0.0F;
|
||
}
|
||
|
||
bool VerifyEntityInSceneForTesting(MetaCoreId objectId) const {
|
||
auto it = ObjectToFilamentEntity_.find(objectId);
|
||
if (it == ObjectToFilamentEntity_.end()) {
|
||
std::cout << "[DEBUG Verify] Cannot find objectId " << objectId << " in ObjectToFilamentEntity_" << std::endl;
|
||
std::cout << "[DEBUG Verify] ObjectToFilamentEntity_ size: " << ObjectToFilamentEntity_.size() << std::endl;
|
||
for (const auto& [oid, ent] : ObjectToFilamentEntity_) {
|
||
std::cout << " - OID: " << oid << ", entity: " << ent.second.getId() << std::endl;
|
||
}
|
||
return false;
|
||
}
|
||
bool inScene = EntitiesInScene_.contains(it->second.second);
|
||
if (!inScene) {
|
||
std::cout << "[DEBUG Verify] Object " << objectId << " maps to entity "
|
||
<< it->second.second.getId() << ", but this entity is NOT in EntitiesInScene_" << std::endl;
|
||
std::cout << "[DEBUG Verify] EntitiesInScene_ contains: ";
|
||
for (auto ent : EntitiesInScene_) {
|
||
std::cout << ent.getId() << " ";
|
||
}
|
||
std::cout << std::endl;
|
||
}
|
||
return inScene;
|
||
}
|
||
|
||
private:
|
||
// 判断当前实体是否为过渡节点(即无名且无 Mesh 渲染组件的节点)
|
||
bool IsTransitionNode(utils::Entity entity, filament::gltfio::FilamentAsset* asset) const {
|
||
if (!entity) return false;
|
||
if (entity == asset->getRoot()) return false;
|
||
|
||
// 1. 判断是否有名字
|
||
const char* name = asset->getName(entity);
|
||
if (name && strlen(name) > 0) {
|
||
return false;
|
||
}
|
||
|
||
// 2. 判断是否是渲染实体
|
||
auto& rm = Engine_->getRenderableManager();
|
||
if (rm.getInstance(entity)) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// 穿透子节点链中的所有过渡节点,提取出所有具备有效名称或为渲染实体的第一代有效子节点
|
||
void GetEffectiveFilamentChildren(
|
||
utils::Entity parent,
|
||
filament::gltfio::FilamentAsset* asset,
|
||
const std::unordered_map<utils::Entity, std::vector<utils::Entity>, utils::Entity::Hasher>& parentToChildren,
|
||
std::vector<utils::Entity>& outEffectiveChildren
|
||
) const {
|
||
auto it = parentToChildren.find(parent);
|
||
if (it == parentToChildren.end()) return;
|
||
|
||
for (utils::Entity child : it->second) {
|
||
if (IsTransitionNode(child, asset)) {
|
||
GetEffectiveFilamentChildren(child, asset, parentToChildren, outEffectiveChildren);
|
||
} else {
|
||
outEffectiveChildren.push_back(child);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 自底向上溯源生成覆盖所有中间隐藏过渡节点的完整父子映射表
|
||
std::unordered_map<utils::Entity, std::vector<utils::Entity>, utils::Entity::Hasher>
|
||
BuildFullParentToChildrenMap(filament::gltfio::FilamentAsset* asset) const {
|
||
std::unordered_map<utils::Entity, std::vector<utils::Entity>, utils::Entity::Hasher> parentToChildren;
|
||
if (!asset) return parentToChildren;
|
||
|
||
const utils::Entity* entities = asset->getEntities();
|
||
size_t entityCount = asset->getEntityCount();
|
||
auto& tm = Engine_->getTransformManager();
|
||
|
||
for (size_t i = 0; i < entityCount; ++i) {
|
||
utils::Entity current = entities[i];
|
||
if (current == asset->getRoot()) continue;
|
||
|
||
while (current && current != asset->getRoot()) {
|
||
auto instance = tm.getInstance(current);
|
||
if (!instance) break;
|
||
|
||
utils::Entity parent = tm.getParent(instance);
|
||
if (parent.isNull()) break;
|
||
|
||
auto& children = parentToChildren[parent];
|
||
if (std::find(children.begin(), children.end(), current) == children.end()) {
|
||
children.push_back(current);
|
||
}
|
||
|
||
current = parent;
|
||
}
|
||
}
|
||
|
||
return parentToChildren;
|
||
}
|
||
|
||
static filament::math::float3 ToFilamentFloat3(const glm::vec3& value) {
|
||
return filament::math::float3{ value.x, value.y, value.z };
|
||
}
|
||
|
||
static filament::math::float3 BuildFilamentLightDirection(const glm::mat4& worldMatrix) {
|
||
glm::vec3 direction = glm::vec3(worldMatrix * glm::vec4(0.0F, 0.0F, -1.0F, 0.0F));
|
||
if (glm::length(direction) <= 0.0001F) {
|
||
direction = glm::vec3(0.5F, 0.5F, -1.0F);
|
||
}
|
||
direction = glm::normalize(direction);
|
||
return ToFilamentFloat3(direction);
|
||
}
|
||
|
||
static filament::LinearColor BuildFilamentLightColor(const glm::vec3& color) {
|
||
return filament::Color::toLinear<filament::ACCURATE>({ color.x, color.y, color.z });
|
||
}
|
||
|
||
static float BuildFilamentLightIntensity(float intensity) {
|
||
return (intensity > 0.0F ? intensity : 0.0F) * 100000.0F;
|
||
}
|
||
|
||
void SyncSceneLights(const MetaCoreSceneRenderSyncSnapshot& snapshot) {
|
||
if (!Engine_ || !Scene_) {
|
||
return;
|
||
}
|
||
|
||
std::unordered_set<MetaCoreId> activeLightIds;
|
||
auto& lightManager = Engine_->getLightManager();
|
||
const auto defaultLightInstance = lightManager.getInstance(Light_);
|
||
|
||
// 统计当前是否有处于启用状态的光源
|
||
bool hasActiveLight = false;
|
||
for (const auto& light : snapshot.Lights) {
|
||
if (light.Enabled) {
|
||
hasActiveLight = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (defaultLightInstance) {
|
||
// 如果场景中没有任何起作用的灯光,则自动亮起默认灯光作为兜底
|
||
lightManager.setIntensity(defaultLightInstance, hasActiveLight ? 0.0F : 100000.0F);
|
||
}
|
||
|
||
for (const MetaCoreRenderSyncLight& light : snapshot.Lights) {
|
||
if (!light.Enabled) {
|
||
continue;
|
||
}
|
||
activeLightIds.insert(light.ObjectId);
|
||
auto lightIt = SceneLightEntities_.find(light.ObjectId);
|
||
if (lightIt == SceneLightEntities_.end()) {
|
||
filament::LightManager::ShadowOptions shadowOptions;
|
||
shadowOptions.mapSize = 2048;
|
||
shadowOptions.shadowCascades = 3;
|
||
|
||
utils::Entity lightEntity = utils::EntityManager::get().create();
|
||
filament::LightManager::Builder(filament::LightManager::Type::DIRECTIONAL)
|
||
.color(BuildFilamentLightColor(light.Color))
|
||
.intensity(BuildFilamentLightIntensity(light.Intensity))
|
||
.direction(BuildFilamentLightDirection(light.WorldMatrix))
|
||
.castShadows(true)
|
||
.shadowOptions(shadowOptions)
|
||
.build(*Engine_, lightEntity);
|
||
Scene_->addEntity(lightEntity);
|
||
EntitiesInScene_.insert(lightEntity);
|
||
lightIt = SceneLightEntities_.emplace(light.ObjectId, lightEntity).first;
|
||
}
|
||
|
||
const auto lightInstance = lightManager.getInstance(lightIt->second);
|
||
if (lightInstance) {
|
||
lightManager.setColor(lightInstance, BuildFilamentLightColor(light.Color));
|
||
lightManager.setIntensity(lightInstance, BuildFilamentLightIntensity(light.Intensity));
|
||
lightManager.setDirection(lightInstance, BuildFilamentLightDirection(light.WorldMatrix));
|
||
}
|
||
}
|
||
|
||
for (auto it = SceneLightEntities_.begin(); it != SceneLightEntities_.end();) {
|
||
if (activeLightIds.contains(it->first)) {
|
||
++it;
|
||
continue;
|
||
}
|
||
|
||
Scene_->remove(it->second);
|
||
EntitiesInScene_.erase(it->second);
|
||
Engine_->destroy(it->second);
|
||
utils::EntityManager::get().destroy(it->second);
|
||
it = SceneLightEntities_.erase(it);
|
||
}
|
||
}
|
||
|
||
void UpdateTransform(MetaCoreId objectId, const glm::mat4& localMatrix, const glm::mat4& worldMatrix) {
|
||
auto it = ObjectToFilamentEntity_.find(objectId);
|
||
if (it == ObjectToFilamentEntity_.end()) return;
|
||
|
||
// glTF child entities keep their authored local transforms. The ECS child objects are
|
||
// editor-facing handles; only the loaded model host/pivot drives the imported asset.
|
||
if (!LoadedAssets_.contains(objectId)) {
|
||
return;
|
||
}
|
||
|
||
utils::Entity entity = it->second.second;
|
||
|
||
auto& tm = Engine_->getTransformManager();
|
||
auto instance = tm.getInstance(entity);
|
||
if (instance) {
|
||
auto parent = tm.getParent(instance);
|
||
glm::mat4 matrix = parent ? localMatrix : worldMatrix;
|
||
tm.setTransform(instance, *reinterpret_cast<const filament::math::mat4f*>(glm::value_ptr(matrix)));
|
||
}
|
||
}
|
||
|
||
static bool IsEngineLoadedModelRenderable(const MetaCoreRenderSyncRenderable& renderable) {
|
||
if (renderable.MeshSource == MetaCoreMeshSourceKind::Asset) {
|
||
return renderable.SourceModelAssetGuid.IsValid() || !renderable.SourceModelPath.empty();
|
||
}
|
||
|
||
return renderable.MeshSource == MetaCoreMeshSourceKind::Builtin &&
|
||
(renderable.BuiltinMesh == MetaCoreBuiltinMeshType::Cube ||
|
||
renderable.BuiltinMesh == MetaCoreBuiltinMeshType::Plane);
|
||
}
|
||
|
||
filament::Engine* Engine_ = nullptr;
|
||
filament::SwapChain* SwapChain_ = nullptr;
|
||
filament::Renderer* Renderer_ = nullptr;
|
||
filament::Scene* Scene_ = nullptr;
|
||
filament::Camera* Camera_ = nullptr;
|
||
filament::View* View_ = nullptr;
|
||
|
||
filament::gltfio::AssetLoader* AssetLoader_ = nullptr;
|
||
filament::gltfio::MaterialProvider* MaterialProvider_ = nullptr;
|
||
filament::gltfio::ResourceLoader* ResourceLoader_ = nullptr;
|
||
utils::NameComponentManager* NameManager_ = nullptr;
|
||
filament::IndirectLight* IndirectLight_ = nullptr;
|
||
filament::Texture* IblTexture_ = nullptr;
|
||
filament::gltfio::TextureProvider* StbDecoder_ = nullptr;
|
||
|
||
MetaCoreSceneRenderSync RenderSync_{};
|
||
MetaCoreSceneRenderSyncSnapshot LastSyncSnapshot_{};
|
||
std::unordered_map<MetaCoreId, glm::mat4> ObjectWorldMatrices_{};
|
||
std::unordered_map<MetaCoreId, std::pair<filament::gltfio::FilamentAsset*, utils::Entity>> ObjectToFilamentEntity_;
|
||
std::unordered_map<MetaCoreId, filament::gltfio::FilamentAsset*> LoadedAssets_;
|
||
std::unordered_map<MetaCoreId, std::vector<char>> AssetBuffers_;
|
||
std::unordered_set<MetaCoreId> FailedModelLoads_;
|
||
std::unordered_set<std::string> FailedModelLoadPaths_;
|
||
|
||
std::unordered_map<MetaCoreId, utils::Entity> SceneLightEntities_;
|
||
std::unordered_set<utils::Entity, utils::Entity::Hasher> EntitiesInScene_;
|
||
|
||
filament::View* UIView_ = nullptr;
|
||
MetaCoreImGuiHelper* ImGuiHelper_ = nullptr;
|
||
filament::View* RuntimeUIView_ = nullptr;
|
||
MetaCoreImGuiHelper* RuntimeUiHelper_ = nullptr;
|
||
MetaCoreRuntimeUiFrame RuntimeUiFrame_{};
|
||
int RuntimeUiWidth_ = 0;
|
||
int RuntimeUiHeight_ = 0;
|
||
|
||
GLuint GLTextureId_ = 0;
|
||
filament::Texture* FilamentTexture_ = nullptr;
|
||
filament::Texture* DepthTexture_ = nullptr;
|
||
filament::RenderTarget* RenderTarget_ = nullptr;
|
||
std::vector<filament::Texture*> OldTextures_;
|
||
utils::Entity Light_;
|
||
|
||
std::filesystem::path ProjectRootPath_{};
|
||
std::string EmptyString_{};
|
||
};
|
||
|
||
MetaCoreFilamentSceneBridge::MetaCoreFilamentSceneBridge()
|
||
: Impl_(std::make_unique<MetaCoreFilamentSceneBridgeImpl>()) {}
|
||
|
||
MetaCoreFilamentSceneBridge::~MetaCoreFilamentSceneBridge() = default;
|
||
|
||
bool MetaCoreFilamentSceneBridge::Initialize(MetaCoreWindow& window, bool offscreen, MetaCoreRenderBackend backend) {
|
||
return Impl_->Initialize(window, offscreen, backend);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::Shutdown() {
|
||
Impl_->Shutdown();
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::SetProjectRootPath(const std::filesystem::path& projectRootPath) {
|
||
Impl_->SetProjectRootPath(projectRootPath);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::SyncScene(MetaCoreScene& scene, bool compatibilityMeshOnly, bool useScenePrimaryCamera) {
|
||
auto snapshot = Impl_->BuildSnapshot(scene);
|
||
Impl_->SyncScene(snapshot, &scene, compatibilityMeshOnly, useScenePrimaryCamera);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::SyncScene(const MetaCoreSceneRenderSyncSnapshot& snapshot, MetaCoreScene* scene, bool compatibilityMeshOnly, bool useScenePrimaryCamera) {
|
||
Impl_->SyncScene(snapshot, scene, compatibilityMeshOnly, useScenePrimaryCamera);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::ApplySceneView(const MetaCoreSceneView& sceneView) {
|
||
Impl_->ApplySceneView(sceneView);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::SetRuntimeUiOverlayFrame(const MetaCoreRuntimeUiFrame& frame, int width, int height) {
|
||
Impl_->SetRuntimeUiOverlayFrame(frame, width, height);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::RenderAll() {
|
||
Impl_->RenderAll();
|
||
}
|
||
|
||
uint32_t MetaCoreFilamentSceneBridge::GetGLTextureId() const {
|
||
return Impl_->GetGLTextureId();
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::Resize(int width, int height) {
|
||
Impl_->Resize(width, height);
|
||
}
|
||
|
||
void* MetaCoreFilamentSceneBridge::GetFilamentTexturePointer() const {
|
||
return Impl_->GetFilamentTexturePointer();
|
||
}
|
||
|
||
bool MetaCoreFilamentSceneBridge::TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const {
|
||
return Impl_->TryGetObjectWorldMatrix(objectId, worldMatrix);
|
||
}
|
||
|
||
bool MetaCoreFilamentSceneBridge::HasRuntimeSyncFailure() const {
|
||
return Impl_->HasRuntimeSyncFailure();
|
||
}
|
||
|
||
const std::string& MetaCoreFilamentSceneBridge::GetLastRuntimeSyncFailure() const {
|
||
return Impl_->GetLastRuntimeSyncFailure();
|
||
}
|
||
|
||
bool MetaCoreFilamentSceneBridge::VerifyTransformForTesting(MetaCoreId objectId, const glm::mat4& expectedMatrix) const {
|
||
return Impl_->VerifyTransformForTesting(objectId, expectedMatrix);
|
||
}
|
||
|
||
bool MetaCoreFilamentSceneBridge::VerifyLightExistsForTesting(MetaCoreId objectId) const {
|
||
return Impl_->VerifyLightExistsForTesting(objectId);
|
||
}
|
||
|
||
float MetaCoreFilamentSceneBridge::GetDefaultLightIntensityForTesting() const {
|
||
return Impl_->GetDefaultLightIntensityForTesting();
|
||
}
|
||
|
||
bool MetaCoreFilamentSceneBridge::VerifyEntityInSceneForTesting(MetaCoreId objectId) const {
|
||
return Impl_->VerifyEntityInSceneForTesting(objectId);
|
||
}
|
||
|
||
} // namespace MetaCore
|