371 lines
11 KiB
C++
371 lines
11 KiB
C++
#define TEST_NO_MAIN
|
|
#include "acutest.h"
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <cstdlib>
|
|
#include "../include/ECSComponents.h"
|
|
#include "../include/SceneGraph.h"
|
|
#include "../include/SceneLoader.h"
|
|
#include "../include/ModelManager.h"
|
|
#include "../include/ShaderManager.h"
|
|
#include "../include/TextureManager.h"
|
|
#include "../include/AssetManager.h"
|
|
#include "raylib.h"
|
|
#include "entt.hpp"
|
|
#include "physfs_test_utils.h"
|
|
|
|
extern "C" {
|
|
void acutest_skip_(const char* file, int line, const char* fmt, ...);
|
|
}
|
|
static bool HasDisplay() {
|
|
#if defined(_WIN32)
|
|
return true;
|
|
#elif defined(__APPLE__)
|
|
return true;
|
|
#else
|
|
const char* display = std::getenv("DISPLAY");
|
|
const char* wayland = std::getenv("WAYLAND_DISPLAY");
|
|
return (display && *display) || (wayland && *wayland);
|
|
#endif
|
|
}
|
|
|
|
static bool InitRaylibHidden() {
|
|
const char* forceHeadless = std::getenv("SIMIAN_HEADLESS");
|
|
if (forceHeadless && forceHeadless[0] == '1') {
|
|
return false;
|
|
}
|
|
|
|
if (!HasDisplay()) {
|
|
return false;
|
|
}
|
|
|
|
if (!IsWindowReady()) {
|
|
SetConfigFlags(FLAG_WINDOW_HIDDEN);
|
|
InitWindow(1, 1, "simian_test_scene");
|
|
SetTargetFPS(60);
|
|
}
|
|
|
|
return IsWindowReady();
|
|
}
|
|
|
|
static void ShutdownRaylibHidden() {
|
|
if (IsWindowReady()) {
|
|
CloseWindow();
|
|
}
|
|
}
|
|
|
|
void test_scenegraph_world_transform(void) {
|
|
entt::registry registry;
|
|
|
|
entt::entity parent = registry.create();
|
|
entt::entity child = registry.create();
|
|
|
|
registry.emplace<ECSTransform>(parent, Vector3{1.0f, 0.0f, 0.0f}, Vector3{2.0f, 2.0f, 2.0f}, 0.0f);
|
|
registry.emplace<ECSTransform>(child, Vector3{1.0f, 0.0f, 0.0f}, Vector3{1.0f, 1.0f, 1.0f}, 0.0f);
|
|
|
|
AttachChild(registry, child, parent);
|
|
UpdateWorldTransforms(registry);
|
|
|
|
auto* childWorld = registry.try_get<WorldTransform>(child);
|
|
TEST_CHECK(childWorld != nullptr);
|
|
if (childWorld) {
|
|
TEST_CHECK(childWorld->position.x == 3.0f);
|
|
TEST_CHECK(childWorld->position.y == 0.0f);
|
|
TEST_CHECK(childWorld->position.z == 0.0f);
|
|
}
|
|
}
|
|
|
|
void test_scenegraph_detach(void) {
|
|
entt::registry registry;
|
|
|
|
entt::entity parent = registry.create();
|
|
entt::entity child = registry.create();
|
|
|
|
AttachChild(registry, child, parent);
|
|
DetachFromParent(registry, child);
|
|
|
|
auto* childHierarchy = registry.try_get<Hierarchy>(child);
|
|
TEST_CHECK(childHierarchy != nullptr);
|
|
if (childHierarchy) {
|
|
TEST_CHECK(childHierarchy->parent == entt::null);
|
|
TEST_CHECK(childHierarchy->prev == entt::null);
|
|
TEST_CHECK(childHierarchy->next == entt::null);
|
|
}
|
|
|
|
auto* parentHierarchy = registry.try_get<Hierarchy>(parent);
|
|
TEST_CHECK(parentHierarchy != nullptr);
|
|
if (parentHierarchy) {
|
|
TEST_CHECK(parentHierarchy->first == entt::null);
|
|
}
|
|
}
|
|
|
|
void test_scene_loader_basic(void) {
|
|
if (!InitRaylibHidden()) {
|
|
TEST_SKIP("No display available for raylib");
|
|
return;
|
|
}
|
|
|
|
entt::registry registry;
|
|
|
|
std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene";
|
|
std::filesystem::create_directories(tmpDir);
|
|
std::filesystem::path scenePath = tmpDir / "scene.toml";
|
|
|
|
{
|
|
std::ofstream scene(scenePath);
|
|
scene << "[[entity]]\n";
|
|
scene << "id = \"root\"\n";
|
|
scene << "tag = \"Root\"\n";
|
|
scene << "position = [0.0, 0.0, 0.0]\n";
|
|
scene << "\n";
|
|
scene << "[[entity]]\n";
|
|
scene << "id = \"parent\"\n";
|
|
scene << "parent = \"root\"\n";
|
|
scene << "tag = \"Parent\"\n";
|
|
scene << "position = [1.0, 0.0, 0.0]\n";
|
|
scene << "model = \"cube\"\n";
|
|
scene << "model_size = [1.0, 1.0, 1.0]\n";
|
|
scene << "color = 0x00FF00FF\n";
|
|
scene << "\n";
|
|
scene << "[[entity]]\n";
|
|
scene << "id = \"child\"\n";
|
|
scene << "parent = \"parent\"\n";
|
|
scene << "tag = \"Child\"\n";
|
|
scene << "position = [0.0, 1.0, 0.0]\n";
|
|
scene << "model = \"sphere\"\n";
|
|
scene << "radius = 0.5\n";
|
|
scene << "color = 0xFF0000FF\n";
|
|
}
|
|
|
|
TEST_CHECK(InitPhysFSTest(tmpDir) == true);
|
|
TEST_CHECK(MountPhysFSTest(tmpDir, "") == true);
|
|
|
|
{
|
|
ModelManager modelManager;
|
|
ShaderManager shaderManager;
|
|
TextureManager textureManager;
|
|
SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager);
|
|
|
|
entt::entity root = LoadSceneFromFile("scene.toml", true);
|
|
TEST_CHECK(root != entt::null);
|
|
}
|
|
|
|
entt::entity parentEntity = entt::null;
|
|
entt::entity childEntity = entt::null;
|
|
|
|
auto tagView = registry.view<Tag>();
|
|
for (auto entity : tagView) {
|
|
const auto& tag = tagView.get<Tag>(entity);
|
|
if (tag.name == "Parent") {
|
|
parentEntity = entity;
|
|
} else if (tag.name == "Child") {
|
|
childEntity = entity;
|
|
}
|
|
}
|
|
|
|
TEST_CHECK(parentEntity != entt::null);
|
|
TEST_CHECK(childEntity != entt::null);
|
|
|
|
if (parentEntity != entt::null && childEntity != entt::null) {
|
|
auto* childHierarchy = registry.try_get<Hierarchy>(childEntity);
|
|
TEST_CHECK(childHierarchy != nullptr);
|
|
if (childHierarchy) {
|
|
TEST_CHECK(childHierarchy->parent == parentEntity);
|
|
}
|
|
TEST_CHECK(registry.all_of<ModelRenderer>(parentEntity));
|
|
TEST_CHECK(registry.all_of<ModelRenderer>(childEntity));
|
|
}
|
|
|
|
ShutdownRaylibHidden();
|
|
|
|
|
|
std::filesystem::remove(scenePath);
|
|
std::filesystem::remove(tmpDir);
|
|
}
|
|
|
|
void test_scene_loader_scene_script_path(void) {
|
|
entt::registry registry;
|
|
ModelManager modelManager;
|
|
ShaderManager shaderManager;
|
|
TextureManager textureManager;
|
|
SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager);
|
|
|
|
std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_script";
|
|
std::filesystem::create_directories(tmpDir);
|
|
std::filesystem::path scenePath = tmpDir / "scene.toml";
|
|
|
|
{
|
|
std::ofstream scene(scenePath);
|
|
scene << "scene_script = \"scripts/demo_scene.as\"\n";
|
|
scene << "\n";
|
|
scene << "[[entity]]\n";
|
|
scene << "id = \"root\"\n";
|
|
scene << "tag = \"Root\"\n";
|
|
}
|
|
|
|
TEST_CHECK(InitPhysFSTest(tmpDir) == true);
|
|
TEST_CHECK(MountPhysFSTest(tmpDir, "") == true);
|
|
|
|
entt::entity root = LoadSceneFromFile("scene.toml", true);
|
|
TEST_CHECK(root != entt::null);
|
|
|
|
std::string sceneScript;
|
|
bool changed = ConsumeSceneScriptPath(sceneScript);
|
|
TEST_CHECK(changed == true);
|
|
TEST_CHECK(sceneScript == "scripts/demo_scene.as");
|
|
|
|
|
|
std::filesystem::remove(scenePath);
|
|
std::filesystem::remove(tmpDir);
|
|
}
|
|
|
|
void test_scene_loader_clear_scene_script(void) {
|
|
entt::registry registry;
|
|
ModelManager modelManager;
|
|
ShaderManager shaderManager;
|
|
TextureManager textureManager;
|
|
SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager);
|
|
|
|
SetSceneScriptPath("scripts/old_scene.as");
|
|
std::string previous;
|
|
ConsumeSceneScriptPath(previous);
|
|
|
|
std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_clear";
|
|
std::filesystem::create_directories(tmpDir);
|
|
std::filesystem::path scenePath = tmpDir / "scene.toml";
|
|
|
|
{
|
|
std::ofstream scene(scenePath);
|
|
scene << "[[entity]]\n";
|
|
scene << "id = \"root\"\n";
|
|
scene << "tag = \"Root\"\n";
|
|
}
|
|
|
|
TEST_CHECK(InitPhysFSTest(tmpDir) == true);
|
|
TEST_CHECK(MountPhysFSTest(tmpDir, "") == true);
|
|
|
|
LoadSceneFromFile("scene.toml", true);
|
|
|
|
std::string sceneScript;
|
|
bool changed = ConsumeSceneScriptPath(sceneScript);
|
|
TEST_CHECK(changed == true);
|
|
TEST_CHECK(sceneScript.empty());
|
|
|
|
ShutdownPhysFSTest();
|
|
|
|
std::filesystem::remove(scenePath);
|
|
std::filesystem::remove(tmpDir);
|
|
}
|
|
|
|
void test_scene_loader_asset_keys(void) {
|
|
if (!InitRaylibHidden()) {
|
|
TEST_SKIP("No display available for raylib");
|
|
return;
|
|
}
|
|
|
|
std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_assets";
|
|
std::filesystem::create_directories(tmpDir);
|
|
std::filesystem::path registryPath = tmpDir / "assets" / "registry.toml";
|
|
std::filesystem::path scenePath = tmpDir / "scene_asset.toml";
|
|
|
|
TEST_CHECK(InitPhysFSTest(tmpDir) == true);
|
|
TEST_CHECK(MountPhysFSTest(tmpDir, "") == true);
|
|
|
|
{
|
|
ModelManager modelManager;
|
|
ShaderManager shaderManager;
|
|
TextureManager textureManager;
|
|
AssetManager assets;
|
|
assets.SetManagers(&modelManager, &shaderManager, nullptr, &textureManager);
|
|
assets.LoadCube("demo_cube", 1.0f, 1.0f, 1.0f);
|
|
assets.SaveRegistry("assets/registry.toml");
|
|
}
|
|
|
|
entt::registry registry;
|
|
{
|
|
ModelManager modelManager;
|
|
ShaderManager shaderManager;
|
|
TextureManager textureManager;
|
|
AssetManager assets;
|
|
assets.SetManagers(&modelManager, &shaderManager, nullptr, &textureManager);
|
|
assets.LoadRegistry("assets/registry.toml");
|
|
|
|
SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager);
|
|
SetSceneAssetManager(&assets);
|
|
|
|
{
|
|
std::ofstream scene(scenePath);
|
|
scene << "[[entity]]\n";
|
|
scene << "id = \"root\"\n";
|
|
scene << "tag = \"Root\"\n";
|
|
scene << "position = [0.0, 0.0, 0.0]\n";
|
|
scene << "model_asset = \"demo_cube\"\n";
|
|
scene << "color = 0xFFFFFFFF\n";
|
|
scene << "\n";
|
|
}
|
|
|
|
entt::entity root = LoadSceneFromFile("scene_asset.toml", true);
|
|
TEST_CHECK(root != entt::null);
|
|
|
|
auto tagView = registry.view<Tag>();
|
|
entt::entity rootEntity = entt::null;
|
|
for (auto entity : tagView) {
|
|
const auto& tag = tagView.get<Tag>(entity);
|
|
if (tag.name == "Root") {
|
|
rootEntity = entity;
|
|
break;
|
|
}
|
|
}
|
|
|
|
TEST_CHECK(rootEntity != entt::null);
|
|
if (rootEntity != entt::null) {
|
|
auto* renderer = registry.try_get<ModelRenderer>(rootEntity);
|
|
TEST_CHECK(renderer != nullptr);
|
|
if (renderer) {
|
|
unsigned int expectedId = assets.GetModelId("demo_cube");
|
|
TEST_CHECK(expectedId != 0);
|
|
TEST_CHECK(renderer->modelId == expectedId);
|
|
}
|
|
}
|
|
}
|
|
|
|
ShutdownRaylibHidden();
|
|
|
|
ShutdownPhysFSTest();
|
|
|
|
std::filesystem::remove(scenePath);
|
|
std::filesystem::remove(registryPath);
|
|
std::filesystem::remove(registryPath.parent_path());
|
|
std::filesystem::remove(tmpDir);
|
|
}
|
|
|
|
void test_scene_loader_rejects_escape(void) {
|
|
entt::registry registry;
|
|
ModelManager modelManager;
|
|
ShaderManager shaderManager;
|
|
TextureManager textureManager;
|
|
SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager);
|
|
|
|
std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_sandbox";
|
|
std::filesystem::create_directories(tmpDir);
|
|
std::filesystem::path outsidePath = std::filesystem::current_path() / "outside_scene.toml";
|
|
|
|
{
|
|
std::ofstream scene(outsidePath);
|
|
scene << "[[entity]]\n";
|
|
scene << "id = \"root\"\n";
|
|
scene << "tag = \"Root\"\n";
|
|
}
|
|
|
|
TEST_CHECK(InitPhysFSTest(tmpDir) == true);
|
|
TEST_CHECK(MountPhysFSTest(tmpDir, "") == true);
|
|
|
|
entt::entity root = LoadSceneFromFile("../outside_scene.toml", true);
|
|
TEST_CHECK(root == entt::null);
|
|
|
|
ShutdownPhysFSTest();
|
|
|
|
std::filesystem::remove(outsidePath);
|
|
std::filesystem::remove(tmpDir);
|
|
}
|