5577 lines
248 KiB
C++
5577 lines
248 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <XCEngine/Components/BoxColliderComponent.h>
|
|
#include <XCEngine/Components/CameraComponent.h>
|
|
#include <XCEngine/Components/LightComponent.h>
|
|
#include <XCEngine/Components/MeshFilterComponent.h>
|
|
#include <XCEngine/Components/MeshRendererComponent.h>
|
|
#include <XCEngine/Components/RigidbodyComponent.h>
|
|
#include <XCEngine/Components/SphereColliderComponent.h>
|
|
#include <XCEngine/Debug/ILogSink.h>
|
|
#include <XCEngine/Debug/Logger.h>
|
|
#include <XCEngine/Core/Math/Vector2.h>
|
|
#include <XCEngine/Core/Math/Vector3.h>
|
|
#include <XCEngine/Core/Math/Vector4.h>
|
|
#include <XCEngine/Core/Asset/ResourceManager.h>
|
|
#include <XCEngine/Input/InputManager.h>
|
|
#include <XCEngine/Physics/PhysicsWorld.h>
|
|
#include <XCEngine/Input/InputTypes.h>
|
|
#include <XCEngine/Rendering/Execution/CameraFramePlan.h>
|
|
#include <XCEngine/Rendering/Execution/CameraFrameRenderGraphFrameData.h>
|
|
#include <XCEngine/Rendering/Execution/CameraRenderer.h>
|
|
#include <XCEngine/Rendering/Execution/SceneRenderer.h>
|
|
#include <XCEngine/Rendering/Extraction/RenderSceneExtractor.h>
|
|
#include <XCEngine/Rendering/Extraction/RenderSceneUtility.h>
|
|
#include <XCEngine/Rendering/Graph/RenderGraph.h>
|
|
#include <XCEngine/Rendering/Graph/RenderGraphCompiler.h>
|
|
#include <XCEngine/Rendering/Planning/SceneRenderRequestPlanner.h>
|
|
#include <XCEngine/Rendering/Pipelines/ManagedScriptableRenderPipelineAsset.h>
|
|
#include <XCEngine/Rendering/Pipelines/ScriptableRenderPipelineHost.h>
|
|
#include <XCEngine/Rendering/RenderSurface.h>
|
|
#include <XCEngine/RHI/RHICommandList.h>
|
|
#include <XCEngine/RHI/RHICommandQueue.h>
|
|
#include <XCEngine/RHI/RHIDescriptorPool.h>
|
|
#include <XCEngine/RHI/RHIDescriptorSet.h>
|
|
#include <XCEngine/RHI/RHIDevice.h>
|
|
#include <XCEngine/RHI/RHIPipelineLayout.h>
|
|
#include <XCEngine/RHI/RHIPipelineState.h>
|
|
#include <XCEngine/RHI/RHIResourceView.h>
|
|
#include <XCEngine/RHI/RHISampler.h>
|
|
#include <XCEngine/RHI/RHITexture.h>
|
|
#include <XCEngine/Scene/Scene.h>
|
|
#include <XCEngine/Scene/SceneRuntime.h>
|
|
#include <XCEngine/Scripting/Mono/MonoScriptRuntime.h>
|
|
#include <XCEngine/Scripting/ScriptComponent.h>
|
|
#include <XCEngine/Scripting/ScriptEngine.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "../Fixtures/RenderTestRhiStubs.h"
|
|
|
|
using namespace XCEngine::Components;
|
|
using namespace XCEngine::Scripting;
|
|
using namespace XCTest;
|
|
|
|
namespace {
|
|
|
|
void ExpectVector3Near(const XCEngine::Math::Vector3& actual, const XCEngine::Math::Vector3& expected, float tolerance = 0.001f) {
|
|
EXPECT_NEAR(actual.x, expected.x, tolerance);
|
|
EXPECT_NEAR(actual.y, expected.y, tolerance);
|
|
EXPECT_NEAR(actual.z, expected.z, tolerance);
|
|
}
|
|
|
|
void ExpectVector4Near(const XCEngine::Math::Vector4& actual, const XCEngine::Math::Vector4& expected, float tolerance = 0.001f) {
|
|
EXPECT_NEAR(actual.x, expected.x, tolerance);
|
|
EXPECT_NEAR(actual.y, expected.y, tolerance);
|
|
EXPECT_NEAR(actual.z, expected.z, tolerance);
|
|
EXPECT_NEAR(actual.w, expected.w, tolerance);
|
|
}
|
|
|
|
class CapturingLogSink final : public XCEngine::Debug::ILogSink {
|
|
public:
|
|
void Log(const XCEngine::Debug::LogEntry& entry) override {
|
|
entries.push_back(entry);
|
|
}
|
|
|
|
void Flush() override {
|
|
}
|
|
|
|
std::vector<std::string> CollectMessagesWithPrefix(const char* prefix) const {
|
|
std::vector<std::string> messages;
|
|
for (const XCEngine::Debug::LogEntry& entry : entries) {
|
|
const std::string message = entry.message.CStr();
|
|
if (message.rfind(prefix, 0) == 0) {
|
|
messages.push_back(message);
|
|
}
|
|
}
|
|
|
|
return messages;
|
|
}
|
|
|
|
std::vector<XCEngine::Debug::LogEntry> entries;
|
|
};
|
|
|
|
ScriptComponent* FindScriptComponentByClass(GameObject* gameObject, const std::string& namespaceName, const std::string& className) {
|
|
if (!gameObject) {
|
|
return nullptr;
|
|
}
|
|
|
|
for (ScriptComponent* component : gameObject->GetComponents<ScriptComponent>()) {
|
|
if (!component) {
|
|
continue;
|
|
}
|
|
|
|
if (component->GetNamespaceName() == namespaceName && component->GetClassName() == className) {
|
|
return component;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
MonoScriptRuntime::Settings CreateMonoSettings() {
|
|
MonoScriptRuntime::Settings settings;
|
|
settings.assemblyDirectory = XCENGINE_TEST_MANAGED_OUTPUT_DIR;
|
|
settings.corlibDirectory = XCENGINE_TEST_MANAGED_OUTPUT_DIR;
|
|
settings.coreAssemblyPath = XCENGINE_TEST_SCRIPT_CORE_DLL;
|
|
settings.appAssemblyPath = XCENGINE_TEST_GAME_SCRIPTS_DLL;
|
|
return settings;
|
|
}
|
|
|
|
class MonoScriptRuntimeTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
engine = &ScriptEngine::Get();
|
|
engine->OnRuntimeStop();
|
|
engine->SetRuntimeFixedDeltaTime(ScriptEngine::DefaultFixedDeltaTime);
|
|
XCEngine::Input::InputManager::Get().Shutdown();
|
|
XCEngine::Resources::ResourceManager::Get().Shutdown();
|
|
XCEngine::Rendering::Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor();
|
|
|
|
std::string engineAssemblyError;
|
|
MonoScriptRuntime::Settings settings = CreateMonoSettings();
|
|
ASSERT_TRUE(
|
|
MonoScriptRuntime::DiscoverEngineAssemblies(
|
|
settings,
|
|
&engineAssemblyError))
|
|
<< engineAssemblyError;
|
|
runtime = std::make_unique<MonoScriptRuntime>(std::move(settings));
|
|
ASSERT_TRUE(runtime->Initialize()) << runtime->GetLastError();
|
|
|
|
engine->SetRuntime(runtime.get());
|
|
}
|
|
|
|
void TearDown() override {
|
|
engine->OnRuntimeStop();
|
|
engine->SetRuntime(nullptr);
|
|
XCEngine::Input::InputManager::Get().Shutdown();
|
|
XCEngine::Rendering::Pipelines::ClearConfiguredManagedRenderPipelineAssetDescriptor();
|
|
runtime.reset();
|
|
scene.reset();
|
|
XCEngine::Resources::ResourceManager::Get().Shutdown();
|
|
}
|
|
|
|
Scene* CreateScene(const std::string& sceneName) {
|
|
scene = std::make_unique<Scene>(sceneName);
|
|
return scene.get();
|
|
}
|
|
|
|
ScriptComponent* AddScript(GameObject* gameObject, const std::string& namespaceName, const std::string& className) {
|
|
ScriptComponent* component = gameObject->AddComponent<ScriptComponent>();
|
|
component->SetScriptClass("GameScripts", namespaceName, className);
|
|
return component;
|
|
}
|
|
|
|
ScriptEngine* engine = nullptr;
|
|
std::unique_ptr<MonoScriptRuntime> runtime;
|
|
std::unique_ptr<Scene> scene;
|
|
};
|
|
|
|
TEST_F(MonoScriptRuntimeTest, InitializesAndDiscoversConcreteMonoBehaviourClasses) {
|
|
const std::vector<std::string> classNames = runtime->GetScriptClassNames("GameScripts");
|
|
|
|
EXPECT_TRUE(runtime->IsInitialized());
|
|
EXPECT_TRUE(runtime->IsClassAvailable("GameScripts", "Gameplay", "LifecycleProbe"));
|
|
EXPECT_NE(std::find(classNames.begin(), classNames.end(), "Gameplay.LifecycleProbe"), classNames.end());
|
|
EXPECT_EQ(std::find(classNames.begin(), classNames.end(), "Gameplay.AbstractLifecycleProbe"), classNames.end());
|
|
EXPECT_EQ(std::find(classNames.begin(), classNames.end(), "Gameplay.UtilityHelper"), classNames.end());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptClassDescriptorApiReturnsConcreteManagedTypes) {
|
|
std::vector<ScriptClassDescriptor> classes;
|
|
ASSERT_TRUE(runtime->TryGetAvailableScriptClasses(classes));
|
|
ASSERT_FALSE(classes.empty());
|
|
|
|
EXPECT_TRUE(std::is_sorted(
|
|
classes.begin(),
|
|
classes.end(),
|
|
[](const ScriptClassDescriptor& lhs, const ScriptClassDescriptor& rhs) {
|
|
if (lhs.assemblyName != rhs.assemblyName) {
|
|
return lhs.assemblyName < rhs.assemblyName;
|
|
}
|
|
if (lhs.namespaceName != rhs.namespaceName) {
|
|
return lhs.namespaceName < rhs.namespaceName;
|
|
}
|
|
return lhs.className < rhs.className;
|
|
}));
|
|
|
|
EXPECT_NE(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{"GameScripts", "Gameplay", "LifecycleProbe"}),
|
|
classes.end());
|
|
EXPECT_EQ(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{"GameScripts", "Gameplay", "AbstractLifecycleProbe"}),
|
|
classes.end());
|
|
EXPECT_EQ(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{"GameScripts", "Gameplay", "UtilityHelper"}),
|
|
classes.end());
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
RenderPipelineAssetClassDescriptorApiReturnsConcretePipelineAssets) {
|
|
std::vector<ScriptClassDescriptor> classes;
|
|
ASSERT_TRUE(runtime->TryGetAvailableRenderPipelineAssetClasses(classes));
|
|
ASSERT_FALSE(classes.empty());
|
|
|
|
EXPECT_TRUE(std::is_sorted(
|
|
classes.begin(),
|
|
classes.end(),
|
|
[](const ScriptClassDescriptor& lhs, const ScriptClassDescriptor& rhs) {
|
|
if (lhs.assemblyName != rhs.assemblyName) {
|
|
return lhs.assemblyName < rhs.assemblyName;
|
|
}
|
|
if (lhs.namespaceName != rhs.namespaceName) {
|
|
return lhs.namespaceName < rhs.namespaceName;
|
|
}
|
|
return lhs.className < rhs.className;
|
|
}));
|
|
|
|
EXPECT_NE(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{
|
|
"XCEngine.RenderPipelines.Universal",
|
|
"XCEngine.Rendering.Universal",
|
|
"UniversalRenderPipelineAsset"}),
|
|
classes.end());
|
|
EXPECT_NE(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"RenderPipelineApiProbeAsset"}),
|
|
classes.end());
|
|
EXPECT_NE(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedRenderPipelineProbeAsset"}),
|
|
classes.end());
|
|
EXPECT_NE(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedUniversalRenderPipelineProbeAsset"}),
|
|
classes.end());
|
|
EXPECT_EQ(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"LegacyRenderPipelineApiProbeAsset"}),
|
|
classes.end());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldMetadataListsSupportedPublicInstanceFields) {
|
|
std::vector<ScriptFieldMetadata> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldMetadata("GameScripts", "Gameplay", "FieldMetadataProbe", fields));
|
|
|
|
const std::vector<ScriptFieldMetadata> expected = {
|
|
{"Health", ScriptFieldType::Int32},
|
|
{"HiddenFlag", ScriptFieldType::Bool},
|
|
{"Label", ScriptFieldType::String},
|
|
{"SpawnPoint", ScriptFieldType::Vector3},
|
|
{"Speed", ScriptFieldType::Float},
|
|
{"State", ScriptFieldType::Int32},
|
|
{"Target", ScriptFieldType::GameObject},
|
|
};
|
|
|
|
EXPECT_EQ(fields, expected);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldMetadataQueryFailsForUnknownClass) {
|
|
std::vector<ScriptFieldMetadata> fields = {
|
|
{"Sentinel", ScriptFieldType::Bool},
|
|
};
|
|
|
|
EXPECT_FALSE(runtime->TryGetClassFieldMetadata("GameScripts", "Gameplay", "MissingProbe", fields));
|
|
EXPECT_TRUE(fields.empty());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsManagedInitializers) {
|
|
std::vector<ScriptFieldDefaultValue> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "Gameplay", "RuntimeGameObjectProbe", fields));
|
|
|
|
const auto fieldIt = std::find_if(
|
|
fields.begin(),
|
|
fields.end(),
|
|
[](const ScriptFieldDefaultValue& field) {
|
|
return field.fieldName == "ObservedRootChildCountAfterDestroy";
|
|
});
|
|
|
|
ASSERT_NE(fieldIt, fields.end());
|
|
EXPECT_EQ(fieldIt->type, ScriptFieldType::Int32);
|
|
EXPECT_EQ(std::get<int32_t>(fieldIt->value), -1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsEnumInitializersAsInt32) {
|
|
std::vector<ScriptFieldDefaultValue> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "Gameplay", "FieldMetadataProbe", fields));
|
|
|
|
const auto fieldIt = std::find_if(
|
|
fields.begin(),
|
|
fields.end(),
|
|
[](const ScriptFieldDefaultValue& field) {
|
|
return field.fieldName == "State";
|
|
});
|
|
|
|
ASSERT_NE(fieldIt, fields.end());
|
|
EXPECT_EQ(fieldIt->type, ScriptFieldType::Int32);
|
|
EXPECT_EQ(std::get<int32_t>(fieldIt->value), 2);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsSerializeFieldPrivateInitializers) {
|
|
std::vector<ScriptFieldDefaultValue> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "Gameplay", "FieldMetadataProbe", fields));
|
|
|
|
const auto fieldIt = std::find_if(
|
|
fields.begin(),
|
|
fields.end(),
|
|
[](const ScriptFieldDefaultValue& field) {
|
|
return field.fieldName == "HiddenFlag";
|
|
});
|
|
|
|
ASSERT_NE(fieldIt, fields.end());
|
|
EXPECT_EQ(fieldIt->type, ScriptFieldType::Bool);
|
|
EXPECT_TRUE(std::get<bool>(fieldIt->value));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldMetadataListsConcreteComponentReferenceFields) {
|
|
std::vector<ScriptFieldMetadata> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldMetadata("GameScripts", "Gameplay", "ComponentFieldMetadataProbe", fields));
|
|
|
|
const std::vector<ScriptFieldMetadata> expected = {
|
|
{"Pivot", ScriptFieldType::Component},
|
|
{"SceneCamera", ScriptFieldType::Component},
|
|
{"ScriptTarget", ScriptFieldType::Component},
|
|
};
|
|
|
|
EXPECT_EQ(fields, expected);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedGraphicsSettingsRoundTripsRenderPipelineAssetSelection) {
|
|
Scene* runtimeScene = CreateScene("ManagedRenderPipelineSelectionScene");
|
|
GameObject* scriptObject = runtimeScene->CreateGameObject("RenderPipelineProbe");
|
|
ScriptComponent* script =
|
|
AddScript(scriptObject, "Gameplay", "RenderPipelineApiProbe");
|
|
ASSERT_NE(script, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor =
|
|
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
|
EXPECT_EQ(descriptor.assemblyName, "GameScripts");
|
|
EXPECT_EQ(descriptor.namespaceName, "Gameplay");
|
|
EXPECT_EQ(descriptor.className, "RenderPipelineApiProbeAsset");
|
|
EXPECT_NE(descriptor.managedAssetHandle, 0u);
|
|
|
|
bool initialAssetWasNull = false;
|
|
bool selectionRoundTripSucceeded = false;
|
|
bool selectionReferencePreserved = false;
|
|
std::string selectedPipelineAssetTypeName;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"InitialAssetWasNull",
|
|
initialAssetWasNull));
|
|
EXPECT_TRUE(initialAssetWasNull);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"SelectionRoundTripSucceeded",
|
|
selectionRoundTripSucceeded));
|
|
EXPECT_TRUE(selectionRoundTripSucceeded);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"SelectionReferencePreserved",
|
|
selectionReferencePreserved));
|
|
EXPECT_TRUE(selectionReferencePreserved);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"SelectedPipelineAssetTypeName",
|
|
selectedPipelineAssetTypeName));
|
|
EXPECT_EQ(
|
|
selectedPipelineAssetTypeName,
|
|
"Gameplay.RenderPipelineApiProbeAsset");
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedGraphicsSettingsRoundTripsScriptCoreUniversalRenderPipelineAssetSelection) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ScriptCoreUniversalRenderPipelineSelectionScene");
|
|
GameObject* scriptObject =
|
|
runtimeScene->CreateGameObject("ScriptCoreUniversalRenderPipelineProbe");
|
|
ScriptComponent* script =
|
|
AddScript(
|
|
scriptObject,
|
|
"Gameplay",
|
|
"ScriptCoreUniversalRenderPipelineSelectionProbe");
|
|
ASSERT_NE(script, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor =
|
|
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
|
EXPECT_EQ(descriptor.assemblyName, "XCEngine.RenderPipelines.Universal");
|
|
EXPECT_EQ(descriptor.namespaceName, "XCEngine.Rendering.Universal");
|
|
EXPECT_EQ(descriptor.className, "UniversalRenderPipelineAsset");
|
|
EXPECT_NE(descriptor.managedAssetHandle, 0u);
|
|
|
|
bool selectionRoundTripSucceeded = false;
|
|
std::string selectedPipelineAssetTypeName;
|
|
std::string selectedRendererDataTypeName;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"SelectionRoundTripSucceeded",
|
|
selectionRoundTripSucceeded));
|
|
EXPECT_TRUE(selectionRoundTripSucceeded);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"SelectedPipelineAssetTypeName",
|
|
selectedPipelineAssetTypeName));
|
|
EXPECT_EQ(
|
|
selectedPipelineAssetTypeName,
|
|
"XCEngine.Rendering.Universal.UniversalRenderPipelineAsset");
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"SelectedRendererDataTypeName",
|
|
selectedRendererDataTypeName));
|
|
EXPECT_EQ(
|
|
selectedRendererDataTypeName,
|
|
"XCEngine.Rendering.Universal.UniversalRendererData");
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedGraphicsSettingsGetterMaterializesConfiguredAssetWithoutExistingHandle) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ManagedRenderPipelineGetterMaterializationScene");
|
|
GameObject* scriptObject =
|
|
runtimeScene->CreateGameObject("RenderPipelineGetterProbe");
|
|
ScriptComponent* script =
|
|
AddScript(
|
|
scriptObject,
|
|
"Gameplay",
|
|
"ProjectConfiguredRenderPipelineGetterProbe");
|
|
ASSERT_NE(script, nullptr);
|
|
|
|
XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor
|
|
configuredDescriptor = {};
|
|
configuredDescriptor.assemblyName = "GameScripts";
|
|
configuredDescriptor.namespaceName = "Gameplay";
|
|
configuredDescriptor.className = "RenderPipelineApiProbeAsset";
|
|
XCEngine::Rendering::Pipelines::SetConfiguredManagedRenderPipelineAssetDescriptor(
|
|
configuredDescriptor);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor
|
|
resolvedDescriptor =
|
|
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
|
EXPECT_EQ(resolvedDescriptor.assemblyName, configuredDescriptor.assemblyName);
|
|
EXPECT_EQ(resolvedDescriptor.namespaceName, configuredDescriptor.namespaceName);
|
|
EXPECT_EQ(resolvedDescriptor.className, configuredDescriptor.className);
|
|
EXPECT_NE(resolvedDescriptor.managedAssetHandle, 0u);
|
|
EXPECT_NE(
|
|
runtime->GetExternalManagedObject(
|
|
resolvedDescriptor.managedAssetHandle),
|
|
nullptr);
|
|
|
|
bool observedAssetWasNull = true;
|
|
std::string observedPipelineAssetTypeName;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"ObservedAssetWasNull",
|
|
observedAssetWasNull));
|
|
EXPECT_FALSE(observedAssetWasNull);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
script,
|
|
"ObservedPipelineAssetTypeName",
|
|
observedPipelineAssetTypeName));
|
|
EXPECT_EQ(
|
|
observedPipelineAssetTypeName,
|
|
"Gameplay.RenderPipelineApiProbeAsset");
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
RuntimeStopClearsManagedGraphicsSettingsSelection) {
|
|
Scene* runtimeScene = CreateScene("ManagedRenderPipelineStopClearsSelectionScene");
|
|
GameObject* scriptObject = runtimeScene->CreateGameObject("RenderPipelineProbe");
|
|
ScriptComponent* script =
|
|
AddScript(scriptObject, "Gameplay", "RenderPipelineApiProbe");
|
|
ASSERT_NE(script, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorBeforeStop =
|
|
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
|
ASSERT_TRUE(descriptorBeforeStop.IsValid());
|
|
ASSERT_NE(descriptorBeforeStop.managedAssetHandle, 0u);
|
|
ASSERT_NE(
|
|
runtime->GetExternalManagedObject(
|
|
descriptorBeforeStop.managedAssetHandle),
|
|
nullptr);
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorAfterStop =
|
|
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
|
EXPECT_FALSE(descriptorAfterStop.IsValid());
|
|
EXPECT_EQ(descriptorAfterStop.managedAssetHandle, 0u);
|
|
EXPECT_EQ(
|
|
runtime->GetExternalManagedObject(
|
|
descriptorBeforeStop.managedAssetHandle),
|
|
nullptr);
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
CameraRendererFallsBackAfterRuntimeStopClearsManagedSelection) {
|
|
Scene* runtimeScene = CreateScene("ManagedRenderPipelineStopFallbackScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject("ManagedRenderPipelineSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderPipelineRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Rendering::CameraRenderer renderer;
|
|
ASSERT_NE(
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset()),
|
|
nullptr);
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
EXPECT_EQ(
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset()),
|
|
nullptr);
|
|
|
|
auto* host =
|
|
dynamic_cast<XCEngine::Rendering::Pipelines::ScriptableRenderPipelineHost*>(
|
|
renderer.GetPipeline());
|
|
ASSERT_NE(host, nullptr);
|
|
EXPECT_EQ(host->GetStageRecorder(), nullptr);
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
RuntimeSceneReplacePreservesManagedGraphicsSettingsSelection) {
|
|
Scene* firstScene = CreateScene("ManagedRenderPipelineFirstScene");
|
|
GameObject* selectionObject =
|
|
firstScene->CreateGameObject("ManagedRenderPipelineSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderPipelineRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
engine->OnRuntimeStart(firstScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorBeforeReplace =
|
|
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
|
ASSERT_TRUE(descriptorBeforeReplace.IsValid());
|
|
ASSERT_NE(descriptorBeforeReplace.managedAssetHandle, 0u);
|
|
MonoObject* const managedAssetBeforeReplace =
|
|
runtime->GetExternalManagedObject(
|
|
descriptorBeforeReplace.managedAssetHandle);
|
|
ASSERT_NE(managedAssetBeforeReplace, nullptr);
|
|
|
|
auto secondScene = std::make_unique<Scene>("ManagedRenderPipelineSecondScene");
|
|
engine->OnRuntimeSceneReplaced(secondScene.get());
|
|
scene = std::move(secondScene);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptorAfterReplace =
|
|
XCEngine::Rendering::Pipelines::GetConfiguredManagedRenderPipelineAssetDescriptor();
|
|
EXPECT_EQ(descriptorAfterReplace.assemblyName, descriptorBeforeReplace.assemblyName);
|
|
EXPECT_EQ(descriptorAfterReplace.namespaceName, descriptorBeforeReplace.namespaceName);
|
|
EXPECT_EQ(descriptorAfterReplace.className, descriptorBeforeReplace.className);
|
|
EXPECT_EQ(
|
|
descriptorAfterReplace.managedAssetHandle,
|
|
descriptorBeforeReplace.managedAssetHandle);
|
|
EXPECT_EQ(
|
|
runtime->GetExternalManagedObject(
|
|
descriptorAfterReplace.managedAssetHandle),
|
|
managedAssetBeforeReplace);
|
|
|
|
XCEngine::Rendering::CameraRenderer renderer;
|
|
const auto* pipelineAsset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(pipelineAsset, nullptr);
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().className,
|
|
descriptorBeforeReplace.className);
|
|
|
|
engine->OnRuntimeStop();
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
DefaultCameraRendererUsesManagedGraphicsSelectionToRecordMainSceneGraph) {
|
|
Scene* runtimeScene = CreateScene("ManagedRenderPipelineDefaultRendererScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject("ManagedRenderPipelineSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderPipelineRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Rendering::CameraRenderer renderer;
|
|
const auto* asset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(asset, nullptr);
|
|
EXPECT_EQ(asset->GetDescriptor().assemblyName, "GameScripts");
|
|
EXPECT_EQ(asset->GetDescriptor().namespaceName, "Gameplay");
|
|
EXPECT_EQ(asset->GetDescriptor().className, "ManagedRenderPipelineProbeAsset");
|
|
|
|
auto* host =
|
|
dynamic_cast<XCEngine::Rendering::Pipelines::ScriptableRenderPipelineHost*>(
|
|
renderer.GetPipeline());
|
|
ASSERT_NE(host, nullptr);
|
|
ASSERT_NE(host->GetStageRecorder(), nullptr);
|
|
EXPECT_TRUE(
|
|
host->GetStageRecorder()->Initialize(
|
|
XCEngine::Rendering::RenderContext{}))
|
|
<< runtime->GetLastError();
|
|
EXPECT_TRUE(
|
|
host->SupportsStageRenderGraph(
|
|
XCEngine::Rendering::CameraFrameStage::MainScene))
|
|
<< runtime->GetLastError();
|
|
|
|
XCEngine::Rendering::RenderGraph graph;
|
|
XCEngine::Rendering::RenderGraphBuilder graphBuilder(graph);
|
|
XCEngine::Rendering::RenderGraphTextureDesc colorDesc = {};
|
|
colorDesc.width = 64u;
|
|
colorDesc.height = 64u;
|
|
colorDesc.format =
|
|
static_cast<XCEngine::Core::uint32>(
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
XCEngine::Rendering::RenderGraphTextureDesc depthDesc = colorDesc;
|
|
depthDesc.format =
|
|
static_cast<XCEngine::Core::uint32>(
|
|
XCEngine::RHI::Format::D32_Float);
|
|
const XCEngine::Rendering::RenderGraphTextureHandle colorTarget =
|
|
graphBuilder.CreateTransientTexture("ManagedSelectedMainSceneColor", colorDesc);
|
|
const XCEngine::Rendering::RenderGraphTextureHandle depthTarget =
|
|
graphBuilder.CreateTransientTexture("ManagedSelectedMainSceneDepth", depthDesc);
|
|
|
|
const XCEngine::Rendering::RenderSceneData sceneData = {};
|
|
const XCEngine::Rendering::RenderSurface surface(64u, 64u);
|
|
bool executionSucceeded = true;
|
|
XCEngine::Rendering::RenderGraphBlackboard blackboard = {};
|
|
const XCEngine::Rendering::RenderPipelineStageRenderGraphContext graphContext = {
|
|
graphBuilder,
|
|
"ManagedSelectedMainScene",
|
|
XCEngine::Rendering::CameraFrameStage::MainScene,
|
|
{},
|
|
sceneData,
|
|
surface,
|
|
nullptr,
|
|
nullptr,
|
|
XCEngine::RHI::ResourceStates::Common,
|
|
{},
|
|
{ colorTarget },
|
|
depthTarget,
|
|
{},
|
|
&executionSucceeded,
|
|
&blackboard
|
|
};
|
|
|
|
EXPECT_TRUE(host->GetStageRecorder()->RecordStageRenderGraph(graphContext))
|
|
<< runtime->GetLastError();
|
|
|
|
XCEngine::Rendering::CompiledRenderGraph compiledGraph = {};
|
|
XCEngine::Containers::String errorMessage;
|
|
ASSERT_TRUE(
|
|
XCEngine::Rendering::RenderGraphCompiler::Compile(
|
|
graph,
|
|
compiledGraph,
|
|
&errorMessage))
|
|
<< errorMessage.CStr();
|
|
ASSERT_EQ(compiledGraph.GetPassCount(), 3u);
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(0).CStr(),
|
|
"ManagedSelectedMainScene.Opaque");
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(1).CStr(),
|
|
"ManagedSelectedMainScene.Skybox");
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(2).CStr(),
|
|
"ManagedSelectedMainScene.Transparent");
|
|
|
|
host->GetStageRecorder()->Shutdown();
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
DefaultCameraRendererUsesScriptCoreUniversalPipelineAssetAndRespectsRendererData) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ScriptCoreUniversalRenderPipelineDefaultRendererScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject("ScriptCoreUniversalRenderPipelineSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ScriptCoreConfiguredUniversalRenderPipelineRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Rendering::CameraRenderer renderer;
|
|
const auto* asset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(asset, nullptr);
|
|
EXPECT_EQ(
|
|
asset->GetDescriptor().assemblyName,
|
|
"XCEngine.RenderPipelines.Universal");
|
|
EXPECT_EQ(
|
|
asset->GetDescriptor().namespaceName,
|
|
"XCEngine.Rendering.Universal");
|
|
EXPECT_EQ(asset->GetDescriptor().className, "UniversalRenderPipelineAsset");
|
|
|
|
auto* host =
|
|
dynamic_cast<XCEngine::Rendering::Pipelines::ScriptableRenderPipelineHost*>(
|
|
renderer.GetPipeline());
|
|
ASSERT_NE(host, nullptr);
|
|
ASSERT_NE(host->GetStageRecorder(), nullptr);
|
|
EXPECT_TRUE(
|
|
host->GetStageRecorder()->Initialize(
|
|
XCEngine::Rendering::RenderContext{}));
|
|
EXPECT_TRUE(
|
|
host->SupportsStageRenderGraph(
|
|
XCEngine::Rendering::CameraFrameStage::MainScene));
|
|
|
|
XCEngine::Rendering::RenderGraph graph;
|
|
XCEngine::Rendering::RenderGraphBuilder graphBuilder(graph);
|
|
XCEngine::Rendering::RenderGraphTextureDesc colorDesc = {};
|
|
colorDesc.width = 64u;
|
|
colorDesc.height = 64u;
|
|
colorDesc.format =
|
|
static_cast<XCEngine::Core::uint32>(
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
XCEngine::Rendering::RenderGraphTextureDesc depthDesc = colorDesc;
|
|
depthDesc.format =
|
|
static_cast<XCEngine::Core::uint32>(
|
|
XCEngine::RHI::Format::D32_Float);
|
|
const XCEngine::Rendering::RenderGraphTextureHandle colorTarget =
|
|
graphBuilder.CreateTransientTexture(
|
|
"ScriptCoreUniversalMainSceneColor",
|
|
colorDesc);
|
|
const XCEngine::Rendering::RenderGraphTextureHandle depthTarget =
|
|
graphBuilder.CreateTransientTexture(
|
|
"ScriptCoreUniversalMainSceneDepth",
|
|
depthDesc);
|
|
|
|
const XCEngine::Rendering::RenderSceneData sceneData = {};
|
|
const XCEngine::Rendering::RenderSurface surface(64u, 64u);
|
|
bool executionSucceeded = true;
|
|
XCEngine::Rendering::RenderGraphBlackboard blackboard = {};
|
|
const XCEngine::Rendering::RenderPipelineStageRenderGraphContext graphContext = {
|
|
graphBuilder,
|
|
"ScriptCoreUniversalMainScene",
|
|
XCEngine::Rendering::CameraFrameStage::MainScene,
|
|
{},
|
|
sceneData,
|
|
surface,
|
|
nullptr,
|
|
nullptr,
|
|
XCEngine::RHI::ResourceStates::Common,
|
|
{},
|
|
{ colorTarget },
|
|
depthTarget,
|
|
{},
|
|
&executionSucceeded,
|
|
&blackboard
|
|
};
|
|
|
|
EXPECT_TRUE(host->GetStageRecorder()->RecordStageRenderGraph(graphContext));
|
|
|
|
XCEngine::Rendering::CompiledRenderGraph compiledGraph = {};
|
|
XCEngine::Containers::String errorMessage;
|
|
ASSERT_TRUE(
|
|
XCEngine::Rendering::RenderGraphCompiler::Compile(
|
|
graph,
|
|
compiledGraph,
|
|
&errorMessage))
|
|
<< errorMessage.CStr();
|
|
ASSERT_EQ(compiledGraph.GetPassCount(), 1u);
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(0).CStr(),
|
|
"ScriptCoreUniversalMainScene.Opaque");
|
|
|
|
host->GetStageRecorder()->Shutdown();
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
DefaultSceneRendererUsesManagedUniversalPipelineForPlannedMainSceneAndPostProcessRender) {
|
|
Scene* runtimeScene = CreateScene("ManagedUniversalRenderPipelineSceneRendererScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject("ManagedUniversalRenderPipelineSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedUniversalRenderPipelineRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(64u, 64u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const auto* pipelineAsset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(pipelineAsset, nullptr);
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().assemblyName, "GameScripts");
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "Gameplay");
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().className,
|
|
"ManagedUniversalRenderPipelineProbeAsset");
|
|
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
EXPECT_TRUE(plans[0].UsesGraphManagedSceneColor());
|
|
EXPECT_TRUE(
|
|
plans[0].IsFullscreenStageRequested(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
EXPECT_TRUE(plans[0].IsPostProcessStageValid());
|
|
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
EXPECT_EQ(commandList.drawCalls, 1u);
|
|
EXPECT_GT(device.createTextureCalls, 0u);
|
|
EXPECT_GT(device.createPipelineLayoutCalls, 0u);
|
|
EXPECT_GT(device.createPipelineStateCalls, 0u);
|
|
EXPECT_GT(device.createDescriptorPoolCalls, 0u);
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int observedCreatePipelineCallCount = 0;
|
|
int observedSupportsMainSceneCallCount = 0;
|
|
int observedSupportsPostProcessCallCount = 0;
|
|
int observedRecordMainSceneCallCount = 0;
|
|
int observedRecordSceneCallCount = 0;
|
|
int observedRecordPostProcessCallCount = 0;
|
|
XCEngine::Math::Vector4 observedPostProcessScale = {};
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedCreatePipelineCallCount",
|
|
observedCreatePipelineCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedSupportsMainSceneCallCount",
|
|
observedSupportsMainSceneCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedSupportsPostProcessCallCount",
|
|
observedSupportsPostProcessCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordMainSceneCallCount",
|
|
observedRecordMainSceneCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordSceneCallCount",
|
|
observedRecordSceneCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordPostProcessCallCount",
|
|
observedRecordPostProcessCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedPostProcessScale",
|
|
observedPostProcessScale));
|
|
|
|
EXPECT_EQ(observedCreatePipelineCallCount, 1);
|
|
EXPECT_GT(observedSupportsMainSceneCallCount, 0);
|
|
EXPECT_GT(observedSupportsPostProcessCallCount, 0);
|
|
EXPECT_EQ(observedRecordMainSceneCallCount, 1);
|
|
EXPECT_EQ(observedRecordSceneCallCount, 1);
|
|
EXPECT_EQ(observedRecordPostProcessCallCount, 1);
|
|
EXPECT_FLOAT_EQ(observedPostProcessScale.x, 1.11f);
|
|
EXPECT_FLOAT_EQ(observedPostProcessScale.y, 0.97f);
|
|
EXPECT_FLOAT_EQ(observedPostProcessScale.z, 0.93f);
|
|
EXPECT_FLOAT_EQ(observedPostProcessScale.w, 1.0f);
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
DefaultSceneRendererUsesScriptCoreUniversalRendererFeatureForPlannedPostProcessRender) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ScriptCoreUniversalRendererFeatureSceneRendererScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject("ScriptCoreUniversalRendererFeatureSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ScriptCoreUniversalPostProcessRendererFeatureRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(64u, 64u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const auto* pipelineAsset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(pipelineAsset, nullptr);
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().assemblyName,
|
|
"XCEngine.RenderPipelines.Universal");
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "XCEngine.Rendering.Universal");
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().className,
|
|
"UniversalRenderPipelineAsset");
|
|
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
EXPECT_TRUE(plans[0].UsesGraphManagedSceneColor());
|
|
EXPECT_TRUE(
|
|
plans[0].IsFullscreenStageRequested(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
EXPECT_TRUE(plans[0].IsPostProcessStageValid());
|
|
EXPECT_EQ(
|
|
plans[0].ResolveStageColorSource(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess),
|
|
XCEngine::Rendering::CameraFrameColorSource::MainSceneColor);
|
|
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
EXPECT_EQ(commandList.drawCalls, 1u);
|
|
EXPECT_GT(device.createTextureCalls, 0u);
|
|
EXPECT_GT(device.createPipelineLayoutCalls, 0u);
|
|
EXPECT_GT(device.createPipelineStateCalls, 0u);
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ScriptCoreUniversalRendererFeatureConfiguresCameraRequestPolicy) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ScriptCoreUniversalRendererFeatureCameraRequestScene");
|
|
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject("ScriptCoreUniversalCameraRequestSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ScriptCoreUniversalShadowlessRendererFeatureRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
|
|
GameObject* lightObject = runtimeScene->CreateGameObject("Light");
|
|
auto* light = lightObject->AddComponent<LightComponent>();
|
|
ASSERT_NE(light, nullptr);
|
|
light->SetLightType(LightType::Directional);
|
|
light->SetCastsShadows(true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Rendering::CameraRenderRequest baselineRequest = {};
|
|
baselineRequest.scene = runtimeScene;
|
|
baselineRequest.camera = camera;
|
|
baselineRequest.surface = XCEngine::Rendering::RenderSurface(64u, 64u);
|
|
XCEngine::Rendering::ApplyDefaultRenderPipelineAssetCameraRenderRequestPolicy(
|
|
baselineRequest,
|
|
0u,
|
|
0u,
|
|
XCEngine::Rendering::DirectionalShadowPlanningSettings{});
|
|
ASSERT_TRUE(baselineRequest.directionalShadow.IsValid());
|
|
|
|
XCEngine::Rendering::CameraRenderer renderer;
|
|
const auto* pipelineAsset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(pipelineAsset, nullptr);
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().assemblyName,
|
|
"XCEngine.RenderPipelines.Universal");
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "XCEngine.Rendering.Universal");
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().className,
|
|
"UniversalRenderPipelineAsset");
|
|
|
|
XCEngine::Rendering::CameraRenderRequest request = {};
|
|
request.scene = runtimeScene;
|
|
request.camera = camera;
|
|
request.surface = XCEngine::Rendering::RenderSurface(64u, 64u);
|
|
pipelineAsset->ConfigureCameraRenderRequest(
|
|
request,
|
|
0u,
|
|
0u,
|
|
XCEngine::Rendering::DirectionalShadowPlanningSettings{});
|
|
|
|
EXPECT_FALSE(request.directionalShadow.IsValid());
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ScriptableRenderContextPublicApiSurfaceUsesDirectContextModel) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ScriptableRenderContextApiSurfaceScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject(
|
|
"ScriptableRenderContextApiSurfaceSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ScriptableRenderContextApiSurfaceProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasPublicContextRecordScene = false;
|
|
bool hasPublicContextRecordOpaqueScenePhase = false;
|
|
bool hasPublicContextRecordBeforeOpaqueInjection = false;
|
|
bool hasPublicContextRecordShaderVectorFullscreenPass = false;
|
|
bool hasPublicContextCameraData = false;
|
|
bool hasPublicContextLightingData = false;
|
|
bool hasPublicContextShadowData = false;
|
|
bool hasPublicContextEnvironmentData = false;
|
|
bool hasPublicContextFinalColorData = false;
|
|
bool hasPublicContextStageColorData = false;
|
|
bool hasPublicCameraRequestContextHasDirectionalShadow = false;
|
|
bool hasPublicCameraRequestContextClearDirectionalShadow = false;
|
|
bool hasUniversalContextRecordSceneExtension = false;
|
|
bool hasUniversalContextRecordOpaqueScenePhaseExtension = false;
|
|
bool hasUniversalContextRecordBeforeOpaqueInjectionExtension = false;
|
|
bool hasUniversalContextRecordShaderVectorFullscreenPassExtension = false;
|
|
bool hasUniversalCameraRequestContextHasDirectionalShadowExtension = false;
|
|
bool hasUniversalCameraRequestContextClearDirectionalShadowExtension = false;
|
|
bool hasPublicPipelineAssetConfigureCameraFramePlan = false;
|
|
bool hasPlanningContextType = false;
|
|
bool hasRendererFeatureConfigureCameraFramePlan = false;
|
|
bool hasRendererRecordingContextType = false;
|
|
bool hasRendererCameraRequestContextType = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextRecordScene",
|
|
hasPublicContextRecordScene));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextRecordOpaqueScenePhase",
|
|
hasPublicContextRecordOpaqueScenePhase));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextRecordBeforeOpaqueInjection",
|
|
hasPublicContextRecordBeforeOpaqueInjection));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextRecordShaderVectorFullscreenPass",
|
|
hasPublicContextRecordShaderVectorFullscreenPass));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextCameraData",
|
|
hasPublicContextCameraData));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextLightingData",
|
|
hasPublicContextLightingData));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextShadowData",
|
|
hasPublicContextShadowData));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextEnvironmentData",
|
|
hasPublicContextEnvironmentData));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextFinalColorData",
|
|
hasPublicContextFinalColorData));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicContextStageColorData",
|
|
hasPublicContextStageColorData));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicCameraRequestContextHasDirectionalShadow",
|
|
hasPublicCameraRequestContextHasDirectionalShadow));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicCameraRequestContextClearDirectionalShadow",
|
|
hasPublicCameraRequestContextClearDirectionalShadow));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasUniversalContextRecordSceneExtension",
|
|
hasUniversalContextRecordSceneExtension));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasUniversalContextRecordOpaqueScenePhaseExtension",
|
|
hasUniversalContextRecordOpaqueScenePhaseExtension));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasUniversalContextRecordBeforeOpaqueInjectionExtension",
|
|
hasUniversalContextRecordBeforeOpaqueInjectionExtension));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasUniversalContextRecordShaderVectorFullscreenPassExtension",
|
|
hasUniversalContextRecordShaderVectorFullscreenPassExtension));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasUniversalCameraRequestContextHasDirectionalShadowExtension",
|
|
hasUniversalCameraRequestContextHasDirectionalShadowExtension));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasUniversalCameraRequestContextClearDirectionalShadowExtension",
|
|
hasUniversalCameraRequestContextClearDirectionalShadowExtension));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPublicPipelineAssetConfigureCameraFramePlan",
|
|
hasPublicPipelineAssetConfigureCameraFramePlan));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasPlanningContextType",
|
|
hasPlanningContextType));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasRendererFeatureConfigureCameraFramePlan",
|
|
hasRendererFeatureConfigureCameraFramePlan));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasRendererRecordingContextType",
|
|
hasRendererRecordingContextType));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"HasRendererCameraRequestContextType",
|
|
hasRendererCameraRequestContextType));
|
|
|
|
EXPECT_FALSE(hasPublicContextRecordScene);
|
|
EXPECT_FALSE(hasPublicContextRecordOpaqueScenePhase);
|
|
EXPECT_FALSE(hasPublicContextRecordBeforeOpaqueInjection);
|
|
EXPECT_FALSE(hasPublicContextRecordShaderVectorFullscreenPass);
|
|
EXPECT_FALSE(hasPublicContextCameraData);
|
|
EXPECT_FALSE(hasPublicContextLightingData);
|
|
EXPECT_FALSE(hasPublicContextShadowData);
|
|
EXPECT_FALSE(hasPublicContextEnvironmentData);
|
|
EXPECT_FALSE(hasPublicContextFinalColorData);
|
|
EXPECT_FALSE(hasPublicContextStageColorData);
|
|
EXPECT_FALSE(hasPublicCameraRequestContextHasDirectionalShadow);
|
|
EXPECT_FALSE(hasPublicCameraRequestContextClearDirectionalShadow);
|
|
EXPECT_TRUE(hasUniversalContextRecordSceneExtension);
|
|
EXPECT_TRUE(hasUniversalContextRecordOpaqueScenePhaseExtension);
|
|
EXPECT_TRUE(hasUniversalContextRecordBeforeOpaqueInjectionExtension);
|
|
EXPECT_TRUE(hasUniversalContextRecordShaderVectorFullscreenPassExtension);
|
|
EXPECT_TRUE(hasUniversalCameraRequestContextHasDirectionalShadowExtension);
|
|
EXPECT_TRUE(hasUniversalCameraRequestContextClearDirectionalShadowExtension);
|
|
EXPECT_FALSE(hasPublicPipelineAssetConfigureCameraFramePlan);
|
|
EXPECT_FALSE(hasPlanningContextType);
|
|
EXPECT_FALSE(hasRendererFeatureConfigureCameraFramePlan);
|
|
EXPECT_FALSE(hasRendererRecordingContextType);
|
|
EXPECT_FALSE(hasRendererCameraRequestContextType);
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderContextExposesCameraDataThroughRenderingData) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ManagedRenderContextCameraDataScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject("ManagedRenderContextCameraDataSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderContextCameraDataRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
camera->SetProjectionType(CameraProjectionType::Orthographic);
|
|
camera->SetFieldOfView(75.0f);
|
|
camera->SetOrthographicSize(7.5f);
|
|
camera->SetNearClipPlane(0.25f);
|
|
camera->SetFarClipPlane(512.0f);
|
|
camera->SetClearMode(CameraClearMode::DepthOnly);
|
|
camera->SetClearColor(
|
|
XCEngine::Math::Color(0.12f, 0.34f, 0.56f, 0.78f));
|
|
cameraObject->GetTransform()->SetLocalPosition(
|
|
XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(160u, 90u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const auto* pipelineAsset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(pipelineAsset, nullptr);
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().assemblyName, "GameScripts");
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "Gameplay");
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().className,
|
|
"ManagedRenderContextCameraDataProbeAsset");
|
|
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
const XCEngine::Rendering::RenderCameraData expectedCameraData =
|
|
XCEngine::Rendering::BuildRenderCameraData(
|
|
*camera,
|
|
160u,
|
|
90u);
|
|
|
|
XCEngine::Math::Vector4 observedViewRow0 = {};
|
|
XCEngine::Math::Vector4 observedViewRow1 = {};
|
|
XCEngine::Math::Vector4 observedViewRow2 = {};
|
|
XCEngine::Math::Vector4 observedViewRow3 = {};
|
|
XCEngine::Math::Vector4 observedProjectionRow0 = {};
|
|
XCEngine::Math::Vector4 observedProjectionRow1 = {};
|
|
XCEngine::Math::Vector4 observedProjectionRow2 = {};
|
|
XCEngine::Math::Vector4 observedProjectionRow3 = {};
|
|
XCEngine::Math::Vector4 observedViewProjectionRow0 = {};
|
|
XCEngine::Math::Vector4 observedViewProjectionRow1 = {};
|
|
XCEngine::Math::Vector4 observedViewProjectionRow2 = {};
|
|
XCEngine::Math::Vector4 observedViewProjectionRow3 = {};
|
|
XCEngine::Math::Vector3 observedWorldPosition = {};
|
|
XCEngine::Math::Vector4 observedClearColor = {};
|
|
int observedClearFlags = 0;
|
|
bool observedPerspectiveProjection = true;
|
|
float observedVerticalFovRadians = 0.0f;
|
|
float observedOrthographicSize = 0.0f;
|
|
float observedAspectRatio = 0.0f;
|
|
float observedNearClipPlane = 0.0f;
|
|
float observedFarClipPlane = 0.0f;
|
|
int observedViewportWidth = 0;
|
|
int observedViewportHeight = 0;
|
|
int observedRecordCallCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewRow0",
|
|
observedViewRow0));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewRow1",
|
|
observedViewRow1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewRow2",
|
|
observedViewRow2));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewRow3",
|
|
observedViewRow3));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedProjectionRow0",
|
|
observedProjectionRow0));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedProjectionRow1",
|
|
observedProjectionRow1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedProjectionRow2",
|
|
observedProjectionRow2));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedProjectionRow3",
|
|
observedProjectionRow3));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewProjectionRow0",
|
|
observedViewProjectionRow0));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewProjectionRow1",
|
|
observedViewProjectionRow1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewProjectionRow2",
|
|
observedViewProjectionRow2));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewProjectionRow3",
|
|
observedViewProjectionRow3));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedWorldPosition",
|
|
observedWorldPosition));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedClearColor",
|
|
observedClearColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedClearFlags",
|
|
observedClearFlags));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedPerspectiveProjection",
|
|
observedPerspectiveProjection));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedVerticalFovRadians",
|
|
observedVerticalFovRadians));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedOrthographicSize",
|
|
observedOrthographicSize));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedAspectRatio",
|
|
observedAspectRatio));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedNearClipPlane",
|
|
observedNearClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFarClipPlane",
|
|
observedFarClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewportWidth",
|
|
observedViewportWidth));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedViewportHeight",
|
|
observedViewportHeight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordCallCount",
|
|
observedRecordCallCount));
|
|
|
|
const auto expectObservedRowMatchesMatrix = [](
|
|
const XCEngine::Math::Vector4& observedRow,
|
|
const XCEngine::Math::Matrix4x4& expectedMatrix,
|
|
int rowIndex) {
|
|
ExpectVector4Near(
|
|
observedRow,
|
|
XCEngine::Math::Vector4(
|
|
expectedMatrix.m[rowIndex][0],
|
|
expectedMatrix.m[rowIndex][1],
|
|
expectedMatrix.m[rowIndex][2],
|
|
expectedMatrix.m[rowIndex][3]));
|
|
};
|
|
|
|
EXPECT_EQ(observedRecordCallCount, 1);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewRow0,
|
|
expectedCameraData.view,
|
|
0);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewRow1,
|
|
expectedCameraData.view,
|
|
1);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewRow2,
|
|
expectedCameraData.view,
|
|
2);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewRow3,
|
|
expectedCameraData.view,
|
|
3);
|
|
expectObservedRowMatchesMatrix(
|
|
observedProjectionRow0,
|
|
expectedCameraData.projection,
|
|
0);
|
|
expectObservedRowMatchesMatrix(
|
|
observedProjectionRow1,
|
|
expectedCameraData.projection,
|
|
1);
|
|
expectObservedRowMatchesMatrix(
|
|
observedProjectionRow2,
|
|
expectedCameraData.projection,
|
|
2);
|
|
expectObservedRowMatchesMatrix(
|
|
observedProjectionRow3,
|
|
expectedCameraData.projection,
|
|
3);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewProjectionRow0,
|
|
expectedCameraData.viewProjection,
|
|
0);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewProjectionRow1,
|
|
expectedCameraData.viewProjection,
|
|
1);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewProjectionRow2,
|
|
expectedCameraData.viewProjection,
|
|
2);
|
|
expectObservedRowMatchesMatrix(
|
|
observedViewProjectionRow3,
|
|
expectedCameraData.viewProjection,
|
|
3);
|
|
EXPECT_FLOAT_EQ(observedWorldPosition.x, 2.0f);
|
|
EXPECT_FLOAT_EQ(observedWorldPosition.y, 4.0f);
|
|
EXPECT_FLOAT_EQ(observedWorldPosition.z, 6.0f);
|
|
EXPECT_FLOAT_EQ(observedClearColor.x, 0.12f);
|
|
EXPECT_FLOAT_EQ(observedClearColor.y, 0.34f);
|
|
EXPECT_FLOAT_EQ(observedClearColor.z, 0.56f);
|
|
EXPECT_FLOAT_EQ(observedClearColor.w, 0.78f);
|
|
EXPECT_EQ(
|
|
observedClearFlags,
|
|
static_cast<int>(XCEngine::Rendering::RenderClearFlags::Depth));
|
|
EXPECT_FALSE(observedPerspectiveProjection);
|
|
EXPECT_FLOAT_EQ(
|
|
observedVerticalFovRadians,
|
|
75.0f * XCEngine::Math::DEG_TO_RAD);
|
|
EXPECT_FLOAT_EQ(observedOrthographicSize, 7.5f);
|
|
EXPECT_FLOAT_EQ(observedAspectRatio, 160.0f / 90.0f);
|
|
EXPECT_FLOAT_EQ(observedNearClipPlane, 0.25f);
|
|
EXPECT_FLOAT_EQ(observedFarClipPlane, 512.0f);
|
|
EXPECT_EQ(observedViewportWidth, 160);
|
|
EXPECT_EQ(observedViewportHeight, 90);
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderContextExposesLightingDataThroughRenderingData) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ManagedRenderContextLightingDataScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject(
|
|
"ManagedRenderContextLightingDataSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderContextLightingDataRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
|
|
GameObject* directionalLightObject =
|
|
runtimeScene->CreateGameObject("DirectionalLight");
|
|
auto* directionalLight =
|
|
directionalLightObject->AddComponent<LightComponent>();
|
|
ASSERT_NE(directionalLight, nullptr);
|
|
directionalLight->SetLightType(LightType::Directional);
|
|
directionalLight->SetColor(
|
|
XCEngine::Math::Color(0.25f, 0.50f, 0.75f, 1.0f));
|
|
directionalLight->SetIntensity(2.75f);
|
|
directionalLight->SetCastsShadows(true);
|
|
directionalLightObject->GetTransform()->SetLocalEulerAngles(
|
|
XCEngine::Math::Vector3(25.0f, -40.0f, 0.0f));
|
|
|
|
GameObject* pointLightObject =
|
|
runtimeScene->CreateGameObject("PointLight");
|
|
auto* pointLight =
|
|
pointLightObject->AddComponent<LightComponent>();
|
|
ASSERT_NE(pointLight, nullptr);
|
|
pointLight->SetLightType(LightType::Point);
|
|
pointLight->SetColor(
|
|
XCEngine::Math::Color(0.9f, 0.3f, 0.2f, 1.0f));
|
|
pointLight->SetIntensity(1.25f);
|
|
pointLight->SetRange(15.0f);
|
|
pointLight->SetCastsShadows(false);
|
|
pointLightObject->GetTransform()->SetLocalPosition(
|
|
XCEngine::Math::Vector3(3.0f, 2.0f, -1.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(192u, 108u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const auto* pipelineAsset =
|
|
dynamic_cast<
|
|
const XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset*>(
|
|
renderer.GetPipelineAsset());
|
|
ASSERT_NE(pipelineAsset, nullptr);
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().assemblyName, "GameScripts");
|
|
EXPECT_EQ(pipelineAsset->GetDescriptor().namespaceName, "Gameplay");
|
|
EXPECT_EQ(
|
|
pipelineAsset->GetDescriptor().className,
|
|
"ManagedRenderContextCameraDataProbeAsset");
|
|
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Rendering::RenderSceneExtractor extractor;
|
|
const XCEngine::Rendering::RenderSceneData expectedSceneData =
|
|
extractor.ExtractForCamera(
|
|
*runtimeScene,
|
|
*camera,
|
|
192u,
|
|
108u);
|
|
ASSERT_TRUE(expectedSceneData.HasCamera());
|
|
|
|
bool observedMainDirectionalLightEnabled = false;
|
|
bool observedMainDirectionalLightCastsShadows = false;
|
|
XCEngine::Math::Vector3 observedMainDirectionalLightDirection = {};
|
|
XCEngine::Math::Vector4 observedMainDirectionalLightColor = {};
|
|
float observedMainDirectionalLightIntensity = 0.0f;
|
|
bool observedHasMainDirectionalShadow = false;
|
|
int observedAdditionalLightCount = 0;
|
|
int observedRecordCallCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainDirectionalLightEnabled",
|
|
observedMainDirectionalLightEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainDirectionalLightCastsShadows",
|
|
observedMainDirectionalLightCastsShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainDirectionalLightDirection",
|
|
observedMainDirectionalLightDirection));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainDirectionalLightColor",
|
|
observedMainDirectionalLightColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainDirectionalLightIntensity",
|
|
observedMainDirectionalLightIntensity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedHasMainDirectionalShadow",
|
|
observedHasMainDirectionalShadow));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedAdditionalLightCount",
|
|
observedAdditionalLightCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordCallCount",
|
|
observedRecordCallCount));
|
|
|
|
EXPECT_EQ(observedRecordCallCount, 1);
|
|
EXPECT_EQ(
|
|
observedMainDirectionalLightEnabled,
|
|
expectedSceneData.lighting.mainDirectionalLight.enabled);
|
|
EXPECT_EQ(
|
|
observedMainDirectionalLightCastsShadows,
|
|
expectedSceneData.lighting.mainDirectionalLight.castsShadows);
|
|
ExpectVector3Near(
|
|
observedMainDirectionalLightDirection,
|
|
expectedSceneData.lighting.mainDirectionalLight.direction);
|
|
ExpectVector4Near(
|
|
observedMainDirectionalLightColor,
|
|
XCEngine::Math::Vector4(
|
|
expectedSceneData.lighting.mainDirectionalLight.color.r,
|
|
expectedSceneData.lighting.mainDirectionalLight.color.g,
|
|
expectedSceneData.lighting.mainDirectionalLight.color.b,
|
|
expectedSceneData.lighting.mainDirectionalLight.color.a));
|
|
EXPECT_FLOAT_EQ(
|
|
observedMainDirectionalLightIntensity,
|
|
expectedSceneData.lighting.mainDirectionalLight.intensity);
|
|
EXPECT_EQ(
|
|
observedHasMainDirectionalShadow,
|
|
plans[0].directionalShadow.IsValid());
|
|
EXPECT_EQ(
|
|
observedAdditionalLightCount,
|
|
static_cast<int>(expectedSceneData.lighting.additionalLightCount));
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderContextExposesShadowDataThroughRenderingData) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ManagedRenderContextShadowDataScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject(
|
|
"ManagedRenderContextShadowDataSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderContextShadowDataRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
|
|
GameObject* directionalLightObject =
|
|
runtimeScene->CreateGameObject("DirectionalLight");
|
|
auto* directionalLight =
|
|
directionalLightObject->AddComponent<LightComponent>();
|
|
ASSERT_NE(directionalLight, nullptr);
|
|
directionalLight->SetLightType(LightType::Directional);
|
|
directionalLight->SetColor(
|
|
XCEngine::Math::Color(0.25f, 0.50f, 0.75f, 1.0f));
|
|
directionalLight->SetIntensity(2.75f);
|
|
directionalLight->SetCastsShadows(true);
|
|
directionalLightObject->GetTransform()->SetLocalEulerAngles(
|
|
XCEngine::Math::Vector3(25.0f, -40.0f, 0.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(192u, 108u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
ASSERT_TRUE(plans[0].directionalShadow.IsValid());
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool observedShadowEnabled = false;
|
|
XCEngine::Math::Vector4 observedShadowViewProjectionRow0 = {};
|
|
XCEngine::Math::Vector4 observedShadowViewProjectionRow1 = {};
|
|
XCEngine::Math::Vector4 observedShadowViewProjectionRow2 = {};
|
|
XCEngine::Math::Vector4 observedShadowViewProjectionRow3 = {};
|
|
float observedShadowOrthographicHalfExtent = 0.0f;
|
|
float observedShadowNearClipPlane = 0.0f;
|
|
float observedShadowFarClipPlane = 0.0f;
|
|
int observedShadowMapWidth = 0;
|
|
int observedShadowMapHeight = 0;
|
|
float observedShadowWorldTexelSize = 0.0f;
|
|
float observedShadowReceiverDepthBias = 0.0f;
|
|
float observedShadowNormalBiasScale = 0.0f;
|
|
float observedShadowStrength = 0.0f;
|
|
float observedShadowDepthBiasFactor = 0.0f;
|
|
int observedShadowDepthBiasUnits = 0;
|
|
int observedRecordCallCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowEnabled",
|
|
observedShadowEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowViewProjectionRow0",
|
|
observedShadowViewProjectionRow0));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowViewProjectionRow1",
|
|
observedShadowViewProjectionRow1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowViewProjectionRow2",
|
|
observedShadowViewProjectionRow2));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowViewProjectionRow3",
|
|
observedShadowViewProjectionRow3));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowOrthographicHalfExtent",
|
|
observedShadowOrthographicHalfExtent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowNearClipPlane",
|
|
observedShadowNearClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowFarClipPlane",
|
|
observedShadowFarClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowMapWidth",
|
|
observedShadowMapWidth));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowMapHeight",
|
|
observedShadowMapHeight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowWorldTexelSize",
|
|
observedShadowWorldTexelSize));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowReceiverDepthBias",
|
|
observedShadowReceiverDepthBias));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowNormalBiasScale",
|
|
observedShadowNormalBiasScale));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowStrength",
|
|
observedShadowStrength));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowDepthBiasFactor",
|
|
observedShadowDepthBiasFactor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedShadowDepthBiasUnits",
|
|
observedShadowDepthBiasUnits));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordCallCount",
|
|
observedRecordCallCount));
|
|
|
|
const XCEngine::Rendering::DirectionalShadowRenderPlan&
|
|
expectedShadowPlan = plans[0].directionalShadow;
|
|
|
|
const auto expectObservedRowMatchesMatrix = [](
|
|
const XCEngine::Math::Vector4& observedRow,
|
|
const XCEngine::Math::Matrix4x4& expectedMatrix,
|
|
int rowIndex) {
|
|
ExpectVector4Near(
|
|
observedRow,
|
|
XCEngine::Math::Vector4(
|
|
expectedMatrix.m[rowIndex][0],
|
|
expectedMatrix.m[rowIndex][1],
|
|
expectedMatrix.m[rowIndex][2],
|
|
expectedMatrix.m[rowIndex][3]));
|
|
};
|
|
|
|
EXPECT_EQ(observedRecordCallCount, 1);
|
|
EXPECT_EQ(observedShadowEnabled, expectedShadowPlan.enabled);
|
|
expectObservedRowMatchesMatrix(
|
|
observedShadowViewProjectionRow0,
|
|
expectedShadowPlan.cameraData.viewProjection,
|
|
0);
|
|
expectObservedRowMatchesMatrix(
|
|
observedShadowViewProjectionRow1,
|
|
expectedShadowPlan.cameraData.viewProjection,
|
|
1);
|
|
expectObservedRowMatchesMatrix(
|
|
observedShadowViewProjectionRow2,
|
|
expectedShadowPlan.cameraData.viewProjection,
|
|
2);
|
|
expectObservedRowMatchesMatrix(
|
|
observedShadowViewProjectionRow3,
|
|
expectedShadowPlan.cameraData.viewProjection,
|
|
3);
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowOrthographicHalfExtent,
|
|
expectedShadowPlan.orthographicHalfExtent);
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowNearClipPlane,
|
|
expectedShadowPlan.nearClipPlane);
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowFarClipPlane,
|
|
expectedShadowPlan.farClipPlane);
|
|
EXPECT_EQ(observedShadowMapWidth, static_cast<int>(expectedShadowPlan.mapWidth));
|
|
EXPECT_EQ(observedShadowMapHeight, static_cast<int>(expectedShadowPlan.mapHeight));
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowWorldTexelSize,
|
|
expectedShadowPlan.texelWorldSize);
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowReceiverDepthBias,
|
|
expectedShadowPlan.sampling.receiverDepthBias);
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowNormalBiasScale,
|
|
expectedShadowPlan.sampling.normalBiasScale);
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowStrength,
|
|
expectedShadowPlan.sampling.shadowStrength);
|
|
EXPECT_FLOAT_EQ(
|
|
observedShadowDepthBiasFactor,
|
|
expectedShadowPlan.casterBias.depthBiasFactor);
|
|
EXPECT_EQ(
|
|
observedShadowDepthBiasUnits,
|
|
expectedShadowPlan.casterBias.depthBiasUnits);
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderContextExposesEnvironmentDataThroughRenderingData) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ManagedRenderContextEnvironmentDataScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject(
|
|
"ManagedRenderContextEnvironmentDataSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderContextEnvironmentDataRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
camera->SetProjectionType(CameraProjectionType::Perspective);
|
|
camera->SetClearMode(CameraClearMode::ColorAndDepth);
|
|
camera->SetSkyboxEnabled(true);
|
|
camera->SetSkyboxTopColor(
|
|
XCEngine::Math::Color(0.11f, 0.22f, 0.33f, 1.0f));
|
|
camera->SetSkyboxHorizonColor(
|
|
XCEngine::Math::Color(0.44f, 0.55f, 0.66f, 1.0f));
|
|
camera->SetSkyboxBottomColor(
|
|
XCEngine::Math::Color(0.77f, 0.88f, 0.99f, 1.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(200u, 120u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int observedEnvironmentMode = 0;
|
|
XCEngine::Math::Vector4 observedSkyboxTopColor = {};
|
|
XCEngine::Math::Vector4 observedSkyboxHorizonColor = {};
|
|
XCEngine::Math::Vector4 observedSkyboxBottomColor = {};
|
|
int observedRecordCallCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedEnvironmentMode",
|
|
observedEnvironmentMode));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedSkyboxTopColor",
|
|
observedSkyboxTopColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedSkyboxHorizonColor",
|
|
observedSkyboxHorizonColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedSkyboxBottomColor",
|
|
observedSkyboxBottomColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordCallCount",
|
|
observedRecordCallCount));
|
|
|
|
EXPECT_EQ(observedRecordCallCount, 1);
|
|
EXPECT_EQ(
|
|
observedEnvironmentMode,
|
|
static_cast<int>(
|
|
XCEngine::Rendering::RenderEnvironmentMode::ProceduralSkybox));
|
|
ExpectVector4Near(
|
|
observedSkyboxTopColor,
|
|
XCEngine::Math::Vector4(0.11f, 0.22f, 0.33f, 1.0f));
|
|
ExpectVector4Near(
|
|
observedSkyboxHorizonColor,
|
|
XCEngine::Math::Vector4(0.44f, 0.55f, 0.66f, 1.0f));
|
|
ExpectVector4Near(
|
|
observedSkyboxBottomColor,
|
|
XCEngine::Math::Vector4(0.77f, 0.88f, 0.99f, 1.0f));
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderContextExposesFinalColorDataThroughRenderingData) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ManagedRenderContextFinalColorDataScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject(
|
|
"ManagedRenderContextFinalColorDataSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderContextFinalColorDataRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
|
|
XCEngine::Rendering::FinalColorOverrideSettings finalColorOverrides = {};
|
|
finalColorOverrides.overrideExposureValue = true;
|
|
finalColorOverrides.exposureValue = 2.5f;
|
|
finalColorOverrides.overrideToneMappingMode = true;
|
|
finalColorOverrides.toneMappingMode =
|
|
XCEngine::Rendering::FinalColorToneMappingMode::Neutral;
|
|
finalColorOverrides.overrideFinalColorScale = true;
|
|
finalColorOverrides.finalColorScale =
|
|
XCEngine::Math::Vector4(0.80f, 0.95f, 1.05f, 1.0f);
|
|
camera->SetFinalColorOverrides(finalColorOverrides);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(160u, 90u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
const XCEngine::Rendering::ResolvedFinalColorPolicy&
|
|
expectedFinalColorPolicy = plans[0].finalColorPolicy;
|
|
|
|
int observedFinalColorOutputTransferMode = 0;
|
|
int observedFinalColorExposureMode = 0;
|
|
float observedFinalColorExposureValue = 0.0f;
|
|
int observedFinalColorToneMappingMode = 0;
|
|
XCEngine::Math::Vector4 observedFinalColorScale = {};
|
|
bool observedFinalColorHasPipelineDefaults = false;
|
|
bool observedFinalColorHasCameraOverrides = false;
|
|
bool observedFinalColorRequiresProcessing = false;
|
|
int observedRecordCallCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorOutputTransferMode",
|
|
observedFinalColorOutputTransferMode));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorExposureMode",
|
|
observedFinalColorExposureMode));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorExposureValue",
|
|
observedFinalColorExposureValue));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorToneMappingMode",
|
|
observedFinalColorToneMappingMode));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorScale",
|
|
observedFinalColorScale));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorHasPipelineDefaults",
|
|
observedFinalColorHasPipelineDefaults));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorHasCameraOverrides",
|
|
observedFinalColorHasCameraOverrides));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalColorRequiresProcessing",
|
|
observedFinalColorRequiresProcessing));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedRecordCallCount",
|
|
observedRecordCallCount));
|
|
|
|
EXPECT_EQ(observedRecordCallCount, 1);
|
|
EXPECT_EQ(
|
|
observedFinalColorOutputTransferMode,
|
|
static_cast<int>(expectedFinalColorPolicy.outputTransferMode));
|
|
EXPECT_EQ(
|
|
observedFinalColorExposureMode,
|
|
static_cast<int>(expectedFinalColorPolicy.exposureMode));
|
|
EXPECT_FLOAT_EQ(
|
|
observedFinalColorExposureValue,
|
|
expectedFinalColorPolicy.exposureValue);
|
|
EXPECT_EQ(
|
|
observedFinalColorToneMappingMode,
|
|
static_cast<int>(expectedFinalColorPolicy.toneMappingMode));
|
|
ExpectVector4Near(
|
|
observedFinalColorScale,
|
|
expectedFinalColorPolicy.finalColorScale);
|
|
EXPECT_EQ(
|
|
observedFinalColorHasPipelineDefaults,
|
|
expectedFinalColorPolicy.hasPipelineDefaults);
|
|
EXPECT_EQ(
|
|
observedFinalColorHasCameraOverrides,
|
|
expectedFinalColorPolicy.hasCameraOverrides);
|
|
EXPECT_EQ(
|
|
observedFinalColorRequiresProcessing,
|
|
expectedFinalColorPolicy.RequiresProcessing());
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderContextExposesStageColorDataThroughRenderingData) {
|
|
Scene* runtimeScene =
|
|
CreateScene("ManagedRenderContextStageColorDataScene");
|
|
GameObject* selectionObject =
|
|
runtimeScene->CreateGameObject(
|
|
"ManagedRenderContextStageColorDataSelection");
|
|
ScriptComponent* selectionScript =
|
|
AddScript(
|
|
selectionObject,
|
|
"Gameplay",
|
|
"ManagedRenderContextStageColorDataRuntimeSelectionProbe");
|
|
ASSERT_NE(selectionScript, nullptr);
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
ASSERT_NE(camera, nullptr);
|
|
camera->SetPrimary(true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
TestRenderDevice device;
|
|
TestRenderCommandList commandList;
|
|
TestRenderCommandQueue commandQueue;
|
|
TestRenderResourceView colorView(
|
|
XCEngine::RHI::ResourceViewType::RenderTarget,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
TestRenderResourceView depthView(
|
|
XCEngine::RHI::ResourceViewType::DepthStencil,
|
|
XCEngine::RHI::ResourceViewDimension::Texture2D,
|
|
XCEngine::RHI::Format::D32_Float);
|
|
|
|
const XCEngine::Rendering::RenderContext context =
|
|
CreateRenderContext(
|
|
device,
|
|
commandList,
|
|
commandQueue);
|
|
XCEngine::Rendering::RenderSurface surface(160u, 90u);
|
|
surface.SetColorAttachment(&colorView);
|
|
surface.SetDepthAttachment(&depthView);
|
|
|
|
XCEngine::Rendering::SceneRenderer renderer;
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
renderer.BuildFramePlans(
|
|
*runtimeScene,
|
|
nullptr,
|
|
context,
|
|
surface);
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
EXPECT_TRUE(
|
|
plans[0].IsFullscreenStageRequested(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
EXPECT_TRUE(
|
|
plans[0].IsFullscreenStageRequested(
|
|
XCEngine::Rendering::CameraFrameStage::FinalOutput));
|
|
ASSERT_TRUE(renderer.Render(plans));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int observedMainSceneSource = 0;
|
|
bool observedMainSceneUsesGraphManagedOutputColor = false;
|
|
int observedPostProcessSource = 0;
|
|
bool observedPostProcessUsesGraphManagedOutputColor = false;
|
|
int observedFinalOutputSource = 0;
|
|
bool observedFinalOutputUsesGraphManagedOutputColor = false;
|
|
int observedMainSceneRecordCallCount = 0;
|
|
int observedPostProcessRecordCallCount = 0;
|
|
int observedFinalOutputRecordCallCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainSceneSource",
|
|
observedMainSceneSource));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainSceneUsesGraphManagedOutputColor",
|
|
observedMainSceneUsesGraphManagedOutputColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedPostProcessSource",
|
|
observedPostProcessSource));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedPostProcessUsesGraphManagedOutputColor",
|
|
observedPostProcessUsesGraphManagedOutputColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalOutputSource",
|
|
observedFinalOutputSource));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalOutputUsesGraphManagedOutputColor",
|
|
observedFinalOutputUsesGraphManagedOutputColor));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedMainSceneRecordCallCount",
|
|
observedMainSceneRecordCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedPostProcessRecordCallCount",
|
|
observedPostProcessRecordCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(
|
|
selectionScript,
|
|
"ObservedFinalOutputRecordCallCount",
|
|
observedFinalOutputRecordCallCount));
|
|
|
|
EXPECT_EQ(observedMainSceneRecordCallCount, 1);
|
|
EXPECT_EQ(observedPostProcessRecordCallCount, 1);
|
|
EXPECT_EQ(observedFinalOutputRecordCallCount, 1);
|
|
EXPECT_EQ(
|
|
observedMainSceneSource,
|
|
static_cast<int>(
|
|
plans[0].ResolveStageColorSource(
|
|
XCEngine::Rendering::CameraFrameStage::MainScene)));
|
|
EXPECT_EQ(
|
|
observedMainSceneUsesGraphManagedOutputColor,
|
|
plans[0].UsesGraphManagedOutputColor(
|
|
XCEngine::Rendering::CameraFrameStage::MainScene));
|
|
EXPECT_EQ(
|
|
observedPostProcessSource,
|
|
static_cast<int>(
|
|
plans[0].ResolveStageColorSource(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess)));
|
|
EXPECT_EQ(
|
|
observedPostProcessUsesGraphManagedOutputColor,
|
|
plans[0].UsesGraphManagedOutputColor(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
EXPECT_EQ(
|
|
observedFinalOutputSource,
|
|
static_cast<int>(
|
|
plans[0].ResolveStageColorSource(
|
|
XCEngine::Rendering::CameraFrameStage::FinalOutput)));
|
|
EXPECT_EQ(
|
|
observedFinalOutputUsesGraphManagedOutputColor,
|
|
plans[0].UsesGraphManagedOutputColor(
|
|
XCEngine::Rendering::CameraFrameStage::FinalOutput));
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
RegistersManagedRenderPipelineBridgeThatCreatesManagedStageRecorders) {
|
|
const auto bridge =
|
|
XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge();
|
|
ASSERT_NE(bridge, nullptr);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedRenderPipelineProbeAsset"
|
|
};
|
|
|
|
std::shared_ptr<const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetRuntime>
|
|
assetRuntime = bridge->CreateAssetRuntime(descriptor);
|
|
ASSERT_NE(assetRuntime, nullptr);
|
|
std::unique_ptr<XCEngine::Rendering::RenderPipelineStageRecorder> recorder =
|
|
assetRuntime->CreateStageRecorder();
|
|
ASSERT_NE(recorder, nullptr);
|
|
|
|
const XCEngine::Rendering::RenderContext context = {};
|
|
EXPECT_TRUE(recorder->Initialize(context));
|
|
EXPECT_TRUE(
|
|
recorder->SupportsStageRenderGraph(
|
|
XCEngine::Rendering::CameraFrameStage::MainScene));
|
|
EXPECT_FALSE(
|
|
recorder->SupportsStageRenderGraph(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
|
|
recorder->Shutdown();
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedStageRecorderRecordsMainSceneThroughScriptableRenderContext) {
|
|
const auto bridge =
|
|
XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge();
|
|
ASSERT_NE(bridge, nullptr);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedRenderPipelineProbeAsset"
|
|
};
|
|
|
|
std::shared_ptr<const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetRuntime>
|
|
assetRuntime = bridge->CreateAssetRuntime(descriptor);
|
|
ASSERT_NE(assetRuntime, nullptr);
|
|
std::unique_ptr<XCEngine::Rendering::RenderPipelineStageRecorder> recorder =
|
|
assetRuntime->CreateStageRecorder();
|
|
ASSERT_NE(recorder, nullptr);
|
|
|
|
const XCEngine::Rendering::RenderContext renderContext = {};
|
|
ASSERT_TRUE(recorder->Initialize(renderContext));
|
|
ASSERT_TRUE(
|
|
recorder->SupportsStageRenderGraph(
|
|
XCEngine::Rendering::CameraFrameStage::MainScene));
|
|
|
|
XCEngine::Rendering::RenderGraph graph;
|
|
XCEngine::Rendering::RenderGraphBuilder graphBuilder(graph);
|
|
XCEngine::Rendering::RenderGraphTextureDesc colorDesc = {};
|
|
colorDesc.width = 64u;
|
|
colorDesc.height = 64u;
|
|
colorDesc.format =
|
|
static_cast<XCEngine::Core::uint32>(
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
XCEngine::Rendering::RenderGraphTextureDesc depthDesc = colorDesc;
|
|
depthDesc.format =
|
|
static_cast<XCEngine::Core::uint32>(
|
|
XCEngine::RHI::Format::D32_Float);
|
|
const XCEngine::Rendering::RenderGraphTextureHandle colorTarget =
|
|
graphBuilder.CreateTransientTexture("ManagedMainSceneColor", colorDesc);
|
|
const XCEngine::Rendering::RenderGraphTextureHandle depthTarget =
|
|
graphBuilder.CreateTransientTexture("ManagedMainSceneDepth", depthDesc);
|
|
|
|
const XCEngine::Rendering::RenderSceneData sceneData = {};
|
|
const XCEngine::Rendering::RenderSurface surface(64u, 64u);
|
|
bool executionSucceeded = true;
|
|
XCEngine::Rendering::RenderGraphBlackboard blackboard = {};
|
|
const XCEngine::Rendering::RenderPipelineStageRenderGraphContext graphContext = {
|
|
graphBuilder,
|
|
"ManagedMainScene",
|
|
XCEngine::Rendering::CameraFrameStage::MainScene,
|
|
renderContext,
|
|
sceneData,
|
|
surface,
|
|
nullptr,
|
|
nullptr,
|
|
XCEngine::RHI::ResourceStates::Common,
|
|
{},
|
|
{ colorTarget },
|
|
depthTarget,
|
|
{},
|
|
&executionSucceeded,
|
|
&blackboard
|
|
};
|
|
|
|
EXPECT_TRUE(recorder->RecordStageRenderGraph(graphContext));
|
|
|
|
XCEngine::Rendering::CompiledRenderGraph compiledGraph = {};
|
|
XCEngine::Containers::String errorMessage;
|
|
ASSERT_TRUE(
|
|
XCEngine::Rendering::RenderGraphCompiler::Compile(
|
|
graph,
|
|
compiledGraph,
|
|
&errorMessage))
|
|
<< errorMessage.CStr();
|
|
ASSERT_EQ(compiledGraph.GetPassCount(), 3u);
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(0).CStr(),
|
|
"ManagedMainScene.Opaque");
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(1).CStr(),
|
|
"ManagedMainScene.Skybox");
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(2).CStr(),
|
|
"ManagedMainScene.Transparent");
|
|
|
|
recorder->Shutdown();
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedStageRecorderRecordsShaderVectorPostProcessThroughScriptableRenderContext) {
|
|
const auto bridge =
|
|
XCEngine::Rendering::Pipelines::GetManagedRenderPipelineBridge();
|
|
ASSERT_NE(bridge, nullptr);
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedPostProcessRenderPipelineProbeAsset"
|
|
};
|
|
|
|
std::shared_ptr<const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetRuntime>
|
|
assetRuntime = bridge->CreateAssetRuntime(descriptor);
|
|
ASSERT_NE(assetRuntime, nullptr);
|
|
std::unique_ptr<XCEngine::Rendering::RenderPipelineStageRecorder> recorder =
|
|
assetRuntime->CreateStageRecorder();
|
|
ASSERT_NE(recorder, nullptr);
|
|
|
|
const XCEngine::Rendering::RenderContext renderContext = {};
|
|
ASSERT_TRUE(recorder->Initialize(renderContext));
|
|
EXPECT_FALSE(
|
|
recorder->SupportsStageRenderGraph(
|
|
XCEngine::Rendering::CameraFrameStage::MainScene));
|
|
ASSERT_TRUE(
|
|
recorder->SupportsStageRenderGraph(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
|
|
XCEngine::Rendering::RenderGraph graph;
|
|
XCEngine::Rendering::RenderGraphBuilder graphBuilder(graph);
|
|
XCEngine::Rendering::RenderGraphTextureDesc colorDesc = {};
|
|
colorDesc.width = 64u;
|
|
colorDesc.height = 64u;
|
|
colorDesc.format =
|
|
static_cast<XCEngine::Core::uint32>(
|
|
XCEngine::RHI::Format::R8G8B8A8_UNorm);
|
|
const XCEngine::Rendering::RenderGraphTextureHandle sourceColor =
|
|
graphBuilder.ImportTexture(
|
|
"ManagedPostProcessSource",
|
|
colorDesc,
|
|
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(801),
|
|
{});
|
|
const XCEngine::Rendering::RenderGraphTextureHandle outputColor =
|
|
graphBuilder.CreateTransientTexture("ManagedPostProcessOutput", colorDesc);
|
|
|
|
const XCEngine::Rendering::RenderSceneData sceneData = {};
|
|
const XCEngine::Rendering::RenderSurface surface(64u, 64u);
|
|
bool executionSucceeded = true;
|
|
XCEngine::Rendering::RenderGraphBlackboard blackboard = {};
|
|
XCEngine::Rendering::EmplaceCameraFrameRenderGraphFrameData(blackboard)
|
|
.resources.mainScene.color = sourceColor;
|
|
const XCEngine::Rendering::RenderPipelineStageRenderGraphContext graphContext = {
|
|
graphBuilder,
|
|
"ManagedPostProcess",
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess,
|
|
renderContext,
|
|
sceneData,
|
|
surface,
|
|
nullptr,
|
|
nullptr,
|
|
XCEngine::RHI::ResourceStates::Common,
|
|
{},
|
|
{ outputColor },
|
|
{},
|
|
{},
|
|
&executionSucceeded,
|
|
&blackboard
|
|
};
|
|
|
|
EXPECT_TRUE(recorder->RecordStageRenderGraph(graphContext));
|
|
|
|
XCEngine::Rendering::CompiledRenderGraph compiledGraph = {};
|
|
XCEngine::Containers::String errorMessage;
|
|
ASSERT_TRUE(
|
|
XCEngine::Rendering::RenderGraphCompiler::Compile(
|
|
graph,
|
|
compiledGraph,
|
|
&errorMessage))
|
|
<< errorMessage.CStr();
|
|
ASSERT_EQ(compiledGraph.GetPassCount(), 2u);
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(0).CStr(),
|
|
"ManagedPostProcess.Pass0");
|
|
EXPECT_STREQ(
|
|
compiledGraph.GetPassName(1).CStr(),
|
|
"ManagedPostProcess.Pass1");
|
|
|
|
recorder->Shutdown();
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderPipelineAssetPlansFullscreenStagesFromPipelineStageSupport) {
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedPlannedFullscreenRenderPipelineProbeAsset"
|
|
};
|
|
|
|
auto asset =
|
|
std::make_shared<
|
|
XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset>(
|
|
descriptor);
|
|
|
|
XCEngine::Rendering::CameraRenderRequest request = {};
|
|
request.context = {};
|
|
request.surface = XCEngine::Rendering::RenderSurface(64u, 64u);
|
|
request.surface.SetColorAttachment(
|
|
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
|
|
request.surface.SetDepthAttachment(
|
|
reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
|
|
|
XCEngine::Rendering::RenderPipelineHost host(asset);
|
|
const std::vector<XCEngine::Rendering::CameraFramePlan> plans =
|
|
host.BuildFramePlans({ request });
|
|
|
|
ASSERT_EQ(plans.size(), 1u);
|
|
const XCEngine::Rendering::CameraFramePlan& plan = plans[0];
|
|
|
|
EXPECT_TRUE(
|
|
plan.IsFullscreenStageRequested(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
EXPECT_TRUE(
|
|
plan.IsFullscreenStageRequested(
|
|
XCEngine::Rendering::CameraFrameStage::FinalOutput));
|
|
EXPECT_EQ(plan.postProcess.passes, nullptr);
|
|
EXPECT_EQ(plan.finalOutput.passes, nullptr);
|
|
EXPECT_TRUE(plan.UsesGraphManagedSceneColor());
|
|
EXPECT_TRUE(
|
|
plan.UsesGraphManagedOutputColor(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess));
|
|
EXPECT_EQ(
|
|
plan.ResolveStageColorSource(
|
|
XCEngine::Rendering::CameraFrameStage::PostProcess),
|
|
XCEngine::Rendering::CameraFrameColorSource::MainSceneColor);
|
|
EXPECT_EQ(
|
|
plan.ResolveStageColorSource(
|
|
XCEngine::Rendering::CameraFrameStage::FinalOutput),
|
|
XCEngine::Rendering::CameraFrameColorSource::PostProcessColor);
|
|
EXPECT_TRUE(plan.IsPostProcessStageValid());
|
|
EXPECT_TRUE(plan.IsFinalOutputStageValid());
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderPipelineAssetConfiguresCameraRequestsThroughRequestContext) {
|
|
Scene* runtimeScene = CreateScene("ManagedRenderPipelineCameraRequestScene");
|
|
|
|
GameObject* cameraObject = runtimeScene->CreateGameObject("Camera");
|
|
auto* camera = cameraObject->AddComponent<CameraComponent>();
|
|
camera->SetPrimary(true);
|
|
|
|
GameObject* lightObject = runtimeScene->CreateGameObject("Light");
|
|
auto* light = lightObject->AddComponent<LightComponent>();
|
|
light->SetLightType(LightType::Directional);
|
|
light->SetCastsShadows(true);
|
|
|
|
XCEngine::Rendering::CameraRenderRequest baselineRequest = {};
|
|
baselineRequest.scene = runtimeScene;
|
|
baselineRequest.camera = camera;
|
|
baselineRequest.surface = XCEngine::Rendering::RenderSurface(64u, 64u);
|
|
XCEngine::Rendering::ApplyDefaultRenderPipelineAssetCameraRenderRequestPolicy(
|
|
baselineRequest,
|
|
0u,
|
|
0u,
|
|
XCEngine::Rendering::DirectionalShadowPlanningSettings{});
|
|
ASSERT_TRUE(baselineRequest.directionalShadow.IsValid());
|
|
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedCameraRequestConfiguredRenderPipelineProbeAsset"
|
|
};
|
|
|
|
XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset asset(
|
|
descriptor);
|
|
|
|
XCEngine::Rendering::CameraRenderRequest request = {};
|
|
request.scene = runtimeScene;
|
|
request.camera = camera;
|
|
request.surface = XCEngine::Rendering::RenderSurface(64u, 64u);
|
|
asset.ConfigureCameraRenderRequest(
|
|
request,
|
|
0u,
|
|
0u,
|
|
XCEngine::Rendering::DirectionalShadowPlanningSettings{});
|
|
|
|
EXPECT_FALSE(request.directionalShadow.IsValid());
|
|
}
|
|
|
|
TEST_F(
|
|
MonoScriptRuntimeTest,
|
|
ManagedRenderPipelineAssetReturnsManagedDefaultFinalColorSettings) {
|
|
const XCEngine::Rendering::Pipelines::ManagedRenderPipelineAssetDescriptor descriptor = {
|
|
"GameScripts",
|
|
"Gameplay",
|
|
"ManagedFinalColorRenderPipelineProbeAsset"
|
|
};
|
|
|
|
XCEngine::Rendering::Pipelines::ManagedScriptableRenderPipelineAsset asset(
|
|
descriptor);
|
|
const XCEngine::Rendering::FinalColorSettings settings =
|
|
asset.GetDefaultFinalColorSettings();
|
|
|
|
EXPECT_EQ(
|
|
settings.outputTransferMode,
|
|
XCEngine::Rendering::FinalColorOutputTransferMode::LinearToSRGB);
|
|
EXPECT_EQ(
|
|
settings.exposureMode,
|
|
XCEngine::Rendering::FinalColorExposureMode::Fixed);
|
|
EXPECT_FLOAT_EQ(settings.exposureValue, 1.75f);
|
|
EXPECT_EQ(
|
|
settings.toneMappingMode,
|
|
XCEngine::Rendering::FinalColorToneMappingMode::ACES);
|
|
EXPECT_FLOAT_EQ(settings.finalColorScale.x, 0.90f);
|
|
EXPECT_FLOAT_EQ(settings.finalColorScale.y, 1.10f);
|
|
EXPECT_FLOAT_EQ(settings.finalColorScale.z, 1.20f);
|
|
EXPECT_FLOAT_EQ(settings.finalColorScale.w, 1.0f);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsNullComponentReferences) {
|
|
std::vector<ScriptFieldDefaultValue> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "Gameplay", "ComponentFieldMetadataProbe", fields));
|
|
|
|
const auto expectNullComponentField = [&](const char* fieldName) {
|
|
const auto fieldIt = std::find_if(
|
|
fields.begin(),
|
|
fields.end(),
|
|
[fieldName](const ScriptFieldDefaultValue& field) {
|
|
return field.fieldName == fieldName;
|
|
});
|
|
|
|
ASSERT_NE(fieldIt, fields.end());
|
|
EXPECT_EQ(fieldIt->type, ScriptFieldType::Component);
|
|
EXPECT_EQ(std::get<ComponentReference>(fieldIt->value), (ComponentReference{}));
|
|
};
|
|
|
|
expectNullComponentField("Pivot");
|
|
expectNullComponentField("SceneCamera");
|
|
expectNullComponentField("ScriptTarget");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineAppliesStoredFieldsAndInvokesLifecycleMethods) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("Speed", 5.0f);
|
|
component->GetFieldStorage().SetFieldValue("Label", "Configured");
|
|
component->GetFieldStorage().SetFieldValue("Target", GameObjectReference{target->GetUUID()});
|
|
component->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 1u);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t enableCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t fixedUpdateCount = 0;
|
|
int32_t updateCount = 0;
|
|
int32_t lateUpdateCount = 0;
|
|
bool wasAwakened = false;
|
|
bool warningLogged = false;
|
|
bool errorLogged = false;
|
|
bool hasTransform = false;
|
|
bool transformLookupSucceeded = false;
|
|
bool hasUnsupportedComponent = true;
|
|
bool unsupportedComponentLookupReturnedNull = false;
|
|
bool targetResolved = false;
|
|
bool rotationAccessed = false;
|
|
bool scaleAccessed = false;
|
|
bool transformAccessed = false;
|
|
bool observedEnabled = false;
|
|
bool observedActiveSelf = false;
|
|
bool observedActiveInHierarchy = false;
|
|
bool observedIsActiveAndEnabled = false;
|
|
float speed = 0.0f;
|
|
float observedFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTimeInUpdate = 0.0f;
|
|
float observedUpdateDeltaTime = 0.0f;
|
|
float observedLateDeltaTime = 0.0f;
|
|
std::string label;
|
|
std::string observedGameObjectName;
|
|
std::string observedTargetName;
|
|
GameObjectReference targetReference;
|
|
GameObjectReference selfReference;
|
|
XCEngine::Math::Vector4 observedLocalRotation;
|
|
XCEngine::Math::Vector3 observedLocalPosition;
|
|
XCEngine::Math::Vector3 observedLocalScale;
|
|
XCEngine::Math::Vector3 spawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FixedUpdateCount", fixedUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WasAwakened", wasAwakened));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WarningLogged", warningLogged));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ErrorLogged", errorLogged));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTransform", hasTransform));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformLookupSucceeded", transformLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasUnsupportedComponent", hasUnsupportedComponent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UnsupportedComponentLookupReturnedNull", unsupportedComponentLookupReturnedNull));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RotationAccessed", rotationAccessed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ScaleAccessed", scaleAccessed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformAccessed", transformAccessed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", speed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFixedDeltaTime", observedFixedDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTime", observedConfiguredFixedDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTimeInUpdate", observedConfiguredFixedDeltaTimeInUpdate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdateDeltaTime", observedUpdateDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLateDeltaTime", observedLateDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", label));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectName", observedGameObjectName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Target", targetReference));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SelfReference", selfReference));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalRotation", observedLocalRotation));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalPosition", observedLocalPosition));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalScale", observedLocalScale));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", spawnPoint));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(enableCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(fixedUpdateCount, 1);
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(lateUpdateCount, 1);
|
|
EXPECT_TRUE(wasAwakened);
|
|
EXPECT_TRUE(warningLogged);
|
|
EXPECT_TRUE(errorLogged);
|
|
EXPECT_TRUE(hasTransform);
|
|
EXPECT_TRUE(transformLookupSucceeded);
|
|
EXPECT_FALSE(hasUnsupportedComponent);
|
|
EXPECT_TRUE(unsupportedComponentLookupReturnedNull);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_TRUE(rotationAccessed);
|
|
EXPECT_TRUE(scaleAccessed);
|
|
EXPECT_TRUE(transformAccessed);
|
|
EXPECT_TRUE(observedEnabled);
|
|
EXPECT_TRUE(observedActiveSelf);
|
|
EXPECT_TRUE(observedActiveInHierarchy);
|
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
|
EXPECT_FLOAT_EQ(speed, 6.0f);
|
|
EXPECT_FLOAT_EQ(observedFixedDeltaTime, 0.02f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTime, 0.02f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTimeInUpdate, 0.02f);
|
|
EXPECT_FLOAT_EQ(observedUpdateDeltaTime, 0.016f);
|
|
EXPECT_FLOAT_EQ(observedLateDeltaTime, 0.016f);
|
|
EXPECT_EQ(label, "Configured|Awake");
|
|
EXPECT_EQ(observedGameObjectName, "Host_Managed");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(targetReference, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(selfReference, GameObjectReference{host->GetUUID()});
|
|
EXPECT_EQ(observedLocalRotation, XCEngine::Math::Vector4(0.0f, 0.5f, 0.0f, 0.8660254f));
|
|
EXPECT_EQ(observedLocalPosition, XCEngine::Math::Vector3(8.0f, 8.0f, 9.0f));
|
|
EXPECT_EQ(observedLocalScale, XCEngine::Math::Vector3(3.0f, 3.0f, 4.0f));
|
|
EXPECT_EQ(spawnPoint, XCEngine::Math::Vector3(3.0f, 4.0f, 6.0f));
|
|
EXPECT_EQ(host->GetName(), "Host_Managed");
|
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(8.0f, 8.0f, 9.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetLocalScale(), XCEngine::Math::Vector3(3.0f, 3.0f, 4.0f));
|
|
|
|
const XCEngine::Math::Quaternion& localRotation = host->GetTransform()->GetLocalRotation();
|
|
EXPECT_FLOAT_EQ(localRotation.x, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.y, 0.5f);
|
|
EXPECT_FLOAT_EQ(localRotation.z, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.w, 0.8660254f);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TimeFixedDeltaTimeUsesConfiguredRuntimeStepAcrossFixedAndVariableUpdates) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->SetRuntimeFixedDeltaTime(0.05f);
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.05f);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
float observedFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTimeInUpdate = 0.0f;
|
|
float observedUpdateDeltaTime = 0.0f;
|
|
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedFixedDeltaTime", observedFixedDeltaTime));
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTime", observedConfiguredFixedDeltaTime));
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTimeInUpdate", observedConfiguredFixedDeltaTimeInUpdate));
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdateDeltaTime", observedUpdateDeltaTime));
|
|
|
|
EXPECT_FLOAT_EQ(observedFixedDeltaTime, 0.05f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTime, 0.05f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTimeInUpdate, 0.05f);
|
|
EXPECT_FLOAT_EQ(observedUpdateDeltaTime, 0.016f);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, SceneRuntimeDispatchesManagedTriggerMessagesWithGameObjectArgumentsAndFallbacks) {
|
|
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
|
|
GTEST_SKIP() << "PhysX is not available in this configuration.";
|
|
}
|
|
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* mover = runtimeScene->CreateGameObject("Mover");
|
|
mover->AddComponent<SphereColliderComponent>();
|
|
RigidbodyComponent* rigidbody = mover->AddComponent<RigidbodyComponent>();
|
|
rigidbody->SetUseGravity(false);
|
|
ScriptComponent* component = AddScript(mover, "Gameplay", "PhysicsEventProbe");
|
|
mover->GetTransform()->SetPosition(XCEngine::Math::Vector3(-3.0f, 0.0f, 0.0f));
|
|
|
|
GameObject* triggerZone = runtimeScene->CreateGameObject("TriggerZone");
|
|
BoxColliderComponent* triggerCollider = triggerZone->AddComponent<BoxColliderComponent>();
|
|
triggerCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
|
triggerCollider->SetTrigger(true);
|
|
|
|
SceneRuntime sceneRuntime;
|
|
sceneRuntime.Start(runtimeScene);
|
|
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
|
|
int32_t triggerEnterCount = 0;
|
|
int32_t triggerStayCount = 0;
|
|
int32_t triggerExitCount = 0;
|
|
bool triggerStayFallbackInvoked = false;
|
|
std::string lastTriggerOtherName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerEnterCount", triggerEnterCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerStayCount", triggerStayCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerExitCount", triggerExitCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TriggerStayFallbackInvoked", triggerStayFallbackInvoked));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LastTriggerOtherName", lastTriggerOtherName));
|
|
|
|
EXPECT_EQ(triggerEnterCount, 1);
|
|
EXPECT_EQ(triggerStayCount, 2);
|
|
EXPECT_EQ(triggerExitCount, 1);
|
|
EXPECT_TRUE(triggerStayFallbackInvoked);
|
|
EXPECT_EQ(lastTriggerOtherName, "TriggerZone");
|
|
|
|
sceneRuntime.Stop();
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, SceneRuntimeDispatchesManagedCollisionMessagesWithGameObjectArgumentsAndFallbacks) {
|
|
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
|
|
GTEST_SKIP() << "PhysX is not available in this configuration.";
|
|
}
|
|
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* mover = runtimeScene->CreateGameObject("Mover");
|
|
mover->AddComponent<SphereColliderComponent>();
|
|
RigidbodyComponent* rigidbody = mover->AddComponent<RigidbodyComponent>();
|
|
rigidbody->SetUseGravity(false);
|
|
ScriptComponent* component = AddScript(mover, "Gameplay", "PhysicsEventProbe");
|
|
mover->GetTransform()->SetPosition(XCEngine::Math::Vector3(-3.0f, 0.0f, 0.0f));
|
|
|
|
GameObject* wall = runtimeScene->CreateGameObject("Wall");
|
|
BoxColliderComponent* wallCollider = wall->AddComponent<BoxColliderComponent>();
|
|
wallCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
|
|
|
SceneRuntime sceneRuntime;
|
|
sceneRuntime.Start(runtimeScene);
|
|
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(150.0f, 0.0f, 0.0f));
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3(-150.0f, 0.0f, 0.0f));
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
rigidbody->SetLinearVelocity(XCEngine::Math::Vector3::Zero());
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
|
|
int32_t collisionEnterCount = 0;
|
|
int32_t collisionStayCount = 0;
|
|
int32_t collisionExitCount = 0;
|
|
bool collisionStayFallbackInvoked = false;
|
|
std::string lastCollisionOtherName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionEnterCount", collisionEnterCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionStayCount", collisionStayCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionExitCount", collisionExitCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CollisionStayFallbackInvoked", collisionStayFallbackInvoked));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LastCollisionOtherName", lastCollisionOtherName));
|
|
|
|
EXPECT_EQ(collisionEnterCount, 1);
|
|
EXPECT_EQ(collisionStayCount, 2);
|
|
EXPECT_EQ(collisionExitCount, 1);
|
|
EXPECT_TRUE(collisionStayFallbackInvoked);
|
|
EXPECT_EQ(lastCollisionOtherName, "Wall");
|
|
|
|
sceneRuntime.Stop();
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedRigidbodyWrapperAndPhysicsRaycastOperateAgainstRuntimePhysicsWorld) {
|
|
if (!XCEngine::Physics::PhysicsWorld::IsPhysXAvailable()) {
|
|
GTEST_SKIP() << "PhysX is not available in this configuration.";
|
|
}
|
|
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
host->AddComponent<SphereColliderComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "PhysicsApiProbe");
|
|
|
|
GameObject* rayTarget = runtimeScene->CreateGameObject("RayTarget");
|
|
rayTarget->GetTransform()->SetPosition(XCEngine::Math::Vector3(0.0f, 3.0f, -4.0f));
|
|
BoxColliderComponent* targetCollider = rayTarget->AddComponent<BoxColliderComponent>();
|
|
targetCollider->SetSize(XCEngine::Math::Vector3(2.0f, 2.0f, 0.5f));
|
|
|
|
SceneRuntime sceneRuntime;
|
|
sceneRuntime.Start(runtimeScene);
|
|
sceneRuntime.Update(0.016f);
|
|
sceneRuntime.FixedUpdate(0.02f);
|
|
|
|
bool initialHasRigidbody = true;
|
|
bool addedRigidbody = false;
|
|
bool hasRigidbodyAfterAdd = false;
|
|
bool rigidbodyLookupSucceeded = false;
|
|
int32_t observedBodyType = -1;
|
|
float observedMass = 0.0f;
|
|
float observedLinearDamping = 0.0f;
|
|
float observedAngularDamping = 0.0f;
|
|
bool observedUseGravity = true;
|
|
bool observedEnableCCD = false;
|
|
XCEngine::Math::Vector3 observedLinearVelocity;
|
|
XCEngine::Math::Vector3 observedAngularVelocity;
|
|
bool raycastHitDetected = false;
|
|
std::string raycastHitName;
|
|
float raycastHitDistance = 0.0f;
|
|
bool raycastHitWasTrigger = true;
|
|
XCEngine::Math::Vector3 raycastHitPoint;
|
|
XCEngine::Math::Vector3 raycastHitNormal;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasRigidbody", initialHasRigidbody));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedRigidbody", addedRigidbody));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasRigidbodyAfterAdd", hasRigidbodyAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RigidbodyLookupSucceeded", rigidbodyLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedBodyType", observedBodyType));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMass", observedMass));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLinearDamping", observedLinearDamping));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAngularDamping", observedAngularDamping));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUseGravity", observedUseGravity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnableCCD", observedEnableCCD));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLinearVelocity", observedLinearVelocity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAngularVelocity", observedAngularVelocity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitDetected", raycastHitDetected));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitName", raycastHitName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitDistance", raycastHitDistance));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitWasTrigger", raycastHitWasTrigger));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitPoint", raycastHitPoint));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RaycastHitNormal", raycastHitNormal));
|
|
|
|
EXPECT_FALSE(initialHasRigidbody);
|
|
EXPECT_TRUE(addedRigidbody);
|
|
EXPECT_TRUE(hasRigidbodyAfterAdd);
|
|
EXPECT_TRUE(rigidbodyLookupSucceeded);
|
|
EXPECT_EQ(observedBodyType, static_cast<int32_t>(XCEngine::Physics::PhysicsBodyType::Dynamic));
|
|
EXPECT_FLOAT_EQ(observedMass, 2.5f);
|
|
EXPECT_FLOAT_EQ(observedLinearDamping, 0.25f);
|
|
EXPECT_FLOAT_EQ(observedAngularDamping, 0.5f);
|
|
EXPECT_FALSE(observedUseGravity);
|
|
EXPECT_TRUE(observedEnableCCD);
|
|
ExpectVector3Near(observedLinearVelocity, XCEngine::Math::Vector3(0.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedAngularVelocity, XCEngine::Math::Vector3(0.0f, 1.0f, 0.0f));
|
|
|
|
EXPECT_TRUE(raycastHitDetected);
|
|
EXPECT_EQ(raycastHitName, "RayTarget");
|
|
EXPECT_NEAR(raycastHitDistance, 3.75f, 0.05f);
|
|
EXPECT_FALSE(raycastHitWasTrigger);
|
|
ExpectVector3Near(raycastHitPoint, XCEngine::Math::Vector3(0.0f, 3.0f, -3.75f), 0.05f);
|
|
ExpectVector3Near(raycastHitNormal, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f), 0.01f);
|
|
|
|
EXPECT_NE(sceneRuntime.GetPhysicsWorld(), nullptr);
|
|
EXPECT_EQ(sceneRuntime.GetPhysicsWorld()->GetTrackedRigidbodyCount(), 1u);
|
|
|
|
RigidbodyComponent* rigidbody = host->GetComponent<RigidbodyComponent>();
|
|
ASSERT_NE(rigidbody, nullptr);
|
|
EXPECT_EQ(host->GetComponents<RigidbodyComponent>().size(), 1u);
|
|
EXPECT_EQ(rigidbody->GetBodyType(), XCEngine::Physics::PhysicsBodyType::Dynamic);
|
|
EXPECT_FLOAT_EQ(rigidbody->GetMass(), 2.5f);
|
|
EXPECT_FLOAT_EQ(rigidbody->GetLinearDamping(), 0.25f);
|
|
EXPECT_FLOAT_EQ(rigidbody->GetAngularDamping(), 0.5f);
|
|
EXPECT_FALSE(rigidbody->GetUseGravity());
|
|
EXPECT_TRUE(rigidbody->GetEnableCCD());
|
|
EXPECT_GT(rigidbody->GetLinearVelocity().x, 0.0f);
|
|
|
|
const XCEngine::Math::Vector3 hostPosition = host->GetTransform()->GetPosition();
|
|
EXPECT_GT(hostPosition.x, 0.01f);
|
|
EXPECT_NEAR(hostPosition.y, 0.0f, 0.01f);
|
|
|
|
sceneRuntime.Stop();
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedInputApiReadsCurrentNativeInputManagerState) {
|
|
XCEngine::Input::InputManager& inputManager = XCEngine::Input::InputManager::Get();
|
|
inputManager.Initialize(nullptr);
|
|
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "InputProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
inputManager.ProcessKeyDown(XCEngine::Input::KeyCode::A, false, false, false, false, false);
|
|
inputManager.ProcessKeyDown(XCEngine::Input::KeyCode::Space, false, false, false, false, false);
|
|
inputManager.ProcessKeyDown(XCEngine::Input::KeyCode::LeftCtrl, false, false, false, false, false);
|
|
inputManager.ProcessMouseButton(XCEngine::Input::MouseButton::Left, true, 120, 48);
|
|
inputManager.ProcessMouseMove(120, 48, 3, -2);
|
|
inputManager.ProcessMouseWheel(1.0f, 120, 48);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t updateCount = 0;
|
|
bool observedKeyA = false;
|
|
bool observedKeyADown = false;
|
|
bool observedKeyAUp = false;
|
|
bool observedKeySpace = false;
|
|
bool observedJump = false;
|
|
bool observedJumpDown = false;
|
|
bool observedJumpUp = false;
|
|
bool observedFire1 = false;
|
|
bool observedFire1Down = false;
|
|
bool observedFire1Up = false;
|
|
bool observedAnyKey = false;
|
|
bool observedAnyKeyDown = false;
|
|
bool observedLeftMouse = false;
|
|
bool observedLeftMouseDown = false;
|
|
bool observedLeftMouseUp = false;
|
|
float observedHorizontal = 0.0f;
|
|
float observedHorizontalRaw = 0.0f;
|
|
XCEngine::Math::Vector2 observedMouseScrollDelta;
|
|
XCEngine::Math::Vector3 observedMousePosition;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyA", observedKeyA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyADown", observedKeyADown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyAUp", observedKeyAUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeySpace", observedKeySpace));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpDown", observedJumpDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1", observedFire1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Down", observedFire1Down));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Up", observedFire1Up));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouse", observedLeftMouse));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseDown", observedLeftMouseDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseUp", observedLeftMouseUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontal", observedHorizontal));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontalRaw", observedHorizontalRaw));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMousePosition", observedMousePosition));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMouseScrollDelta", observedMouseScrollDelta));
|
|
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_TRUE(observedKeyA);
|
|
EXPECT_TRUE(observedKeyADown);
|
|
EXPECT_FALSE(observedKeyAUp);
|
|
EXPECT_TRUE(observedKeySpace);
|
|
EXPECT_TRUE(observedJump);
|
|
EXPECT_TRUE(observedJumpDown);
|
|
EXPECT_FALSE(observedJumpUp);
|
|
EXPECT_TRUE(observedFire1);
|
|
EXPECT_TRUE(observedFire1Down);
|
|
EXPECT_FALSE(observedFire1Up);
|
|
EXPECT_TRUE(observedAnyKey);
|
|
EXPECT_TRUE(observedAnyKeyDown);
|
|
EXPECT_TRUE(observedLeftMouse);
|
|
EXPECT_TRUE(observedLeftMouseDown);
|
|
EXPECT_FALSE(observedLeftMouseUp);
|
|
EXPECT_FLOAT_EQ(observedHorizontal, -1.0f);
|
|
EXPECT_FLOAT_EQ(observedHorizontalRaw, -1.0f);
|
|
EXPECT_EQ(observedMousePosition, XCEngine::Math::Vector3(120.0f, 48.0f, 0.0f));
|
|
EXPECT_EQ(observedMouseScrollDelta, XCEngine::Math::Vector2(0.0f, 1.0f));
|
|
|
|
inputManager.Update(0.016f);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyA", observedKeyA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyADown", observedKeyADown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyAUp", observedKeyAUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpDown", observedJumpDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1", observedFire1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Down", observedFire1Down));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Up", observedFire1Up));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouse", observedLeftMouse));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseDown", observedLeftMouseDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseUp", observedLeftMouseUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontalRaw", observedHorizontalRaw));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMouseScrollDelta", observedMouseScrollDelta));
|
|
|
|
EXPECT_EQ(updateCount, 2);
|
|
EXPECT_TRUE(observedKeyA);
|
|
EXPECT_FALSE(observedKeyADown);
|
|
EXPECT_FALSE(observedKeyAUp);
|
|
EXPECT_TRUE(observedJump);
|
|
EXPECT_FALSE(observedJumpDown);
|
|
EXPECT_FALSE(observedJumpUp);
|
|
EXPECT_TRUE(observedFire1);
|
|
EXPECT_FALSE(observedFire1Down);
|
|
EXPECT_FALSE(observedFire1Up);
|
|
EXPECT_TRUE(observedAnyKey);
|
|
EXPECT_FALSE(observedAnyKeyDown);
|
|
EXPECT_TRUE(observedLeftMouse);
|
|
EXPECT_FALSE(observedLeftMouseDown);
|
|
EXPECT_FALSE(observedLeftMouseUp);
|
|
EXPECT_FLOAT_EQ(observedHorizontalRaw, -1.0f);
|
|
EXPECT_EQ(observedMouseScrollDelta, XCEngine::Math::Vector2(0.0f, 0.0f));
|
|
|
|
inputManager.ProcessKeyUp(XCEngine::Input::KeyCode::A, false, false, false, false);
|
|
inputManager.ProcessKeyUp(XCEngine::Input::KeyCode::LeftCtrl, false, false, false, false);
|
|
inputManager.ProcessMouseButton(XCEngine::Input::MouseButton::Left, false, 120, 48);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyA", observedKeyA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyAUp", observedKeyAUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1", observedFire1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Up", observedFire1Up));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouse", observedLeftMouse));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseUp", observedLeftMouseUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontal", observedHorizontal));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontalRaw", observedHorizontalRaw));
|
|
|
|
EXPECT_EQ(updateCount, 3);
|
|
EXPECT_FALSE(observedKeyA);
|
|
EXPECT_TRUE(observedKeyAUp);
|
|
EXPECT_TRUE(observedJump);
|
|
EXPECT_FALSE(observedJumpUp);
|
|
EXPECT_FALSE(observedFire1);
|
|
EXPECT_TRUE(observedFire1Up);
|
|
EXPECT_TRUE(observedAnyKey);
|
|
EXPECT_FALSE(observedAnyKeyDown);
|
|
EXPECT_FALSE(observedLeftMouse);
|
|
EXPECT_TRUE(observedLeftMouseUp);
|
|
EXPECT_FLOAT_EQ(observedHorizontal, 0.0f);
|
|
EXPECT_FLOAT_EQ(observedHorizontalRaw, 0.0f);
|
|
|
|
inputManager.Update(0.016f);
|
|
inputManager.ProcessKeyUp(XCEngine::Input::KeyCode::Space, false, false, false, false);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeySpace", observedKeySpace));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
|
|
EXPECT_EQ(updateCount, 4);
|
|
EXPECT_FALSE(observedKeySpace);
|
|
EXPECT_FALSE(observedJump);
|
|
EXPECT_TRUE(observedJumpUp);
|
|
EXPECT_FALSE(observedAnyKey);
|
|
EXPECT_FALSE(observedAnyKeyDown);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedDebugLogBridgeWritesLifecycleTickMessagesToNativeLogger) {
|
|
auto sink = std::make_unique<CapturingLogSink>();
|
|
CapturingLogSink* sinkPtr = sink.get();
|
|
XCEngine::Debug::Logger::Get().AddSink(std::move(sink));
|
|
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TickLogProbe");
|
|
ASSERT_NE(component, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
const std::vector<std::string> messages = sinkPtr->CollectMessagesWithPrefix("[TickLogProbe]");
|
|
const std::vector<std::string> expected = {
|
|
"[TickLogProbe] Awake",
|
|
"[TickLogProbe] FixedUpdate 1",
|
|
"[TickLogProbe] Start",
|
|
"[TickLogProbe] Update 1",
|
|
"[TickLogProbe] LateUpdate 1",
|
|
};
|
|
|
|
EXPECT_EQ(messages, expected);
|
|
|
|
XCEngine::Debug::Logger::Get().RemoveSink(sinkPtr);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, DeserializedSceneRebindsManagedScriptsAndRestoresStoredFields) {
|
|
Scene originalScene("SerializedMonoScene");
|
|
GameObject* hostA = originalScene.CreateGameObject("HostA");
|
|
GameObject* hostB = originalScene.CreateGameObject("HostB");
|
|
GameObject* targetA = originalScene.CreateGameObject("TargetA");
|
|
GameObject* targetB = originalScene.CreateGameObject("TargetB");
|
|
|
|
ScriptComponent* originalComponentA = AddScript(hostA, "Gameplay", "LifecycleProbe");
|
|
ScriptComponent* originalComponentB = AddScript(hostB, "Gameplay", "LifecycleProbe");
|
|
|
|
originalComponentA->GetFieldStorage().SetFieldValue("Speed", 2.5f);
|
|
originalComponentA->GetFieldStorage().SetFieldValue("Label", "Alpha");
|
|
originalComponentA->GetFieldStorage().SetFieldValue("Target", GameObjectReference{targetA->GetUUID()});
|
|
originalComponentA->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
|
|
originalComponentB->GetFieldStorage().SetFieldValue("Speed", 9.0f);
|
|
originalComponentB->GetFieldStorage().SetFieldValue("Label", "Beta");
|
|
originalComponentB->GetFieldStorage().SetFieldValue("Target", GameObjectReference{targetB->GetUUID()});
|
|
originalComponentB->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(4.0f, 5.0f, 6.0f));
|
|
|
|
const uint64_t originalUUIDA = originalComponentA->GetScriptComponentUUID();
|
|
const uint64_t originalUUIDB = originalComponentB->GetScriptComponentUUID();
|
|
const std::string serializedScene = originalScene.SerializeToString();
|
|
|
|
scene = std::make_unique<Scene>("LoadedMonoScene");
|
|
scene->DeserializeFromString(serializedScene);
|
|
Scene* runtimeScene = scene.get();
|
|
|
|
GameObject* loadedHostA = runtimeScene->Find("HostA");
|
|
GameObject* loadedHostB = runtimeScene->Find("HostB");
|
|
GameObject* loadedTargetA = runtimeScene->Find("TargetA");
|
|
GameObject* loadedTargetB = runtimeScene->Find("TargetB");
|
|
ASSERT_NE(loadedHostA, nullptr);
|
|
ASSERT_NE(loadedHostB, nullptr);
|
|
ASSERT_NE(loadedTargetA, nullptr);
|
|
ASSERT_NE(loadedTargetB, nullptr);
|
|
|
|
ScriptComponent* loadedComponentA = FindScriptComponentByClass(loadedHostA, "Gameplay", "LifecycleProbe");
|
|
ScriptComponent* loadedComponentB = FindScriptComponentByClass(loadedHostB, "Gameplay", "LifecycleProbe");
|
|
ASSERT_NE(loadedComponentA, nullptr);
|
|
ASSERT_NE(loadedComponentB, nullptr);
|
|
|
|
EXPECT_EQ(loadedComponentA->GetScriptComponentUUID(), originalUUIDA);
|
|
EXPECT_EQ(loadedComponentB->GetScriptComponentUUID(), originalUUIDB);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(loadedComponentA));
|
|
EXPECT_TRUE(runtime->HasManagedInstance(loadedComponentB));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 2u);
|
|
|
|
int32_t awakeCountA = 0;
|
|
int32_t startCountA = 0;
|
|
int32_t awakeCountB = 0;
|
|
int32_t startCountB = 0;
|
|
bool targetResolvedA = false;
|
|
bool targetResolvedB = false;
|
|
float speedA = 0.0f;
|
|
float speedB = 0.0f;
|
|
std::string labelA;
|
|
std::string labelB;
|
|
std::string observedTargetNameA;
|
|
std::string observedTargetNameB;
|
|
GameObjectReference targetReferenceA;
|
|
GameObjectReference targetReferenceB;
|
|
GameObjectReference selfReferenceA;
|
|
GameObjectReference selfReferenceB;
|
|
XCEngine::Math::Vector3 spawnPointA;
|
|
XCEngine::Math::Vector3 spawnPointB;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "AwakeCount", awakeCountA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "StartCount", startCountA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "TargetResolved", targetResolvedA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "Speed", speedA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "Label", labelA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "ObservedTargetName", observedTargetNameA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "Target", targetReferenceA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "SelfReference", selfReferenceA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "SpawnPoint", spawnPointA));
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "AwakeCount", awakeCountB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "StartCount", startCountB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "TargetResolved", targetResolvedB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "Speed", speedB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "Label", labelB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "ObservedTargetName", observedTargetNameB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "Target", targetReferenceB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "SelfReference", selfReferenceB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "SpawnPoint", spawnPointB));
|
|
|
|
EXPECT_EQ(awakeCountA, 1);
|
|
EXPECT_EQ(startCountA, 1);
|
|
EXPECT_TRUE(targetResolvedA);
|
|
EXPECT_FLOAT_EQ(speedA, 3.5f);
|
|
EXPECT_EQ(labelA, "Alpha|Awake");
|
|
EXPECT_EQ(observedTargetNameA, "TargetA");
|
|
EXPECT_EQ(targetReferenceA, GameObjectReference{loadedTargetA->GetUUID()});
|
|
EXPECT_EQ(selfReferenceA, GameObjectReference{loadedHostA->GetUUID()});
|
|
EXPECT_EQ(spawnPointA, XCEngine::Math::Vector3(2.0f, 2.0f, 3.0f));
|
|
|
|
EXPECT_EQ(awakeCountB, 1);
|
|
EXPECT_EQ(startCountB, 1);
|
|
EXPECT_TRUE(targetResolvedB);
|
|
EXPECT_FLOAT_EQ(speedB, 10.0f);
|
|
EXPECT_EQ(labelB, "Beta|Awake");
|
|
EXPECT_EQ(observedTargetNameB, "TargetB");
|
|
EXPECT_EQ(targetReferenceB, GameObjectReference{loadedTargetB->GetUUID()});
|
|
EXPECT_EQ(selfReferenceB, GameObjectReference{loadedHostB->GetUUID()});
|
|
EXPECT_EQ(spawnPointB, XCEngine::Math::Vector3(5.0f, 5.0f, 6.0f));
|
|
|
|
EXPECT_EQ(loadedHostA->GetName(), "HostA_Managed");
|
|
EXPECT_EQ(loadedHostB->GetName(), "HostB_Managed");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedFieldChangesWriteBackToStoredCacheAndPersistAcrossSceneRoundTrip) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("Speed", 5.0f);
|
|
component->GetFieldStorage().SetFieldValue("Label", "Configured");
|
|
component->GetFieldStorage().SetFieldValue("Target", GameObjectReference{target->GetUUID()});
|
|
component->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
float storedSpeed = 0.0f;
|
|
std::string storedLabel;
|
|
GameObjectReference storedTarget;
|
|
XCEngine::Math::Vector3 storedSpawnPoint;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeed));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabel));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTarget));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPoint));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeed, 6.0f);
|
|
EXPECT_EQ(storedLabel, "Configured|Awake");
|
|
EXPECT_EQ(storedTarget, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPoint, XCEngine::Math::Vector3(3.0f, 4.0f, 6.0f));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("AwakeCount"));
|
|
|
|
const std::string persistedHostName = host->GetName();
|
|
const std::string serializedScene = runtimeScene->SerializeToString();
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
scene = std::make_unique<Scene>("ReloadedMonoScene");
|
|
scene->DeserializeFromString(serializedScene);
|
|
Scene* reloadedScene = scene.get();
|
|
|
|
GameObject* loadedHost = reloadedScene->Find(persistedHostName);
|
|
GameObject* loadedTarget = reloadedScene->Find("Target");
|
|
ASSERT_NE(loadedHost, nullptr);
|
|
ASSERT_NE(loadedTarget, nullptr);
|
|
|
|
ScriptComponent* loadedComponent = FindScriptComponentByClass(loadedHost, "Gameplay", "LifecycleProbe");
|
|
ASSERT_NE(loadedComponent, nullptr);
|
|
|
|
float loadedStoredSpeed = 0.0f;
|
|
std::string loadedStoredLabel;
|
|
GameObjectReference loadedStoredTarget;
|
|
XCEngine::Math::Vector3 loadedStoredSpawnPoint;
|
|
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Speed", loadedStoredSpeed));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Label", loadedStoredLabel));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Target", loadedStoredTarget));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("SpawnPoint", loadedStoredSpawnPoint));
|
|
|
|
EXPECT_FLOAT_EQ(loadedStoredSpeed, 6.0f);
|
|
EXPECT_EQ(loadedStoredLabel, "Configured|Awake");
|
|
EXPECT_EQ(loadedStoredTarget, GameObjectReference{loadedTarget->GetUUID()});
|
|
EXPECT_EQ(loadedStoredSpawnPoint, XCEngine::Math::Vector3(3.0f, 4.0f, 6.0f));
|
|
EXPECT_FALSE(loadedComponent->GetFieldStorage().Contains("AwakeCount"));
|
|
|
|
engine->OnRuntimeStart(reloadedScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t updateCount = 0;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedTargetName", observedTargetName));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 7.0f);
|
|
EXPECT_EQ(runtimeLabel, "Configured|Awake|Awake");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, EnumScriptFieldsApplyStoredValuesAndPersistAcrossSceneRoundTrip) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "EnumFieldProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("State", int32_t(5));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedInitialState = 0;
|
|
bool observedStoredStateApplied = false;
|
|
int32_t observedUpdatedState = 0;
|
|
int32_t runtimeState = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialState", observedInitialState));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredStateApplied", observedStoredStateApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedState", observedUpdatedState));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "State", runtimeState));
|
|
|
|
EXPECT_EQ(observedInitialState, 5);
|
|
EXPECT_TRUE(observedStoredStateApplied);
|
|
EXPECT_EQ(observedUpdatedState, 9);
|
|
EXPECT_EQ(runtimeState, 9);
|
|
|
|
int32_t storedState = 0;
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("State", storedState));
|
|
EXPECT_EQ(storedState, 9);
|
|
|
|
const std::string serializedScene = runtimeScene->SerializeToString();
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
scene = std::make_unique<Scene>("ReloadedEnumScene");
|
|
scene->DeserializeFromString(serializedScene);
|
|
Scene* reloadedScene = scene.get();
|
|
|
|
GameObject* loadedHost = reloadedScene->Find("Host");
|
|
ASSERT_NE(loadedHost, nullptr);
|
|
|
|
ScriptComponent* loadedComponent = FindScriptComponentByClass(loadedHost, "Gameplay", "EnumFieldProbe");
|
|
ASSERT_NE(loadedComponent, nullptr);
|
|
|
|
int32_t loadedStoredState = 0;
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("State", loadedStoredState));
|
|
EXPECT_EQ(loadedStoredState, 9);
|
|
|
|
engine->OnRuntimeStart(reloadedScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t loadedObservedInitialState = 0;
|
|
int32_t loadedRuntimeState = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedInitialState", loadedObservedInitialState));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "State", loadedRuntimeState));
|
|
|
|
EXPECT_EQ(loadedObservedInitialState, 9);
|
|
EXPECT_EQ(loadedRuntimeState, 9);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, SerializeFieldPrivateFieldsApplyStoredValuesAndPersistAcrossSceneRoundTrip) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "SerializeFieldProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("HiddenCounter", int32_t(42));
|
|
component->GetFieldStorage().SetFieldValue("HiddenEnabled", false);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedInitialHiddenCounter = 0;
|
|
bool observedInitialHiddenEnabled = true;
|
|
bool observedStoredValuesApplied = false;
|
|
int32_t observedUpdatedHiddenCounter = 0;
|
|
bool observedUpdatedHiddenEnabled = false;
|
|
bool observedIgnoredPrivateCounterUntouched = false;
|
|
int32_t runtimeHiddenCounter = 0;
|
|
bool runtimeHiddenEnabled = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialHiddenCounter", observedInitialHiddenCounter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialHiddenEnabled", observedInitialHiddenEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredValuesApplied", observedStoredValuesApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedHiddenCounter", observedUpdatedHiddenCounter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedHiddenEnabled", observedUpdatedHiddenEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIgnoredPrivateCounterUntouched", observedIgnoredPrivateCounterUntouched));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HiddenCounter", runtimeHiddenCounter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HiddenEnabled", runtimeHiddenEnabled));
|
|
|
|
EXPECT_EQ(observedInitialHiddenCounter, 42);
|
|
EXPECT_FALSE(observedInitialHiddenEnabled);
|
|
EXPECT_TRUE(observedStoredValuesApplied);
|
|
EXPECT_EQ(observedUpdatedHiddenCounter, 43);
|
|
EXPECT_TRUE(observedUpdatedHiddenEnabled);
|
|
EXPECT_TRUE(observedIgnoredPrivateCounterUntouched);
|
|
EXPECT_EQ(runtimeHiddenCounter, 43);
|
|
EXPECT_TRUE(runtimeHiddenEnabled);
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("IgnoredPrivateCounter"));
|
|
|
|
int32_t storedHiddenCounter = 0;
|
|
bool storedHiddenEnabled = false;
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("HiddenCounter", storedHiddenCounter));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("HiddenEnabled", storedHiddenEnabled));
|
|
EXPECT_EQ(storedHiddenCounter, 43);
|
|
EXPECT_TRUE(storedHiddenEnabled);
|
|
|
|
const std::string serializedScene = runtimeScene->SerializeToString();
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
scene = std::make_unique<Scene>("ReloadedSerializeFieldScene");
|
|
scene->DeserializeFromString(serializedScene);
|
|
Scene* reloadedScene = scene.get();
|
|
|
|
GameObject* loadedHost = reloadedScene->Find("Host");
|
|
ASSERT_NE(loadedHost, nullptr);
|
|
|
|
ScriptComponent* loadedComponent = FindScriptComponentByClass(loadedHost, "Gameplay", "SerializeFieldProbe");
|
|
ASSERT_NE(loadedComponent, nullptr);
|
|
|
|
int32_t loadedStoredHiddenCounter = 0;
|
|
bool loadedStoredHiddenEnabled = false;
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("HiddenCounter", loadedStoredHiddenCounter));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("HiddenEnabled", loadedStoredHiddenEnabled));
|
|
EXPECT_EQ(loadedStoredHiddenCounter, 43);
|
|
EXPECT_TRUE(loadedStoredHiddenEnabled);
|
|
|
|
engine->OnRuntimeStart(reloadedScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t loadedObservedInitialHiddenCounter = 0;
|
|
bool loadedObservedInitialHiddenEnabled = false;
|
|
int32_t loadedRuntimeHiddenCounter = 0;
|
|
bool loadedRuntimeHiddenEnabled = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedInitialHiddenCounter", loadedObservedInitialHiddenCounter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedInitialHiddenEnabled", loadedObservedInitialHiddenEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "HiddenCounter", loadedRuntimeHiddenCounter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "HiddenEnabled", loadedRuntimeHiddenEnabled));
|
|
|
|
EXPECT_EQ(loadedObservedInitialHiddenCounter, 43);
|
|
EXPECT_TRUE(loadedObservedInitialHiddenEnabled);
|
|
EXPECT_EQ(loadedRuntimeHiddenCounter, 44);
|
|
EXPECT_FALSE(loadedRuntimeHiddenEnabled);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ComponentReferenceFieldsApplyStoredValuesAndPersistAcrossSceneRoundTrip) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
host->AddComponent<CameraComponent>();
|
|
|
|
GameObject* pivotTarget = runtimeScene->CreateGameObject("PivotTarget");
|
|
GameObject* cameraTarget = runtimeScene->CreateGameObject("CameraTarget");
|
|
cameraTarget->AddComponent<CameraComponent>();
|
|
GameObject* scriptTargetHost = runtimeScene->CreateGameObject("ScriptTarget");
|
|
|
|
ScriptComponent* referencedScript = AddScript(scriptTargetHost, "Gameplay", "ScriptComponentTargetProbe");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "ComponentFieldProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("Pivot", ComponentReference{pivotTarget->GetUUID(), 0});
|
|
component->GetFieldStorage().SetFieldValue("SceneCamera", ComponentReference{cameraTarget->GetUUID(), 0});
|
|
component->GetFieldStorage().SetFieldValue(
|
|
"ScriptTarget",
|
|
ComponentReference{scriptTargetHost->GetUUID(), referencedScript->GetScriptComponentUUID()});
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool observedStoredPivotApplied = false;
|
|
bool observedStoredCameraApplied = false;
|
|
bool observedStoredScriptApplied = false;
|
|
std::string observedPivotName;
|
|
std::string observedCameraName;
|
|
std::string observedScriptName;
|
|
int32_t observedScriptAwakeCount = -1;
|
|
int32_t observedScriptHostCallCount = -1;
|
|
bool observedUpdatedPivotAssigned = false;
|
|
bool observedUpdatedCameraAssigned = false;
|
|
bool observedUpdatedScriptAssigned = false;
|
|
std::string observedUpdatedPivotName;
|
|
std::string observedUpdatedCameraName;
|
|
std::string observedUpdatedScriptName;
|
|
ComponentReference runtimePivot;
|
|
ComponentReference runtimeCamera;
|
|
ComponentReference runtimeScript;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredPivotApplied", observedStoredPivotApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredCameraApplied", observedStoredCameraApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedStoredScriptApplied", observedStoredScriptApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPivotName", observedPivotName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCameraName", observedCameraName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedScriptName", observedScriptName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedScriptAwakeCount", observedScriptAwakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedScriptHostCallCount", observedScriptHostCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedPivotAssigned", observedUpdatedPivotAssigned));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCameraAssigned", observedUpdatedCameraAssigned));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedScriptAssigned", observedUpdatedScriptAssigned));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedPivotName", observedUpdatedPivotName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCameraName", observedUpdatedCameraName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedScriptName", observedUpdatedScriptName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Pivot", runtimePivot));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SceneCamera", runtimeCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ScriptTarget", runtimeScript));
|
|
|
|
EXPECT_TRUE(observedStoredPivotApplied);
|
|
EXPECT_TRUE(observedStoredCameraApplied);
|
|
EXPECT_TRUE(observedStoredScriptApplied);
|
|
EXPECT_EQ(observedPivotName, "PivotTarget");
|
|
EXPECT_EQ(observedCameraName, "CameraTarget");
|
|
EXPECT_EQ(observedScriptName, "ScriptTarget");
|
|
EXPECT_EQ(observedScriptAwakeCount, 1);
|
|
EXPECT_EQ(observedScriptHostCallCount, 1);
|
|
EXPECT_TRUE(observedUpdatedPivotAssigned);
|
|
EXPECT_TRUE(observedUpdatedCameraAssigned);
|
|
EXPECT_TRUE(observedUpdatedScriptAssigned);
|
|
EXPECT_EQ(observedUpdatedPivotName, "Host");
|
|
EXPECT_EQ(observedUpdatedCameraName, "Host");
|
|
EXPECT_EQ(observedUpdatedScriptName, "Host");
|
|
|
|
ScriptComponent* assignedHostScript = FindScriptComponentByClass(host, "Gameplay", "ScriptComponentTargetProbe");
|
|
ASSERT_NE(assignedHostScript, nullptr);
|
|
|
|
EXPECT_EQ(runtimePivot, (ComponentReference{host->GetUUID(), 0}));
|
|
EXPECT_EQ(runtimeCamera, (ComponentReference{host->GetUUID(), 0}));
|
|
EXPECT_EQ(
|
|
runtimeScript,
|
|
(ComponentReference{host->GetUUID(), assignedHostScript->GetScriptComponentUUID()}));
|
|
|
|
ComponentReference storedPivot;
|
|
ComponentReference storedCamera;
|
|
ComponentReference storedScript;
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Pivot", storedPivot));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SceneCamera", storedCamera));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("ScriptTarget", storedScript));
|
|
EXPECT_EQ(storedPivot, runtimePivot);
|
|
EXPECT_EQ(storedCamera, runtimeCamera);
|
|
EXPECT_EQ(storedScript, runtimeScript);
|
|
|
|
const std::string serializedScene = runtimeScene->SerializeToString();
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
scene = std::make_unique<Scene>("ReloadedComponentFieldScene");
|
|
scene->DeserializeFromString(serializedScene);
|
|
Scene* reloadedScene = scene.get();
|
|
|
|
GameObject* loadedHost = reloadedScene->Find("Host");
|
|
ASSERT_NE(loadedHost, nullptr);
|
|
|
|
ScriptComponent* loadedComponent = FindScriptComponentByClass(loadedHost, "Gameplay", "ComponentFieldProbe");
|
|
ScriptComponent* loadedAssignedHostScript =
|
|
FindScriptComponentByClass(loadedHost, "Gameplay", "ScriptComponentTargetProbe");
|
|
ASSERT_NE(loadedComponent, nullptr);
|
|
ASSERT_NE(loadedAssignedHostScript, nullptr);
|
|
|
|
ComponentReference loadedStoredPivot;
|
|
ComponentReference loadedStoredCamera;
|
|
ComponentReference loadedStoredScript;
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Pivot", loadedStoredPivot));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("SceneCamera", loadedStoredCamera));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("ScriptTarget", loadedStoredScript));
|
|
EXPECT_EQ(loadedStoredPivot, (ComponentReference{loadedHost->GetUUID(), 0}));
|
|
EXPECT_EQ(loadedStoredCamera, (ComponentReference{loadedHost->GetUUID(), 0}));
|
|
EXPECT_EQ(
|
|
loadedStoredScript,
|
|
(ComponentReference{loadedHost->GetUUID(), loadedAssignedHostScript->GetScriptComponentUUID()}));
|
|
|
|
engine->OnRuntimeStart(reloadedScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool loadedObservedStoredPivotApplied = false;
|
|
bool loadedObservedStoredCameraApplied = false;
|
|
bool loadedObservedStoredScriptApplied = false;
|
|
std::string loadedObservedPivotName;
|
|
std::string loadedObservedCameraName;
|
|
std::string loadedObservedScriptName;
|
|
ComponentReference loadedRuntimePivot;
|
|
ComponentReference loadedRuntimeCamera;
|
|
ComponentReference loadedRuntimeScript;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedStoredPivotApplied", loadedObservedStoredPivotApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedStoredCameraApplied", loadedObservedStoredCameraApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedStoredScriptApplied", loadedObservedStoredScriptApplied));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedPivotName", loadedObservedPivotName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedCameraName", loadedObservedCameraName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedScriptName", loadedObservedScriptName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "Pivot", loadedRuntimePivot));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "SceneCamera", loadedRuntimeCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ScriptTarget", loadedRuntimeScript));
|
|
|
|
EXPECT_TRUE(loadedObservedStoredPivotApplied);
|
|
EXPECT_TRUE(loadedObservedStoredCameraApplied);
|
|
EXPECT_TRUE(loadedObservedStoredScriptApplied);
|
|
EXPECT_EQ(loadedObservedPivotName, "Host");
|
|
EXPECT_EQ(loadedObservedCameraName, "Host");
|
|
EXPECT_EQ(loadedObservedScriptName, "Host");
|
|
EXPECT_EQ(loadedRuntimePivot, (ComponentReference{loadedHost->GetUUID(), 0}));
|
|
EXPECT_EQ(loadedRuntimeCamera, (ComponentReference{loadedHost->GetUUID(), 0}));
|
|
EXPECT_EQ(
|
|
loadedRuntimeScript,
|
|
(ComponentReference{loadedHost->GetUUID(), loadedAssignedHostScript->GetScriptComponentUUID()}));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiUpdatesLiveManagedInstanceAndStoredCache) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Speed", 41.0f));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Label", "Edited"));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Target", GameObjectReference{target->GetUUID()}));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "SpawnPoint", XCEngine::Math::Vector3(10.0f, 20.0f, 30.0f)));
|
|
|
|
EXPECT_FALSE(engine->TrySetScriptFieldValue(component, "DoesNotExist", int32_t(1)));
|
|
EXPECT_FALSE(engine->TrySetScriptFieldValue(component, "Speed", std::string("wrong")));
|
|
|
|
float storedSpeedBeforeUpdate = 0.0f;
|
|
std::string storedLabelBeforeUpdate;
|
|
GameObjectReference storedTargetBeforeUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointBeforeUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedBeforeUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelBeforeUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetBeforeUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointBeforeUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedBeforeUpdate, 41.0f);
|
|
EXPECT_EQ(storedLabelBeforeUpdate, "Edited");
|
|
EXPECT_EQ(storedTargetBeforeUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointBeforeUpdate, XCEngine::Math::Vector3(10.0f, 20.0f, 30.0f));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("DoesNotExist"));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 42.0f);
|
|
EXPECT_EQ(runtimeLabel, "Edited");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
|
|
float storedSpeedAfterUpdate = 0.0f;
|
|
std::string storedLabelAfterUpdate;
|
|
GameObjectReference storedTargetAfterUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointAfterUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointAfterUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedAfterUpdate, 42.0f);
|
|
EXPECT_EQ(storedLabelAfterUpdate, "Edited");
|
|
EXPECT_EQ(storedTargetAfterUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointAfterUpdate, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiCachesValuesBeforeRuntimeStartAndAppliesThemOnFirstInstance) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
EXPECT_FALSE(runtime->HasManagedInstance(component));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Speed", 9.0f));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Label", "PreStart"));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Target", GameObjectReference{target->GetUUID()}));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "SpawnPoint", XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f)));
|
|
|
|
float storedSpeedBeforeRuntime = 0.0f;
|
|
std::string storedLabelBeforeRuntime;
|
|
GameObjectReference storedTargetBeforeRuntime;
|
|
XCEngine::Math::Vector3 storedSpawnPointBeforeRuntime;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedBeforeRuntime));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelBeforeRuntime));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetBeforeRuntime));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointBeforeRuntime));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedBeforeRuntime, 9.0f);
|
|
EXPECT_EQ(storedLabelBeforeRuntime, "PreStart");
|
|
EXPECT_EQ(storedTargetBeforeRuntime, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointBeforeRuntime, XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 10.0f);
|
|
EXPECT_EQ(runtimeLabel, "PreStart|Awake");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(4.0f, 4.0f, 5.0f));
|
|
|
|
float storedSpeedAfterUpdate = 0.0f;
|
|
std::string storedLabelAfterUpdate;
|
|
GameObjectReference storedTargetAfterUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointAfterUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointAfterUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedAfterUpdate, 10.0f);
|
|
EXPECT_EQ(storedLabelAfterUpdate, "PreStart|Awake");
|
|
EXPECT_EQ(storedTargetAfterUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointAfterUpdate, XCEngine::Math::Vector3(4.0f, 4.0f, 5.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiCachesValuesForInactiveRuntimeScriptUntilActivation) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
host->SetActive(false);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
EXPECT_FALSE(runtime->HasManagedInstance(component));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Speed", 12.0f));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Label", "Dormant"));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Target", GameObjectReference{target->GetUUID()}));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "SpawnPoint", XCEngine::Math::Vector3(5.0f, 6.0f, 7.0f)));
|
|
|
|
float storedSpeedBeforeActivation = 0.0f;
|
|
std::string storedLabelBeforeActivation;
|
|
GameObjectReference storedTargetBeforeActivation;
|
|
XCEngine::Math::Vector3 storedSpawnPointBeforeActivation;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedBeforeActivation));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelBeforeActivation));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetBeforeActivation));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointBeforeActivation));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedBeforeActivation, 12.0f);
|
|
EXPECT_EQ(storedLabelBeforeActivation, "Dormant");
|
|
EXPECT_EQ(storedTargetBeforeActivation, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointBeforeActivation, XCEngine::Math::Vector3(5.0f, 6.0f, 7.0f));
|
|
|
|
host->SetActive(true);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 13.0f);
|
|
EXPECT_EQ(runtimeLabel, "Dormant|Awake");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(6.0f, 6.0f, 7.0f));
|
|
|
|
float storedSpeedAfterUpdate = 0.0f;
|
|
std::string storedLabelAfterUpdate;
|
|
GameObjectReference storedTargetAfterUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointAfterUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointAfterUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedAfterUpdate, 13.0f);
|
|
EXPECT_EQ(storedLabelAfterUpdate, "Dormant|Awake");
|
|
EXPECT_EQ(storedTargetAfterUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointAfterUpdate, XCEngine::Math::Vector3(6.0f, 6.0f, 7.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldBatchApiUpdatesLiveManagedInstanceAndReportsPerFieldStatus) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(99));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
const std::vector<ScriptFieldWriteRequest> requests = {
|
|
{"Speed", ScriptFieldType::Float, ScriptFieldValue(41.0f)},
|
|
{"Label", ScriptFieldType::String, ScriptFieldValue(std::string("BatchEdited"))},
|
|
{"Target", ScriptFieldType::GameObject, ScriptFieldValue(GameObjectReference{target->GetUUID()})},
|
|
{"SpawnPoint", ScriptFieldType::Vector3, ScriptFieldValue(XCEngine::Math::Vector3(10.0f, 20.0f, 30.0f))},
|
|
{"LegacyOnly", ScriptFieldType::UInt64, ScriptFieldValue(uint64_t(100))},
|
|
{"DoesNotExist", ScriptFieldType::Int32, ScriptFieldValue(int32_t(1))},
|
|
{"Speed", ScriptFieldType::String, ScriptFieldValue(std::string("wrong"))},
|
|
{"Label", ScriptFieldType::String, ScriptFieldValue(int32_t(5))}
|
|
};
|
|
|
|
std::vector<ScriptFieldWriteResult> results;
|
|
EXPECT_FALSE(engine->ApplyScriptFieldWrites(component, requests, results));
|
|
ASSERT_EQ(results.size(), requests.size());
|
|
|
|
EXPECT_EQ(results[0].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[1].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[2].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[3].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[4].status, ScriptFieldWriteStatus::StoredOnlyField);
|
|
EXPECT_EQ(results[5].status, ScriptFieldWriteStatus::UnknownField);
|
|
EXPECT_EQ(results[6].status, ScriptFieldWriteStatus::TypeMismatch);
|
|
EXPECT_EQ(results[7].status, ScriptFieldWriteStatus::InvalidValue);
|
|
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 42.0f);
|
|
EXPECT_EQ(runtimeLabel, "BatchEdited");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
|
|
float storedSpeed = 0.0f;
|
|
std::string storedLabel;
|
|
GameObjectReference storedTarget;
|
|
XCEngine::Math::Vector3 storedSpawnPoint;
|
|
uint64_t storedLegacyOnly = 0;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeed));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabel));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTarget));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPoint));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("LegacyOnly", storedLegacyOnly));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeed, 42.0f);
|
|
EXPECT_EQ(storedLabel, "BatchEdited");
|
|
EXPECT_EQ(storedTarget, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPoint, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
EXPECT_EQ(storedLegacyOnly, 99u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldModelUsesManagedClassDefaultValuesBeforeRuntimeStart) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "RuntimeGameObjectProbe");
|
|
|
|
ScriptFieldModel model;
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Available);
|
|
|
|
const auto fieldIt = std::find_if(
|
|
model.fields.begin(),
|
|
model.fields.end(),
|
|
[](const ScriptFieldSnapshot& field) {
|
|
return field.metadata.name == "ObservedRootChildCountAfterDestroy";
|
|
});
|
|
|
|
ASSERT_NE(fieldIt, model.fields.end());
|
|
EXPECT_TRUE(fieldIt->declaredInClass);
|
|
EXPECT_TRUE(fieldIt->hasDefaultValue);
|
|
EXPECT_FALSE(fieldIt->hasValue);
|
|
EXPECT_EQ(fieldIt->valueSource, ScriptFieldValueSource::DefaultValue);
|
|
EXPECT_EQ(std::get<int32_t>(fieldIt->defaultValue), -1);
|
|
EXPECT_EQ(std::get<int32_t>(fieldIt->value), -1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldClearApiRestoresManagedClassDefaultValuesAndRemovesStoredOverrides) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "RuntimeGameObjectProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("ObservedRootChildCountAfterDestroy", int32_t(7));
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(99));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
const std::vector<ScriptFieldClearRequest> requests = {
|
|
{"ObservedRootChildCountAfterDestroy"},
|
|
{"LegacyOnly"},
|
|
{"DoesNotExist"},
|
|
{""}
|
|
};
|
|
|
|
std::vector<ScriptFieldClearResult> results;
|
|
EXPECT_FALSE(engine->ClearScriptFieldOverrides(component, requests, results));
|
|
ASSERT_EQ(results.size(), requests.size());
|
|
|
|
EXPECT_EQ(results[0].status, ScriptFieldClearStatus::Applied);
|
|
EXPECT_EQ(results[1].status, ScriptFieldClearStatus::Applied);
|
|
EXPECT_EQ(results[2].status, ScriptFieldClearStatus::UnknownField);
|
|
EXPECT_EQ(results[3].status, ScriptFieldClearStatus::EmptyFieldName);
|
|
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("ObservedRootChildCountAfterDestroy"));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("LegacyOnly"));
|
|
|
|
int32_t runtimeObservedCountAfterDestroy = 0;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountAfterDestroy", runtimeObservedCountAfterDestroy));
|
|
EXPECT_EQ(runtimeObservedCountAfterDestroy, -1);
|
|
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("ObservedRootChildCountAfterDestroy"));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("LegacyOnly"));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldReadApiReturnsManagedOnlyRuntimeValues) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
float speed = 0.0f;
|
|
int32_t awakeCount = 0;
|
|
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("Speed"));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("AwakeCount"));
|
|
EXPECT_FALSE(engine->TryGetScriptFieldValue(component, "Speed", speed));
|
|
EXPECT_FALSE(engine->TryGetScriptFieldValue(component, "AwakeCount", awakeCount));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
EXPECT_TRUE(engine->TryGetScriptFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("AwakeCount"));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(engine->TryGetScriptFieldValue(component, "Speed", speed));
|
|
EXPECT_FLOAT_EQ(speed, 1.0f);
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("Speed"));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldSnapshotApiReportsMetadataAndCurrentValues) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("AwakeCount", uint64_t(7));
|
|
component->GetFieldStorage().SetFieldValue("Label", "Stored");
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(99));
|
|
|
|
ScriptFieldModel model;
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Available);
|
|
|
|
std::vector<ScriptFieldSnapshot>& snapshots = model.fields;
|
|
|
|
const auto findSnapshot = [&snapshots](const std::string& fieldName) -> const ScriptFieldSnapshot* {
|
|
const auto it = std::find_if(
|
|
snapshots.begin(),
|
|
snapshots.end(),
|
|
[&fieldName](const ScriptFieldSnapshot& snapshot) {
|
|
return snapshot.metadata.name == fieldName;
|
|
});
|
|
return it != snapshots.end() ? &(*it) : nullptr;
|
|
};
|
|
|
|
const ScriptFieldSnapshot* awakeSnapshot = findSnapshot("AwakeCount");
|
|
const ScriptFieldSnapshot* labelSnapshot = findSnapshot("Label");
|
|
const ScriptFieldSnapshot* speedSnapshot = findSnapshot("Speed");
|
|
const ScriptFieldSnapshot* legacySnapshot = findSnapshot("LegacyOnly");
|
|
|
|
ASSERT_NE(awakeSnapshot, nullptr);
|
|
ASSERT_NE(labelSnapshot, nullptr);
|
|
ASSERT_NE(speedSnapshot, nullptr);
|
|
ASSERT_NE(legacySnapshot, nullptr);
|
|
|
|
EXPECT_EQ(awakeSnapshot->metadata.type, ScriptFieldType::Int32);
|
|
EXPECT_TRUE(awakeSnapshot->declaredInClass);
|
|
EXPECT_TRUE(awakeSnapshot->hasDefaultValue);
|
|
EXPECT_FALSE(awakeSnapshot->hasValue);
|
|
EXPECT_EQ(awakeSnapshot->valueSource, ScriptFieldValueSource::DefaultValue);
|
|
EXPECT_EQ(awakeSnapshot->issue, ScriptFieldIssue::TypeMismatch);
|
|
EXPECT_TRUE(awakeSnapshot->hasStoredValue);
|
|
EXPECT_EQ(awakeSnapshot->storedType, ScriptFieldType::UInt64);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->defaultValue), 0);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->value), 0);
|
|
EXPECT_EQ(std::get<uint64_t>(awakeSnapshot->storedValue), 7u);
|
|
|
|
EXPECT_EQ(labelSnapshot->metadata.type, ScriptFieldType::String);
|
|
EXPECT_TRUE(labelSnapshot->declaredInClass);
|
|
EXPECT_TRUE(labelSnapshot->hasDefaultValue);
|
|
EXPECT_TRUE(labelSnapshot->hasValue);
|
|
EXPECT_EQ(labelSnapshot->valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(labelSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_TRUE(labelSnapshot->hasStoredValue);
|
|
EXPECT_EQ(labelSnapshot->storedType, ScriptFieldType::String);
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->defaultValue), "");
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->value), "Stored");
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->storedValue), "Stored");
|
|
|
|
EXPECT_EQ(speedSnapshot->metadata.type, ScriptFieldType::Float);
|
|
EXPECT_TRUE(speedSnapshot->declaredInClass);
|
|
EXPECT_TRUE(speedSnapshot->hasDefaultValue);
|
|
EXPECT_FALSE(speedSnapshot->hasValue);
|
|
EXPECT_EQ(speedSnapshot->valueSource, ScriptFieldValueSource::DefaultValue);
|
|
EXPECT_EQ(speedSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->defaultValue), 0.0f);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->value), 0.0f);
|
|
|
|
EXPECT_EQ(legacySnapshot->metadata.type, ScriptFieldType::UInt64);
|
|
EXPECT_FALSE(legacySnapshot->declaredInClass);
|
|
EXPECT_FALSE(legacySnapshot->hasDefaultValue);
|
|
EXPECT_TRUE(legacySnapshot->hasValue);
|
|
EXPECT_EQ(legacySnapshot->valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(legacySnapshot->issue, ScriptFieldIssue::StoredOnly);
|
|
EXPECT_TRUE(legacySnapshot->hasStoredValue);
|
|
EXPECT_EQ(legacySnapshot->storedType, ScriptFieldType::UInt64);
|
|
EXPECT_EQ(std::get<uint64_t>(legacySnapshot->value), 99u);
|
|
EXPECT_EQ(std::get<uint64_t>(legacySnapshot->storedValue), 99u);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Available);
|
|
awakeSnapshot = findSnapshot("AwakeCount");
|
|
labelSnapshot = findSnapshot("Label");
|
|
speedSnapshot = findSnapshot("Speed");
|
|
legacySnapshot = findSnapshot("LegacyOnly");
|
|
|
|
ASSERT_NE(awakeSnapshot, nullptr);
|
|
ASSERT_NE(labelSnapshot, nullptr);
|
|
ASSERT_NE(speedSnapshot, nullptr);
|
|
ASSERT_NE(legacySnapshot, nullptr);
|
|
|
|
EXPECT_TRUE(awakeSnapshot->hasValue);
|
|
EXPECT_TRUE(awakeSnapshot->hasDefaultValue);
|
|
EXPECT_EQ(awakeSnapshot->valueSource, ScriptFieldValueSource::ManagedValue);
|
|
EXPECT_EQ(awakeSnapshot->issue, ScriptFieldIssue::TypeMismatch);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->defaultValue), 0);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->value), 1);
|
|
EXPECT_TRUE(labelSnapshot->hasValue);
|
|
EXPECT_TRUE(labelSnapshot->hasDefaultValue);
|
|
EXPECT_EQ(labelSnapshot->valueSource, ScriptFieldValueSource::ManagedValue);
|
|
EXPECT_EQ(labelSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->defaultValue), "");
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->value), "Stored|Awake");
|
|
EXPECT_TRUE(speedSnapshot->hasValue);
|
|
EXPECT_TRUE(speedSnapshot->hasDefaultValue);
|
|
EXPECT_EQ(speedSnapshot->valueSource, ScriptFieldValueSource::ManagedValue);
|
|
EXPECT_EQ(speedSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->defaultValue), 0.0f);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->value), 1.0f);
|
|
EXPECT_TRUE(legacySnapshot->hasValue);
|
|
EXPECT_FALSE(legacySnapshot->hasDefaultValue);
|
|
EXPECT_EQ(legacySnapshot->valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(legacySnapshot->issue, ScriptFieldIssue::StoredOnly);
|
|
EXPECT_EQ(std::get<uint64_t>(legacySnapshot->value), 99u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldModelReportsMissingScriptClassAndStoredFields) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MissingLifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("Label", "Stored");
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(9));
|
|
|
|
ScriptFieldModel model;
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Missing);
|
|
ASSERT_EQ(model.fields.size(), 2u);
|
|
EXPECT_EQ(model.fields[0].metadata.name, "Label");
|
|
EXPECT_EQ(model.fields[1].metadata.name, "LegacyOnly");
|
|
|
|
for (const ScriptFieldSnapshot& field : model.fields) {
|
|
EXPECT_FALSE(field.declaredInClass);
|
|
EXPECT_FALSE(field.hasDefaultValue);
|
|
EXPECT_TRUE(field.hasValue);
|
|
EXPECT_EQ(field.valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(field.issue, ScriptFieldIssue::StoredOnly);
|
|
EXPECT_TRUE(field.hasStoredValue);
|
|
EXPECT_EQ(field.metadata.type, field.storedType);
|
|
EXPECT_EQ(field.value, field.storedValue);
|
|
}
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectComponentApiResolvesTransformAndRejectsUnsupportedManagedTypes) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasTransform = false;
|
|
bool transformLookupSucceeded = false;
|
|
bool hasUnsupportedComponent = true;
|
|
bool unsupportedComponentLookupReturnedNull = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTransform", hasTransform));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformLookupSucceeded", transformLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasUnsupportedComponent", hasUnsupportedComponent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UnsupportedComponentLookupReturnedNull", unsupportedComponentLookupReturnedNull));
|
|
|
|
EXPECT_TRUE(hasTransform);
|
|
EXPECT_TRUE(transformLookupSucceeded);
|
|
EXPECT_FALSE(hasUnsupportedComponent);
|
|
EXPECT_TRUE(unsupportedComponentLookupReturnedNull);
|
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedBuiltInComponentWrappersReadAndWriteCameraAndLight) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
CameraComponent* camera = host->AddComponent<CameraComponent>();
|
|
LightComponent* light = host->AddComponent<LightComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "BuiltinComponentProbe");
|
|
|
|
camera->SetFieldOfView(52.0f);
|
|
camera->SetNearClipPlane(0.2f);
|
|
camera->SetFarClipPlane(300.0f);
|
|
camera->SetDepth(1.5f);
|
|
camera->SetPrimary(true);
|
|
|
|
light->SetIntensity(1.25f);
|
|
light->SetRange(12.0f);
|
|
light->SetSpotAngle(33.0f);
|
|
light->SetCastsShadows(false);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasCamera = false;
|
|
bool hasLight = false;
|
|
bool cameraLookupSucceeded = false;
|
|
bool lightLookupSucceeded = false;
|
|
bool observedPrimary = false;
|
|
bool observedCastsShadows = true;
|
|
float observedFieldOfView = 0.0f;
|
|
float observedNearClipPlane = 0.0f;
|
|
float observedFarClipPlane = 0.0f;
|
|
float observedDepth = 0.0f;
|
|
float observedIntensity = 0.0f;
|
|
float observedRange = 0.0f;
|
|
float observedSpotAngle = 0.0f;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasCamera", hasCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasLight", hasLight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFieldOfView", observedFieldOfView));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedNearClipPlane", observedNearClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFarClipPlane", observedFarClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedDepth", observedDepth));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPrimary", observedPrimary));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIntensity", observedIntensity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRange", observedRange));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedSpotAngle", observedSpotAngle));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastsShadows", observedCastsShadows));
|
|
|
|
EXPECT_TRUE(hasCamera);
|
|
EXPECT_TRUE(hasLight);
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_TRUE(lightLookupSucceeded);
|
|
|
|
EXPECT_FLOAT_EQ(observedFieldOfView, 52.0f);
|
|
EXPECT_FLOAT_EQ(observedNearClipPlane, 0.2f);
|
|
EXPECT_FLOAT_EQ(observedFarClipPlane, 300.0f);
|
|
EXPECT_FLOAT_EQ(observedDepth, 1.5f);
|
|
EXPECT_TRUE(observedPrimary);
|
|
EXPECT_FLOAT_EQ(observedIntensity, 1.25f);
|
|
EXPECT_FLOAT_EQ(observedRange, 12.0f);
|
|
EXPECT_FLOAT_EQ(observedSpotAngle, 33.0f);
|
|
EXPECT_FALSE(observedCastsShadows);
|
|
|
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 75.0f);
|
|
EXPECT_FLOAT_EQ(camera->GetNearClipPlane(), 0.3f);
|
|
EXPECT_FLOAT_EQ(camera->GetFarClipPlane(), 500.0f);
|
|
EXPECT_FLOAT_EQ(camera->GetDepth(), 3.0f);
|
|
EXPECT_FALSE(camera->IsPrimary());
|
|
|
|
EXPECT_FLOAT_EQ(light->GetIntensity(), 2.5f);
|
|
EXPECT_FLOAT_EQ(light->GetRange(), 42.0f);
|
|
EXPECT_FLOAT_EQ(light->GetSpotAngle(), 55.0f);
|
|
EXPECT_TRUE(light->GetCastsShadows());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedBuiltInComponentLookupOnlyCameraAndLight) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
host->AddComponent<CameraComponent>();
|
|
host->AddComponent<LightComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "CameraLightLookupProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasCamera = false;
|
|
bool hasLight = false;
|
|
bool cameraLookupSucceeded = false;
|
|
bool lightLookupSucceeded = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasCamera", hasCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasLight", hasLight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
|
|
|
|
EXPECT_TRUE(hasCamera);
|
|
EXPECT_TRUE(hasLight);
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_TRUE(lightLookupSucceeded);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedCameraWrapperReadAndWriteProperties) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
CameraComponent* camera = host->AddComponent<CameraComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "CameraPropertyProbe");
|
|
|
|
camera->SetFieldOfView(52.0f);
|
|
camera->SetNearClipPlane(0.2f);
|
|
camera->SetFarClipPlane(300.0f);
|
|
camera->SetDepth(1.5f);
|
|
camera->SetPrimary(true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool cameraLookupSucceeded = false;
|
|
bool observedPrimary = false;
|
|
float observedFieldOfView = 0.0f;
|
|
float observedNearClipPlane = 0.0f;
|
|
float observedFarClipPlane = 0.0f;
|
|
float observedDepth = 0.0f;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFieldOfView", observedFieldOfView));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedNearClipPlane", observedNearClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFarClipPlane", observedFarClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedDepth", observedDepth));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPrimary", observedPrimary));
|
|
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_FLOAT_EQ(observedFieldOfView, 52.0f);
|
|
EXPECT_FLOAT_EQ(observedNearClipPlane, 0.2f);
|
|
EXPECT_FLOAT_EQ(observedFarClipPlane, 300.0f);
|
|
EXPECT_FLOAT_EQ(observedDepth, 1.5f);
|
|
EXPECT_TRUE(observedPrimary);
|
|
|
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 75.0f);
|
|
EXPECT_FLOAT_EQ(camera->GetNearClipPlane(), 0.3f);
|
|
EXPECT_FLOAT_EQ(camera->GetFarClipPlane(), 500.0f);
|
|
EXPECT_FLOAT_EQ(camera->GetDepth(), 3.0f);
|
|
EXPECT_FALSE(camera->IsPrimary());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedLightWrapperReadAndWriteProperties) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
LightComponent* light = host->AddComponent<LightComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LightPropertyProbe");
|
|
|
|
light->SetIntensity(1.25f);
|
|
light->SetRange(12.0f);
|
|
light->SetSpotAngle(33.0f);
|
|
light->SetCastsShadows(false);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool lightLookupSucceeded = false;
|
|
bool observedCastsShadows = true;
|
|
float observedIntensity = 0.0f;
|
|
float observedRange = 0.0f;
|
|
float observedSpotAngle = 0.0f;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIntensity", observedIntensity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRange", observedRange));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedSpotAngle", observedSpotAngle));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastsShadows", observedCastsShadows));
|
|
|
|
EXPECT_TRUE(lightLookupSucceeded);
|
|
EXPECT_FLOAT_EQ(observedIntensity, 1.25f);
|
|
EXPECT_FLOAT_EQ(observedRange, 12.0f);
|
|
EXPECT_FLOAT_EQ(observedSpotAngle, 33.0f);
|
|
EXPECT_FALSE(observedCastsShadows);
|
|
|
|
EXPECT_FLOAT_EQ(light->GetIntensity(), 2.5f);
|
|
EXPECT_FLOAT_EQ(light->GetRange(), 42.0f);
|
|
EXPECT_FLOAT_EQ(light->GetSpotAngle(), 55.0f);
|
|
EXPECT_TRUE(light->GetCastsShadows());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedMeshRendererWrapperReadAndWriteFlagsOnly) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
MeshRendererComponent* meshRenderer = host->AddComponent<MeshRendererComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MeshRendererFlagsProbe");
|
|
|
|
meshRenderer->SetCastShadows(true);
|
|
meshRenderer->SetReceiveShadows(false);
|
|
meshRenderer->SetRenderLayer(7);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool meshRendererLookupSucceeded = false;
|
|
bool observedCastShadows = false;
|
|
bool observedReceiveShadows = true;
|
|
int32_t observedRenderLayer = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastShadows", observedCastShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedReceiveShadows", observedReceiveShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayer", observedRenderLayer));
|
|
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_TRUE(observedCastShadows);
|
|
EXPECT_FALSE(observedReceiveShadows);
|
|
EXPECT_EQ(observedRenderLayer, 7);
|
|
|
|
EXPECT_FALSE(meshRenderer->GetCastShadows());
|
|
EXPECT_TRUE(meshRenderer->GetReceiveShadows());
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 11u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedMeshRendererWrapperReadAndWriteMaterialPathsOnly) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
MeshRendererComponent* meshRenderer = host->AddComponent<MeshRendererComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MeshRendererPathProbe");
|
|
|
|
meshRenderer->SetMaterialPath(0, "Materials/initial.mat");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool meshRendererLookupSucceeded = false;
|
|
int32_t observedInitialMaterialCount = 0;
|
|
std::string observedInitialMaterial0Path;
|
|
int32_t observedUpdatedMaterialCount = 0;
|
|
std::string observedUpdatedMaterial1Path;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterialCount", observedInitialMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterial0Path", observedInitialMaterial0Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMaterialCount", observedUpdatedMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMaterial1Path", observedUpdatedMaterial1Path));
|
|
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_EQ(observedInitialMaterialCount, 1);
|
|
EXPECT_EQ(observedInitialMaterial0Path, "Materials/initial.mat");
|
|
EXPECT_EQ(observedUpdatedMaterialCount, 2);
|
|
EXPECT_EQ(observedUpdatedMaterial1Path, "Materials/runtime_override.mat");
|
|
|
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 2u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/initial.mat");
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(1), "Materials/runtime_override.mat");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedMeshComponentWrappersReadAndWritePathsAndFlags) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
MeshFilterComponent* meshFilter = host->AddComponent<MeshFilterComponent>();
|
|
MeshRendererComponent* meshRenderer = host->AddComponent<MeshRendererComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MeshComponentProbe");
|
|
|
|
meshFilter->SetMeshPath("Meshes/initial.mesh");
|
|
meshRenderer->SetMaterialPath(0, "Materials/initial.mat");
|
|
meshRenderer->SetCastShadows(true);
|
|
meshRenderer->SetReceiveShadows(false);
|
|
meshRenderer->SetRenderLayer(7);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasMeshFilter = false;
|
|
bool hasMeshRenderer = false;
|
|
bool meshFilterLookupSucceeded = false;
|
|
bool meshRendererLookupSucceeded = false;
|
|
std::string observedInitialMeshPath;
|
|
std::string observedUpdatedMeshPath;
|
|
int32_t observedInitialMaterialCount = 0;
|
|
std::string observedInitialMaterial0Path;
|
|
bool observedInitialCastShadows = false;
|
|
bool observedInitialReceiveShadows = true;
|
|
int32_t observedInitialRenderLayer = 0;
|
|
int32_t observedUpdatedMaterialCount = 0;
|
|
std::string observedUpdatedMaterial1Path;
|
|
bool observedUpdatedCastShadows = true;
|
|
bool observedUpdatedReceiveShadows = false;
|
|
int32_t observedUpdatedRenderLayer = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshFilter", hasMeshFilter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshRenderer", hasMeshRenderer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshFilterLookupSucceeded", meshFilterLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMeshPath", observedInitialMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMeshPath", observedUpdatedMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterialCount", observedInitialMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterial0Path", observedInitialMaterial0Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialCastShadows", observedInitialCastShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialReceiveShadows", observedInitialReceiveShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialRenderLayer", observedInitialRenderLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMaterialCount", observedUpdatedMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMaterial1Path", observedUpdatedMaterial1Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCastShadows", observedUpdatedCastShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedReceiveShadows", observedUpdatedReceiveShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedRenderLayer", observedUpdatedRenderLayer));
|
|
|
|
EXPECT_TRUE(hasMeshFilter);
|
|
EXPECT_TRUE(hasMeshRenderer);
|
|
EXPECT_TRUE(meshFilterLookupSucceeded);
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_EQ(observedInitialMeshPath, "Meshes/initial.mesh");
|
|
EXPECT_EQ(observedUpdatedMeshPath, "Meshes/runtime_override.mesh");
|
|
EXPECT_EQ(observedInitialMaterialCount, 1);
|
|
EXPECT_EQ(observedInitialMaterial0Path, "Materials/initial.mat");
|
|
EXPECT_TRUE(observedInitialCastShadows);
|
|
EXPECT_FALSE(observedInitialReceiveShadows);
|
|
EXPECT_EQ(observedInitialRenderLayer, 7);
|
|
EXPECT_EQ(observedUpdatedMaterialCount, 2);
|
|
EXPECT_EQ(observedUpdatedMaterial1Path, "Materials/runtime_override.mat");
|
|
EXPECT_FALSE(observedUpdatedCastShadows);
|
|
EXPECT_TRUE(observedUpdatedReceiveShadows);
|
|
EXPECT_EQ(observedUpdatedRenderLayer, 11);
|
|
|
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/runtime_override.mesh");
|
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 2u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/initial.mat");
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(1), "Materials/runtime_override.mat");
|
|
EXPECT_FALSE(meshRenderer->GetCastShadows());
|
|
EXPECT_TRUE(meshRenderer->GetReceiveShadows());
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 11u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedMeshRendererWrapperHandlesClearAndBoundaryCases) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
MeshRendererComponent* meshRenderer = host->AddComponent<MeshRendererComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MeshRendererEdgeCaseProbe");
|
|
|
|
meshRenderer->SetMaterialPath(0, "Materials/initial0.mat");
|
|
meshRenderer->SetMaterialPath(1, "Materials/initial1.mat");
|
|
meshRenderer->SetCastShadows(false);
|
|
meshRenderer->SetReceiveShadows(true);
|
|
meshRenderer->SetRenderLayer(9);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedInitialMaterialCount = 0;
|
|
std::string observedNegativeIndexPath;
|
|
std::string observedOutOfRangePathBeforeClear;
|
|
std::string observedMaterial0PathAfterNegativeWrite;
|
|
std::string observedMaterial1PathAfterNegativeWrite;
|
|
int32_t observedMaterialCountAfterNegativeWrite = 0;
|
|
int32_t observedRenderLayerAfterNegativeWrite = -1;
|
|
int32_t observedMaterialCountAfterClear = -1;
|
|
std::string observedMaterial0PathAfterClear;
|
|
std::string observedMaterial3PathAfterClear;
|
|
bool observedCastShadowsAfterClear = true;
|
|
bool observedReceiveShadowsAfterClear = false;
|
|
int32_t observedRenderLayerAfterClear = -1;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterialCount", observedInitialMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedNegativeIndexPath", observedNegativeIndexPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedOutOfRangePathBeforeClear", observedOutOfRangePathBeforeClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0PathAfterNegativeWrite", observedMaterial0PathAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial1PathAfterNegativeWrite", observedMaterial1PathAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCountAfterNegativeWrite", observedMaterialCountAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayerAfterNegativeWrite", observedRenderLayerAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCountAfterClear", observedMaterialCountAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0PathAfterClear", observedMaterial0PathAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial3PathAfterClear", observedMaterial3PathAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastShadowsAfterClear", observedCastShadowsAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedReceiveShadowsAfterClear", observedReceiveShadowsAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayerAfterClear", observedRenderLayerAfterClear));
|
|
|
|
EXPECT_EQ(observedInitialMaterialCount, 2);
|
|
EXPECT_EQ(observedNegativeIndexPath, "");
|
|
EXPECT_EQ(observedOutOfRangePathBeforeClear, "");
|
|
EXPECT_EQ(observedMaterial0PathAfterNegativeWrite, "Materials/initial0.mat");
|
|
EXPECT_EQ(observedMaterial1PathAfterNegativeWrite, "Materials/initial1.mat");
|
|
EXPECT_EQ(observedMaterialCountAfterNegativeWrite, 2);
|
|
EXPECT_EQ(observedRenderLayerAfterNegativeWrite, 0);
|
|
EXPECT_EQ(observedMaterialCountAfterClear, 0);
|
|
EXPECT_EQ(observedMaterial0PathAfterClear, "");
|
|
EXPECT_EQ(observedMaterial3PathAfterClear, "");
|
|
EXPECT_FALSE(observedCastShadowsAfterClear);
|
|
EXPECT_TRUE(observedReceiveShadowsAfterClear);
|
|
EXPECT_EQ(observedRenderLayerAfterClear, 0);
|
|
|
|
EXPECT_EQ(meshRenderer->GetMaterialCount(), 0u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "");
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(3), "");
|
|
EXPECT_FALSE(meshRenderer->GetCastShadows());
|
|
EXPECT_TRUE(meshRenderer->GetReceiveShadows());
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 0u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectAddComponentApiCreatesBuiltinComponentsAndAvoidsDuplicates) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "AddComponentProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool initialHasCamera = true;
|
|
bool initialHasLight = true;
|
|
bool initialHasMeshFilter = true;
|
|
bool initialHasMeshRenderer = true;
|
|
bool addedTransform = false;
|
|
bool addedCamera = false;
|
|
bool addedLight = false;
|
|
bool addedMeshFilter = false;
|
|
bool addedMeshRenderer = false;
|
|
bool hasCameraAfterAdd = false;
|
|
bool hasLightAfterAdd = false;
|
|
bool hasMeshFilterAfterAdd = false;
|
|
bool hasMeshRendererAfterAdd = false;
|
|
bool cameraLookupSucceeded = false;
|
|
bool lightLookupSucceeded = false;
|
|
bool meshFilterLookupSucceeded = false;
|
|
bool meshRendererLookupSucceeded = false;
|
|
float observedCameraFieldOfView = 0.0f;
|
|
float observedLightIntensity = 0.0f;
|
|
std::string observedMeshPath;
|
|
int32_t observedMaterialCount = 0;
|
|
std::string observedMaterial0Path;
|
|
int32_t observedRenderLayer = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasCamera", initialHasCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasLight", initialHasLight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasMeshFilter", initialHasMeshFilter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasMeshRenderer", initialHasMeshRenderer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedTransform", addedTransform));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedCamera", addedCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedLight", addedLight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedMeshFilter", addedMeshFilter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedMeshRenderer", addedMeshRenderer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasCameraAfterAdd", hasCameraAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasLightAfterAdd", hasLightAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshFilterAfterAdd", hasMeshFilterAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshRendererAfterAdd", hasMeshRendererAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshFilterLookupSucceeded", meshFilterLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCameraFieldOfView", observedCameraFieldOfView));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLightIntensity", observedLightIntensity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMeshPath", observedMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCount", observedMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0Path", observedMaterial0Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayer", observedRenderLayer));
|
|
|
|
EXPECT_FALSE(initialHasCamera);
|
|
EXPECT_FALSE(initialHasLight);
|
|
EXPECT_FALSE(initialHasMeshFilter);
|
|
EXPECT_FALSE(initialHasMeshRenderer);
|
|
EXPECT_TRUE(addedTransform);
|
|
EXPECT_TRUE(addedCamera);
|
|
EXPECT_TRUE(addedLight);
|
|
EXPECT_TRUE(addedMeshFilter);
|
|
EXPECT_TRUE(addedMeshRenderer);
|
|
EXPECT_TRUE(hasCameraAfterAdd);
|
|
EXPECT_TRUE(hasLightAfterAdd);
|
|
EXPECT_TRUE(hasMeshFilterAfterAdd);
|
|
EXPECT_TRUE(hasMeshRendererAfterAdd);
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_TRUE(lightLookupSucceeded);
|
|
EXPECT_TRUE(meshFilterLookupSucceeded);
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_FLOAT_EQ(observedCameraFieldOfView, 82.0f);
|
|
EXPECT_FLOAT_EQ(observedLightIntensity, 4.5f);
|
|
EXPECT_EQ(observedMeshPath, "Meshes/added.mesh");
|
|
EXPECT_EQ(observedMaterialCount, 1);
|
|
EXPECT_EQ(observedMaterial0Path, "Materials/added.mat");
|
|
EXPECT_EQ(observedRenderLayer, 6);
|
|
|
|
ASSERT_NE(host->GetTransform(), nullptr);
|
|
EXPECT_EQ(host->GetComponents<CameraComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<LightComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<MeshFilterComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<MeshRendererComponent>().size(), 1u);
|
|
|
|
CameraComponent* camera = host->GetComponent<CameraComponent>();
|
|
LightComponent* light = host->GetComponent<LightComponent>();
|
|
MeshFilterComponent* meshFilter = host->GetComponent<MeshFilterComponent>();
|
|
MeshRendererComponent* meshRenderer = host->GetComponent<MeshRendererComponent>();
|
|
|
|
ASSERT_NE(camera, nullptr);
|
|
ASSERT_NE(light, nullptr);
|
|
ASSERT_NE(meshFilter, nullptr);
|
|
ASSERT_NE(meshRenderer, nullptr);
|
|
|
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 82.0f);
|
|
EXPECT_FLOAT_EQ(light->GetIntensity(), 4.5f);
|
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/added.mesh");
|
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 1u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/added.mat");
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 6u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectComponentApiSupportsManagedScriptTypes) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "ScriptComponentApiProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool initialHasTarget = true;
|
|
bool initialLookupReturnedNull = false;
|
|
bool addedTarget = false;
|
|
bool hasTargetAfterAdd = false;
|
|
bool lookupSucceededAfterAdd = false;
|
|
bool tryGetSucceededAfterAdd = false;
|
|
bool returnedSameInstance = false;
|
|
bool targetEnabledAfterAdd = false;
|
|
int32_t observedTargetAwakeCount = 0;
|
|
int32_t observedTargetStartCount = -1;
|
|
int32_t observedTargetHostCallCount = 0;
|
|
std::string observedTargetHostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasTarget", initialHasTarget));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialLookupReturnedNull", initialLookupReturnedNull));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedTarget", addedTarget));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTargetAfterAdd", hasTargetAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LookupSucceededAfterAdd", lookupSucceededAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TryGetSucceededAfterAdd", tryGetSucceededAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ReturnedSameInstance", returnedSameInstance));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetEnabledAfterAdd", targetEnabledAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetAwakeCount", observedTargetAwakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetStartCount", observedTargetStartCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetHostCallCount", observedTargetHostCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetHostName", observedTargetHostName));
|
|
|
|
EXPECT_FALSE(initialHasTarget);
|
|
EXPECT_TRUE(initialLookupReturnedNull);
|
|
EXPECT_TRUE(addedTarget);
|
|
EXPECT_TRUE(hasTargetAfterAdd);
|
|
EXPECT_TRUE(lookupSucceededAfterAdd);
|
|
EXPECT_TRUE(tryGetSucceededAfterAdd);
|
|
EXPECT_TRUE(returnedSameInstance);
|
|
EXPECT_TRUE(targetEnabledAfterAdd);
|
|
EXPECT_EQ(observedTargetAwakeCount, 1);
|
|
EXPECT_EQ(observedTargetStartCount, 0);
|
|
EXPECT_EQ(observedTargetHostCallCount, 1);
|
|
EXPECT_EQ(observedTargetHostName, "Host");
|
|
|
|
ASSERT_EQ(host->GetComponents<ScriptComponent>().size(), 2u);
|
|
ScriptComponent* targetScript = FindScriptComponentByClass(host, "Gameplay", "ScriptComponentTargetProbe");
|
|
ASSERT_NE(targetScript, nullptr);
|
|
EXPECT_TRUE(runtime->HasManagedInstance(targetScript));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 2u);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = -1;
|
|
int32_t hostCallCount = 0;
|
|
std::string hostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "HostCallCount", hostCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "HostName", hostName));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 0);
|
|
EXPECT_EQ(hostCallCount, 1);
|
|
EXPECT_EQ(hostName, "Host");
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
startCount = 0;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "StartCount", startCount));
|
|
EXPECT_EQ(startCount, 1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectRuntimeApiCreatesFindsAndDestroysSceneObjects) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "RuntimeGameObjectProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool missingBeforeCreate = false;
|
|
bool createdRootSucceeded = false;
|
|
bool createdChildSucceeded = false;
|
|
bool foundRootSucceeded = false;
|
|
bool foundChildSucceeded = false;
|
|
std::string observedFoundRootName;
|
|
std::string observedFoundChildParentName;
|
|
int32_t observedRootChildCountBeforeDestroy = 0;
|
|
bool cameraLookupSucceeded = false;
|
|
bool meshFilterLookupSucceeded = false;
|
|
bool meshRendererLookupSucceeded = false;
|
|
float observedCameraFieldOfView = 0.0f;
|
|
std::string observedMeshPath;
|
|
int32_t observedMaterialCount = 0;
|
|
std::string observedMaterial0Path;
|
|
int32_t observedRenderLayer = 0;
|
|
bool missingChildAfterDestroy = false;
|
|
bool foundRootAfterDestroySucceeded = false;
|
|
int32_t observedRootChildCountAfterDestroy = -1;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MissingBeforeCreate", missingBeforeCreate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CreatedRootSucceeded", createdRootSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CreatedChildSucceeded", createdChildSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundRootSucceeded", foundRootSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundChildSucceeded", foundChildSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFoundRootName", observedFoundRootName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFoundChildParentName", observedFoundChildParentName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountBeforeDestroy", observedRootChildCountBeforeDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshFilterLookupSucceeded", meshFilterLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCameraFieldOfView", observedCameraFieldOfView));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMeshPath", observedMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCount", observedMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0Path", observedMaterial0Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayer", observedRenderLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MissingChildAfterDestroy", missingChildAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundRootAfterDestroySucceeded", foundRootAfterDestroySucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountAfterDestroy", observedRootChildCountAfterDestroy));
|
|
|
|
EXPECT_TRUE(missingBeforeCreate);
|
|
EXPECT_TRUE(createdRootSucceeded);
|
|
EXPECT_TRUE(createdChildSucceeded);
|
|
EXPECT_TRUE(foundRootSucceeded);
|
|
EXPECT_TRUE(foundChildSucceeded);
|
|
EXPECT_EQ(observedFoundRootName, "RuntimeCreatedRoot");
|
|
EXPECT_EQ(observedFoundChildParentName, "RuntimeCreatedRoot");
|
|
EXPECT_EQ(observedRootChildCountBeforeDestroy, 1);
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_TRUE(meshFilterLookupSucceeded);
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_FLOAT_EQ(observedCameraFieldOfView, 68.0f);
|
|
EXPECT_EQ(observedMeshPath, "Meshes/runtime_created.mesh");
|
|
EXPECT_EQ(observedMaterialCount, 1);
|
|
EXPECT_EQ(observedMaterial0Path, "Materials/runtime_created.mat");
|
|
EXPECT_EQ(observedRenderLayer, 4);
|
|
EXPECT_TRUE(missingChildAfterDestroy);
|
|
EXPECT_TRUE(foundRootAfterDestroySucceeded);
|
|
EXPECT_EQ(observedRootChildCountAfterDestroy, 0);
|
|
|
|
GameObject* createdRoot = runtimeScene->Find("RuntimeCreatedRoot");
|
|
ASSERT_NE(createdRoot, nullptr);
|
|
EXPECT_EQ(runtimeScene->Find("RuntimeCreatedChild"), nullptr);
|
|
EXPECT_EQ(createdRoot->GetParent(), nullptr);
|
|
EXPECT_EQ(createdRoot->GetChildCount(), 0u);
|
|
|
|
CameraComponent* camera = createdRoot->GetComponent<CameraComponent>();
|
|
MeshFilterComponent* meshFilter = createdRoot->GetComponent<MeshFilterComponent>();
|
|
MeshRendererComponent* meshRenderer = createdRoot->GetComponent<MeshRendererComponent>();
|
|
|
|
ASSERT_NE(camera, nullptr);
|
|
ASSERT_NE(meshFilter, nullptr);
|
|
ASSERT_NE(meshRenderer, nullptr);
|
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 68.0f);
|
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/runtime_created.mesh");
|
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 1u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/runtime_created.mat");
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 4u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectTagAndLayerApiExposeUnityStylePropertiesAndCompareTag) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
host->SetTag("Enemy");
|
|
host->SetLayer(7);
|
|
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TagLayerProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
std::string observedInitialTag;
|
|
int32_t observedInitialLayer = -1;
|
|
bool observedInitialCompareTag = false;
|
|
bool observedGameObjectRouteMatches = false;
|
|
std::string observedUpdatedTag;
|
|
int32_t observedUpdatedLayer = -1;
|
|
bool observedUpdatedCompareTag = false;
|
|
bool observedOriginalTagRejected = false;
|
|
bool observedEmptyTagRejected = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialTag", observedInitialTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialLayer", observedInitialLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialCompareTag", observedInitialCompareTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectRouteMatches", observedGameObjectRouteMatches));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedTag", observedUpdatedTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedLayer", observedUpdatedLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCompareTag", observedUpdatedCompareTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedOriginalTagRejected", observedOriginalTagRejected));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEmptyTagRejected", observedEmptyTagRejected));
|
|
|
|
EXPECT_EQ(observedInitialTag, "Enemy");
|
|
EXPECT_EQ(observedInitialLayer, 7);
|
|
EXPECT_TRUE(observedInitialCompareTag);
|
|
EXPECT_TRUE(observedGameObjectRouteMatches);
|
|
EXPECT_EQ(observedUpdatedTag, "Player");
|
|
EXPECT_EQ(observedUpdatedLayer, 31);
|
|
EXPECT_TRUE(observedUpdatedCompareTag);
|
|
EXPECT_TRUE(observedOriginalTagRejected);
|
|
EXPECT_TRUE(observedEmptyTagRejected);
|
|
|
|
EXPECT_EQ(host->GetTag(), "Player");
|
|
EXPECT_EQ(host->GetLayer(), 31u);
|
|
EXPECT_EQ(runtimeScene->FindGameObjectWithTag("Player"), host);
|
|
EXPECT_EQ(runtimeScene->FindGameObjectWithTag("Enemy"), nullptr);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, RuntimeCreatedScriptComponentCreatesManagedInstanceAfterClassAssignment) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
GameObject* spawned = runtimeScene->CreateGameObject("RuntimeSpawned");
|
|
ScriptComponent* component = spawned->AddComponent<ScriptComponent>();
|
|
component->GetFieldStorage().SetFieldValue("Label", "RuntimeConfigured");
|
|
component->SetScriptClass("GameScripts", "Gameplay", "LifecycleProbe");
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t enableCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t updateCount = 0;
|
|
std::string label;
|
|
std::string observedGameObjectName;
|
|
bool wasAwakened = false;
|
|
bool observedEnabled = false;
|
|
bool observedActiveSelf = false;
|
|
bool observedActiveInHierarchy = false;
|
|
bool observedIsActiveAndEnabled = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", label));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectName", observedGameObjectName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WasAwakened", wasAwakened));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(enableCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(label, "RuntimeConfigured|Awake");
|
|
EXPECT_EQ(observedGameObjectName, "RuntimeSpawned_Managed");
|
|
EXPECT_TRUE(wasAwakened);
|
|
EXPECT_TRUE(observedEnabled);
|
|
EXPECT_TRUE(observedActiveSelf);
|
|
EXPECT_TRUE(observedActiveInHierarchy);
|
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
|
EXPECT_EQ(spawned->GetName(), "RuntimeSpawned_Managed");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformHierarchyApiExposesParentChildAndReparenting) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* root = runtimeScene->CreateGameObject("Root");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host", root);
|
|
GameObject* child = runtimeScene->CreateGameObject("Child", host);
|
|
GameObject* reparentTarget = runtimeScene->CreateGameObject("ReparentTarget");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "HierarchyProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("ReparentTarget", GameObjectReference{reparentTarget->GetUUID()});
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedChildCount = 0;
|
|
bool parentLookupSucceeded = false;
|
|
bool childLookupSucceeded = false;
|
|
std::string observedParentName;
|
|
std::string observedFirstChildName;
|
|
std::string reparentedChildParentName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedChildCount", observedChildCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ParentLookupSucceeded", parentLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ChildLookupSucceeded", childLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedParentName", observedParentName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFirstChildName", observedFirstChildName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ReparentedChildParentName", reparentedChildParentName));
|
|
|
|
EXPECT_TRUE(parentLookupSucceeded);
|
|
EXPECT_TRUE(childLookupSucceeded);
|
|
EXPECT_EQ(observedChildCount, 1);
|
|
EXPECT_EQ(observedParentName, "Root");
|
|
EXPECT_EQ(observedFirstChildName, "Child");
|
|
EXPECT_EQ(reparentedChildParentName, "ReparentTarget");
|
|
|
|
EXPECT_EQ(host->GetParent(), root);
|
|
EXPECT_EQ(host->GetChildCount(), 0u);
|
|
EXPECT_EQ(child->GetParent(), reparentTarget);
|
|
EXPECT_EQ(reparentTarget->GetChildCount(), 1u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, UnityObjectApiGetComponentsReturnsDirectComponentsAndReusesManagedInstances) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* child = runtimeScene->CreateGameObject("Child", host);
|
|
|
|
host->AddComponent<CameraComponent>();
|
|
child->AddComponent<CameraComponent>();
|
|
child->AddComponent<MeshRendererComponent>();
|
|
|
|
AddScript(host, "Gameplay", "ObjectApiMarkerProbe");
|
|
AddScript(host, "Gameplay", "ObjectApiMarkerProbe");
|
|
ScriptComponent* hostProbe = AddScript(host, "Gameplay", "GetComponentsProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedTransformCount = -1;
|
|
int32_t observedCameraCount = -1;
|
|
int32_t observedCameraCountViaGameObject = -1;
|
|
int32_t observedMarkerCount = -1;
|
|
int32_t observedMeshRendererCount = -1;
|
|
bool observedAllMarkersNonNull = false;
|
|
bool observedFirstMarkerMatchesGetComponent = false;
|
|
bool observedMarkerInstancesAreDistinct = false;
|
|
bool observedTransformBoundToHost = false;
|
|
std::string observedFirstMarkerHostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedTransformCount", observedTransformCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedCameraCount", observedCameraCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedCameraCountViaGameObject", observedCameraCountViaGameObject));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedMarkerCount", observedMarkerCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedMeshRendererCount", observedMeshRendererCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedAllMarkersNonNull", observedAllMarkersNonNull));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedFirstMarkerMatchesGetComponent", observedFirstMarkerMatchesGetComponent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedMarkerInstancesAreDistinct", observedMarkerInstancesAreDistinct));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedTransformBoundToHost", observedTransformBoundToHost));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedFirstMarkerHostName", observedFirstMarkerHostName));
|
|
|
|
EXPECT_EQ(observedTransformCount, 1);
|
|
EXPECT_EQ(observedCameraCount, 1);
|
|
EXPECT_EQ(observedCameraCountViaGameObject, 1);
|
|
EXPECT_EQ(observedMarkerCount, 2);
|
|
EXPECT_EQ(observedMeshRendererCount, 0);
|
|
EXPECT_TRUE(observedAllMarkersNonNull);
|
|
EXPECT_TRUE(observedFirstMarkerMatchesGetComponent);
|
|
EXPECT_TRUE(observedMarkerInstancesAreDistinct);
|
|
EXPECT_TRUE(observedTransformBoundToHost);
|
|
EXPECT_EQ(observedFirstMarkerHostName, "Host");
|
|
|
|
EXPECT_EQ(host->GetComponents<CameraComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<ScriptComponent>().size(), 3u);
|
|
EXPECT_EQ(child->GetComponents<CameraComponent>().size(), 1u);
|
|
EXPECT_EQ(child->GetComponents<MeshRendererComponent>().size(), 1u);
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 3u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, UnityObjectApiSupportsHierarchyLookupAndDestroy) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* root = runtimeScene->CreateGameObject("Root");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host", root);
|
|
GameObject* child = runtimeScene->CreateGameObject("Child", host);
|
|
|
|
root->AddComponent<CameraComponent>();
|
|
child->AddComponent<CameraComponent>();
|
|
child->AddComponent<MeshRendererComponent>();
|
|
|
|
AddScript(root, "Gameplay", "ObjectApiMarkerProbe");
|
|
AddScript(child, "Gameplay", "ObjectApiDestroyTargetProbe");
|
|
ScriptComponent* hostProbe = AddScript(host, "Gameplay", "ObjectApiProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool foundSelfScriptViaParentLookup = false;
|
|
bool foundSelfScriptViaGameObjectParentLookup = false;
|
|
bool foundSelfTransformViaChildrenLookup = false;
|
|
bool foundSelfTransformViaGameObjectChildrenLookup = false;
|
|
bool foundCameraInParent = false;
|
|
bool foundCameraInParentViaGameObject = false;
|
|
bool foundMarkerInParent = false;
|
|
bool foundMeshRendererInChildren = false;
|
|
bool foundMeshRendererInChildrenViaGameObject = false;
|
|
bool foundTargetScriptInChildren = false;
|
|
bool childCameraMissingAfterDestroy = false;
|
|
bool childScriptMissingAfterDestroy = false;
|
|
bool childGameObjectMissingAfterDestroy = false;
|
|
int32_t observedChildScriptDisableCount = -1;
|
|
int32_t observedChildCountAfterDestroy = -1;
|
|
std::string observedParentCameraHostName;
|
|
std::string observedChildMeshRendererHostName;
|
|
std::string observedChildScriptHostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfScriptViaParentLookup", foundSelfScriptViaParentLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfScriptViaGameObjectParentLookup", foundSelfScriptViaGameObjectParentLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfTransformViaChildrenLookup", foundSelfTransformViaChildrenLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfTransformViaGameObjectChildrenLookup", foundSelfTransformViaGameObjectChildrenLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundCameraInParent", foundCameraInParent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundCameraInParentViaGameObject", foundCameraInParentViaGameObject));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundMarkerInParent", foundMarkerInParent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundMeshRendererInChildren", foundMeshRendererInChildren));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundMeshRendererInChildrenViaGameObject", foundMeshRendererInChildrenViaGameObject));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundTargetScriptInChildren", foundTargetScriptInChildren));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ChildCameraMissingAfterDestroy", childCameraMissingAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ChildScriptMissingAfterDestroy", childScriptMissingAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ChildGameObjectMissingAfterDestroy", childGameObjectMissingAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildScriptDisableCount", observedChildScriptDisableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildCountAfterDestroy", observedChildCountAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedParentCameraHostName", observedParentCameraHostName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildMeshRendererHostName", observedChildMeshRendererHostName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildScriptHostName", observedChildScriptHostName));
|
|
|
|
EXPECT_TRUE(foundSelfScriptViaParentLookup);
|
|
EXPECT_TRUE(foundSelfScriptViaGameObjectParentLookup);
|
|
EXPECT_TRUE(foundSelfTransformViaChildrenLookup);
|
|
EXPECT_TRUE(foundSelfTransformViaGameObjectChildrenLookup);
|
|
EXPECT_TRUE(foundCameraInParent);
|
|
EXPECT_TRUE(foundCameraInParentViaGameObject);
|
|
EXPECT_TRUE(foundMarkerInParent);
|
|
EXPECT_TRUE(foundMeshRendererInChildren);
|
|
EXPECT_TRUE(foundMeshRendererInChildrenViaGameObject);
|
|
EXPECT_TRUE(foundTargetScriptInChildren);
|
|
EXPECT_TRUE(childCameraMissingAfterDestroy);
|
|
EXPECT_TRUE(childScriptMissingAfterDestroy);
|
|
EXPECT_TRUE(childGameObjectMissingAfterDestroy);
|
|
EXPECT_EQ(observedChildScriptDisableCount, 1);
|
|
EXPECT_EQ(observedChildCountAfterDestroy, 0);
|
|
EXPECT_EQ(observedParentCameraHostName, "Root");
|
|
EXPECT_EQ(observedChildMeshRendererHostName, "Child");
|
|
EXPECT_EQ(observedChildScriptHostName, "Child");
|
|
|
|
EXPECT_EQ(runtimeScene->Find("Child"), nullptr);
|
|
EXPECT_EQ(host->GetChildCount(), 0u);
|
|
EXPECT_EQ(host->GetComponentInChildren<CameraComponent>(), nullptr);
|
|
EXPECT_EQ(host->GetComponentInChildren<MeshRendererComponent>(), nullptr);
|
|
EXPECT_EQ(host->GetComponents<ScriptComponent>().size(), 1u);
|
|
EXPECT_EQ(root->GetComponents<ScriptComponent>().size(), 1u);
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 2u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformSpaceApiReadsAndWritesWorldAndLocalValues) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* parent = runtimeScene->CreateGameObject("Parent");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host", parent);
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformSpaceProbe");
|
|
|
|
parent->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(10.0f, 0.0f, 0.0f));
|
|
parent->GetTransform()->SetLocalScale(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
|
host->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
host->GetTransform()->SetLocalScale(XCEngine::Math::Vector3(1.0f, 1.0f, 1.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedInitialWorldPosition;
|
|
XCEngine::Math::Vector3 observedInitialWorldScale;
|
|
XCEngine::Math::Vector3 observedInitialLocalEulerAngles;
|
|
XCEngine::Math::Vector3 observedEulerAfterSet;
|
|
XCEngine::Math::Vector3 observedWorldPositionAfterSet;
|
|
XCEngine::Math::Vector3 observedWorldScaleAfterSet;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialWorldPosition", observedInitialWorldPosition));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialWorldScale", observedInitialWorldScale));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialLocalEulerAngles", observedInitialLocalEulerAngles));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEulerAfterSet", observedEulerAfterSet));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldPositionAfterSet", observedWorldPositionAfterSet));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldScaleAfterSet", observedWorldScaleAfterSet));
|
|
|
|
EXPECT_EQ(observedInitialWorldPosition, XCEngine::Math::Vector3(12.0f, 4.0f, 6.0f));
|
|
EXPECT_EQ(observedInitialWorldScale, XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
|
EXPECT_EQ(observedInitialLocalEulerAngles, XCEngine::Math::Vector3(0.0f, 0.0f, 0.0f));
|
|
EXPECT_NEAR(observedEulerAfterSet.x, 0.0f, 0.001f);
|
|
EXPECT_NEAR(observedEulerAfterSet.y, 45.0f, 0.001f);
|
|
EXPECT_NEAR(observedEulerAfterSet.z, 0.0f, 0.001f);
|
|
EXPECT_EQ(observedWorldPositionAfterSet, XCEngine::Math::Vector3(20.0f, 6.0f, 10.0f));
|
|
EXPECT_EQ(observedWorldScaleAfterSet, XCEngine::Math::Vector3(6.0f, 8.0f, 10.0f));
|
|
|
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(5.0f, 3.0f, 5.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetPosition(), XCEngine::Math::Vector3(20.0f, 6.0f, 10.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetLocalScale(), XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetScale(), XCEngine::Math::Vector3(6.0f, 8.0f, 10.0f));
|
|
|
|
const XCEngine::Math::Quaternion& localRotation = host->GetTransform()->GetLocalRotation();
|
|
EXPECT_FLOAT_EQ(localRotation.x, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.y, 0.5f);
|
|
EXPECT_FLOAT_EQ(localRotation.z, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.w, 0.8660254f);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformMotionApiExposesDirectionVectorsAndAppliesOperations) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformMotionProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedForward;
|
|
XCEngine::Math::Vector3 observedRight;
|
|
XCEngine::Math::Vector3 observedUp;
|
|
XCEngine::Math::Vector3 observedPositionAfterSelfTranslate;
|
|
XCEngine::Math::Vector3 observedPositionAfterWorldTranslate;
|
|
XCEngine::Math::Vector3 observedForwardAfterWorldRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterSelfRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterLookAt;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForward", observedForward));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRight", observedRight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUp", observedUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPositionAfterSelfTranslate", observedPositionAfterSelfTranslate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPositionAfterWorldTranslate", observedPositionAfterWorldTranslate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterWorldRotate", observedForwardAfterWorldRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterSelfRotate", observedForwardAfterSelfRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterLookAt", observedForwardAfterLookAt));
|
|
|
|
ExpectVector3Near(observedForward, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedRight, XCEngine::Math::Vector3(0.0f, 0.0f, -1.0f));
|
|
ExpectVector3Near(observedUp, XCEngine::Math::Vector3(0.0f, 1.0f, 0.0f));
|
|
ExpectVector3Near(observedPositionAfterSelfTranslate, XCEngine::Math::Vector3(2.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedPositionAfterWorldTranslate, XCEngine::Math::Vector3(2.0f, 0.0f, 3.0f));
|
|
ExpectVector3Near(observedForwardAfterWorldRotate, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
ExpectVector3Near(observedForwardAfterSelfRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedForwardAfterLookAt, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
|
|
ExpectVector3Near(host->GetTransform()->GetPosition(), XCEngine::Math::Vector3(2.0f, 0.0f, 3.0f));
|
|
ExpectVector3Near(host->GetTransform()->GetForward(), XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformConversionApiTransformsPointsAndDirectionsBetweenSpaces) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformConversionProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedWorldPoint;
|
|
XCEngine::Math::Vector3 observedLocalPoint;
|
|
XCEngine::Math::Vector3 observedWorldDirection;
|
|
XCEngine::Math::Vector3 observedLocalDirection;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldPoint", observedWorldPoint));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalPoint", observedLocalPoint));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldDirection", observedWorldDirection));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalDirection", observedLocalDirection));
|
|
|
|
ExpectVector3Near(observedWorldPoint, XCEngine::Math::Vector3(7.0f, 0.0f, 1.0f));
|
|
ExpectVector3Near(observedLocalPoint, XCEngine::Math::Vector3(0.0f, 0.0f, 2.0f));
|
|
ExpectVector3Near(observedWorldDirection, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedLocalDirection, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
|
|
ExpectVector3Near(host->GetTransform()->TransformPoint(XCEngine::Math::Vector3(0.0f, 0.0f, 2.0f)), XCEngine::Math::Vector3(7.0f, 0.0f, 1.0f));
|
|
ExpectVector3Near(host->GetTransform()->InverseTransformDirection(XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f)), XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformOrientationOverloadsSupportAxisAngleAndCustomUpVector) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformOrientationProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedForwardAfterAxisAngleRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterWorldAxisAngleRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterDefaultLookAt;
|
|
XCEngine::Math::Vector3 observedRightAfterDefaultLookAt;
|
|
XCEngine::Math::Vector3 observedUpAfterDefaultLookAt;
|
|
XCEngine::Math::Vector3 observedForwardAfterLookAtWithUp;
|
|
XCEngine::Math::Vector3 observedRightAfterLookAtWithUp;
|
|
XCEngine::Math::Vector3 observedUpAfterLookAtWithUp;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterAxisAngleRotate", observedForwardAfterAxisAngleRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterWorldAxisAngleRotate", observedForwardAfterWorldAxisAngleRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterDefaultLookAt", observedForwardAfterDefaultLookAt));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRightAfterDefaultLookAt", observedRightAfterDefaultLookAt));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpAfterDefaultLookAt", observedUpAfterDefaultLookAt));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterLookAtWithUp", observedForwardAfterLookAtWithUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRightAfterLookAtWithUp", observedRightAfterLookAtWithUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpAfterLookAtWithUp", observedUpAfterLookAtWithUp));
|
|
|
|
ExpectVector3Near(observedForwardAfterAxisAngleRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedForwardAfterWorldAxisAngleRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
|
|
EXPECT_NEAR(observedForwardAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedRightAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedUpAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedForwardAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedRightAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedUpAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f);
|
|
|
|
EXPECT_GT((observedRightAfterLookAtWithUp - observedRightAfterDefaultLookAt).Magnitude(), 0.5f);
|
|
EXPECT_GT((observedUpAfterLookAtWithUp - observedUpAfterDefaultLookAt).Magnitude(), 0.5f);
|
|
|
|
ExpectVector3Near(host->GetTransform()->GetForward(), observedForwardAfterLookAtWithUp);
|
|
ExpectVector3Near(host->GetTransform()->GetRight(), observedRightAfterLookAtWithUp);
|
|
ExpectVector3Near(host->GetTransform()->GetUp(), observedUpAfterLookAtWithUp);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedBehaviourCanDisableItselfThroughEnabledProperty) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("DisableSelfOnFirstUpdate", true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t updateCount = 0;
|
|
int32_t lateUpdateCount = 0;
|
|
int32_t disableCount = 0;
|
|
bool observedEnabled = false;
|
|
bool observedIsActiveAndEnabled = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
|
|
|
EXPECT_FALSE(component->IsEnabled());
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(lateUpdateCount, 0);
|
|
EXPECT_EQ(disableCount, 1);
|
|
EXPECT_TRUE(observedEnabled);
|
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
|
|
|
engine->OnUpdate(0.016f);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_EQ(updateCount, 1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedScriptCanDeactivateItsHostGameObject) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("DeactivateGameObjectOnFirstUpdate", true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t updateCount = 0;
|
|
int32_t lateUpdateCount = 0;
|
|
int32_t disableCount = 0;
|
|
bool observedActiveSelf = false;
|
|
bool observedActiveInHierarchy = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
|
|
|
EXPECT_FALSE(host->IsActive());
|
|
EXPECT_FALSE(host->IsActiveInHierarchy());
|
|
EXPECT_TRUE(component->IsEnabled());
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(lateUpdateCount, 0);
|
|
EXPECT_EQ(disableCount, 1);
|
|
EXPECT_TRUE(observedActiveSelf);
|
|
EXPECT_TRUE(observedActiveInHierarchy);
|
|
|
|
engine->OnUpdate(0.016f);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_EQ(updateCount, 1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, DisableEnablePreservesSingleInstanceAndStartOnlyRunsOnce) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
component->SetEnabled(false);
|
|
|
|
int32_t disableCount = 0;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
|
EXPECT_EQ(disableCount, 1);
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 1u);
|
|
|
|
component->SetEnabled(true);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t enableCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t updateCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
|
|
EXPECT_EQ(enableCount, 2);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(updateCount, 2);
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
EXPECT_FALSE(runtime->HasManagedInstance(component));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 0u);
|
|
}
|
|
|
|
} // namespace
|
|
|