Compare commits
1
Commits
master
...
star-catcher
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08a6df4e68 |
@@ -119,7 +119,7 @@ cmake --install build --config Release
|
||||
|
||||
## Demo app
|
||||
|
||||
`lightkeeper` is the current demo application. It creates an SDL window, initializes the Destrum app, loads assets, sets up a scene, renders meshes, skyboxes, and a skinned animated character.
|
||||
`lightkeeper` is the current game application. It launches **Star Catcher**, a small playable loop built from the engine's scene and rendering systems. Move the blue catcher with `A`/`D` or the arrow keys, catch orange stars, and press `R` to restart after three misses. Press `Escape` to quit.
|
||||
|
||||
## Development notes
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#ifndef LIGHTKEEPER_H
|
||||
#define LIGHTKEEPER_H
|
||||
|
||||
#include <string>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include <destrum/App.h>
|
||||
#include <destrum/Scene/SceneManager.h>
|
||||
#include <destrum/Graphics/RenderResources.h>
|
||||
|
||||
#include "destrum/Graphics/Resources/Cubemap.h"
|
||||
@@ -21,19 +21,35 @@ public:
|
||||
void customFixedUpdate(float dt) override;
|
||||
|
||||
void onWindowResize(int newWidth, int newHeight) override;
|
||||
|
||||
private:
|
||||
struct FallingStar {
|
||||
GameObject* object{};
|
||||
float speed{};
|
||||
};
|
||||
|
||||
void resetGame();
|
||||
void respawnStar(FallingStar& star, float minimumHeight);
|
||||
void drawHud();
|
||||
|
||||
Camera camera{glm::vec3(0.f, 0.f, -5.f), glm::vec3(0, 1, 0)};
|
||||
|
||||
MeshID sphereMesh;
|
||||
MaterialID sphereMaterial;
|
||||
MeshID sphereMesh{NULL_MESH_ID};
|
||||
MaterialID playerMaterial{NULL_MATERIAL_ID};
|
||||
MaterialID starMaterial{NULL_MATERIAL_ID};
|
||||
|
||||
std::unique_ptr<CubeMap> skyboxCubemap;
|
||||
|
||||
GameObject* capybara = nullptr;
|
||||
GameObject* player{nullptr};
|
||||
std::vector<FallingStar> stars;
|
||||
std::mt19937 randomEngine{0xD35A1234u};
|
||||
std::uniform_real_distribution<float> spawnX{-5.5f, 5.5f};
|
||||
std::uniform_real_distribution<float> spawnHeight{0.0f, 2.0f};
|
||||
std::uniform_real_distribution<float> spawnSpeed{2.8f, 4.2f};
|
||||
|
||||
char scenePath[512]{"scenes/debug_scene.json"};
|
||||
char newSceneName[128]{"EmptyScene"};
|
||||
std::string sceneStatus;
|
||||
int score{0};
|
||||
int misses{0};
|
||||
bool gameOver{false};
|
||||
};
|
||||
|
||||
#endif //LIGHTKEEPER_H
|
||||
|
||||
+203
-510
@@ -1,52 +1,35 @@
|
||||
#include "Lightkeeper.h"
|
||||
|
||||
#include <destrum/FS/AssetFS.h>
|
||||
#include <destrum/Assets/AssetManager.h>
|
||||
#include "glm/gtx/transform.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/Components/Physics/BoxCollider.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
#include <destrum/Serialization/SceneSerializer.h>
|
||||
|
||||
#include "destrum/Components/MeshRendererComponent.h"
|
||||
#include "destrum/Components/Rotator.h"
|
||||
#include "destrum/Components/Spinner.h"
|
||||
#include "destrum/Components/OrbitAndSpin.h"
|
||||
#include "destrum/ObjectModel/GameObject.h"
|
||||
#include "destrum/Util/ModelDoc.h"
|
||||
#include "destrum/Components/Animator.h"
|
||||
|
||||
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "destrum/Components/Physics/SphereCollider.h"
|
||||
#include "destrum/Util/ModelDocUtils.h"
|
||||
#include <SDL.h>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <imgui.h>
|
||||
|
||||
#include <destrum/Assets/AssetManager.h>
|
||||
#include <destrum/Components/MeshRendererComponent.h>
|
||||
#include <destrum/FS/AssetFS.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/Scene/Scene.h>
|
||||
#include <destrum/Scene/SceneManager.h>
|
||||
#include <destrum/Util/ModelDoc.h>
|
||||
#include <destrum/Util/ModelDocUtils.h>
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
namespace {
|
||||
std::filesystem::path ResolveScenePath(
|
||||
std::string_view value,
|
||||
const std::filesystem::path& exeDir) {
|
||||
if (value.empty()) {
|
||||
throw std::invalid_argument("Scene path cannot be empty");
|
||||
}
|
||||
|
||||
if (value.find("://") != std::string_view::npos) {
|
||||
return AssetFS::GetInstance().GetFullPath(value);
|
||||
}
|
||||
|
||||
const std::filesystem::path path{value};
|
||||
return path.is_absolute() ? path : exeDir / path;
|
||||
}
|
||||
constexpr int kStarCount = 3;
|
||||
constexpr int kMaxMisses = 3;
|
||||
constexpr float kPlayerY = -5.0f;
|
||||
constexpr float kCatchY = -4.25f;
|
||||
constexpr float kPlayerSpeed = 7.0f;
|
||||
constexpr float kPlayerHalfWidth = 1.15f;
|
||||
}
|
||||
|
||||
LightKeeper::LightKeeper() : App()
|
||||
{
|
||||
}
|
||||
LightKeeper::LightKeeper() = default;
|
||||
|
||||
LightKeeper::~LightKeeper()
|
||||
{
|
||||
@@ -63,461 +46,180 @@ void LightKeeper::customInit()
|
||||
resources.init(gfxDevice);
|
||||
renderer.init(gfxDevice, resources, m_params.renderSize);
|
||||
|
||||
const float aspectRatio = static_cast<float>(m_params.renderSize.x) / static_cast<float>(m_params.renderSize.y);
|
||||
camera.setAspectRatio(aspectRatio);
|
||||
camera.m_position = glm::vec3{0.0f, 0.0f, -10.0f};
|
||||
camera.SetRotation(glm::radians(90.0f), 0.0f);
|
||||
camera.setAspectRatio(
|
||||
static_cast<float>(m_params.renderSize.x) /
|
||||
static_cast<float>(m_params.renderSize.y));
|
||||
|
||||
ModelDoc::LoadOptions staticModelOptions{};
|
||||
staticModelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||
staticModelOptions.loadMaterials = true;
|
||||
staticModelOptions.loadSkeleton = false;
|
||||
staticModelOptions.loadAnimations = false;
|
||||
const auto skyboxID =
|
||||
AssetFS::GetInstance().GetCookedPathForFile("game://starmap_2020_4k.exr");
|
||||
const auto vertShaderPath =
|
||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert");
|
||||
const auto fragShaderPath =
|
||||
AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag");
|
||||
|
||||
camera.SetRotation(glm::radians(glm::vec2(90.f, 0.f)));
|
||||
|
||||
auto& scene = SceneManager::GetInstance().CreateScene("Main");
|
||||
|
||||
// scene.Add(testCube);
|
||||
|
||||
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/skybox.jpg");
|
||||
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/mars.jpg");
|
||||
const auto skyboxID = AssetFS::GetInstance().GetCookedPathForFile("game://starmap_2020_4k.exr");
|
||||
//
|
||||
// const auto skyboxID = AssetFS::GetInstance().GetFullPath("engine://textures/test-skybox.png");
|
||||
//
|
||||
const auto vertShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.vert");
|
||||
const auto fragShaderPath = AssetFS::GetInstance().GetCookedPathForFile("engine://shaders/cubemap.frag");
|
||||
//
|
||||
skyboxCubemap = std::make_unique<CubeMap>();
|
||||
skyboxCubemap->LoadCubeMap(gfxDevice, resources, skyboxID.generic_string());
|
||||
skyboxCubemap->InitCubemapPipeline(gfxDevice, resources, vertShaderPath.generic_string(), fragShaderPath.generic_string());
|
||||
skyboxCubemap->LoadCubeMap(gfxDevice, resources, skyboxID);
|
||||
skyboxCubemap->InitCubemapPipeline(
|
||||
gfxDevice,
|
||||
resources,
|
||||
vertShaderPath.generic_string(),
|
||||
fragShaderPath.generic_string());
|
||||
skyboxCubemap->CreateCubeMap(gfxDevice, resources);
|
||||
|
||||
renderer.setSkyboxTexture(skyboxCubemap->GetCubeMapImageID());
|
||||
|
||||
const auto planeObj = scene.CreateGameObject("GroundPlane");
|
||||
const auto planeMeshComp = planeObj->AddComponent<MeshRendererComponent>();
|
||||
ModelDoc::LoadOptions modelOptions{};
|
||||
modelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||
modelOptions.loadMaterials = true;
|
||||
modelOptions.loadSkeleton = false;
|
||||
modelOptions.loadAnimations = false;
|
||||
|
||||
auto planeModel = ModelDoc::LoadModel(
|
||||
AssetFS::GetInstance().GetFullPath("game://plane.glb").generic_string(),
|
||||
staticModelOptions
|
||||
);
|
||||
ModelDocUtils::LogModelDocSummary(planeModel, "plane.glb");
|
||||
|
||||
const auto& planePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(planeModel, "game://plane.glb");
|
||||
const auto planeMeshID = resources.meshes().addMesh(gfxDevice, planePrimitive.mesh);
|
||||
|
||||
const auto planeTexturePath = ModelDocUtils::PickTexturePath(
|
||||
planeModel,
|
||||
planePrimitive,
|
||||
AssetFS::GetInstance().GetFullPath("game://grass.png")
|
||||
);
|
||||
|
||||
const auto planeTextureID = resources.loadImageFromFile(gfxDevice, planeTexturePath);
|
||||
const auto planeMaterialID = resources.materials().addMaterial({
|
||||
.baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||
planeModel, planePrimitive),
|
||||
.textureFilteringMode = TextureFilteringMode::Nearest,
|
||||
.diffuseTexture = planeTextureID,
|
||||
.name = ModelDocUtils::GetImportedMaterialName(
|
||||
planeModel, planePrimitive, "GroundPlaneMaterial"),
|
||||
});
|
||||
|
||||
planeMeshComp->SetMeshID(planeMeshID);
|
||||
planeMeshComp->SetMaterialID(planeMaterialID);
|
||||
planeObj->GetTransform().SetWorldPosition(glm::vec3(0.f, -1.0f, 0.f));
|
||||
planeObj->GetTransform().SetWorldScale(glm::vec3(10.f, 1.f, 10.f));
|
||||
planeObj->AddComponent<BoxCollider>(glm::vec3{10.0f, 0.5f, 10.0f});
|
||||
|
||||
auto* floorRb = planeObj->AddComponent<Rigidbody>();
|
||||
floorRb->SetType(RigidbodyType::Static);
|
||||
scene.GetPhysics().RegisterGameObject(*planeObj);
|
||||
|
||||
const auto CharObj = scene.CreateGameObject("Character");
|
||||
capybara = CharObj;
|
||||
|
||||
ModelDoc::LoadOptions characterModelOptions{};
|
||||
characterModelOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||
characterModelOptions.loadMaterials = true;
|
||||
characterModelOptions.loadSkeleton = true;
|
||||
characterModelOptions.loadAnimations = true;
|
||||
|
||||
// auto charModel = ModelDoc::LoadModel(
|
||||
// AssetFS::GetInstance().GetFullPath("engine://cotw-capybara-male/source/capybara.fbx").generic_string(),
|
||||
// characterModelOptions
|
||||
// );
|
||||
// ModelDocUtils::LogModelDocSummary(charModel, "capybara.fbx");
|
||||
//
|
||||
// const auto& charPrimitive = ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
||||
// charModel,
|
||||
// "engine://cotw-capybara-male/source/capybara.fbx"
|
||||
// );
|
||||
//
|
||||
// const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh);
|
||||
//
|
||||
// const auto charTexturePath = ModelDocUtils::PickTexturePath(
|
||||
// charModel,
|
||||
// charPrimitive,
|
||||
// AssetFS::GetInstance().GetFullPath(
|
||||
// "engine://cotw-capybara-male/textures/capybara_male_light_brown_dif.ddsc.DECA.RE.pngballs")
|
||||
// );
|
||||
//
|
||||
// const auto charTextureID = resources.loadImageFromFile(gfxDevice, charTexturePath);
|
||||
// // const auto charTextureID = gfxDevice.loadImageFromFile(AssetFS::GetInstance().GetFullPath("engine://char.jpg"));
|
||||
// const auto charMaterialID = resources.materials().addMaterial({
|
||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||
// charModel, charPrimitive),
|
||||
// .diffuseTexture = charTextureID,
|
||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
||||
// charModel, charPrimitive, "CharacterMaterial"),
|
||||
// });
|
||||
//
|
||||
// const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
||||
// charMeshComp->SetMeshID(charMeshID);
|
||||
// charMeshComp->SetMaterialID(charMaterialID);
|
||||
//
|
||||
// const auto animator = CharObj->AddComponent<Animator>();
|
||||
// animator->setSkeleton(std::move(charModel.skeleton));
|
||||
//
|
||||
// std::string firstAnimationName;
|
||||
//
|
||||
// for (auto& clip : charModel.animations)
|
||||
// {
|
||||
// spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
||||
//
|
||||
// if (firstAnimationName.empty())
|
||||
// firstAnimationName = clip.name;
|
||||
//
|
||||
// animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
|
||||
// }
|
||||
//
|
||||
// if (!firstAnimationName.empty())
|
||||
// {
|
||||
// spdlog::info("Playing animation: '{}'", firstAnimationName);
|
||||
// animator->play(firstAnimationName);
|
||||
// }
|
||||
//
|
||||
// animator->play("capybara_canter_fwd_01|capybara_canter_fwd_01|run");
|
||||
//
|
||||
// CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f, 0.f, 0.f));
|
||||
// CharObj->GetTransform().SetWorldScale(0.01f, 0.01f, 0.01f);
|
||||
|
||||
// ModelDoc::LoadOptions options{};
|
||||
// options.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||
//
|
||||
// auto model = ModelDoc::LoadModel(
|
||||
// AssetFS::GetInstance()
|
||||
// .GetFullPath("engine://multiple_object_test.fbx")
|
||||
// .generic_string(),
|
||||
// options
|
||||
// );
|
||||
//
|
||||
// ModelDocUtils::LogModelDocSummary(model, "engine://multiple_object_test.fbx");
|
||||
//
|
||||
// auto root = std::make_shared<GameObject>("MultiMeshModel");
|
||||
// root->GetTransform().SetWorldPosition(glm::vec3(0.1f, 0.1f, 0.1f));
|
||||
// scene.Add(root);
|
||||
//
|
||||
// for (std::size_t i = 0; i < model.primitives.size(); ++i) {
|
||||
// const auto &primitive = model.primitives[i];
|
||||
//
|
||||
// const auto meshID = meshCache.addMesh(gfxDevice, primitive.mesh);
|
||||
//
|
||||
// const auto texturePath = ModelDocUtils::PickTexturePath(
|
||||
// model,
|
||||
// primitive,
|
||||
// AssetFS::GetInstance().GetFullPath("engine://textures/white.png")
|
||||
// );
|
||||
//
|
||||
// const auto textureID = gfxDevice.loadImageFromFile(texturePath);
|
||||
//
|
||||
// const auto materialID = materialCache.addMaterial(gfxDevice, {
|
||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||
// model, primitive),
|
||||
// .diffuseTexture = textureID,
|
||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
||||
// model,
|
||||
// primitive,
|
||||
// primitive.mesh.name + "_Material"
|
||||
// ),
|
||||
// });
|
||||
//
|
||||
// auto part = std::make_shared<GameObject>(
|
||||
// primitive.mesh.name.empty()
|
||||
// ? "Primitive_" + std::to_string(i)
|
||||
// : primitive.mesh.name
|
||||
// );
|
||||
//
|
||||
// auto meshComp = part->AddComponent<MeshRendererComponent>();
|
||||
// meshComp->SetMeshID(meshID);
|
||||
// meshComp->SetMaterialID(materialID);
|
||||
//
|
||||
// scene.Add(part);
|
||||
// }
|
||||
|
||||
|
||||
// {
|
||||
// const auto CharObj = scene.CreateGameObject("Character");
|
||||
//
|
||||
// ModelDoc::LoadOptions characterOptions{};
|
||||
// characterOptions.meshImportMode = ModelDoc::MeshImportMode::MergedPerNode;
|
||||
// characterOptions.loadMaterials = true;
|
||||
// characterOptions.loadSkeleton = true;
|
||||
// characterOptions.loadAnimations = false;
|
||||
//
|
||||
// auto charModel = AssetManager::GetInstance().LoadModel("engine://characterMedium.fbx", characterOptions);
|
||||
//
|
||||
// const auto& charPrimitive =
|
||||
// ModelDocUtils::GetFirstSkinnedPrimitiveOrFirstOrThrow(
|
||||
// charModel,
|
||||
// "engine://characterMedium.fbx"
|
||||
// );
|
||||
//
|
||||
// const auto charMeshID = resources.meshes().addMesh(gfxDevice, charPrimitive.mesh);
|
||||
//
|
||||
// const auto charTextureID = resources.loadImageFromFile(gfxDevice,
|
||||
// AssetFS::GetInstance().GetFullPath("engine://textures/criminalMaleA.png")
|
||||
// );
|
||||
//
|
||||
// const auto charMaterialID = resources.materials().addMaterial({
|
||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||
// charModel, charPrimitive),
|
||||
// .diffuseTexture = charTextureID,
|
||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
||||
// charModel,
|
||||
// charPrimitive,
|
||||
// "CharacterMaterial"
|
||||
// ),
|
||||
// });
|
||||
//
|
||||
// const auto charMeshComp = CharObj->AddComponent<MeshRendererComponent>();
|
||||
// charMeshComp->SetMeshID(charMeshID);
|
||||
// charMeshComp->SetMaterialID(charMaterialID);
|
||||
//
|
||||
// const auto animator = CharObj->AddComponent<Animator>();
|
||||
// animator->setSkeleton(charModel.skeleton);
|
||||
//
|
||||
// auto runClips = AssetManager::GetInstance().LoadAnimationClips(
|
||||
// "engine://run.fbx",
|
||||
// charModel.skeleton
|
||||
// );
|
||||
//
|
||||
// for (auto& clip : runClips)
|
||||
// {
|
||||
// spdlog::info("Loaded animation: '{}' ({:.2f}s)", clip.name, clip.duration);
|
||||
// animator->addClip(std::make_shared<SkeletalAnimation>(std::move(clip)));
|
||||
// }
|
||||
//
|
||||
// if (!runClips.empty())
|
||||
// {
|
||||
// animator->play("Root|Run");
|
||||
// }
|
||||
//
|
||||
// CharObj->GetTransform().SetWorldPosition(glm::vec3(0.f));
|
||||
// CharObj->GetTransform().SetWorldPosition(glm::vec3(5, 0, 0));
|
||||
// }
|
||||
//
|
||||
// auto cubeModel = ModelDoc::LoadModel(
|
||||
// AssetFS::GetInstance()
|
||||
// .GetFullPath("engine://cube.fbx")
|
||||
// .generic_string(),
|
||||
// staticModelOptions
|
||||
// );
|
||||
// ModelDocUtils::LogModelDocSummary(cubeModel, "cube.fbx");
|
||||
//
|
||||
// const auto& cubePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(cubeModel, "game://cube.fbx");
|
||||
//
|
||||
//
|
||||
// const auto eliasTextuerPath = ModelDocUtils::PickTexturePath(
|
||||
// cubeModel,
|
||||
// cubePrimitive,
|
||||
// AssetFS::GetInstance().GetFullPath("game://grass.png")
|
||||
// );
|
||||
//
|
||||
// const auto eliasTextueID = resources.loadImageFromFile(gfxDevice, eliasTextuerPath);
|
||||
// const auto eliasMaterialID = resources.materials().addMaterial({
|
||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(
|
||||
// planeModel, planePrimitive),
|
||||
// .textureFilteringMode =
|
||||
// TextureFilteringMode::Anisotropic,
|
||||
// .diffuseTexture = eliasTextueID,
|
||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
||||
// planeModel, planePrimitive, "GroundPlaneMaterial"),
|
||||
// });
|
||||
//
|
||||
// const auto cubeMeshID = resources.meshes().addMesh(gfxDevice, cubePrimitive.mesh);
|
||||
// //
|
||||
// // for (int i{0}; i < 100; i++)
|
||||
// // {
|
||||
// // auto cube = std::make_shared<GameObject>("Cube");
|
||||
// //
|
||||
// // cube->AddComponent<BoxCollider>(glm::vec3{0.5f});
|
||||
// // cube->AddComponent<Rigidbody>();
|
||||
// //
|
||||
// // auto meshComp = cube->AddComponent<MeshRendererComponent>();
|
||||
// // meshComp->SetMeshID(cubeMeshID);
|
||||
// // meshComp->SetMaterialID(eliasMaterialID);
|
||||
// //
|
||||
// // cube->GetTransform().SetWorldPosition(glm::vec3(0.0f, i, 0.0f));
|
||||
// // cube->GetTransform().SetWorldScale(glm::vec3(0.005f));
|
||||
// //
|
||||
// // scene.Add(cube);
|
||||
// // scene.GetPhysics().RegisterGameObject(*cube);
|
||||
// // }
|
||||
//
|
||||
// // const int cubeCount = 10;
|
||||
// // const float spacing = 1.0f;
|
||||
// //
|
||||
// // for (int x = 0; x < cubeCount; x++)
|
||||
// // {
|
||||
// // for (int y = 0; y < 3; y++)
|
||||
// // {
|
||||
// // for (int z = 0; z < cubeCount; z++)
|
||||
// // {
|
||||
// // auto cube = std::make_shared<GameObject>("Cube");
|
||||
// //
|
||||
// // cube->AddComponent<BoxCollider>(glm::vec3{0.5f});
|
||||
// // cube->AddComponent<Rigidbody>();
|
||||
// //
|
||||
// // auto meshComp = cube->AddComponent<MeshRendererComponent>();
|
||||
// // meshComp->SetMeshID(cubeMeshID);
|
||||
// // meshComp->SetMaterialID(eliasMaterialID);
|
||||
// //
|
||||
// // cube->GetTransform().SetWorldPosition(glm::vec3(
|
||||
// // (x - cubeCount / 2.0f) * spacing,
|
||||
// // y * spacing + 0,
|
||||
// // (z - cubeCount / 2.0f) * spacing
|
||||
// // ));
|
||||
// //
|
||||
// // cube->GetTransform().SetWorldScale(glm::vec3(0.005f));
|
||||
// //
|
||||
// // scene.Add(cube);
|
||||
// // scene.GetPhysics().RegisterGameObject(*cube);
|
||||
// // }
|
||||
// // }
|
||||
// // }
|
||||
|
||||
|
||||
// {
|
||||
auto sphereModel = AssetManager::GetInstance().LoadModel("engine://sphere.fbx", staticModelOptions);
|
||||
ModelDocUtils::LogModelDocSummary(sphereModel, "sphere.fbx");
|
||||
//
|
||||
const auto& spherePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(sphereModel, "game://sphere.fbx");
|
||||
//
|
||||
//
|
||||
// const auto sphereTexturePath = ModelDocUtils::PickTexturePath(
|
||||
// sphereModel,
|
||||
// spherePrimitive,
|
||||
// AssetFS::GetInstance().GetFullPath("game://238882.png")
|
||||
// );
|
||||
//
|
||||
// const auto sphereTextureID = resources.loadImageFromFile(gfxDevice, sphereTexturePath);
|
||||
// sphereMaterial = resources.materials().addMaterial({
|
||||
// .baseColor = ModelDocUtils::GetImportedBaseColor(planeModel, planePrimitive),
|
||||
// .textureFilteringMode =TextureFilteringMode::Anisotropic,
|
||||
// .diffuseTexture = sphereTextureID,
|
||||
// .name = ModelDocUtils::GetImportedMaterialName(
|
||||
// planeModel, planePrimitive,
|
||||
// "GroundPlaneMaterial"),
|
||||
// });
|
||||
//
|
||||
const auto sphereModel = AssetManager::GetInstance().LoadModel(
|
||||
"engine://sphere.fbx",
|
||||
modelOptions);
|
||||
const auto& spherePrimitive = ModelDocUtils::GetFirstPrimitiveOrThrow(
|
||||
sphereModel,
|
||||
"engine://sphere.fbx");
|
||||
sphereMesh = resources.meshes().addMesh(gfxDevice, spherePrimitive.mesh);
|
||||
//
|
||||
// }
|
||||
|
||||
sphereMaterial = resources.materials().addSimpleColorMaterial({0.5f, 0.3f, 0.8f}, "Blue");
|
||||
playerMaterial = resources.materials().addSimpleColorMaterial(
|
||||
{0.08f, 0.75f, 1.0f},
|
||||
"Catcher Blue");
|
||||
starMaterial = resources.materials().addSimpleColorMaterial(
|
||||
{1.0f, 0.55f, 0.05f},
|
||||
"Falling Star Orange");
|
||||
|
||||
auto& scene = SceneManager::GetInstance().CreateScene("Star Catcher");
|
||||
|
||||
player = scene.CreateGameObject("Catcher");
|
||||
auto* playerRenderer = player->AddComponent<MeshRendererComponent>();
|
||||
playerRenderer->SetMeshID(sphereMesh);
|
||||
playerRenderer->SetMaterialID(playerMaterial);
|
||||
player->GetTransform().SetLocalPosition({0.0f, kPlayerY, 0.0f});
|
||||
player->GetTransform().SetLocalScale(glm::vec3{0.012f, 0.0035f, 0.009f});
|
||||
|
||||
stars.reserve(kStarCount);
|
||||
for (int index = 0; index < kStarCount; ++index) {
|
||||
auto* starObject = scene.CreateGameObject(
|
||||
"Star " + std::to_string(index + 1));
|
||||
auto* starRenderer = starObject->AddComponent<MeshRendererComponent>();
|
||||
starRenderer->SetMeshID(sphereMesh);
|
||||
starRenderer->SetMaterialID(starMaterial);
|
||||
starObject->GetTransform().SetLocalScale(glm::vec3{0.0045f});
|
||||
stars.push_back({starObject, 0.0f});
|
||||
}
|
||||
|
||||
resetGame();
|
||||
}
|
||||
|
||||
void LightKeeper::respawnStar(FallingStar& star, float minimumHeight)
|
||||
{
|
||||
star.speed = spawnSpeed(randomEngine) + static_cast<float>(score) * 0.08f;
|
||||
star.object->SetActive(true);
|
||||
star.object->GetTransform().SetLocalPosition({
|
||||
spawnX(randomEngine),
|
||||
minimumHeight + spawnHeight(randomEngine),
|
||||
0.0f});
|
||||
}
|
||||
|
||||
void LightKeeper::resetGame()
|
||||
{
|
||||
score = 0;
|
||||
misses = 0;
|
||||
gameOver = false;
|
||||
|
||||
if (player != nullptr) {
|
||||
player->SetActive(true);
|
||||
player->GetTransform().SetLocalPosition({0.0f, kPlayerY, 0.0f});
|
||||
}
|
||||
|
||||
for (std::size_t index = 0; index < stars.size(); ++index) {
|
||||
respawnStar(stars[index], 6.5f + static_cast<float>(index) * 0.85f);
|
||||
}
|
||||
}
|
||||
|
||||
void LightKeeper::customUpdate(float dt)
|
||||
{
|
||||
// LightKeeper owns a camera that hides App::camera; update this instance
|
||||
// after SDL events have been consumed.
|
||||
camera.Update(dt);
|
||||
auto& input = InputManager::GetInstance();
|
||||
|
||||
if (input.WasKeyPressed(SDL_SCANCODE_ESCAPE)) {
|
||||
isRunning = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.WasKeyPressed(SDL_SCANCODE_R)) {
|
||||
resetGame();
|
||||
}
|
||||
|
||||
if (!gameOver) {
|
||||
float moveDirection = 0.0f;
|
||||
if (input.IsKeyDown(SDL_SCANCODE_A) || input.IsKeyDown(SDL_SCANCODE_LEFT)) {
|
||||
moveDirection += 1.0f;
|
||||
}
|
||||
if (input.IsKeyDown(SDL_SCANCODE_D) || input.IsKeyDown(SDL_SCANCODE_RIGHT)) {
|
||||
moveDirection -= 1.0f;
|
||||
}
|
||||
|
||||
glm::vec3 playerPosition = player->GetTransform().GetLocalPosition();
|
||||
playerPosition.x = std::clamp(
|
||||
playerPosition.x + moveDirection * kPlayerSpeed * dt,
|
||||
-5.8f,
|
||||
5.8f);
|
||||
player->GetTransform().SetLocalPosition(playerPosition);
|
||||
|
||||
for (auto& star : stars) {
|
||||
glm::vec3 starPosition = star.object->GetTransform().GetLocalPosition();
|
||||
starPosition.y -= star.speed * dt;
|
||||
star.object->GetTransform().SetLocalPosition(starPosition);
|
||||
|
||||
if (starPosition.y > kCatchY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool caught = std::abs(starPosition.x - playerPosition.x) <=
|
||||
kPlayerHalfWidth;
|
||||
if (caught) {
|
||||
++score;
|
||||
} else {
|
||||
++misses;
|
||||
if (misses >= kMaxMisses) {
|
||||
gameOver = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!gameOver) {
|
||||
respawnStar(star, 7.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SceneManager::GetInstance().Update(dt);
|
||||
SceneManager::GetInstance().LateUpdate(dt);
|
||||
drawHud();
|
||||
}
|
||||
|
||||
if (InputManager::GetInstance().WasKeyPressed(SDL_SCANCODE_1))
|
||||
{
|
||||
renderer.setRenderWireframe(!renderer.getRenderWireframe());
|
||||
}
|
||||
void LightKeeper::drawHud()
|
||||
{
|
||||
constexpr ImGuiWindowFlags flags =
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoNav;
|
||||
|
||||
ImGui::Begin("Test");
|
||||
if (ImGui::Button("SPawn ball"))
|
||||
{
|
||||
auto sphere = SceneManager::GetInstance().GetCurrentScene().CreateGameObject("Sphere");
|
||||
sphere->AddComponent<SphereCollider>(1.5f);
|
||||
auto rb = sphere->AddComponent<Rigidbody>();
|
||||
rb->SetMass(1000);
|
||||
ImGui::SetNextWindowPos({24.0f, 24.0f}, ImGuiCond_Always);
|
||||
ImGui::SetNextWindowBgAlpha(0.65f);
|
||||
ImGui::Begin("Star Catcher HUD", nullptr, flags);
|
||||
ImGui::Text("STAR CATCHER");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Score %03d", score);
|
||||
ImGui::Text("Lives %d", kMaxMisses - misses);
|
||||
ImGui::TextDisabled("A/D or arrow keys to move");
|
||||
|
||||
auto meshRenderComp = sphere->AddComponent<MeshRendererComponent>();
|
||||
meshRenderComp->SetMaterialID(sphereMaterial);
|
||||
meshRenderComp->SetMeshID(sphereMesh);
|
||||
|
||||
sphere->GetTransform().SetWorldPosition((std::rand() % 2) - 0.5f, 100, (std::rand() % 2) - 0.5f);
|
||||
sphere->GetTransform().SetWorldScale(glm::vec3{0.015f});
|
||||
|
||||
|
||||
SceneManager::GetInstance().GetCurrentScene().GetPhysics().RefreshGameObject(*sphere);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::Begin("Scene Debug");
|
||||
ImGui::InputText("Scene path", scenePath, sizeof(scenePath));
|
||||
ImGui::InputText("New scene name", newSceneName, sizeof(newSceneName));
|
||||
|
||||
if (ImGui::Button("Save Current Scene")) {
|
||||
try {
|
||||
const auto path = ResolveScenePath(scenePath, m_params.exeDir);
|
||||
const auto parent = path.parent_path();
|
||||
if (!parent.empty()) {
|
||||
std::filesystem::create_directories(parent);
|
||||
}
|
||||
|
||||
if (SceneSerializer::Save(
|
||||
SceneManager::GetInstance().GetCurrentScene(),
|
||||
path)) {
|
||||
sceneStatus = "Saved scene to " + path.string();
|
||||
} else {
|
||||
sceneStatus = "Failed to save scene to " + path.string();
|
||||
}
|
||||
} catch (const std::exception& exception) {
|
||||
sceneStatus = std::string{"Save failed: "} + exception.what();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Load Scene")) {
|
||||
try {
|
||||
const auto path = ResolveScenePath(scenePath, m_params.exeDir);
|
||||
if (SceneSerializer::Load(
|
||||
SceneManager::GetInstance().GetCurrentScene(),
|
||||
path)) {
|
||||
sceneStatus = "Loaded scene from " + path.string();
|
||||
} else {
|
||||
sceneStatus = "Failed to load scene from " + path.string();
|
||||
}
|
||||
} catch (const std::exception& exception) {
|
||||
sceneStatus = std::string{"Load failed: "} + exception.what();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::Button("New Empty Scene")) {
|
||||
try {
|
||||
const std::string name = newSceneName[0] != '\0'
|
||||
? newSceneName
|
||||
: "EmptyScene";
|
||||
auto& sceneManager = SceneManager::GetInstance();
|
||||
const int newSceneIndex = sceneManager.GetSceneCount();
|
||||
sceneManager.CreateScene(name);
|
||||
sceneManager.SwitchScene(newSceneIndex);
|
||||
sceneStatus = "Created and switched to scene '" + name + "'";
|
||||
} catch (const std::exception& exception) {
|
||||
sceneStatus = std::string{"New scene failed: "} + exception.what();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sceneStatus.empty()) {
|
||||
ImGui::TextWrapped("%s", sceneStatus.c_str());
|
||||
if (gameOver) {
|
||||
ImGui::Separator();
|
||||
ImGui::Text("GAME OVER");
|
||||
ImGui::Text("Press R to play again");
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -531,53 +233,41 @@ void LightKeeper::customDraw()
|
||||
|
||||
renderer.beginDrawing(gfxDevice);
|
||||
|
||||
const RenderContext ctx{
|
||||
const GameRenderer::SceneData sceneData{
|
||||
.camera = camera,
|
||||
.ambientColor = glm::vec3{0.1f},
|
||||
.ambientIntensity = 0.5f,
|
||||
.fogColor = glm::vec3{0.02f, 0.03f, 0.08f},
|
||||
.fogDensity = 0.01f};
|
||||
const RenderContext context{
|
||||
.renderer = renderer,
|
||||
.camera = camera,
|
||||
.sceneData = {
|
||||
.camera = camera,
|
||||
.ambientColor = glm::vec3(0.1f),
|
||||
.ambientIntensity = 0.5f,
|
||||
.fogColor = glm::vec3(0.5f),
|
||||
.fogDensity = 0.01f
|
||||
}
|
||||
};
|
||||
.sceneData = sceneData};
|
||||
|
||||
SceneManager::GetInstance().Render(ctx);
|
||||
SceneManager::GetInstance().Render(context);
|
||||
renderer.endDrawing();
|
||||
|
||||
const auto& drawImage = renderer.getDrawImage();
|
||||
|
||||
renderer.draw(
|
||||
cmd, gfxDevice, camera, GameRenderer::SceneData{
|
||||
camera, glm::vec3(0.1f), 0.5f, glm::vec3(0.5f), 0.01f
|
||||
});
|
||||
|
||||
renderer.draw(cmd, gfxDevice, camera, sceneData);
|
||||
gfxDevice.endFrame(
|
||||
cmd, drawImage, {
|
||||
.clearColor = {{0.f, 0.f, 0.5f, 1.f}},
|
||||
cmd,
|
||||
renderer.getDrawImage(),
|
||||
{
|
||||
.clearColor = {{0.005f, 0.008f, 0.03f, 1.0f}},
|
||||
.drawImageBlitRect = glm::ivec4{},
|
||||
.imguiPass = &imguiPass,
|
||||
});
|
||||
.imguiPass = &imguiPass});
|
||||
}
|
||||
|
||||
void LightKeeper::customCleanup()
|
||||
{
|
||||
// auto device = gfxDevice.getDevice().device;
|
||||
|
||||
// vkDeviceWaitIdle(device);
|
||||
|
||||
gfxDevice.waitIdle();
|
||||
|
||||
SceneManager::GetInstance().Destroy();
|
||||
stars.clear();
|
||||
player = nullptr;
|
||||
|
||||
if (skyboxCubemap)
|
||||
{
|
||||
if (skyboxCubemap) {
|
||||
skyboxCubemap->cleanup(gfxDevice);
|
||||
skyboxCubemap.reset();
|
||||
}
|
||||
|
||||
renderer.cleanup(gfxDevice);
|
||||
}
|
||||
|
||||
void LightKeeper::customFixedUpdate(float dt)
|
||||
@@ -587,7 +277,10 @@ void LightKeeper::customFixedUpdate(float dt)
|
||||
|
||||
void LightKeeper::onWindowResize(int newWidth, int newHeight)
|
||||
{
|
||||
renderer.resize(gfxDevice, glm::ivec2{newWidth, newHeight});
|
||||
const float aspectRatio = static_cast<float>(newWidth) / static_cast<float>(newHeight);
|
||||
camera.setAspectRatio(aspectRatio);
|
||||
if (newWidth <= 0 || newHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderer.resize(gfxDevice, {newWidth, newHeight});
|
||||
camera.setAspectRatio(static_cast<float>(newWidth) / static_cast<float>(newHeight));
|
||||
}
|
||||
|
||||
@@ -25,9 +25,10 @@ int main(int argc, char* argv[]) {
|
||||
app.init({
|
||||
.windowSize = {1200, 800},
|
||||
.renderSize = {1200, 800},
|
||||
.appName = "Destrum Engine",
|
||||
.windowTitle = "Lightkeeper",
|
||||
.appName = "Destrum Star Catcher",
|
||||
.windowTitle = "Star Catcher",
|
||||
.exeDir = exeDir,
|
||||
.showDebugUi = false,
|
||||
});
|
||||
app.run();
|
||||
app.cleanup();
|
||||
|
||||
Reference in New Issue
Block a user