#include "MetaCoreRender/MetaCorePandaSceneBridge.h" #include "MetaCoreRender/MetaCoreRenderDevice.h" #include "MetaCoreRender/MetaCoreRenderTypes.h" #include "MetaCoreScene/MetaCoreComponents.h" #include "MetaCoreScene/MetaCoreScene.h" #include "ambientLight.h" #include "camera.h" #include "directionalLight.h" #include "geom.h" #include "geomNode.h" #include "geomLines.h" #include "geomTriangles.h" #include "geomVertexData.h" #include "geomVertexFormat.h" #include "geomVertexWriter.h" #include "internalName.h" #include "lineSegs.h" #include "loader.h" #include "nodePath.h" #include "perspectiveLens.h" #include "pandaNode.h" #include "shader.h" #include "texture.h" #include "texturePool.h" #include "transparencyAttrib.h" #include "windowFramework.h" #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include #include #include #include #include #include namespace MetaCore { namespace { void MetaCoreTrace(const char* message) { if (message == nullptr) { return; } const auto now = std::chrono::system_clock::now(); const auto time = std::chrono::system_clock::to_time_t(now); struct tm timeInfo {}; localtime_s(&timeInfo, &time); char timeBuffer[32]{}; std::strftime(timeBuffer, sizeof(timeBuffer), "[%H:%M:%S] ", &timeInfo); FILE* file = nullptr; if (fopen_s(&file, "editor.crash.log", "a") == 0 && file != nullptr) { std::fputs(timeBuffer, file); std::fputs(message, file); std::fputs("\n", file); std::fflush(file); std::fclose(file); } std::printf("%s%s\n", timeBuffer, message); } glm::mat4 MetaCoreBuildBasisSwapMatrix() { glm::mat4 basis(1.0F); basis[0] = glm::vec4(1.0F, 0.0F, 0.0F, 0.0F); basis[1] = glm::vec4(0.0F, 0.0F, 1.0F, 0.0F); basis[2] = glm::vec4(0.0F, 1.0F, 0.0F, 0.0F); basis[3] = glm::vec4(0.0F, 0.0F, 0.0F, 1.0F); return basis; } LMatrix4f MetaCoreConvertTransformToPanda(const MetaCoreTransformComponent& transform) { const glm::mat4 translation = glm::translate(glm::mat4(1.0F), transform.Position); const glm::mat4 rotation = glm::yawPitchRoll( glm::radians(transform.RotationEulerDegrees.y), glm::radians(transform.RotationEulerDegrees.x), glm::radians(transform.RotationEulerDegrees.z) ); const glm::mat4 scale = glm::scale(glm::mat4(1.0F), transform.Scale); const glm::mat4 metaCoreMatrix = translation * rotation * scale; const glm::mat4 basis = MetaCoreBuildBasisSwapMatrix(); const glm::mat4 pandaMatrix = basis * metaCoreMatrix * basis; LMatrix4f result; for (int row = 0; row < 4; ++row) { for (int column = 0; column < 4; ++column) { result.set_cell(row, column, pandaMatrix[column][row]); } } return result; } LPoint3f MetaCoreToPandaPoint(const glm::vec3& value) { return LPoint3f(value.x, -value.z, value.y); } LVector3f MetaCoreToPandaVector(const glm::vec3& value) { return LVector3f(value.x, -value.z, value.y); } LVecBase3f MetaCoreToPandaScale(const glm::vec3& value) { return LVecBase3f(value.x, value.z, value.y); } void MetaCoreApplyTransformToPandaNode(const MetaCoreTransformComponent& transform, NodePath& nodePath) { nodePath.set_pos(MetaCoreToPandaPoint(transform.Position)); nodePath.set_hpr( transform.RotationEulerDegrees.y, transform.RotationEulerDegrees.x, transform.RotationEulerDegrees.z ); nodePath.set_scale(MetaCoreToPandaScale(transform.Scale)); } NodePath MetaCoreCreateGridNode(const NodePath& parentNode) { MetaCoreTrace("create_grid: begin"); try { MetaCoreTrace("create_grid: create vertex data"); PT(GeomVertexData) vertexData = new GeomVertexData( "MetaCoreGrid", GeomVertexFormat::get_v3c4(), Geom::UH_static ); GeomVertexWriter vertexWriter(vertexData, "vertex"); GeomVertexWriter colorWriter(vertexData, "color"); MetaCoreTrace("create_grid: loop start"); constexpr int gridHalfExtent = 10; int vertexCount = 0; for (int lineIndex = -gridHalfExtent; lineIndex <= gridHalfExtent; ++lineIndex) { const bool isAxisLine = (lineIndex == 0); const LColor color(isAxisLine ? 0.42F : 0.30F, isAxisLine ? 0.45F : 0.33F, isAxisLine ? 0.50F : 0.36F, 1.0F); // X-direction lines vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(static_cast(lineIndex), 0.0F, static_cast(-gridHalfExtent)))); colorWriter.add_data4(color); vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(static_cast(lineIndex), 0.0F, static_cast(gridHalfExtent)))); colorWriter.add_data4(color); // Z-direction lines vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(static_cast(-gridHalfExtent), 0.0F, static_cast(lineIndex)))); colorWriter.add_data4(color); vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(static_cast(gridHalfExtent), 0.0F, static_cast(lineIndex)))); colorWriter.add_data4(color); vertexCount += 4; } MetaCoreTrace("create_grid: loop end"); MetaCoreTrace("create_grid: create primitive"); PT(GeomLines) lines = new GeomLines(Geom::UH_static); for (int i = 0; i < vertexCount; i += 2) { lines->add_vertices(i, i + 1); } MetaCoreTrace("create_grid: create geom"); PT(Geom) geom = new Geom(vertexData); geom->add_primitive(lines.p()); PT(GeomNode) geomNode = new GeomNode("MetaCoreGridNode"); geomNode->add_geom(geom); return parentNode.attach_new_node(geomNode); } catch (...) { return NodePath(); } } NodePath MetaCoreCreateUnitCubeNode(const NodePath& parentNode) { PT(GeomVertexData) vertexData = new GeomVertexData( "MetaCoreUnitCube", GeomVertexFormat::get_v3n3(), Geom::UH_static ); GeomVertexWriter vertexWriter(vertexData, "vertex"); GeomVertexWriter normalWriter(vertexData, "normal"); const auto addFace = [&](const glm::vec3& normal, const glm::vec3& a, const glm::vec3& b, const glm::vec3& c, const glm::vec3& d) { const glm::vec3 corners[4] = {a, b, c, d}; for (const glm::vec3& corner : corners) { const LPoint3f pandaPoint = MetaCoreToPandaPoint(corner); const LVector3f pandaNormal = MetaCoreToPandaVector(normal); vertexWriter.add_data3(pandaPoint); normalWriter.add_data3(pandaNormal); } }; addFace(glm::vec3(0.0F, 0.0F, 1.0F), glm::vec3(-0.5F, -0.5F, 0.5F), glm::vec3(0.5F, -0.5F, 0.5F), glm::vec3(0.5F, 0.5F, 0.5F), glm::vec3(-0.5F, 0.5F, 0.5F)); addFace(glm::vec3(0.0F, 0.0F, -1.0F), glm::vec3(0.5F, -0.5F, -0.5F), glm::vec3(-0.5F, -0.5F, -0.5F), glm::vec3(-0.5F, 0.5F, -0.5F), glm::vec3(0.5F, 0.5F, -0.5F)); addFace(glm::vec3(-1.0F, 0.0F, 0.0F), glm::vec3(-0.5F, -0.5F, -0.5F), glm::vec3(-0.5F, -0.5F, 0.5F), glm::vec3(-0.5F, 0.5F, 0.5F), glm::vec3(-0.5F, 0.5F, -0.5F)); addFace(glm::vec3(1.0F, 0.0F, 0.0F), glm::vec3(0.5F, -0.5F, 0.5F), glm::vec3(0.5F, -0.5F, -0.5F), glm::vec3(0.5F, 0.5F, -0.5F), glm::vec3(0.5F, 0.5F, 0.5F)); addFace(glm::vec3(0.0F, 1.0F, 0.0F), glm::vec3(-0.5F, 0.5F, 0.5F), glm::vec3(0.5F, 0.5F, 0.5F), glm::vec3(0.5F, 0.5F, -0.5F), glm::vec3(-0.5F, 0.5F, -0.5F)); addFace(glm::vec3(0.0F, -1.0F, 0.0F), glm::vec3(-0.5F, -0.5F, -0.5F), glm::vec3(0.5F, -0.5F, -0.5F), glm::vec3(0.5F, -0.5F, 0.5F), glm::vec3(-0.5F, -0.5F, 0.5F)); PT(GeomTriangles) triangles = new GeomTriangles(Geom::UH_static); for (int faceIndex = 0; faceIndex < 6; ++faceIndex) { const int baseVertex = faceIndex * 4; triangles->add_vertices(baseVertex + 0, baseVertex + 1, baseVertex + 2); triangles->add_vertices(baseVertex + 0, baseVertex + 2, baseVertex + 3); } PT(Geom) geom = new Geom(vertexData); geom->add_primitive(triangles); PT(GeomNode) geomNode = new GeomNode("MetaCoreUnitCubeNode"); geomNode->add_geom(geom); return parentNode.attach_new_node(geomNode); } [[nodiscard]] std::filesystem::path MetaCoreResolveProjectRootFromEnvironment() { char* rawProjectPath = nullptr; std::size_t valueLength = 0; if (_dupenv_s(&rawProjectPath, &valueLength, "METACORE_PROJECT_PATH") != 0 || rawProjectPath == nullptr || valueLength == 0) { if (rawProjectPath != nullptr) { std::free(rawProjectPath); } return {}; } const std::filesystem::path projectRoot(rawProjectPath); std::free(rawProjectPath); return projectRoot; } [[nodiscard]] NodePath MetaCoreTryLoadModelNode( WindowFramework& windowFramework, const NodePath& parentNode, const std::filesystem::path& projectRoot, const std::string& relativeSourcePath ) { if (projectRoot.empty() || relativeSourcePath.empty()) { return NodePath(); } const std::filesystem::path absoluteSourcePath = projectRoot / std::filesystem::path(relativeSourcePath); if (!std::filesystem::exists(absoluteSourcePath)) { MetaCoreTrace(("load_model: source file missing: " + absoluteSourcePath.string()).c_str()); return NodePath(); } const Filename pandaPath = Filename::from_os_specific(absoluteSourcePath.string()); NodePath loadedNode = windowFramework.load_model(parentNode, pandaPath); if (loadedNode.is_empty()) { MetaCoreTrace(("load_model: Panda3D failed to load: " + absoluteSourcePath.string()).c_str()); return NodePath(); } MetaCoreTrace(("load_model: success: " + absoluteSourcePath.string()).c_str()); return loadedNode; } [[nodiscard]] PT(Shader) MetaCoreLoadSimplePbrShader() { static PT(Shader) cachedShader; static bool attemptedLoad = false; if (attemptedLoad) { return cachedShader; } attemptedLoad = true; const std::filesystem::path shaderRoot = std::filesystem::current_path() / "simplepbr" / "shaders"; const std::filesystem::path vertexPath = shaderRoot / "simplepbr.vert"; const std::filesystem::path fragmentPath = shaderRoot / "simplepbr.frag"; if (!std::filesystem::exists(vertexPath) || !std::filesystem::exists(fragmentPath)) { MetaCoreTrace("simplepbr: shader files missing in runtime directory"); return nullptr; } cachedShader = Shader::load( Shader::SL_GLSL, Filename::from_os_specific(vertexPath.string()), Filename::from_os_specific(fragmentPath.string()) ); if (cachedShader == nullptr) { MetaCoreTrace("simplepbr: failed to load GLSL shader"); } else { MetaCoreTrace("simplepbr: shader loaded"); } return cachedShader; } [[nodiscard]] PT(Texture) MetaCoreCreateSolidTexture( const std::string& textureName, unsigned char r, unsigned char g, unsigned char b, unsigned char a ) { PT(Texture) texture = new Texture(textureName); texture->setup_2d_texture(1, 1, Texture::T_unsigned_byte, Texture::F_rgba8); PTA_uchar image = texture->modify_ram_image(); if (image.size() >= 4) { image[0] = r; image[1] = g; image[2] = b; image[3] = a; } texture->set_minfilter(SamplerState::FT_nearest); texture->set_magfilter(SamplerState::FT_nearest); texture->set_wrap_u(SamplerState::WM_repeat); texture->set_wrap_v(SamplerState::WM_repeat); return texture; } [[nodiscard]] PT(Texture) MetaCoreCreateSolidCubeMap( const std::string& textureName, unsigned char r, unsigned char g, unsigned char b, unsigned char a ) { PT(Texture) texture = new Texture(textureName); texture->setup_cube_map(1, Texture::T_unsigned_byte, Texture::F_rgba8); PTA_uchar image = texture->modify_ram_image(); for (size_t index = 0; index + 3 < image.size(); index += 4) { image[index + 0] = r; image[index + 1] = g; image[index + 2] = b; image[index + 3] = a; } texture->set_minfilter(SamplerState::FT_nearest); texture->set_magfilter(SamplerState::FT_nearest); texture->set_wrap_u(SamplerState::WM_clamp); texture->set_wrap_v(SamplerState::WM_clamp); texture->set_wrap_w(SamplerState::WM_clamp); return texture; } [[nodiscard]] PT(Texture) MetaCoreGetDefaultBaseColorTexture() { static PT(Texture) texture = MetaCoreCreateSolidTexture("MetaCoreDefaultBaseColor", 255, 255, 255, 255); return texture; } [[nodiscard]] PT(Texture) MetaCoreGetDefaultMetalRoughnessTexture() { static PT(Texture) texture = MetaCoreCreateSolidTexture("MetaCoreDefaultMetalRoughness", 255, 255, 255, 255); return texture; } [[nodiscard]] PT(Texture) MetaCoreGetDefaultEmissionTexture() { static PT(Texture) texture = MetaCoreCreateSolidTexture("MetaCoreDefaultEmission", 0, 0, 0, 255); return texture; } [[nodiscard]] PT(Texture) MetaCoreGetDefaultAoTexture() { static PT(Texture) texture = MetaCoreCreateSolidTexture("MetaCoreDefaultAo", 255, 255, 255, 255); return texture; } [[nodiscard]] PT(Texture) MetaCoreGetDefaultNormalTexture() { static PT(Texture) texture = MetaCoreCreateSolidTexture("MetaCoreDefaultNormal", 128, 128, 255, 255); return texture; } [[nodiscard]] PT(Texture) MetaCoreLoadBrdfLutTexture() { static PT(Texture) cachedTexture; static bool attemptedLoad = false; if (attemptedLoad) { return cachedTexture; } attemptedLoad = true; const std::filesystem::path texturePath = std::filesystem::current_path() / "simplepbr" / "textures" / "brdf_lut.txo"; if (!std::filesystem::exists(texturePath)) { MetaCoreTrace("simplepbr: brdf_lut.txo missing in runtime directory"); cachedTexture = MetaCoreCreateSolidTexture("MetaCoreFallbackBrdfLut", 255, 255, 255, 255); return cachedTexture; } cachedTexture = TexturePool::load_texture(Filename::from_os_specific(texturePath.string())); if (cachedTexture == nullptr) { MetaCoreTrace("simplepbr: failed to load brdf_lut.txo"); cachedTexture = MetaCoreCreateSolidTexture("MetaCoreFallbackBrdfLut", 255, 255, 255, 255); } return cachedTexture; } [[nodiscard]] PT(Texture) MetaCoreGetDefaultFilteredEnvMap() { static PT(Texture) texture = MetaCoreCreateSolidCubeMap("MetaCoreDefaultFilteredEnvMap", 0, 0, 0, 255); return texture; } [[nodiscard]] PT(Texture) MetaCoreResolveBaseColorTexture( const std::filesystem::path& projectRoot, const std::string& relativeTexturePath ) { if (projectRoot.empty() || relativeTexturePath.empty()) { return MetaCoreGetDefaultBaseColorTexture(); } const std::filesystem::path absoluteTexturePath = projectRoot / std::filesystem::path(relativeTexturePath); if (!std::filesystem::exists(absoluteTexturePath)) { return MetaCoreGetDefaultBaseColorTexture(); } PT(Texture) texture = TexturePool::load_texture(Filename::from_os_specific(absoluteTexturePath.string())); if (texture == nullptr) { MetaCoreTrace(("load_texture: Panda3D failed to load: " + absoluteTexturePath.string()).c_str()); return MetaCoreGetDefaultBaseColorTexture(); } return texture; } [[nodiscard]] PT(Texture) MetaCoreResolveTextureWithFallback( const std::filesystem::path& projectRoot, const std::string& relativeTexturePath, const PT(Texture)& fallbackTexture ) { if (projectRoot.empty() || relativeTexturePath.empty()) { return fallbackTexture; } const std::filesystem::path absoluteTexturePath = projectRoot / std::filesystem::path(relativeTexturePath); if (!std::filesystem::exists(absoluteTexturePath)) { return fallbackTexture; } PT(Texture) texture = TexturePool::load_texture(Filename::from_os_specific(absoluteTexturePath.string())); if (texture == nullptr) { MetaCoreTrace(("load_texture: Panda3D failed to load: " + absoluteTexturePath.string()).c_str()); return fallbackTexture; } return texture; } void MetaCoreApplyBaseColorTexture( NodePath& meshNode, const std::filesystem::path& projectRoot, const std::string& relativeTexturePath ) { if (meshNode.is_empty()) { return; } PT(Texture) texture = MetaCoreResolveBaseColorTexture(projectRoot, relativeTexturePath); if (texture == nullptr) { meshNode.set_texture_off(1); return; } meshNode.set_texture(texture, 1); meshNode.set_shader_input(InternalName::make("p3d_TextureBaseColor"), texture); } void MetaCoreApplyMetallicRoughnessTexture( NodePath& meshNode, const std::filesystem::path& projectRoot, const std::string& relativeTexturePath ) { if (meshNode.is_empty()) { return; } PT(Texture) texture = MetaCoreResolveTextureWithFallback( projectRoot, relativeTexturePath, MetaCoreGetDefaultMetalRoughnessTexture() ); if (texture == nullptr) { return; } meshNode.set_shader_input(InternalName::make("p3d_TextureMetalRoughness"), texture); } void MetaCoreApplyNormalTexture( NodePath& meshNode, const std::filesystem::path& projectRoot, const std::string& relativeTexturePath ) { if (meshNode.is_empty()) { return; } PT(Texture) texture = MetaCoreResolveTextureWithFallback( projectRoot, relativeTexturePath, MetaCoreGetDefaultNormalTexture() ); if (texture == nullptr) { return; } meshNode.set_shader_input(InternalName::make("p3d_TextureNormal"), texture); } void MetaCoreApplyEmissionTexture( NodePath& meshNode, const std::filesystem::path& projectRoot, const std::string& relativeTexturePath ) { if (meshNode.is_empty()) { return; } PT(Texture) texture = MetaCoreResolveTextureWithFallback( projectRoot, relativeTexturePath, MetaCoreGetDefaultEmissionTexture() ); if (texture == nullptr) { return; } meshNode.set_shader_input(InternalName::make("p3d_TextureEmission"), texture); } void MetaCoreApplyAoTexture( NodePath& meshNode, const std::filesystem::path& projectRoot, const std::string& relativeTexturePath ) { if (meshNode.is_empty()) { return; } PT(Texture) texture = MetaCoreResolveTextureWithFallback( projectRoot, relativeTexturePath, MetaCoreGetDefaultAoTexture() ); if (texture == nullptr) { return; } meshNode.set_shader_input(InternalName::make("p3d_TextureOcclusion"), texture); } void MetaCoreApplySimplePbrPreview(NodePath& meshNode) { if (meshNode.is_empty()) { return; } PT(Shader) simplePbrShader = MetaCoreLoadSimplePbrShader(); if (simplePbrShader == nullptr) { meshNode.clear_shader(); return; } meshNode.set_shader(simplePbrShader, 1); } void MetaCoreApplySimplePbrMaterialInputs( NodePath& meshNode, const MetaCoreMeshRendererComponent& meshRenderer ) { if (meshNode.is_empty()) { return; } meshNode.set_shader_input( InternalName::make("p3d_Material.baseColor"), LVecBase4( meshRenderer.BaseColor.r, meshRenderer.BaseColor.g, meshRenderer.BaseColor.b, 1.0F ) ); meshNode.set_shader_input( InternalName::make("p3d_Material.roughness"), LVecBase4(meshRenderer.Roughness, 0.0F, 0.0F, 0.0F) ); meshNode.set_shader_input( InternalName::make("p3d_Material.metallic"), LVecBase4(meshRenderer.Metallic, 0.0F, 0.0F, 0.0F) ); meshNode.set_shader_input( InternalName::make("p3d_Material.emission"), LVecBase4( meshRenderer.EmissiveColor.r, meshRenderer.EmissiveColor.g, meshRenderer.EmissiveColor.b, 1.0F ) ); meshNode.set_shader_input( InternalName::make("p3d_TextureMetalRoughness"), MetaCoreGetDefaultMetalRoughnessTexture() ); meshNode.set_shader_input( InternalName::make("p3d_TextureNormal"), MetaCoreGetDefaultNormalTexture() ); meshNode.set_shader_input( InternalName::make("p3d_TextureEmission"), MetaCoreGetDefaultEmissionTexture() ); meshNode.set_shader_input( InternalName::make("p3d_TextureOcclusion"), MetaCoreGetDefaultAoTexture() ); meshNode.set_shader_input( InternalName::make("brdf_lut"), MetaCoreLoadBrdfLutTexture() ); meshNode.set_shader_input( InternalName::make("filtered_env_map"), MetaCoreGetDefaultFilteredEnvMap() ); meshNode.set_shader_input( InternalName::make("max_reflection_lod"), LVecBase4(0.0F, 0.0F, 0.0F, 0.0F) ); meshNode.set_shader_input( InternalName::make("metacore_AlphaCutoff"), LVecBase4(meshRenderer.AlphaCutoff, 0.0F, 0.0F, 0.0F) ); meshNode.set_shader_input( InternalName::make("metacore_AlphaMode"), LVecBase4(static_cast(meshRenderer.AlphaMode), 0.0F, 0.0F, 0.0F) ); } void MetaCoreApplySimplePbrViewInputs( NodePath& meshNode, const NodePath& sceneRootNode, const NodePath& cameraNode ) { if (meshNode.is_empty()) { return; } LPoint3f cameraWorldPosition(0.0F, 0.0F, 0.0F); if (!cameraNode.is_empty()) { cameraWorldPosition = cameraNode.get_pos(sceneRootNode); } meshNode.set_shader_input( InternalName::make("camera_world_position"), LVecBase3f(cameraWorldPosition.get_x(), cameraWorldPosition.get_y(), cameraWorldPosition.get_z()) ); } glm::mat4 MetaCoreConvertPandaMatrixToGlm(const LMatrix4f& matrix) { glm::mat4 result(1.0F); for (int col = 0; col < 4; ++col) { for (int row = 0; row < 4; ++row) { result[col][row] = matrix.get_cell(col, row); } } return result; } } // namespace class MetaCorePandaSceneBridge::MetaCorePandaSceneBridgeImpl { public: struct MetaCorePandaObjectState { NodePath RootNode{}; NodePath MeshNode{}; NodePath LightNode{}; MetaCoreMeshSourceKind MeshSource = MetaCoreMeshSourceKind::Builtin; MetaCoreBuiltinMeshType BuiltinMesh = MetaCoreBuiltinMeshType::Cube; MetaCoreAssetGuid MeshAssetGuid{}; std::string SourceModelPath{}; }; MetaCoreRenderDevice* RenderDevice = nullptr; NodePath GridNode{}; NodePath AmbientLightNode{}; std::filesystem::path ProjectRootPath{}; MetaCoreId SelectedObjectId = 0; std::unordered_map ObjectStates{}; }; MetaCorePandaSceneBridge::MetaCorePandaSceneBridge() : Impl_(std::make_unique()) { } MetaCorePandaSceneBridge::~MetaCorePandaSceneBridge() { Shutdown(); } bool MetaCorePandaSceneBridge::Initialize(MetaCoreRenderDevice& renderDevice) { Shutdown(); auto* windowFrameworkHandle = static_cast(renderDevice.GetNativeWindowFrameworkHandle()); auto* sceneRootHandle = static_cast(renderDevice.GetNativeSceneRootHandle()); if (windowFrameworkHandle == nullptr || sceneRootHandle == nullptr || sceneRootHandle->is_empty()) { return false; } Impl_->RenderDevice = &renderDevice; Impl_->ProjectRootPath = MetaCoreResolveProjectRootFromEnvironment(); Impl_->GridNode = MetaCoreCreateGridNode(*sceneRootHandle); Impl_->GridNode.set_light_off(1); PT(AmbientLight) ambientLight = new AmbientLight("MetaCoreAmbientLight"); ambientLight->set_color(LColor(0.85F, 0.85F, 0.85F, 1.0F)); Impl_->AmbientLightNode = sceneRootHandle->attach_new_node(ambientLight); sceneRootHandle->set_light(Impl_->AmbientLightNode); return true; } void MetaCorePandaSceneBridge::Shutdown() { if (Impl_ == nullptr) { return; } for (auto& [objectId, objectState] : Impl_->ObjectStates) { (void)objectId; if (!objectState.RootNode.is_empty()) { objectState.RootNode.remove_node(); } } Impl_->ObjectStates.clear(); if (!Impl_->AmbientLightNode.is_empty()) { Impl_->AmbientLightNode.remove_node(); Impl_->AmbientLightNode = NodePath(); } if (!Impl_->GridNode.is_empty()) { Impl_->GridNode.remove_node(); Impl_->GridNode = NodePath(); } Impl_->RenderDevice = nullptr; Impl_->ProjectRootPath.clear(); Impl_->SelectedObjectId = 0; } void MetaCorePandaSceneBridge::SyncScene(const MetaCoreScene& scene) { if (Impl_->RenderDevice == nullptr) { return; } auto* windowFrameworkHandle = static_cast(Impl_->RenderDevice->GetNativeWindowFrameworkHandle()); auto* sceneRootHandle = static_cast(Impl_->RenderDevice->GetNativeSceneRootHandle()); if (windowFrameworkHandle == nullptr || sceneRootHandle == nullptr || sceneRootHandle->is_empty()) { return; } std::unordered_set aliveObjectIds; for (const MetaCoreGameObject& gameObject : scene.GetGameObjects()) { aliveObjectIds.insert(gameObject.Id); auto [iterator, inserted] = Impl_->ObjectStates.try_emplace(gameObject.Id); MetaCorePandaSceneBridgeImpl::MetaCorePandaObjectState& objectState = iterator->second; if (inserted || objectState.RootNode.is_empty()) { PT(PandaNode) rootNode = new PandaNode(gameObject.Name); objectState.RootNode = sceneRootHandle->attach_new_node(rootNode); } objectState.RootNode.set_name(gameObject.Name); MetaCoreApplyTransformToPandaNode(gameObject.Transform, objectState.RootNode); if (gameObject.ParentId != 0) { const auto parentIterator = Impl_->ObjectStates.find(gameObject.ParentId); if (parentIterator != Impl_->ObjectStates.end() && !parentIterator->second.RootNode.is_empty()) { objectState.RootNode.reparent_to(parentIterator->second.RootNode); } else { objectState.RootNode.reparent_to(*sceneRootHandle); } } else { objectState.RootNode.reparent_to(*sceneRootHandle); } if (gameObject.MeshRenderer.has_value()) { const bool requiresMeshRebuild = objectState.MeshNode.is_empty() || objectState.MeshSource != gameObject.MeshRenderer->MeshSource || (gameObject.MeshRenderer->MeshSource == MetaCoreMeshSourceKind::Builtin && objectState.BuiltinMesh != gameObject.MeshRenderer->BuiltinMesh) || (gameObject.MeshRenderer->MeshSource == MetaCoreMeshSourceKind::Asset && (objectState.MeshAssetGuid != gameObject.MeshRenderer->MeshAssetGuid || objectState.SourceModelPath != gameObject.MeshRenderer->SourceModelPath)); if (requiresMeshRebuild && !objectState.MeshNode.is_empty()) { objectState.MeshNode.remove_node(); objectState.MeshNode = NodePath(); } if (requiresMeshRebuild) { if (gameObject.MeshRenderer->MeshSource == MetaCoreMeshSourceKind::Builtin) { objectState.MeshNode = MetaCoreCreateUnitCubeNode(objectState.RootNode); } else { objectState.MeshNode = MetaCoreTryLoadModelNode( *windowFrameworkHandle, objectState.RootNode, Impl_->ProjectRootPath, gameObject.MeshRenderer->SourceModelPath ); if (objectState.MeshNode.is_empty()) { objectState.MeshNode = MetaCoreCreateUnitCubeNode(objectState.RootNode); } } objectState.MeshSource = gameObject.MeshRenderer->MeshSource; objectState.BuiltinMesh = gameObject.MeshRenderer->BuiltinMesh; objectState.MeshAssetGuid = gameObject.MeshRenderer->MeshAssetGuid; objectState.SourceModelPath = gameObject.MeshRenderer->SourceModelPath; } if (!objectState.MeshNode.is_empty()) { objectState.MeshNode.set_material_off(1); objectState.MeshNode.set_light_off(1); objectState.MeshNode.set_two_sided(gameObject.MeshRenderer->DoubleSided); objectState.MeshNode.set_color( gameObject.MeshRenderer->BaseColor.r, gameObject.MeshRenderer->BaseColor.g, gameObject.MeshRenderer->BaseColor.b, 1.0F ); MetaCoreApplyBaseColorTexture( objectState.MeshNode, Impl_->ProjectRootPath, gameObject.MeshRenderer->BaseColorTexturePath ); MetaCoreApplyMetallicRoughnessTexture( objectState.MeshNode, Impl_->ProjectRootPath, gameObject.MeshRenderer->MetallicRoughnessTexturePath ); MetaCoreApplyNormalTexture( objectState.MeshNode, Impl_->ProjectRootPath, gameObject.MeshRenderer->NormalTexturePath ); MetaCoreApplyEmissionTexture( objectState.MeshNode, Impl_->ProjectRootPath, gameObject.MeshRenderer->EmissiveTexturePath ); MetaCoreApplyAoTexture( objectState.MeshNode, Impl_->ProjectRootPath, gameObject.MeshRenderer->AoTexturePath ); if (gameObject.MeshRenderer->MeshSource == MetaCoreMeshSourceKind::Asset) { MetaCoreApplySimplePbrPreview(objectState.MeshNode); MetaCoreApplySimplePbrMaterialInputs(objectState.MeshNode, *gameObject.MeshRenderer); auto* editorCameraHandle = static_cast(Impl_->RenderDevice->GetNativeEditorCameraHandle()); if (editorCameraHandle != nullptr) { MetaCoreApplySimplePbrViewInputs(objectState.MeshNode, *sceneRootHandle, *editorCameraHandle); } } else { objectState.MeshNode.set_shader_off(1); } if (gameObject.MeshRenderer->AlphaMode == MetaCoreMeshAlphaMode::Blend) { objectState.MeshNode.set_transparency(TransparencyAttrib::M_alpha); } else if (gameObject.MeshRenderer->AlphaMode == MetaCoreMeshAlphaMode::Mask) { objectState.MeshNode.set_transparency(TransparencyAttrib::M_binary); } else { objectState.MeshNode.clear_transparency(); } if (gameObject.MeshRenderer->Visible) { objectState.MeshNode.show(); } else { objectState.MeshNode.hide(); } if (gameObject.Id == Impl_->SelectedObjectId) { objectState.MeshNode.set_color_scale(1.18F, 1.02F, 0.72F, 1.0F); } else { objectState.MeshNode.clear_color_scale(); } } } else if (!objectState.MeshNode.is_empty()) { objectState.MeshNode.remove_node(); objectState.MeshNode = NodePath(); } if (gameObject.Light.has_value()) { if (objectState.LightNode.is_empty()) { PT(DirectionalLight) directionalLight = new DirectionalLight(gameObject.Name); objectState.LightNode = objectState.RootNode.attach_new_node(directionalLight); sceneRootHandle->set_light(objectState.LightNode); } auto* directionalLight = DCAST(DirectionalLight, objectState.LightNode.node()); if (directionalLight != nullptr) { directionalLight->set_color(LColor( gameObject.Light->Color.r * gameObject.Light->Intensity, gameObject.Light->Color.g * gameObject.Light->Intensity, gameObject.Light->Color.b * gameObject.Light->Intensity, 1.0F )); } } else if (!objectState.LightNode.is_empty()) { sceneRootHandle->clear_light(objectState.LightNode); objectState.LightNode.remove_node(); objectState.LightNode = NodePath(); } } for (auto iterator = Impl_->ObjectStates.begin(); iterator != Impl_->ObjectStates.end();) { if (!aliveObjectIds.contains(iterator->first)) { if (!iterator->second.RootNode.is_empty()) { iterator->second.RootNode.remove_node(); } iterator = Impl_->ObjectStates.erase(iterator); } else { ++iterator; } } } void MetaCorePandaSceneBridge::ApplySceneView(const MetaCoreSceneView& sceneView) { if (Impl_->RenderDevice == nullptr) { return; } auto* editorCameraHandle = static_cast(Impl_->RenderDevice->GetNativeEditorCameraHandle()); if (editorCameraHandle == nullptr || editorCameraHandle->is_empty()) { return; } Impl_->SelectedObjectId = sceneView.SelectedObjectId; editorCameraHandle->set_pos(MetaCoreToPandaPoint(sceneView.CameraPosition)); editorCameraHandle->look_at(MetaCoreToPandaPoint(sceneView.CameraTarget), MetaCoreToPandaVector(sceneView.CameraUp)); auto* cameraNode = DCAST(Camera, editorCameraHandle->node()); if (cameraNode != nullptr) { auto* perspectiveLens = DCAST(PerspectiveLens, cameraNode->get_lens()); if (perspectiveLens != nullptr) { const float aspect = std::max(0.001F, perspectiveLens->get_aspect_ratio()); const float vFovRad = glm::radians(sceneView.VerticalFieldOfViewDegrees); const float hFovDeg = glm::degrees(2.0F * std::atan(aspect * std::tan(vFovRad * 0.5F))); perspectiveLens->set_fov(LVecBase2f(std::max(0.001F, hFovDeg), std::max(0.001F, sceneView.VerticalFieldOfViewDegrees))); } } } bool MetaCorePandaSceneBridge::TryGetObjectWorldMatrix(MetaCoreId objectId, glm::mat4& worldMatrix) const { if (Impl_ == nullptr || Impl_->RenderDevice == nullptr) { return false; } auto* sceneRootHandle = static_cast(Impl_->RenderDevice->GetNativeSceneRootHandle()); if (sceneRootHandle == nullptr || sceneRootHandle->is_empty()) { return false; } const auto objectIterator = Impl_->ObjectStates.find(objectId); if (objectIterator == Impl_->ObjectStates.end() || objectIterator->second.RootNode.is_empty()) { return false; } worldMatrix = MetaCoreConvertPandaMatrixToGlm(objectIterator->second.RootNode.get_mat(*sceneRootHandle)); return true; } } // namespace MetaCore