#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 "lineSegs.h" #include "loader.h" #include "nodePath.h" #include "perspectiveLens.h" #include "pandaNode.h" #include "windowFramework.h" #define GLM_ENABLE_EXPERIMENTAL #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 MetaCoreCreateAxisNode(const NodePath& parentNode) { MetaCoreTrace("create_axis: begin"); try { PT(GeomVertexData) vertexData = new GeomVertexData( "MetaCoreAxis", GeomVertexFormat::get_v3c4(), Geom::UH_static ); GeomVertexWriter vertexWriter(vertexData, "vertex"); GeomVertexWriter colorWriter(vertexData, "color"); // X Axis (Red) vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F))); colorWriter.add_data4(LColor(0.92F, 0.26F, 0.26F, 1.0F)); vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(2.0F, 0.0F, 0.0F))); colorWriter.add_data4(LColor(0.92F, 0.26F, 0.26F, 1.0F)); // Y Axis (Green) vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F))); colorWriter.add_data4(LColor(0.28F, 0.86F, 0.36F, 1.0F)); vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(0.0F, 2.0F, 0.0F))); colorWriter.add_data4(LColor(0.28F, 0.86F, 0.36F, 1.0F)); // Z Axis (Blue) vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F))); colorWriter.add_data4(LColor(0.32F, 0.56F, 0.96F, 1.0F)); vertexWriter.add_data3(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 2.0F))); colorWriter.add_data4(LColor(0.32F, 0.56F, 0.96F, 1.0F)); PT(GeomLines) lines = new GeomLines(Geom::UH_static); lines->add_vertices(0, 1); lines->add_vertices(2, 3); lines->add_vertices(4, 5); PT(Geom) geom = new Geom(vertexData); geom->add_primitive(lines.p()); PT(GeomNode) geomNode = new GeomNode("MetaCoreAxisNode"); 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); } 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{}; }; MetaCoreRenderDevice* RenderDevice = nullptr; NodePath GridNode{}; NodePath AxisNode{}; NodePath AmbientLightNode{}; 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_->GridNode = MetaCoreCreateGridNode(*sceneRootHandle); Impl_->AxisNode = MetaCoreCreateAxisNode(*sceneRootHandle); Impl_->GridNode.set_light_off(1); Impl_->AxisNode.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_->AxisNode.is_empty()) { Impl_->AxisNode.remove_node(); Impl_->AxisNode = NodePath(); } if (!Impl_->GridNode.is_empty()) { Impl_->GridNode.remove_node(); Impl_->GridNode = NodePath(); } Impl_->RenderDevice = nullptr; 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()) { if (objectState.MeshNode.is_empty()) { objectState.MeshNode = MetaCoreCreateUnitCubeNode(objectState.RootNode); } if (!objectState.MeshNode.is_empty()) { objectState.MeshNode.set_texture_off(1); objectState.MeshNode.set_material_off(1); objectState.MeshNode.set_shader_off(1); objectState.MeshNode.set_light_off(1); objectState.MeshNode.set_two_sided(true); objectState.MeshNode.set_color( gameObject.MeshRenderer->BaseColor.r, gameObject.MeshRenderer->BaseColor.g, gameObject.MeshRenderer->BaseColor.b, 1.0F ); 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