feat: move editor to project / add post processing etc
This commit is contained in:
@@ -8,6 +8,7 @@ add_executable(destrum_tests
|
||||
graphics_tests.cpp
|
||||
ui_tests.cpp
|
||||
gameplay_tests.cpp
|
||||
editor_tests.cpp
|
||||
${CMAKE_SOURCE_DIR}/lightkeeper/src/components/Activatable.cpp
|
||||
${CMAKE_SOURCE_DIR}/lightkeeper/src/components/Interactable.cpp
|
||||
${CMAKE_SOURCE_DIR}/lightkeeper/src/components/InteractableButton.cpp
|
||||
@@ -28,12 +29,13 @@ set_target_properties(destrum_tests PROPERTIES
|
||||
target_compile_definitions(destrum_tests PRIVATE
|
||||
SDL_MAIN_HANDLED
|
||||
DESTRUM_ENGINE_ASSETS_SRC_DIR="${CMAKE_SOURCE_DIR}/destrum/assets_src"
|
||||
LIGHTKEEPER_ASSETS_SRC_DIR="${CMAKE_SOURCE_DIR}/lightkeeper/assets_src"
|
||||
)
|
||||
target_include_directories(destrum_tests PRIVATE "${CMAKE_SOURCE_DIR}/lightkeeper/include")
|
||||
target_link_libraries(destrum_tests PRIVATE destrum::destrum)
|
||||
target_link_libraries(destrum_tests PRIVATE destrum::destrum destrum::editor)
|
||||
|
||||
destrum_enable_warnings(destrum_tests)
|
||||
|
||||
foreach(suite core serialization physics assets graphics ui gameplay)
|
||||
foreach(suite core serialization physics assets graphics ui gameplay editor)
|
||||
add_test(NAME destrum_tests.${suite} COMMAND destrum_tests ${suite})
|
||||
endforeach()
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include <string>
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
#include <destrum/Assets/AssetDatabase.h>
|
||||
#include <destrum/Assets/AssetManager.h>
|
||||
#include <destrum/Assets/AssetTypes.h>
|
||||
#include <destrum/Assets/AssetReference.h>
|
||||
#include <destrum/Assets/ModelFile.h>
|
||||
#include <destrum/Assets/TextureFile.h>
|
||||
@@ -16,6 +18,8 @@
|
||||
#include <destrum/Components/Animator.h>
|
||||
#include <destrum/Graphics/Material.h>
|
||||
#include <destrum/Graphics/imageLoader.h>
|
||||
#include <destrum/Serialization/MaterialSerialization.h>
|
||||
#include <destrum/Serialization/ObjectSerialization.h>
|
||||
|
||||
#include "TestUtils.h"
|
||||
|
||||
@@ -447,6 +451,71 @@ namespace {
|
||||
check(!AssetReference::fromCacheKey("CharacterMedium.fbx").has_value(),
|
||||
"plain cache names must not be treated as file-backed assets");
|
||||
}
|
||||
|
||||
void testAssetGuids()
|
||||
{
|
||||
const AssetGuid first = AssetGuid::Generate();
|
||||
const AssetGuid second = AssetGuid::Generate();
|
||||
check(first.IsValid() && second.IsValid() && first != second, "generated asset ids must be valid and unique");
|
||||
|
||||
const std::string text = first.ToString();
|
||||
check(text.size() == 32 && AssetGuid::Parse(text) == first, "asset ids must round-trip through their text form");
|
||||
check(!AssetGuid::Parse("1234").has_value() && !AssetGuid::Parse(std::string(32, 'z')).has_value() &&
|
||||
!AssetGuid::Parse(std::string(32, '0')).has_value(),
|
||||
"malformed or empty asset ids must be rejected");
|
||||
}
|
||||
|
||||
void testAssetDatabaseFollowsMovedAssets()
|
||||
{
|
||||
auto& database = AssetDatabase::GetInstance();
|
||||
database.Clear();
|
||||
|
||||
const AssetGuid guid = AssetGuid::Generate();
|
||||
database.Register("game://models/crate.glb", guid);
|
||||
check(database.FindGuid("game://models/crate.glb") == guid && database.FindPath(guid) == "game://models/crate.glb",
|
||||
"registered assets must be found by path and by id");
|
||||
|
||||
const nlohmann::json reference = database.WriteReference("game://models/crate.glb", "mesh:0");
|
||||
check(reference.at("guid") == guid.ToString() && reference.at("sub") == "mesh:0",
|
||||
"references to known assets must carry the id and subresource");
|
||||
check(!database.WriteReference("game://unknown.png").contains("guid"),
|
||||
"assets without a meta file must not get an id outside a save");
|
||||
|
||||
database.Clear();
|
||||
database.Register("game://props/crate.glb", guid);
|
||||
const auto moved = database.ReadReference(reference);
|
||||
check(moved.has_value() && moved->path == "game://props/crate.glb" && moved->subresource == "mesh:0",
|
||||
"references must follow their id when the asset moved");
|
||||
|
||||
const auto byPath = database.ReadReference(nlohmann::json{{"path", "game://other.png"}});
|
||||
check(byPath.has_value() && byPath->path == "game://other.png", "references without a known id must keep their path");
|
||||
check(!database.ReadReference(nlohmann::json{{"guid", guid.ToString()}}).has_value(),
|
||||
"a reference needs a path to fall back on");
|
||||
database.Clear();
|
||||
}
|
||||
|
||||
void testAssetTypes()
|
||||
{
|
||||
check(AssetTypes::IsModel("game://a/B.GLB") && AssetTypes::IsImage("x.Png") && AssetTypes::IsMaterial("m.dmat") &&
|
||||
AssetTypes::IsScene("game://scenes/main.scene") && AssetTypes::IsPrefab("p.prefab") && AssetTypes::IsMeta("a.glb.meta"),
|
||||
"asset types must be recognised by extension, ignoring case");
|
||||
check(!AssetTypes::IsModel("model.glb.meta") && !AssetTypes::IsScene("scene.json"),
|
||||
"only the last extension decides the asset type");
|
||||
check(MaterialSerialization::IsFileKey("game://materials/a.dmat") && !MaterialSerialization::IsFileKey("local:3:1") &&
|
||||
!MaterialSerialization::IsFileKey("game://a.glb#material:0"),
|
||||
"only .dmat paths are material files");
|
||||
}
|
||||
|
||||
void testMainSceneFileIsValid()
|
||||
{
|
||||
std::ifstream file{std::filesystem::path{LIGHTKEEPER_ASSETS_SRC_DIR} / "scenes" / "main.scene"};
|
||||
const auto root = nlohmann::json::parse(file, nullptr, false);
|
||||
check(!root.is_discarded() && root.at("version") == ObjectSerialization::CurrentObjectVersion &&
|
||||
root.at("objects").size() > 100,
|
||||
"the main scene must be saved in the current format with the whole level");
|
||||
check(!MaterialSerialization::Validate(root.at("materials")).has_value(),
|
||||
"the main scene's local materials must be valid");
|
||||
}
|
||||
}
|
||||
|
||||
void RunAssetsTests()
|
||||
@@ -460,4 +529,8 @@ void RunAssetsTests()
|
||||
testTextureFileRoundTrip();
|
||||
testImageLoaderReadsCookedTextures();
|
||||
testAssetReferenceCacheKeys();
|
||||
testAssetGuids();
|
||||
testAssetDatabaseFollowsMovedAssets();
|
||||
testAssetTypes();
|
||||
testMainSceneFileIsValid();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
#include <destrum/Scene/SceneManager.h>
|
||||
|
||||
#include <editor/UndoStack.h>
|
||||
|
||||
#include "TestUtils.h"
|
||||
|
||||
using namespace test;
|
||||
|
||||
namespace {
|
||||
void testUndoRedoRestoresEdits()
|
||||
{
|
||||
Scene& scene = SceneManager::GetInstance().CreateScene("undo");
|
||||
GameObject* box = scene.CreateGameObject("box");
|
||||
box->GetTransform().SetLocalPosition({1.0f, 0.0f, 0.0f});
|
||||
scene.CommitPendingAdditions();
|
||||
|
||||
UndoStack history;
|
||||
history.Reset(scene);
|
||||
check(!history.CanUndo() && !history.Record(scene), "an unchanged scene must not create an undo step");
|
||||
|
||||
findByName(scene, "box")->GetTransform().SetLocalPosition({5.0f, 0.0f, 0.0f});
|
||||
scene.SetSelectedObject(findByName(scene, "box")->GetId());
|
||||
check(history.Record(scene) && history.CanUndo() && !history.CanRedo(), "an edit must create an undo step");
|
||||
|
||||
check(history.Undo(scene), "undo must succeed");
|
||||
scene.CommitPendingAdditions();
|
||||
const GameObject* undone = findByName(scene, "box");
|
||||
check(undone != nullptr && close(undone->GetTransform().GetLocalPosition().x, 1.0f), "undo must restore the earlier state");
|
||||
check(scene.GetSelectedObject() == undone, "undo must keep the edited object selected");
|
||||
check(history.CanRedo(), "undo must allow redo");
|
||||
|
||||
check(history.Redo(scene), "redo must succeed");
|
||||
scene.CommitPendingAdditions();
|
||||
check(close(findByName(scene, "box")->GetTransform().GetLocalPosition().x, 5.0f), "redo must bring the edit back");
|
||||
check(!history.Record(scene), "reloading the scene for undo must not count as an edit");
|
||||
|
||||
scene.CreateGameObject("spawned");
|
||||
scene.CommitPendingAdditions();
|
||||
check(history.Record(scene) && !history.CanRedo(), "a new edit must drop the redo steps");
|
||||
|
||||
SceneManager::GetInstance().Destroy();
|
||||
}
|
||||
|
||||
void testUndoHistoryIsCapped()
|
||||
{
|
||||
Scene& scene = SceneManager::GetInstance().CreateScene("undo cap");
|
||||
GameObject* box = scene.CreateGameObject("box");
|
||||
scene.CommitPendingAdditions();
|
||||
|
||||
UndoStack history;
|
||||
history.Reset(scene);
|
||||
for (std::size_t i = 0; i < UndoStack::MaxSteps + 10; ++i) {
|
||||
box->GetTransform().SetLocalPosition({static_cast<float>(i + 1), 0.0f, 0.0f});
|
||||
history.Record(scene);
|
||||
}
|
||||
check(history.UndoCount() == UndoStack::MaxSteps, "the undo history must keep only the newest steps");
|
||||
|
||||
SceneManager::GetInstance().Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void RunEditorTests()
|
||||
{
|
||||
testUndoRedoRestoresEdits();
|
||||
testUndoHistoryIsCapped();
|
||||
}
|
||||
@@ -9,6 +9,7 @@ void RunAssetsTests();
|
||||
void RunGraphicsTests();
|
||||
void RunUiTests();
|
||||
void RunGameplayTests();
|
||||
void RunEditorTests();
|
||||
|
||||
namespace {
|
||||
struct Suite {
|
||||
@@ -24,6 +25,7 @@ namespace {
|
||||
{"graphics", RunGraphicsTests},
|
||||
{"ui", RunUiTests},
|
||||
{"gameplay", RunGameplayTests},
|
||||
{"editor", RunEditorTests},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace {
|
||||
environment.fogDensity = 0.05f;
|
||||
environment.skyboxRotationSpeed = 0.5f;
|
||||
environment.skyboxRotation = 1.0f;
|
||||
environment.exposure = -1.5f;
|
||||
environment.tonemapper = Tonemapper::Aces;
|
||||
scene.Update(1.0f);
|
||||
check(close(environment.skyboxRotation, 1.5f), "scene updates must spin the skybox");
|
||||
|
||||
@@ -56,7 +58,8 @@ namespace {
|
||||
loadedEnvironment.fogColor == glm::vec3{0.6f, 0.7f, 0.8f} &&
|
||||
close(loadedEnvironment.fogDensity, 0.05f) &&
|
||||
close(loadedEnvironment.skyboxRotationSpeed, 0.5f) &&
|
||||
close(loadedEnvironment.skyboxRotation, 1.5f),
|
||||
close(loadedEnvironment.skyboxRotation, 1.5f) &&
|
||||
close(loadedEnvironment.exposure, -1.5f) && loadedEnvironment.tonemapper == Tonemapper::Aces,
|
||||
"scene round-trip must preserve the environment settings");
|
||||
|
||||
SceneManager::GetInstance().Destroy();
|
||||
@@ -370,8 +373,19 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void testTonemapperNames()
|
||||
{
|
||||
for (const Tonemapper tonemapper : {Tonemapper::None, Tonemapper::Aces, Tonemapper::PbrNeutral}) {
|
||||
check(ParseTonemapper(ToString(tonemapper)) == tonemapper, "tonemapper names must round-trip");
|
||||
}
|
||||
check(!ParseTonemapper("filmic").has_value(), "unknown tonemapper names must be rejected");
|
||||
}
|
||||
}
|
||||
|
||||
void RunSerializationTests()
|
||||
{
|
||||
testTonemapperNames();
|
||||
testSceneRoundTrip();
|
||||
testLegacySceneLoad();
|
||||
testSceneLoadRollback();
|
||||
|
||||
Reference in New Issue
Block a user