291 lines
13 KiB
C++
291 lines
13 KiB
C++
#include "MetaCorePlatform/MetaCoreWindow.h"
|
|
#include "MetaCoreFoundation/MetaCoreGeneratedReflection.h"
|
|
#include "MetaCoreFoundation/MetaCoreProject.h"
|
|
#include "MetaCoreRender/MetaCoreEditorViewportRenderer.h"
|
|
#include "MetaCoreRender/MetaCoreRenderDevice.h"
|
|
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
|
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataDispatcher.h"
|
|
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataProject.h"
|
|
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataSource.h"
|
|
#include "MetaCoreScene/MetaCoreScenePackage.h"
|
|
#include "MetaCoreScene/MetaCoreScene.h"
|
|
|
|
#include <filesystem>
|
|
#include <iostream>
|
|
#include <memory>
|
|
|
|
namespace {
|
|
|
|
MetaCore::MetaCoreSceneView MetaCoreBuildPlayerSceneView() {
|
|
MetaCore::MetaCoreSceneView sceneView;
|
|
sceneView.CameraPosition = {0.0F, 2.5F, 6.5F};
|
|
sceneView.CameraTarget = {0.0F, 0.7F, 0.0F};
|
|
sceneView.CameraUp = {0.0F, 1.0F, 0.0F};
|
|
sceneView.VerticalFieldOfViewDegrees = 60.0F;
|
|
return sceneView;
|
|
}
|
|
|
|
[[nodiscard]] MetaCore::MetaCoreRuntimeProjectDocument MetaCoreBuildDefaultRuntimeProjectDocument() {
|
|
MetaCore::MetaCoreRuntimeProjectDocument document;
|
|
document.StartupScenePath = std::filesystem::path("Scenes") / "Main.mcscene";
|
|
document.DataSourcesPath = std::filesystem::path("Runtime") / "DataSources.mcruntime";
|
|
document.BindingsPath = std::filesystem::path("Runtime") / "Bindings.mcruntime";
|
|
document.DiagnosticsPath = std::filesystem::path("Runtime") / "Diagnostics.mcruntimestate";
|
|
return document;
|
|
}
|
|
|
|
[[nodiscard]] MetaCore::MetaCoreRuntimeDataSourcesDocument MetaCoreBuildDefaultRuntimeDataSourcesDocument() {
|
|
MetaCore::MetaCoreRuntimeDataSourcesDocument document;
|
|
document.Sources.push_back(MetaCore::MetaCoreDataSourceDefinition{
|
|
"replay-source",
|
|
"file_replay",
|
|
"Replay Source",
|
|
{MetaCore::MetaCoreDataSourceSetting{"file_path", "TestProject/Runtime/RuntimeReplay.mcstream"}},
|
|
true,
|
|
1000
|
|
});
|
|
document.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"cube.position",
|
|
"replay-source",
|
|
"cube.position",
|
|
MetaCore::MetaCoreRuntimeValueType::Vec3
|
|
});
|
|
document.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"cube.visible",
|
|
"replay-source",
|
|
"cube.visible",
|
|
MetaCore::MetaCoreRuntimeValueType::Bool
|
|
});
|
|
document.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"cube.base_color",
|
|
"replay-source",
|
|
"cube.base_color",
|
|
MetaCore::MetaCoreRuntimeValueType::Vec3
|
|
});
|
|
document.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"valve.visible",
|
|
"replay-source",
|
|
"valve.visible",
|
|
MetaCore::MetaCoreRuntimeValueType::Bool
|
|
});
|
|
document.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"tank.base_color",
|
|
"replay-source",
|
|
"tank.base_color",
|
|
MetaCore::MetaCoreRuntimeValueType::Vec3
|
|
});
|
|
document.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"alarm.intensity",
|
|
"replay-source",
|
|
"alarm.intensity",
|
|
MetaCore::MetaCoreRuntimeValueType::Double
|
|
});
|
|
return document;
|
|
}
|
|
|
|
[[nodiscard]] MetaCore::MetaCoreRuntimeBindingsDocument MetaCoreBuildDefaultRuntimeBindingsDocument() {
|
|
MetaCore::MetaCoreRuntimeBindingsDocument document;
|
|
document.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.cube.position",
|
|
"cube.position",
|
|
3,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
document.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.cube.visible",
|
|
"cube.visible",
|
|
3,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
document.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.cube.base_color",
|
|
"cube.base_color",
|
|
3,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
document.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.valve.visible",
|
|
"valve.visible",
|
|
4,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
document.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.tank.base_color",
|
|
"tank.base_color",
|
|
5,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
document.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.alarm.intensity",
|
|
"alarm.intensity",
|
|
6,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::LightIntensity,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
return document;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char* argv[]) {
|
|
if (argc > 0) {
|
|
std::filesystem::current_path(std::filesystem::path(argv[0]).parent_path());
|
|
}
|
|
|
|
MetaCore::MetaCoreWindow window;
|
|
if (!window.Initialize(1280, 720, "MetaCore Player")) {
|
|
std::cerr << "MetaCorePlayer: window initialize failed\n";
|
|
return 1;
|
|
}
|
|
|
|
MetaCore::MetaCoreRenderDevice renderDevice;
|
|
if (!renderDevice.Initialize(window)) {
|
|
std::cerr << "MetaCorePlayer: render device initialize failed\n";
|
|
return 1;
|
|
}
|
|
|
|
MetaCore::MetaCoreEditorViewportRenderer viewportRenderer;
|
|
if (!viewportRenderer.Initialize(renderDevice)) {
|
|
std::cerr << "MetaCorePlayer: viewport renderer initialize failed\n";
|
|
return 1;
|
|
}
|
|
|
|
const auto projectRoot = MetaCore::MetaCoreDiscoverProjectRoot();
|
|
if (!projectRoot.has_value()) {
|
|
std::cerr << "MetaCorePlayer: failed to discover project root\n";
|
|
return 1;
|
|
}
|
|
const std::filesystem::path projectPath = MetaCore::MetaCoreGetProjectFilePath(*projectRoot);
|
|
MetaCore::MetaCoreTypeRegistry typeRegistry;
|
|
MetaCore::MetaCoreRegisterRuntimeDataGeneratedTypes(typeRegistry);
|
|
const auto loadedRuntimeProjectDocument = MetaCore::MetaCoreReadRuntimeProjectDocument(
|
|
*projectRoot / "Runtime" / "ProjectRuntime.mcruntimecfg",
|
|
typeRegistry
|
|
);
|
|
const auto runtimeProjectDocument = loadedRuntimeProjectDocument.value_or(MetaCoreBuildDefaultRuntimeProjectDocument());
|
|
|
|
std::optional<MetaCore::MetaCoreSceneDocument> startupSceneDocument;
|
|
if (!runtimeProjectDocument.StartupScenePath.empty()) {
|
|
startupSceneDocument = MetaCore::MetaCoreReadScenePackage(*projectRoot / runtimeProjectDocument.StartupScenePath);
|
|
}
|
|
if (!startupSceneDocument.has_value()) {
|
|
startupSceneDocument = MetaCore::MetaCoreLoadStartupSceneDocument(projectPath);
|
|
}
|
|
MetaCore::MetaCoreScene scene;
|
|
if (startupSceneDocument.has_value()) {
|
|
MetaCore::MetaCoreSceneSnapshot snapshot;
|
|
snapshot.GameObjects = startupSceneDocument->GameObjects;
|
|
scene.RestoreSnapshot(snapshot);
|
|
std::cout << "MetaCorePlayer: loaded startup scene from project "
|
|
<< projectPath.string() << '\n';
|
|
} else {
|
|
scene = MetaCore::MetaCoreCreateDefaultScene();
|
|
std::cout << "MetaCorePlayer: startup scene unavailable, using built-in default scene\n";
|
|
}
|
|
MetaCore::MetaCoreRuntimeDataDispatcher runtimeDataDispatcher(scene);
|
|
|
|
const auto sourcesPath = (*projectRoot / runtimeProjectDocument.DataSourcesPath).lexically_normal();
|
|
const auto bindingsPath = (*projectRoot / runtimeProjectDocument.BindingsPath).lexically_normal();
|
|
const auto diagnosticsPath = (*projectRoot / runtimeProjectDocument.DiagnosticsPath).lexically_normal();
|
|
const auto loadedSourcesDocument = MetaCore::MetaCoreReadRuntimeDataSourcesDocument(sourcesPath, typeRegistry);
|
|
const auto loadedBindingsDocument = MetaCore::MetaCoreReadRuntimeBindingsDocument(bindingsPath, typeRegistry);
|
|
const auto sourcesDocument = loadedSourcesDocument.value_or(MetaCoreBuildDefaultRuntimeDataSourcesDocument());
|
|
const auto bindingsDocument = loadedBindingsDocument.value_or(MetaCoreBuildDefaultRuntimeBindingsDocument());
|
|
|
|
if (loadedSourcesDocument.has_value() && loadedBindingsDocument.has_value()) {
|
|
std::cout << "MetaCorePlayer: loaded runtime config from binary .mcruntime documents\n";
|
|
} else {
|
|
const bool sourcesExists = std::filesystem::exists(sourcesPath);
|
|
const bool bindingsExists = std::filesystem::exists(bindingsPath);
|
|
if ((sourcesExists && !loadedSourcesDocument.has_value()) ||
|
|
(bindingsExists && !loadedBindingsDocument.has_value())) {
|
|
std::cerr << "MetaCorePlayer: runtime config exists but is unreadable or corrupted, using built-in fallback config\n";
|
|
} else {
|
|
std::cout << "MetaCorePlayer: runtime config missing, using built-in fallback config\n";
|
|
}
|
|
}
|
|
|
|
runtimeDataDispatcher.SetDataPointDefinitions(sourcesDocument.DataPoints);
|
|
runtimeDataDispatcher.SetBindingDefinitions(bindingsDocument.Bindings);
|
|
|
|
std::unique_ptr<MetaCore::MetaCoreIRuntimeDataSourceAdapter> runtimeAdapter;
|
|
if (!sourcesDocument.Sources.empty()) {
|
|
runtimeAdapter = MetaCore::MetaCoreCreateRuntimeDataSourceAdapter(sourcesDocument.Sources.front().AdapterType);
|
|
if (runtimeAdapter == nullptr) {
|
|
std::cerr << "MetaCorePlayer: unsupported adapter type "
|
|
<< sourcesDocument.Sources.front().AdapterType << '\n';
|
|
return 1;
|
|
}
|
|
if (!runtimeAdapter->Configure(sourcesDocument.Sources.front())) {
|
|
std::cerr << "MetaCorePlayer: failed to configure runtime adapter error="
|
|
<< runtimeAdapter->GetStatus().LastError << '\n';
|
|
return 1;
|
|
}
|
|
if (!runtimeAdapter->Connect()) {
|
|
std::cerr << "MetaCorePlayer: failed to connect runtime adapter error="
|
|
<< runtimeAdapter->GetStatus().LastError << '\n';
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
MetaCore::MetaCoreRuntimeDataSourceState lastReportedSourceState =
|
|
runtimeAdapter != nullptr
|
|
? runtimeAdapter->GetStatus().State
|
|
: MetaCore::MetaCoreRuntimeDataSourceState::Disconnected;
|
|
|
|
std::uint64_t diagnosticsWriteFrame = 0;
|
|
while (!window.ShouldClose()) {
|
|
window.BeginFrame();
|
|
const auto [windowWidth, windowHeight] = window.GetWindowSize();
|
|
viewportRenderer.SetViewportRect(MetaCore::MetaCoreViewportRect{
|
|
0.0F,
|
|
0.0F,
|
|
static_cast<float>(windowWidth),
|
|
static_cast<float>(windowHeight)
|
|
});
|
|
if (runtimeAdapter != nullptr) {
|
|
runtimeAdapter->Tick(1.0 / 60.0);
|
|
runtimeDataDispatcher.ApplyUpdates(runtimeAdapter->PollUpdates());
|
|
runtimeDataDispatcher.TickStaleness(runtimeAdapter->GetStatus().LastUpdateAt);
|
|
}
|
|
const auto diagnostics = runtimeDataDispatcher.BuildDiagnosticsSnapshot(
|
|
runtimeAdapter != nullptr
|
|
? std::vector<MetaCore::MetaCoreRuntimeDataSourceStatus>{runtimeAdapter->GetStatus()}
|
|
: std::vector<MetaCore::MetaCoreRuntimeDataSourceStatus>{}
|
|
);
|
|
if ((diagnosticsWriteFrame % 15ULL) == 0ULL) {
|
|
(void)MetaCore::MetaCoreWriteRuntimeDiagnosticsSnapshot(diagnosticsPath, diagnostics, typeRegistry);
|
|
}
|
|
++diagnosticsWriteFrame;
|
|
if (runtimeAdapter != nullptr && runtimeAdapter->GetStatus().State != lastReportedSourceState) {
|
|
std::cout << "MetaCorePlayer: data source state changed to "
|
|
<< static_cast<int>(runtimeAdapter->GetStatus().State)
|
|
<< " error=" << runtimeAdapter->GetStatus().LastError << '\n';
|
|
lastReportedSourceState = runtimeAdapter->GetStatus().State;
|
|
}
|
|
if (diagnostics.HasFaults) {
|
|
for (const MetaCore::MetaCoreRuntimeBindingStatus& bindingStatus : diagnostics.BindingStatuses) {
|
|
if (!bindingStatus.Healthy || bindingStatus.Stale) {
|
|
std::cout << "MetaCorePlayer: binding issue id=" << bindingStatus.BindingId
|
|
<< " stale=" << (bindingStatus.Stale ? "true" : "false")
|
|
<< " error=" << bindingStatus.LastError << '\n';
|
|
}
|
|
}
|
|
}
|
|
viewportRenderer.RenderSceneToViewport(scene, MetaCoreBuildPlayerSceneView());
|
|
renderDevice.RenderFrame();
|
|
renderDevice.PresentFrame();
|
|
window.EndFrame();
|
|
}
|
|
|
|
viewportRenderer.Shutdown();
|
|
renderDevice.Shutdown();
|
|
window.Shutdown();
|
|
return 0;
|
|
}
|