114 lines
8.0 KiB
C++
114 lines
8.0 KiB
C++
#include "MetaCoreRuntimeInput/MetaCoreRuntimeInput.h"
|
|
#include "MetaCoreRuntimeInput/MetaCoreRuntimeInteraction.h"
|
|
#include "MetaCorePlatform/MetaCoreInput.h"
|
|
#include "MetaCoreFoundation/MetaCoreGeneratedReflection.h"
|
|
#include "MetaCoreRender/MetaCoreRenderTypes.h"
|
|
#include "MetaCoreScene/MetaCoreScene.h"
|
|
#include "MetaCoreScene/MetaCoreComponents.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
void Expect(bool value, const char* message) { if (!value) { std::cerr << message << '\n'; std::exit(1); } }
|
|
}
|
|
|
|
int main() {
|
|
MetaCore::MetaCoreRuntimeInput runtime;
|
|
runtime.SetInputMap(MetaCore::MetaCoreBuildDefaultInputMap());
|
|
MetaCore::MetaCoreInput input;
|
|
input.BeginFrame(); input.SetControlValue("<Keyboard>/W", 1.0F);
|
|
runtime.Update(input);
|
|
Expect(std::abs(runtime.GetAxis("MoveY") - 1.0F) < 0.001F, "W should drive MoveY");
|
|
input.BeginFrame(); input.SetControlValue("<Mouse>/leftButton", 1.0F); runtime.Update(input);
|
|
Expect(runtime.GetAction("Interact").Pressed && runtime.GetAction("Interact").Held, "action should start");
|
|
input.BeginFrame(); runtime.Update(input); Expect(!runtime.GetAction("Interact").Pressed && runtime.GetAction("Interact").Held, "action should remain held");
|
|
input.BeginFrame(); input.SetControlValue("<Mouse>/leftButton", 0.0F); runtime.Update(input);
|
|
Expect(runtime.GetAction("Interact").Released, "action should cancel");
|
|
Expect(runtime.Rebind("game.move.forward", "<Keyboard>/Up"), "rebind should succeed");
|
|
input.BeginFrame(); input.SetControlValue("<Keyboard>/W", 0.0F); input.SetControlValue("<Keyboard>/Up", 1.0F); runtime.Update(input);
|
|
Expect(runtime.GetAxis("MoveY") > 0.9F, "override should be evaluated");
|
|
runtime.CancelAll(); Expect(!runtime.GetAction("Interact").Held && runtime.GetAxis("MoveY") == 0.0F, "cancel should clear state");
|
|
MetaCore::MetaCoreTypeRegistry registry; MetaCore::MetaCoreRegisterRuntimeInputGeneratedTypes(registry);
|
|
const auto mapPath = std::filesystem::temp_directory_path() / "MetaCoreInputMap.test";
|
|
Expect(MetaCore::MetaCoreWriteInputMap(mapPath, MetaCore::MetaCoreBuildDefaultInputMap(), registry), "input map should serialize");
|
|
const auto loaded = MetaCore::MetaCoreReadInputMap(mapPath, registry);
|
|
Expect(loaded && loaded->Bindings.size() == 10 && loaded->Contexts.size() == 1, "input map should round trip");
|
|
const auto overridePath = std::filesystem::temp_directory_path() / "MetaCoreInputOverrides.test.json";
|
|
Expect(runtime.SaveOverrides(overridePath), "overrides should save atomically");
|
|
MetaCore::MetaCoreRuntimeInput loadedRuntime; loadedRuntime.SetInputMap(MetaCore::MetaCoreBuildDefaultInputMap());
|
|
Expect(loadedRuntime.LoadOverrides(overridePath), "overrides should load");
|
|
auto invalidMap = MetaCore::MetaCoreBuildDefaultInputMap();
|
|
invalidMap.Bindings.push_back(invalidMap.Bindings.front());
|
|
invalidMap.Bindings.back().ControlPath = "invalid";
|
|
const auto invalidIssues = MetaCore::MetaCoreValidateInputMap(invalidMap);
|
|
Expect(std::count_if(invalidIssues.begin(), invalidIssues.end(), [](const auto& issue){ return issue.Error; }) >= 2,
|
|
"duplicate binding IDs and invalid paths should fail validation");
|
|
{ std::ofstream corrupted(overridePath, std::ios::trunc); corrupted << "{broken"; }
|
|
Expect(!loadedRuntime.LoadOverrides(overridePath) && !loadedRuntime.GetWarnings().empty(), "corrupt overrides should fall back with diagnostics");
|
|
|
|
MetaCore::MetaCoreInputMapDocument priorityMap;
|
|
priorityMap.Actions = {{"High", "High"}, {"Low", "Low"}};
|
|
priorityMap.Bindings = {{"high", "High", MetaCore::MetaCoreInputBindingTarget::Action, "<Keyboard>/Space", 1.0F, {}},
|
|
{"low", "Low", MetaCore::MetaCoreInputBindingTarget::Action, "<Keyboard>/Space", 1.0F, {}}};
|
|
priorityMap.Contexts = {{"HighContext", 10, true, true, {"high"}}, {"LowContext", 0, true, false, {"low"}}};
|
|
MetaCore::MetaCoreRuntimeInput priorityRuntime; priorityRuntime.SetInputMap(priorityMap);
|
|
MetaCore::MetaCoreInput priorityInput; priorityInput.BeginFrame(); priorityInput.SetControlValue("<Keyboard>/Space", 1.0F); priorityRuntime.Update(priorityInput);
|
|
Expect(priorityRuntime.GetAction("High").Held && !priorityRuntime.GetAction("Low").Held, "higher context should consume the physical control");
|
|
priorityInput.SetWindowFocused(false); priorityRuntime.Update(priorityInput);
|
|
Expect(priorityRuntime.GetAction("High").Released, "focus loss should cancel held actions");
|
|
|
|
MetaCore::MetaCoreScene scene;
|
|
auto interactiveObject = scene.CreateGameObject("Interactive");
|
|
interactiveObject.AddComponent<MetaCore::MetaCoreMeshRendererComponent>();
|
|
interactiveObject.AddComponent<MetaCore::MetaCoreInteractableComponent>();
|
|
MetaCore::MetaCoreRuntimeInteraction interaction;
|
|
std::vector<MetaCore::MetaCorePointerEvent> pointerEvents;
|
|
std::vector<MetaCore::MetaCoreRuntimeInteraction::PickCallback> pickCallbacks;
|
|
interaction.SetEventCallback([&](const auto& event) { pointerEvents.push_back(event); });
|
|
const MetaCore::MetaCoreRuntimeInteraction::PickRequest pickBackend = [&](std::uint32_t, std::uint32_t, auto callback) {
|
|
pickCallbacks.push_back(std::move(callback)); return true;
|
|
};
|
|
const MetaCore::MetaCoreViewportRect viewport{0.0F, 0.0F, 100.0F, 100.0F};
|
|
MetaCore::MetaCoreInput pointerInput;
|
|
pointerInput.BeginFrame(); pointerInput.SetCursorPosition({20.0F, 20.0F});
|
|
interaction.Update(scene, pickBackend, pointerInput, viewport, false);
|
|
Expect(pickCallbacks.size() == 1, "hover should submit one pick");
|
|
auto firstHover = std::move(pickCallbacks.back()); pickCallbacks.clear();
|
|
pointerInput.BeginFrame(); pointerInput.SetCursorPosition({21.0F, 20.0F});
|
|
interaction.Update(scene, pickBackend, pointerInput, viewport, false);
|
|
auto latestHover = std::move(pickCallbacks.back()); pickCallbacks.clear();
|
|
latestHover({interactiveObject.GetId(), {1.0F, 2.0F, 3.0F}});
|
|
firstHover({0, {}});
|
|
pointerInput.BeginFrame(); interaction.Update(scene, pickBackend, pointerInput, viewport, false);
|
|
Expect(!pointerEvents.empty() && pointerEvents.back().Type == MetaCore::MetaCorePointerEventType::Enter,
|
|
"out-of-order hover should accept only the latest result");
|
|
|
|
pickCallbacks.clear(); pointerEvents.clear();
|
|
pointerInput.BeginFrame(); pointerInput.SetMouseButtonState(MetaCore::MetaCoreMouseButton::Left, true);
|
|
interaction.Update(scene, pickBackend, pointerInput, viewport, false);
|
|
Expect(pickCallbacks.size() == 2, "press frame should preserve hover and down picks");
|
|
auto down = std::move(pickCallbacks[1]); pickCallbacks.clear(); down({interactiveObject.GetId(), {}});
|
|
pointerInput.BeginFrame(); interaction.Update(scene, pickBackend, pointerInput, viewport, false);
|
|
Expect(std::any_of(pointerEvents.begin(), pointerEvents.end(), [](const auto& e){ return e.Type == MetaCore::MetaCorePointerEventType::Down; }), "press result should capture target");
|
|
pickCallbacks.clear();
|
|
pointerInput.BeginFrame(); pointerInput.SetMouseButtonState(MetaCore::MetaCoreMouseButton::Left, false);
|
|
interaction.Update(scene, pickBackend, pointerInput, viewport, false);
|
|
Expect(pickCallbacks.size() == 2, "release frame should preserve hover and up picks");
|
|
auto up = std::move(pickCallbacks[1]); pickCallbacks.clear(); up({interactiveObject.GetId(), {}});
|
|
pointerInput.BeginFrame(); interaction.Update(scene, pickBackend, pointerInput, viewport, false);
|
|
Expect(std::any_of(pointerEvents.begin(), pointerEvents.end(), [](const auto& e){ return e.Type == MetaCore::MetaCorePointerEventType::Click; }), "same-target release should click");
|
|
pickCallbacks.clear();
|
|
pointerInput.BeginFrame(); interaction.Update(scene, pickBackend, pointerInput, viewport, true);
|
|
Expect(pickCallbacks.empty(), "UI-consumed mouse should not submit picking");
|
|
|
|
std::filesystem::remove(mapPath); std::filesystem::remove(overridePath);
|
|
std::cout << "MetaCoreRuntimeInputTests passed\n";
|
|
return 0;
|
|
}
|