MetaCore/Source/MetaCoreRender/Private/MetaCorePandaSceneBridge.cpp

318 lines
12 KiB
C++

#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 "lineSegs.h"
#include "loader.h"
#include "nodePath.h"
#include "perspectiveLens.h"
#include "pandaNode.h"
#include "windowFramework.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <memory>
#include <unordered_map>
#include <unordered_set>
namespace MetaCore {
namespace {
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);
}
NodePath MetaCoreCreateGridNode(const NodePath& parentNode) {
LineSegs gridBuilder("MetaCoreGrid");
gridBuilder.set_thickness(1.0F);
constexpr int gridHalfExtent = 10;
for (int lineIndex = -gridHalfExtent; lineIndex <= gridHalfExtent; ++lineIndex) {
const bool isAxisLine = (lineIndex == 0);
gridBuilder.set_color(isAxisLine ? 0.42F : 0.30F, isAxisLine ? 0.45F : 0.33F, isAxisLine ? 0.50F : 0.36F, 1.0F);
gridBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(lineIndex), 0.0F, static_cast<float>(-gridHalfExtent))));
gridBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(lineIndex), 0.0F, static_cast<float>(gridHalfExtent))));
gridBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(-gridHalfExtent), 0.0F, static_cast<float>(lineIndex))));
gridBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(static_cast<float>(gridHalfExtent), 0.0F, static_cast<float>(lineIndex))));
}
return parentNode.attach_new_node(gridBuilder.create());
}
NodePath MetaCoreCreateAxisNode(const NodePath& parentNode) {
LineSegs axisBuilder("MetaCoreAxis");
axisBuilder.set_thickness(3.0F);
axisBuilder.set_color(0.92F, 0.26F, 0.26F, 1.0F);
axisBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F)));
axisBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(2.0F, 0.0F, 0.0F)));
axisBuilder.set_color(0.28F, 0.86F, 0.36F, 1.0F);
axisBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F)));
axisBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 2.0F, 0.0F)));
axisBuilder.set_color(0.32F, 0.56F, 0.96F, 1.0F);
axisBuilder.move_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 0.0F)));
axisBuilder.draw_to(MetaCoreToPandaPoint(glm::vec3(0.0F, 0.0F, 2.0F)));
return parentNode.attach_new_node(axisBuilder.create());
}
} // 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<MetaCoreId, MetaCorePandaObjectState> ObjectStates{};
};
MetaCorePandaSceneBridge::MetaCorePandaSceneBridge()
: Impl_(std::make_unique<MetaCorePandaSceneBridgeImpl>()) {
}
MetaCorePandaSceneBridge::~MetaCorePandaSceneBridge() {
Shutdown();
}
bool MetaCorePandaSceneBridge::Initialize(MetaCoreRenderDevice& renderDevice) {
Shutdown();
auto* windowFrameworkHandle = static_cast<WindowFramework*>(renderDevice.GetNativeWindowFrameworkHandle());
auto* sceneRootHandle = static_cast<NodePath*>(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<WindowFramework*>(Impl_->RenderDevice->GetNativeWindowFrameworkHandle());
auto* sceneRootHandle = static_cast<NodePath*>(Impl_->RenderDevice->GetNativeSceneRootHandle());
if (windowFrameworkHandle == nullptr || sceneRootHandle == nullptr || sceneRootHandle->is_empty()) {
return;
}
std::unordered_set<MetaCoreId> 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);
objectState.RootNode.set_mat(MetaCoreConvertTransformToPanda(gameObject.Transform));
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 = windowFrameworkHandle->load_model(objectState.RootNode, Filename("models/box"));
}
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<NodePath*>(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) {
perspectiveLens->set_fov(sceneView.VerticalFieldOfViewDegrees);
}
}
}
} // namespace MetaCore