366 lines
17 KiB
C++
366 lines
17 KiB
C++
#include "MetaCoreFoundation/MetaCoreGeneratedReflection.h"
|
|
#include "MetaCoreFoundation/MetaCoreProject.h"
|
|
#include "MetaCoreRuntimeData/MetaCoreRuntimeDataProject.h"
|
|
#include "MetaCoreScene/MetaCoreScene.h"
|
|
#include "MetaCoreScene/MetaCoreSceneSerializer.h"
|
|
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
namespace {
|
|
|
|
[[nodiscard]] MetaCore::MetaCoreSceneDocument MetaCoreBuildPilotSceneDocument() {
|
|
MetaCore::MetaCoreSceneDocument document;
|
|
document.Name = "Main";
|
|
|
|
MetaCore::MetaCoreScene scene;
|
|
MetaCore::MetaCoreGameObject mainCamera = scene.CreateGameObjectWithId(1, "Main Camera");
|
|
mainCamera.AddComponent<MetaCore::MetaCoreCameraComponent>().IsPrimary = true;
|
|
mainCamera.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 2.4F, 7.0F);
|
|
mainCamera.GetComponent<MetaCore::MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-16.0F, 0.0F, 0.0F);
|
|
|
|
MetaCore::MetaCoreGameObject keyLight = scene.CreateGameObjectWithId(2, "Directional Light");
|
|
keyLight.AddComponent<MetaCore::MetaCoreLightComponent>();
|
|
keyLight.GetComponent<MetaCore::MetaCoreTransformComponent>().RotationEulerDegrees = glm::vec3(-45.0F, 30.0F, 0.0F);
|
|
|
|
MetaCore::MetaCoreGameObject cube = scene.CreateGameObjectWithId(3, "Runtime Cube");
|
|
cube.AddComponent<MetaCore::MetaCoreMeshRendererComponent>().BaseColor = glm::vec3(0.4F, 0.6F, 0.9F);
|
|
cube.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 0.5F, 0.0F);
|
|
|
|
MetaCore::MetaCoreGameObject valve = scene.CreateGameObjectWithId(4, "Runtime Valve");
|
|
valve.AddComponent<MetaCore::MetaCoreMeshRendererComponent>().BaseColor = glm::vec3(0.9F, 0.55F, 0.25F);
|
|
valve.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(-2.0F, 0.5F, 0.0F);
|
|
valve.GetComponent<MetaCore::MetaCoreTransformComponent>().Scale = glm::vec3(0.65F, 0.65F, 0.65F);
|
|
|
|
MetaCore::MetaCoreGameObject tank = scene.CreateGameObjectWithId(5, "Runtime Tank");
|
|
tank.AddComponent<MetaCore::MetaCoreMeshRendererComponent>().BaseColor = glm::vec3(0.35F, 0.65F, 0.90F);
|
|
tank.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(2.0F, 0.5F, 0.0F);
|
|
tank.GetComponent<MetaCore::MetaCoreTransformComponent>().Scale = glm::vec3(1.25F, 1.25F, 1.25F);
|
|
|
|
MetaCore::MetaCoreGameObject alarm = scene.CreateGameObjectWithId(6, "Runtime Alarm Light");
|
|
alarm.AddComponent<MetaCore::MetaCoreLightComponent>().Intensity = 0.0F;
|
|
alarm.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(0.0F, 3.0F, 0.0F);
|
|
|
|
MetaCore::MetaCoreIdGenerator::EnsureAbove(6);
|
|
document.GameObjects = scene.CaptureSnapshot().GameObjects;
|
|
return document;
|
|
}
|
|
|
|
[[nodiscard]] MetaCore::MetaCoreUiDocument MetaCoreBuildPilotUiDocument() {
|
|
MetaCore::MetaCoreUiDocument document;
|
|
document.Name = "RuntimeDataHud";
|
|
document.ReferenceWidth = 1280;
|
|
document.ReferenceHeight = 720;
|
|
document.RootNodeIds = {"runtime.root"};
|
|
|
|
MetaCore::MetaCoreUiNodeDocument root;
|
|
root.Id = "runtime.root";
|
|
root.Name = "Runtime Root";
|
|
root.Type = MetaCore::MetaCoreUiNodeType::Panel;
|
|
root.Children = {"runtime.status"};
|
|
root.RectTransform.AnchorMin = glm::vec3(0.0F, 0.0F, 0.0F);
|
|
root.RectTransform.AnchorMax = glm::vec3(1.0F, 1.0F, 0.0F);
|
|
root.RectTransform.Size = glm::vec3(0.0F, 0.0F, 0.0F);
|
|
root.Style.BackgroundColor = glm::vec3(0.0F, 0.0F, 0.0F);
|
|
|
|
MetaCore::MetaCoreUiNodeDocument status;
|
|
status.Id = "runtime.status";
|
|
status.Name = "Runtime Status";
|
|
status.Type = MetaCore::MetaCoreUiNodeType::Text;
|
|
status.ParentId = "runtime.root";
|
|
status.Text = "RuntimeData waiting";
|
|
status.RectTransform.Position = glm::vec3(24.0F, 24.0F, 0.0F);
|
|
status.RectTransform.Size = glm::vec3(560.0F, 40.0F, 0.0F);
|
|
status.Style.FontSize = 18.0F;
|
|
status.Style.TextColor = glm::vec3(0.88F, 0.94F, 1.0F);
|
|
|
|
document.Nodes = {root, status};
|
|
return document;
|
|
}
|
|
|
|
[[nodiscard]] const MetaCore::MetaCoreGameObjectData* MetaCoreFindSceneObject(
|
|
const MetaCore::MetaCoreSceneDocument& sceneDocument,
|
|
MetaCore::MetaCoreId objectId
|
|
) {
|
|
const auto iterator = std::find_if(
|
|
sceneDocument.GameObjects.begin(),
|
|
sceneDocument.GameObjects.end(),
|
|
[objectId](const MetaCore::MetaCoreGameObjectData& objectData) {
|
|
return objectData.Id == objectId;
|
|
}
|
|
);
|
|
return iterator == sceneDocument.GameObjects.end() ? nullptr : &(*iterator);
|
|
}
|
|
|
|
[[nodiscard]] bool MetaCoreValidatePilotRuntimeDocuments(
|
|
const MetaCore::MetaCoreSceneDocument& sceneDocument,
|
|
const MetaCore::MetaCoreUiDocument& uiDocument,
|
|
const MetaCore::MetaCoreRuntimeBindingsDocument& bindings,
|
|
std::string& error
|
|
) {
|
|
for (const MetaCore::MetaCoreSceneBindingDefinition& binding : bindings.Bindings) {
|
|
const MetaCore::MetaCoreGameObjectData* targetObject =
|
|
MetaCoreFindSceneObject(sceneDocument, binding.TargetObjectId);
|
|
if (targetObject == nullptr) {
|
|
error = "Scene binding target object does not exist: " + binding.BindingId;
|
|
return false;
|
|
}
|
|
|
|
const bool targetHasRequiredComponent =
|
|
binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition ||
|
|
((binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible ||
|
|
binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor) &&
|
|
targetObject->MeshRenderer.has_value()) ||
|
|
((binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::LightIntensity ||
|
|
binding.Target == MetaCore::MetaCoreRuntimeBindingTarget::LightColor) &&
|
|
targetObject->Light.has_value());
|
|
if (!targetHasRequiredComponent) {
|
|
error = "Scene binding target object is missing the required component: " + binding.BindingId;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (const MetaCore::MetaCoreUiBindingDefinition& binding : bindings.UiBindings) {
|
|
if (binding.Target != MetaCore::MetaCoreRuntimeUiBindingTarget::Text) {
|
|
continue;
|
|
}
|
|
|
|
const auto nodeIterator = std::find_if(
|
|
uiDocument.Nodes.begin(),
|
|
uiDocument.Nodes.end(),
|
|
[&](const MetaCore::MetaCoreUiNodeDocument& node) {
|
|
return node.Id == binding.TargetNodeId;
|
|
}
|
|
);
|
|
if (nodeIterator == uiDocument.Nodes.end() || nodeIterator->Type != MetaCore::MetaCoreUiNodeType::Text) {
|
|
error = "UI binding target Text node does not exist: " + binding.BindingId;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char* argv[]) {
|
|
if (argc < 2) {
|
|
std::cerr << "Usage: MetaCoreRuntimeConfigTool <runtime-directory> [--tcp]\n";
|
|
return 1;
|
|
}
|
|
|
|
const bool generateTcpConfig = argc >= 3 && std::string_view(argv[2]) == "--tcp";
|
|
|
|
MetaCore::MetaCoreTypeRegistry registry;
|
|
MetaCore::MetaCoreRegisterFoundationGeneratedTypes(registry);
|
|
MetaCore::MetaCoreRegisterSceneGeneratedTypes(registry);
|
|
MetaCore::MetaCoreRegisterRuntimeDataGeneratedTypes(registry);
|
|
|
|
const std::filesystem::path runtimeDirectory = std::filesystem::absolute(argv[1]).lexically_normal();
|
|
const std::filesystem::path projectRoot = runtimeDirectory.parent_path();
|
|
const std::filesystem::path runtimeDirectoryRelative =
|
|
MetaCore::MetaCoreBuildRuntimeDirectoryRelativePath(projectRoot, runtimeDirectory);
|
|
const std::filesystem::path sceneRelativePath = std::filesystem::path("Scenes") / "Main.mcscene.json";
|
|
const std::filesystem::path uiRelativePath = std::filesystem::path("Assets") / "UI" / "Hud.mcui.json";
|
|
|
|
MetaCore::MetaCoreRuntimeDataSourcesDocument sources;
|
|
sources.Sources.push_back(MetaCore::MetaCoreDataSourceDefinition{
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
generateTcpConfig ? "tcp" : "file_replay",
|
|
generateTcpConfig ? "Tcp Source" : "Replay Source",
|
|
generateTcpConfig
|
|
? std::vector<MetaCore::MetaCoreDataSourceSetting>{
|
|
MetaCore::MetaCoreDataSourceSetting{"host", "127.0.0.1"},
|
|
MetaCore::MetaCoreDataSourceSetting{"port", "7001"}
|
|
}
|
|
: std::vector<MetaCore::MetaCoreDataSourceSetting>{
|
|
// 动态计算相对于项目根目录的播放流文件路径,替代原先硬编码的 TestProject
|
|
MetaCore::MetaCoreDataSourceSetting{"file_path", (runtimeDirectoryRelative / "RuntimeReplay.mcstream").generic_string()}
|
|
},
|
|
true,
|
|
1000
|
|
});
|
|
sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"cube.position",
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
"cube.position",
|
|
MetaCore::MetaCoreRuntimeValueType::Vec3
|
|
});
|
|
sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"cube.visible",
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
"cube.visible",
|
|
MetaCore::MetaCoreRuntimeValueType::Bool
|
|
});
|
|
sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"cube.base_color",
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
"cube.base_color",
|
|
MetaCore::MetaCoreRuntimeValueType::Vec3
|
|
});
|
|
sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"valve.visible",
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
"valve.visible",
|
|
MetaCore::MetaCoreRuntimeValueType::Bool
|
|
});
|
|
sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"tank.base_color",
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
"tank.base_color",
|
|
MetaCore::MetaCoreRuntimeValueType::Vec3
|
|
});
|
|
sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"alarm.intensity",
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
"alarm.intensity",
|
|
MetaCore::MetaCoreRuntimeValueType::Double
|
|
});
|
|
sources.DataPoints.push_back(MetaCore::MetaCoreDataPointDefinition{
|
|
"runtime.status",
|
|
generateTcpConfig ? "tcp-source" : "replay-source",
|
|
"runtime.status",
|
|
MetaCore::MetaCoreRuntimeValueType::String
|
|
});
|
|
|
|
MetaCore::MetaCoreRuntimeBindingsDocument bindings;
|
|
bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.cube.position",
|
|
"cube.position",
|
|
3,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::TransformPosition,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.cube.visible",
|
|
"cube.visible",
|
|
3,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.cube.base_color",
|
|
"cube.base_color",
|
|
3,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.valve.visible",
|
|
"valve.visible",
|
|
4,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererVisible,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.tank.base_color",
|
|
"tank.base_color",
|
|
5,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::MeshRendererBaseColor,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
bindings.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
|
|
"binding.alarm.intensity",
|
|
"alarm.intensity",
|
|
6,
|
|
MetaCore::MetaCoreRuntimeBindingTarget::LightIntensity,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
bindings.UiBindings.push_back(MetaCore::MetaCoreUiBindingDefinition{
|
|
"binding.runtime.status",
|
|
"runtime.status",
|
|
"runtime.status",
|
|
MetaCore::MetaCoreRuntimeUiBindingTarget::Text,
|
|
MetaCore::MetaCoreRuntimeMissingDataPolicy::KeepLastValue
|
|
});
|
|
|
|
const MetaCore::MetaCoreSceneDocument sceneDocument = MetaCoreBuildPilotSceneDocument();
|
|
const MetaCore::MetaCoreUiDocument uiDocument = MetaCoreBuildPilotUiDocument();
|
|
std::string validationError;
|
|
if (!MetaCoreValidatePilotRuntimeDocuments(sceneDocument, uiDocument, bindings, validationError)) {
|
|
std::cerr << "Generated runtime config is invalid: " << validationError << '\n';
|
|
return 1;
|
|
}
|
|
|
|
const std::filesystem::path scenePath = projectRoot / sceneRelativePath;
|
|
const std::filesystem::path uiPath = projectRoot / uiRelativePath;
|
|
std::filesystem::create_directories(runtimeDirectory);
|
|
if (!MetaCore::MetaCoreWriteRuntimeDataSourcesDocument(runtimeDirectory / "DataSources.mcruntime", sources, registry)) {
|
|
std::cerr << "Failed to write DataSources.mcruntime\n";
|
|
return 1;
|
|
}
|
|
if (!MetaCore::MetaCoreWriteRuntimeBindingsDocument(runtimeDirectory / "Bindings.mcruntime", bindings, registry)) {
|
|
std::cerr << "Failed to write Bindings.mcruntime\n";
|
|
return 1;
|
|
}
|
|
MetaCore::MetaCoreRuntimeProjectDocument runtimeProjectDocument =
|
|
MetaCore::MetaCoreBuildDefaultRuntimeProjectDocument(runtimeDirectoryRelative);
|
|
runtimeProjectDocument.StartupScenePath = sceneRelativePath;
|
|
runtimeProjectDocument.StartupUiPath = uiRelativePath;
|
|
if (!MetaCore::MetaCoreWriteRuntimeProjectDocument(runtimeDirectory / "ProjectRuntime.mcruntimecfg", runtimeProjectDocument, registry)) {
|
|
std::cerr << "Failed to write ProjectRuntime.mcruntimecfg\n";
|
|
return 1;
|
|
}
|
|
|
|
MetaCore::MetaCoreProjectFileDocument projectDocument;
|
|
projectDocument.Name = generateTcpConfig ? "MetaCoreTcpRuntimeDataPilot" : "MetaCoreRuntimeDataPilot";
|
|
projectDocument.RuntimeDirectory = runtimeDirectoryRelative;
|
|
projectDocument.UiDirectory = std::filesystem::path("Assets") / "UI";
|
|
projectDocument.BuildDirectory = std::filesystem::path("Build");
|
|
projectDocument.StartupScenePath = sceneRelativePath;
|
|
projectDocument.ScenePaths = {sceneRelativePath};
|
|
if (!MetaCore::MetaCoreWriteProjectFile(MetaCore::MetaCoreGetProjectFilePath(projectRoot), projectDocument)) {
|
|
std::cerr << "Failed to write MetaCore.project.json\n";
|
|
return 1;
|
|
}
|
|
|
|
std::filesystem::create_directories(scenePath.parent_path());
|
|
if (!MetaCore::MetaCoreSceneSerializer::SaveSceneToJson(scenePath, sceneDocument, registry)) {
|
|
std::cerr << "Failed to write Main.mcscene.json\n";
|
|
return 1;
|
|
}
|
|
std::filesystem::create_directories(uiPath.parent_path());
|
|
if (!MetaCore::MetaCoreSceneSerializer::SaveUiToJson(uiPath, uiDocument, registry)) {
|
|
std::cerr << "Failed to write Hud.mcui.json\n";
|
|
return 1;
|
|
}
|
|
|
|
if (!generateTcpConfig) {
|
|
std::ofstream replayFile(runtimeDirectory / "RuntimeReplay.mcstream", std::ios::trunc);
|
|
if (!replayFile) {
|
|
std::cerr << "Failed to write RuntimeReplay.mcstream\n";
|
|
return 1;
|
|
}
|
|
replayFile
|
|
<< "# emit_after_seconds data_point_id value_type payload\n"
|
|
<< "0.00 cube.position vec3 0.0 0.5 0.0\n"
|
|
<< "0.00 cube.visible bool true\n"
|
|
<< "0.00 cube.base_color vec3 0.4 0.6 0.9\n"
|
|
<< "0.00 valve.visible bool true\n"
|
|
<< "0.00 tank.base_color vec3 0.35 0.65 0.90\n"
|
|
<< "0.00 alarm.intensity double 0.0\n"
|
|
<< "0.00 runtime.status string RuntimeData replay started\n"
|
|
<< "0.50 cube.position vec3 1.5 0.5 0.0\n"
|
|
<< "0.50 cube.base_color vec3 0.8 0.6 0.5\n"
|
|
<< "0.50 tank.base_color vec3 0.20 0.80 0.25\n"
|
|
<< "0.50 alarm.intensity double 2.0\n"
|
|
<< "0.50 runtime.status string Replay frame 0.50s active\n"
|
|
<< "1.00 cube.visible bool false\n"
|
|
<< "1.00 valve.visible bool false\n"
|
|
<< "1.00 runtime.status string Replay toggled visibility\n"
|
|
<< "1.50 cube.position vec3 -1.5 0.5 0.0\n"
|
|
<< "1.50 cube.visible bool true\n"
|
|
<< "1.50 valve.visible bool true\n"
|
|
<< "1.50 alarm.intensity double 0.0\n"
|
|
<< "1.50 runtime.status string Replay restored scene\n";
|
|
}
|
|
|
|
std::cout << "Runtime config generated at " << runtimeDirectory.string()
|
|
<< " scene=" << scenePath.string()
|
|
<< " ui=" << uiPath.string()
|
|
<< " mode=" << (generateTcpConfig ? "tcp" : "file_replay") << '\n';
|
|
return 0;
|
|
}
|