492 lines
18 KiB
C++
492 lines
18 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 <imgui.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 <gltfio/AssetLoader.h>
|
||
#include <gltfio/FilamentAsset.h>
|
||
#include <gltfio/ResourceLoader.h>
|
||
#include <gltfio/MaterialProvider.h>
|
||
|
||
#include <utils/Entity.h>
|
||
#include <utils/EntityManager.h>
|
||
|
||
#define GLM_ENABLE_EXPERIMENTAL
|
||
#include <glm/gtc/type_ptr.hpp>
|
||
#include <glm/gtc/matrix_transform.hpp>
|
||
|
||
#include <iostream>
|
||
#include <fstream>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
|
||
// 引入 Windows 和 OpenGL 头文件以创建纹理
|
||
#define WIN32_LEAN_AND_MEAN
|
||
#include <windows.h>
|
||
#include <GL/gl.h>
|
||
|
||
namespace MetaCore {
|
||
|
||
class MetaCoreFilamentSceneBridge::MetaCoreFilamentSceneBridgeImpl {
|
||
public:
|
||
MetaCoreFilamentSceneBridgeImpl() = default;
|
||
|
||
~MetaCoreFilamentSceneBridgeImpl() {
|
||
Shutdown();
|
||
}
|
||
|
||
bool Initialize(MetaCoreWindow& window) {
|
||
std::cout << "FilamentSceneBridge: Initializing..." << std::endl;
|
||
|
||
// 创建 Filament Engine (不共享上下文,避免闪退)
|
||
Engine_ = filament::Engine::create();
|
||
|
||
if (!Engine_) {
|
||
std::cerr << "Failed to create Filament Engine with shared context!" << 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();
|
||
|
||
// 创建默认灯光
|
||
Light_ = utils::EntityManager::get().create();
|
||
filament::LightManager::Builder(filament::LightManager::Type::DIRECTIONAL)
|
||
.color(filament::Color::toLinear<filament::ACCURATE>({ 1.0f, 1.0f, 1.0f }))
|
||
.intensity(100000.0f)
|
||
.direction({ 0.5f, -1.0f, -0.5f })
|
||
.castShadows(true)
|
||
.build(*Engine_, Light_);
|
||
Scene_->addEntity(Light_);
|
||
|
||
// 创建相机
|
||
utils::Entity cameraEntity = utils::EntityManager::get().create();
|
||
Camera_ = Engine_->createCamera(cameraEntity);
|
||
|
||
// 创建视图
|
||
View_ = Engine_->createView();
|
||
View_->setScene(Scene_);
|
||
View_->setCamera(Camera_);
|
||
|
||
// 创建 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)});
|
||
|
||
// 确保开启后处理(为了HDR)
|
||
View_->setPostProcessingEnabled(true);
|
||
|
||
// 初始化 gltfio (使用 JIT Shader Provider,需要链接 filamat)
|
||
MaterialProvider_ = filament::gltfio::createJitShaderProvider(Engine_);
|
||
AssetLoader_ = filament::gltfio::AssetLoader::create({ Engine_, MaterialProvider_, nullptr });
|
||
|
||
std::cout << "FilamentSceneBridge: Initialized with Offscreen Texture ID: " << GLTextureId_ << std::endl;
|
||
return true;
|
||
}
|
||
|
||
void Shutdown() {
|
||
if (Engine_) {
|
||
std::cout << "FilamentSceneBridge: Shutting down..." << std::endl;
|
||
|
||
// 销毁所有加载的资产
|
||
for (auto& [id, asset] : LoadedAssets_) {
|
||
AssetLoader_->destroyAsset(asset);
|
||
}
|
||
LoadedAssets_.clear();
|
||
|
||
// 销毁 gltfio 资源
|
||
if (AssetLoader_) {
|
||
filament::gltfio::AssetLoader::destroy(&AssetLoader_);
|
||
AssetLoader_ = nullptr;
|
||
}
|
||
if (MaterialProvider_) {
|
||
MaterialProvider_->destroyMaterials();
|
||
delete MaterialProvider_;
|
||
MaterialProvider_ = nullptr;
|
||
}
|
||
|
||
// 销毁离屏渲染资源
|
||
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 (Camera_) {
|
||
utils::Entity cameraEntity = Camera_->getEntity();
|
||
Engine_->destroyCameraComponent(cameraEntity);
|
||
utils::EntityManager::get().destroy(cameraEntity);
|
||
}
|
||
|
||
// 销毁其他资源
|
||
Engine_->destroy(Scene_);
|
||
Engine_->destroy(Renderer_);
|
||
Engine_->destroy(SwapChain_);
|
||
|
||
// 最后销毁 Engine
|
||
filament::Engine::destroy(&Engine_);
|
||
Engine_ = nullptr;
|
||
}
|
||
}
|
||
|
||
void SetProjectRootPath(const std::filesystem::path& projectRootPath) {
|
||
ProjectRootPath_ = projectRootPath;
|
||
}
|
||
|
||
void SyncScene(const MetaCoreScene& scene, bool compatibilityMeshOnly) {
|
||
if (!AssetLoader_) return;
|
||
|
||
for (const auto& gameObject : scene.GetGameObjects()) {
|
||
if (!gameObject.HasComponent<MetaCoreMeshRendererComponent>()) {
|
||
continue;
|
||
}
|
||
|
||
const auto& meshRenderer = gameObject.GetComponent<MetaCoreMeshRendererComponent>();
|
||
if (meshRenderer.MeshSource != MetaCoreMeshSourceKind::Asset) {
|
||
continue;
|
||
}
|
||
|
||
std::string modelPath = meshRenderer.SourceModelPath;
|
||
if (modelPath.empty()) {
|
||
continue;
|
||
}
|
||
|
||
std::filesystem::path absolutePath = ProjectRootPath_ / modelPath;
|
||
|
||
// 如果已经加载过,更新位置即可
|
||
if (LoadedAssets_.contains(gameObject.GetId())) {
|
||
UpdateTransform(gameObject);
|
||
continue;
|
||
}
|
||
|
||
// 加载新模型
|
||
std::cout << "FilamentSceneBridge: Loading GLTF: " << absolutePath << std::endl;
|
||
|
||
std::ifstream file(absolutePath, std::ios::binary);
|
||
if (!file.is_open()) {
|
||
std::cerr << "Failed to open file: " << absolutePath << std::endl;
|
||
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;
|
||
continue;
|
||
}
|
||
|
||
// 加载贴图等资源
|
||
filament::gltfio::ResourceLoader resourceLoader({ Engine_ });
|
||
resourceLoader.loadResources(asset);
|
||
|
||
// 添加到场景
|
||
Scene_->addEntities(asset->getEntities(), asset->getEntityCount());
|
||
|
||
LoadedAssets_[gameObject.GetId()] = asset;
|
||
|
||
// 更新初始位置
|
||
UpdateTransform(gameObject);
|
||
}
|
||
}
|
||
|
||
void ApplySceneView(const MetaCoreSceneView& sceneView) {
|
||
if (!Camera_ || !View_) return;
|
||
|
||
// 设置相机位置和朝向
|
||
Camera_->lookAt(
|
||
{ sceneView.CameraPosition.x, sceneView.CameraPosition.y, sceneView.CameraPosition.z },
|
||
{ sceneView.CameraTarget.x, sceneView.CameraTarget.y, sceneView.CameraTarget.z },
|
||
{ sceneView.CameraUp.x, sceneView.CameraUp.y, sceneView.CameraUp.z }
|
||
);
|
||
|
||
// 获取当前视口大小计算宽高比
|
||
const auto& viewport = View_->getViewport();
|
||
double aspect = 1.0;
|
||
if (viewport.height > 0) {
|
||
aspect = static_cast<double>(viewport.width) / static_cast<double>(viewport.height);
|
||
}
|
||
|
||
// 设置透视投影
|
||
double fov = sceneView.VerticalFieldOfViewDegrees;
|
||
double nearClip = 0.1;
|
||
double farClip = 1000.0;
|
||
|
||
Camera_->setProjection(fov, aspect, nearClip, farClip, filament::Camera::Fov::VERTICAL);
|
||
}
|
||
|
||
void RenderAll() {
|
||
if (!Renderer_ || !SwapChain_ || !View_ || !UIView_ || !ImGuiHelper_) {
|
||
return;
|
||
}
|
||
|
||
if (Renderer_->beginFrame(SwapChain_)) {
|
||
// 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_);
|
||
|
||
// 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_);
|
||
|
||
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;
|
||
|
||
// 【关键修复】:如果大小没有改变,不要重新创建纹理!
|
||
// 否则会导致上一帧传给 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 {
|
||
return false;
|
||
}
|
||
|
||
bool HasRuntimeSyncFailure() const { return false; }
|
||
const std::string& GetLastRuntimeSyncFailure() const { return EmptyString_; }
|
||
|
||
private:
|
||
void UpdateTransform(const MetaCoreGameObject& gameObject) {
|
||
auto it = LoadedAssets_.find(gameObject.GetId());
|
||
if (it == LoadedAssets_.end()) return;
|
||
|
||
filament::gltfio::FilamentAsset* asset = it->second;
|
||
utils::Entity rootEntity = asset->getRoot();
|
||
|
||
auto& tm = Engine_->getTransformManager();
|
||
auto instance = tm.getInstance(rootEntity);
|
||
if (instance) {
|
||
if (!gameObject.HasComponent<MetaCoreTransformComponent>()) return;
|
||
|
||
const auto& transform = gameObject.GetComponent<MetaCoreTransformComponent>();
|
||
|
||
glm::mat4 matrix = glm::mat4(1.0f);
|
||
matrix = glm::translate(matrix, transform.Position);
|
||
matrix = glm::rotate(matrix, glm::radians(transform.RotationEulerDegrees.x), {1, 0, 0});
|
||
matrix = glm::rotate(matrix, glm::radians(transform.RotationEulerDegrees.y), {0, 1, 0});
|
||
matrix = glm::rotate(matrix, glm::radians(transform.RotationEulerDegrees.z), {0, 0, 1});
|
||
matrix = glm::scale(matrix, transform.Scale);
|
||
|
||
tm.setTransform(instance, *reinterpret_cast<const filament::math::mat4f*>(glm::value_ptr(matrix)));
|
||
}
|
||
}
|
||
|
||
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;
|
||
|
||
std::unordered_map<MetaCoreId, filament::gltfio::FilamentAsset*> LoadedAssets_;
|
||
|
||
filament::View* UIView_ = nullptr;
|
||
MetaCoreImGuiHelper* ImGuiHelper_ = nullptr;
|
||
|
||
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) {
|
||
return Impl_->Initialize(window);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::Shutdown() {
|
||
Impl_->Shutdown();
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::SetProjectRootPath(const std::filesystem::path& projectRootPath) {
|
||
Impl_->SetProjectRootPath(projectRootPath);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::SyncScene(const MetaCoreScene& scene, bool compatibilityMeshOnly) {
|
||
Impl_->SyncScene(scene, compatibilityMeshOnly);
|
||
}
|
||
|
||
void MetaCoreFilamentSceneBridge::ApplySceneView(const MetaCoreSceneView& sceneView) {
|
||
Impl_->ApplySceneView(sceneView);
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
} // namespace MetaCore
|