Example scene loading
This commit is contained in:
@@ -27,14 +27,10 @@ add_subdirectory(component)
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(environment)
|
||||
add_subdirectory(game)
|
||||
# add_subdirectory(games)
|
||||
# add_subdirectory(input)
|
||||
add_subdirectory(locale)
|
||||
add_subdirectory(prefab)
|
||||
add_subdirectory(physics)
|
||||
add_subdirectory(save)
|
||||
add_subdirectory(scene)
|
||||
# add_subdirectory(state)
|
||||
add_subdirectory(time)
|
||||
add_subdirectory(util)
|
||||
add_subdirectory(ui)
|
||||
|
||||
@@ -84,6 +84,17 @@ void assertTrueImplement(
|
||||
map.find(key) != map.end(), __VA_ARGS__ \
|
||||
)
|
||||
|
||||
/**
|
||||
* Asserts that a given map does not have a specific key.
|
||||
* @param map Map to check.
|
||||
* @param key Key to check for.
|
||||
* @param message Message (sprintf format) to send to the logger.
|
||||
* @param args Optional TParam args for the sprintf message to accept.
|
||||
*/
|
||||
#define assertMapNotHasKey(map, key, ...) assertTrue( \
|
||||
map.find(key) == map.end(), __VA_ARGS__ \
|
||||
)
|
||||
|
||||
/**
|
||||
* Asserts that a given value has a specific flag turned off.
|
||||
*
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "AssetManager.hpp"
|
||||
#include "assert/assert.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
void AssetManager::init() {
|
||||
|
||||
void AssetManager::init(const std::shared_ptr<Game> &game) {
|
||||
assertNotNull(game, "Game is NULL?");
|
||||
this->game = game;
|
||||
}
|
||||
|
||||
void AssetManager::update() {
|
||||
@@ -27,6 +29,22 @@ void AssetManager::update() {
|
||||
}
|
||||
}
|
||||
|
||||
void AssetManager::remove(const std::shared_ptr<AssetLoader> loader) {
|
||||
auto existing = std::find(
|
||||
pendingAssetLoaders.begin(), pendingAssetLoaders.end(), loader
|
||||
);
|
||||
if(existing != pendingAssetLoaders.end()) {
|
||||
pendingAssetLoaders.erase(existing);
|
||||
}
|
||||
|
||||
existing = std::find(
|
||||
finishedAssetLoaders.begin(), finishedAssetLoaders.end(), loader
|
||||
);
|
||||
if(existing != finishedAssetLoaders.end()) {
|
||||
finishedAssetLoaders.erase(existing);
|
||||
}
|
||||
}
|
||||
|
||||
// Disabled because it does not respect scopes
|
||||
// void AssetManager::removeExisting(const std::string &filename) {
|
||||
// auto existing = std::find_if(
|
||||
@@ -54,6 +72,12 @@ bool_t AssetManager::isEverythingLoaded() {
|
||||
return pendingAssetLoaders.size() == 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<Game> AssetManager::getGame() {
|
||||
auto g = game.lock();
|
||||
assertNotNull(g, "Game is NULL?");
|
||||
return g;
|
||||
}
|
||||
|
||||
AssetManager::~AssetManager() {
|
||||
|
||||
}
|
||||
@@ -8,8 +8,11 @@
|
||||
#include "asset/AssetLoader.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class Game;
|
||||
|
||||
class AssetManager final : public std::enable_shared_from_this<AssetManager> {
|
||||
private:
|
||||
std::weak_ptr<Game> game;
|
||||
std::vector<std::shared_ptr<AssetLoader>> pendingAssetLoaders;
|
||||
std::vector<std::shared_ptr<AssetLoader>> finishedAssetLoaders;
|
||||
|
||||
@@ -59,8 +62,10 @@ namespace Dawn {
|
||||
public:
|
||||
/**
|
||||
* Initializes this asset manager so it can begin accepting assets.
|
||||
*
|
||||
* @param game Game context that this asset manager is attached to.
|
||||
*/
|
||||
void init();
|
||||
void init(const std::shared_ptr<Game> &game);
|
||||
|
||||
/**
|
||||
* Updates the asset manager.
|
||||
@@ -96,6 +101,22 @@ namespace Dawn {
|
||||
return loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the game context that this asset manager is attached to.
|
||||
*
|
||||
* @return The game context.
|
||||
*/
|
||||
std::shared_ptr<Game> getGame();
|
||||
|
||||
/**
|
||||
* Removes the given asset loader from the asset manager, assumes that
|
||||
* nothing else needs to access it and any dangling shared_ptrs will have
|
||||
* to remain in memory.
|
||||
*
|
||||
* @param loader The asset loader to remove.
|
||||
*/
|
||||
void remove(const std::shared_ptr<AssetLoader> loader);
|
||||
|
||||
/**
|
||||
* Dispose the asset manager, and all attached assets.
|
||||
*/
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
#pragma once
|
||||
#include "asset/AssetLoader.hpp"
|
||||
#include "asset/AssetDataLoader.hpp"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace Dawn {
|
||||
enum class JSONLoaderState {
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "SceneLoader.hpp"
|
||||
#include "game/Game.hpp"
|
||||
#include "assert/assert.hpp"
|
||||
#include "asset/loader/TextureLoader.hpp"
|
||||
#include "scene/Scene.hpp"
|
||||
#include "component/SceneComponentRegistry.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
@@ -18,6 +23,83 @@ SceneLoader::SceneLoader(
|
||||
{
|
||||
}
|
||||
|
||||
void SceneLoader::setupDependencies() {
|
||||
std::cout << "Setting up dependencies" << std::endl;
|
||||
// Begin loading dependencies.
|
||||
auto &data = this->jsonLoader->data;
|
||||
if(data.contains("assets")) {
|
||||
for(auto &asset : data["assets"].items()) {
|
||||
auto &assetName = asset.key();
|
||||
auto &assetData = asset.value();
|
||||
assertTrue(assetData.contains("type"), "Asset missing type");
|
||||
assertTrue(assetData.contains("path"), "Asset missing path");
|
||||
auto type = assetData["type"].get<std::string>();
|
||||
auto path = assetData["path"].get<std::string>();
|
||||
|
||||
std::shared_ptr<AssetLoader> loader;
|
||||
if(type == "texture") {
|
||||
loader = getAssetManager()->get<TextureLoader>(path);
|
||||
} else if(type == "json") {
|
||||
loader = getAssetManager()->get<JSONLoader>(path);
|
||||
} else {
|
||||
assertUnreachable("Unknown asset type: %s", type.c_str());
|
||||
}
|
||||
|
||||
assertMapNotHasKey(
|
||||
ctx.assets,
|
||||
assetName,
|
||||
"Asset already exists: %s", assetName.c_str()
|
||||
);
|
||||
|
||||
ctx.assets[assetName] = loader;
|
||||
}
|
||||
}
|
||||
this->state = SceneLoaderState::LOADING_DEPENDENCIES;
|
||||
}
|
||||
|
||||
void SceneLoader::sceneInitializer(Scene &scene) {
|
||||
std::cout << "Initializing scene" << std::endl;
|
||||
|
||||
auto &data = this->jsonLoader->data;
|
||||
if(data.contains("items")) {
|
||||
// Create the scene items
|
||||
for(auto &item : data["items"].items()) {
|
||||
auto &itemName = item.key();
|
||||
auto &itemData = item.value();
|
||||
auto sceneItem = scene.createSceneItem();
|
||||
ctx.items[itemName] = sceneItem;
|
||||
}
|
||||
|
||||
// Add components to each scene item
|
||||
for(auto &item : data["items"].items()) {
|
||||
auto &itemName = item.key();
|
||||
auto &itemData = item.value();
|
||||
auto sceneItem = ctx.items[itemName];
|
||||
|
||||
ctx.data = itemData;
|
||||
sceneItem->load(ctx);
|
||||
|
||||
if(itemData.contains("components")) {
|
||||
for(auto &cmpItem : itemData["components"].items()) {
|
||||
auto &cmpName = cmpItem.key();
|
||||
auto &cmpData = cmpItem.value();
|
||||
assertTrue(cmpData.contains("type"), "Component missing type in %s", itemName.c_str());
|
||||
|
||||
auto cmpType = cmpData["type"].get<std::string>();
|
||||
auto cmp = SceneComponentRegistry::createComponent(cmpType, sceneItem);
|
||||
ctx.data = cmpData;
|
||||
ctx.components[cmpName] = cmp;
|
||||
cmp->load(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->jsonLoader = nullptr;
|
||||
this->state = SceneLoaderState::DONE;
|
||||
getAssetManager()->remove(shared_from_this());
|
||||
}
|
||||
|
||||
void SceneLoader::updateAsync() {
|
||||
switch(this->state) {
|
||||
case SceneLoaderState::INITIAL:
|
||||
@@ -26,6 +108,31 @@ void SceneLoader::updateAsync() {
|
||||
break;
|
||||
|
||||
case SceneLoaderState::LOADING_JSON:
|
||||
assertNotNull(this->jsonLoader, "JSON Loader is NULL?");
|
||||
if(!this->jsonLoader->loaded) return;
|
||||
this->setupDependencies();
|
||||
break;
|
||||
|
||||
case SceneLoaderState::LOADING_DEPENDENCIES:
|
||||
// Check if all dependencies are loaded.
|
||||
for(auto &asset : ctx.assets) {
|
||||
if(!asset.second->loaded) return;
|
||||
}
|
||||
|
||||
this->state = SceneLoaderState::DEPENDENCIES_LOADED;
|
||||
break;
|
||||
|
||||
case SceneLoaderState::DEPENDENCIES_LOADED:
|
||||
std::cout << "Deps Loaded" << std::endl;
|
||||
this->loaded = true;
|
||||
ctx.scene = std::make_shared<Scene>(
|
||||
this->getAssetManager()->getGame(),
|
||||
[this](Scene &scene) -> void {
|
||||
this->sceneInitializer(scene);
|
||||
}
|
||||
);
|
||||
this->state = SceneLoaderState::PENDING_STAGE;
|
||||
this->loaded = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -42,9 +149,11 @@ std::string SceneLoader::getAssetType() const {
|
||||
}
|
||||
|
||||
std::shared_ptr<Scene> SceneLoader::getScene() {
|
||||
return scene;
|
||||
return ctx.scene;
|
||||
}
|
||||
|
||||
SceneLoader::~SceneLoader() {
|
||||
|
||||
ctx = {};
|
||||
jsonLoader = nullptr;
|
||||
std::cout << "Scene Loader removed" << std::endl;
|
||||
}
|
||||
@@ -11,14 +11,32 @@ namespace Dawn {
|
||||
enum class SceneLoaderState {
|
||||
INITIAL,
|
||||
LOADING_JSON,
|
||||
LOADING_DEPENDENCIES,
|
||||
DEPENDENCIES_LOADED,
|
||||
PENDING_STAGE,
|
||||
DONE
|
||||
};
|
||||
|
||||
class SceneLoader : public AssetLoader {
|
||||
class SceneLoader :
|
||||
public AssetLoader,
|
||||
public std::enable_shared_from_this<SceneLoader>
|
||||
{
|
||||
protected:
|
||||
SceneLoaderState state;
|
||||
|
||||
std::shared_ptr<JSONLoader> jsonLoader;
|
||||
std::shared_ptr<Scene> scene;
|
||||
struct SceneComponentLoadContext ctx;
|
||||
|
||||
/**
|
||||
* Loads the dependencies of the scene.
|
||||
*/
|
||||
void setupDependencies();
|
||||
|
||||
/**
|
||||
* Scene intializer function to stage the loaded scene.
|
||||
*
|
||||
* @param scene Scene that is being staged.
|
||||
*/
|
||||
void sceneInitializer(Scene &scene);
|
||||
|
||||
public:
|
||||
const static std::string ASSET_TYPE;
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
SimpleComponent.cpp
|
||||
SceneComponentRegistry.cpp
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(display)
|
||||
add_subdirectory(ui)
|
||||
add_subdirectory(physics)
|
||||
add_subdirectory(ui)
|
||||
39
src/dawn/component/SceneComponentRegistry.cpp
Normal file
39
src/dawn/component/SceneComponentRegistry.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "SceneComponentRegistry.hpp"
|
||||
|
||||
#include "component/display/Camera.hpp"
|
||||
#include "component/display/material/SimpleTexturedMaterial.hpp"
|
||||
#include "component/display/MeshRenderer.hpp"
|
||||
#include "component/display/mesh/CubeMeshComponent.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
std::shared_ptr<SceneComponent> SceneComponentRegistry::createComponent(
|
||||
const std::string &type,
|
||||
const std::shared_ptr<SceneItem> &item
|
||||
) {
|
||||
if(type.length() == 0) {
|
||||
assertUnreachable("Component type is empty!");
|
||||
|
||||
} else if(type == "Camera") {
|
||||
return item->addComponent<Camera>();
|
||||
|
||||
} else if(type == "SimpleTexturedMaterial") {
|
||||
return item->addComponent<SimpleTexturedMaterial>();
|
||||
|
||||
} else if(type == "MeshRenderer") {
|
||||
return item->addComponent<MeshRenderer>();
|
||||
|
||||
} else if(type == "CubeMesh" || type == "CubeMeshComponent") {
|
||||
return item->addComponent<CubeMeshComponent>();
|
||||
|
||||
} else {
|
||||
assertUnreachable("Unknown component type: %s", type.c_str());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
25
src/dawn/component/SceneComponentRegistry.hpp
Normal file
25
src/dawn/component/SceneComponentRegistry.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "scene/Scene.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class SceneComponentRegistry final {
|
||||
public:
|
||||
/**
|
||||
* Creates a scene component by its type name. Type names are unique and
|
||||
* must be registered in order to be constructed by their string name.
|
||||
*
|
||||
* @param type Type name of the scene component to create.
|
||||
* @param item Scene item that the component belongs to.
|
||||
* @return The created scene component.
|
||||
*/
|
||||
static std::shared_ptr<SceneComponent> createComponent(
|
||||
const std::string &type,
|
||||
const std::shared_ptr<SceneItem> &item
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -10,4 +10,5 @@ target_sources(${DAWN_TARGET_NAME}
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(material)
|
||||
add_subdirectory(material)
|
||||
add_subdirectory(mesh)
|
||||
@@ -21,6 +21,68 @@ void Camera::onDispose() {
|
||||
renderTarget = nullptr;
|
||||
}
|
||||
|
||||
void Camera::load(const SceneComponentLoadContext &ctx) {
|
||||
SceneComponent::load(ctx);
|
||||
|
||||
if(ctx.data.contains("fov")) {
|
||||
this->fov = Math::deg2rad(ctx.data["fov"].get<float_t>());
|
||||
}
|
||||
|
||||
if(ctx.data.contains("cameraType")) {
|
||||
if(
|
||||
ctx.data["cameraType"] == "orthogonal" ||
|
||||
ctx.data["cameraType"] == "orthographic" ||
|
||||
ctx.data["cameraType"] == "ortho"
|
||||
) {
|
||||
this->type = CameraType::ORTHOGONAL;
|
||||
} else if(ctx.data["cameraType"] == "perspective") {
|
||||
this->type = CameraType::PERSPECTIVE;
|
||||
} else {
|
||||
assertUnreachable("Invalid Camera Type!");
|
||||
}
|
||||
}
|
||||
|
||||
if(ctx.data.contains("orthoLeft")) {
|
||||
this->orthoLeft = ctx.data["orthoLeft"].get<float_t>();
|
||||
} else if(ctx.data.contains("left")) {
|
||||
this->orthoLeft = ctx.data["left"].get<float_t>();
|
||||
}
|
||||
|
||||
if(ctx.data.contains("orthoRight")) {
|
||||
this->orthoRight = ctx.data["orthoRight"].get<float_t>();
|
||||
} else if(ctx.data.contains("right")) {
|
||||
this->orthoRight = ctx.data["right"].get<float_t>();
|
||||
}
|
||||
|
||||
if(ctx.data.contains("orthoBottom")) {
|
||||
this->orthoBottom = ctx.data["orthoBottom"].get<float_t>();
|
||||
} else if(ctx.data.contains("bottom")) {
|
||||
this->orthoBottom = ctx.data["bottom"].get<float_t>();
|
||||
}
|
||||
|
||||
if(ctx.data.contains("orthoTop")) {
|
||||
this->orthoTop = ctx.data["orthoTop"].get<float_t>();
|
||||
} else if(ctx.data.contains("top")) {
|
||||
this->orthoTop = ctx.data["top"].get<float_t>();
|
||||
}
|
||||
|
||||
if(ctx.data.contains("clipNear")) {
|
||||
this->clipNear = ctx.data["clipNear"].get<float_t>();
|
||||
} else if(ctx.data.contains("near")) {
|
||||
this->clipNear = ctx.data["near"].get<float_t>();
|
||||
} else if(ctx.data.contains("zNear")) {
|
||||
this->clipNear = ctx.data["zNear"].get<float_t>();
|
||||
}
|
||||
|
||||
if(ctx.data.contains("clipFar")) {
|
||||
this->clipFar = ctx.data["clipFar"].get<float_t>();
|
||||
} else if(ctx.data.contains("far")) {
|
||||
this->clipFar = ctx.data["far"].get<float_t>();
|
||||
} else if(ctx.data.contains("zFar")) {
|
||||
this->clipFar = ctx.data["zFar"].get<float_t>();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RenderTarget> Camera::getRenderTarget() {
|
||||
if(this->renderTarget) return this->renderTarget;
|
||||
return getGame()->renderHost->getBackBufferRenderTarget();
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Dawn {
|
||||
|
||||
void onInit() override;
|
||||
void onDispose() override;
|
||||
void load(const SceneComponentLoadContext &ctx) override;
|
||||
|
||||
/**
|
||||
* Returns the aspect ratio that the camera is using. In future I may
|
||||
|
||||
@@ -4,9 +4,24 @@
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "SimpleTexturedMaterial.hpp"
|
||||
#include "util/JSON.hpp"
|
||||
#include "asset/loader/TextureLoader.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
void SimpleTexturedMaterial::load(const SceneComponentLoadContext &ctx) {
|
||||
if(ctx.data.contains("color")) {
|
||||
this->setColor(JSON::color(ctx.data["color"]));
|
||||
}
|
||||
|
||||
if(ctx.data.contains("texture")) {
|
||||
auto asset = ctx.getAsset<TextureLoader>(
|
||||
ctx.data["texture"].get<std::string>()
|
||||
);
|
||||
this->setTexture(asset->getTexture());
|
||||
}
|
||||
}
|
||||
|
||||
struct Color SimpleTexturedMaterial::getColor() {
|
||||
return this->data.color;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Dawn {
|
||||
std::shared_ptr<Texture> texture;
|
||||
|
||||
public:
|
||||
void load(const SceneComponentLoadContext &ctx) override;
|
||||
|
||||
/**
|
||||
* Returns the color of this material.
|
||||
*/
|
||||
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
PhysicsManager.cpp
|
||||
CubeMeshComponent.cpp
|
||||
)
|
||||
33
src/dawn/component/display/mesh/CubeMeshComponent.cpp
Normal file
33
src/dawn/component/display/mesh/CubeMeshComponent.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "CubeMeshComponent.hpp"
|
||||
#include "display/mesh/CubeMesh.hpp"
|
||||
#include "component/display/MeshRenderer.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
void CubeMeshComponent::onInit() {
|
||||
if(!mesh) {
|
||||
mesh = std::make_shared<Mesh>();
|
||||
}
|
||||
|
||||
mesh->createBuffers(CUBE_VERTICE_COUNT, CUBE_INDICE_COUNT);
|
||||
CubeMesh::buffer(
|
||||
mesh, glm::vec3(-0.5f, -0.5f, -0.5f), glm::vec3(1.0f, 1.0f, 1.0f), 0, 0
|
||||
);
|
||||
|
||||
auto renderer = getItem()->getComponent<MeshRenderer>();
|
||||
if(renderer) renderer->mesh = mesh;
|
||||
}
|
||||
|
||||
void CubeMeshComponent::onDispose() {
|
||||
mesh = nullptr;
|
||||
}
|
||||
|
||||
void CubeMeshComponent::load(const SceneComponentLoadContext &ctx) {
|
||||
if(!mesh) mesh = std::make_shared<Mesh>();
|
||||
|
||||
}
|
||||
19
src/dawn/component/display/mesh/CubeMeshComponent.hpp
Normal file
19
src/dawn/component/display/mesh/CubeMeshComponent.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "scene/SceneItem.hpp"
|
||||
#include "display/mesh/Mesh.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class CubeMeshComponent final : public SceneComponent {
|
||||
public:
|
||||
std::shared_ptr<Mesh> mesh;
|
||||
|
||||
void onInit() override;
|
||||
void onDispose() override;
|
||||
void load(const SceneComponentLoadContext &ctx) override;
|
||||
};
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
# Copyright (c) 2023 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
Collider.cpp
|
||||
CubeCollider.cpp
|
||||
SphereCollider.cpp
|
||||
)
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "Collider.hpp"
|
||||
#include "assert/assert.hpp"
|
||||
#include "game/Game.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
using namespace JPH;
|
||||
using namespace JPH::literals;
|
||||
|
||||
EMotionType Collider::getMotionType(const ColliderType colliderType) {
|
||||
EMotionType motionType;
|
||||
|
||||
switch(colliderType) {
|
||||
case ColliderType::DYNAMIC:
|
||||
return EMotionType::Dynamic;
|
||||
break;
|
||||
|
||||
case ColliderType::STATIC:
|
||||
return EMotionType::Static;
|
||||
break;
|
||||
|
||||
case ColliderType::KINEMATIC:
|
||||
return EMotionType::Kinematic;
|
||||
break;
|
||||
}
|
||||
|
||||
assertUnreachable("Invalid ColliderType");
|
||||
return EMotionType::Kinematic;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void Collider::onInit() {
|
||||
assertNull(this->body, "Body is not NULL?");
|
||||
|
||||
auto settings = this->getShapeSettings();
|
||||
auto shapeResult = settings->Create();
|
||||
auto shape = shapeResult.Get();
|
||||
|
||||
auto pos = getItem()->getLocalPosition();
|
||||
|
||||
BodyCreationSettings bodySettings(
|
||||
shape,
|
||||
RVec3(pos.x, pos.y, pos.z),
|
||||
Quat::sIdentity(),
|
||||
Collider::getMotionType(this->colliderType),
|
||||
layer
|
||||
);
|
||||
|
||||
this->body = getBodyInterface().CreateBody(bodySettings);
|
||||
assertNotNull(this->body, "Body failed to create?");
|
||||
this->bodyId = this->body->GetID();
|
||||
|
||||
getBodyInterface().AddBody(this->bodyId, EActivation::Activate);
|
||||
}
|
||||
|
||||
void Collider::onDispose() {
|
||||
getBodyInterface().RemoveBody(this->bodyId);
|
||||
getBodyInterface().DestroyBody(this->bodyId);
|
||||
}
|
||||
|
||||
void Collider::notifyShapeChanged() {
|
||||
if(!this->isColliderReady()) return;
|
||||
|
||||
auto settings = this->getShapeSettings();
|
||||
auto shapeResult = settings->Create();
|
||||
auto shape = shapeResult.Get();
|
||||
|
||||
getBodyInterface().SetShape(
|
||||
this->bodyId,
|
||||
shape,
|
||||
// TODO: I may not always need to re-activate the body here.
|
||||
true,
|
||||
EActivation::Activate
|
||||
);
|
||||
}
|
||||
|
||||
bool_t Collider::isColliderReady() {
|
||||
return this->body != nullptr;
|
||||
}
|
||||
|
||||
BodyInterface & Collider::getBodyInterface() {
|
||||
return getGame()->physicsManager->getBodyInterface();
|
||||
}
|
||||
|
||||
ColliderType Collider::getColliderType() {
|
||||
return colliderType;
|
||||
}
|
||||
|
||||
BodyID Collider::getBodyId() {
|
||||
return bodyId;
|
||||
}
|
||||
|
||||
glm::vec3 Collider::getVelocity() {
|
||||
if(!this->isColliderReady()) return glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
auto vel = getBodyInterface().GetLinearVelocity(this->bodyId);
|
||||
return glm::vec3(vel.GetX(), vel.GetY(), vel.GetZ());
|
||||
}
|
||||
|
||||
void Collider::setVelocity(const glm::vec3 velocity) {
|
||||
if(!this->isColliderReady()) {
|
||||
assertUnreachable("Collider is not ready.");
|
||||
}
|
||||
|
||||
getBodyInterface().SetLinearVelocity(
|
||||
this->bodyId, RVec3(velocity.x, velocity.y, velocity.z)
|
||||
);
|
||||
}
|
||||
|
||||
void Collider::setColliderType(const ColliderType type) {
|
||||
this->colliderType = type;
|
||||
|
||||
if(!this->isColliderReady()) return;
|
||||
|
||||
getBodyInterface().SetMotionType(
|
||||
this->bodyId,
|
||||
Collider::getMotionType(type),
|
||||
EActivation::Activate// TODO: Should be false on kinematics
|
||||
);
|
||||
}
|
||||
|
||||
void Collider::addForce(
|
||||
const glm::vec3 force,
|
||||
const glm::vec3 inPoint
|
||||
) {
|
||||
if(!this->isColliderReady()) assertUnreachable("Collider is not ready.");
|
||||
|
||||
getBodyInterface().AddForce(
|
||||
this->bodyId,
|
||||
RVec3(force.x, force.y, force.z),
|
||||
RVec3(0, 0, 0)
|
||||
);
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "scene/SceneItem.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
enum class ColliderType {
|
||||
DYNAMIC,
|
||||
STATIC,
|
||||
KINEMATIC
|
||||
};
|
||||
|
||||
class Collider : public SceneComponent {
|
||||
private:
|
||||
JPH::Body *body = nullptr;
|
||||
ColliderType colliderType = ColliderType::DYNAMIC;
|
||||
|
||||
/**
|
||||
* Returns the JoltPhysics motion type for the collider type.
|
||||
*
|
||||
* @param colliderType The collider type.
|
||||
* @return The JoltPhysics motion type.
|
||||
*/
|
||||
static JPH::EMotionType getMotionType(const ColliderType colliderType);
|
||||
|
||||
protected:
|
||||
JPH::BodyID bodyId;
|
||||
JPH::ObjectLayer layer = 1;
|
||||
|
||||
/**
|
||||
* Returns the shape settings for the collider.
|
||||
*
|
||||
* @return The shape settings for the collider.
|
||||
*/
|
||||
virtual std::shared_ptr<JPH::ShapeSettings> getShapeSettings() = 0;
|
||||
|
||||
/**
|
||||
* Returns the JoltPhysics body interface system.
|
||||
*
|
||||
* @return The JoltPhysics body interface system.
|
||||
*/
|
||||
JPH::BodyInterface & getBodyInterface();
|
||||
|
||||
/**
|
||||
* Callable by subclasses to notify that the shape has changed.
|
||||
*/
|
||||
void notifyShapeChanged();
|
||||
|
||||
public:
|
||||
void onInit() override;
|
||||
void onDispose() override;
|
||||
|
||||
/**
|
||||
* Returns whether the collider is ready.
|
||||
*
|
||||
* @return Whether the collider is ready.
|
||||
*/
|
||||
bool_t isColliderReady();
|
||||
|
||||
/**
|
||||
* Returns the collider type.
|
||||
*
|
||||
* @return The collider type.
|
||||
*/
|
||||
ColliderType getColliderType();
|
||||
|
||||
/**
|
||||
* Returns the JoltPhysics body ID of the collider.
|
||||
*
|
||||
* @return The body ID of the collider.
|
||||
*/
|
||||
JPH::BodyID getBodyId();
|
||||
|
||||
/**
|
||||
* Returns the velocity of the collider.
|
||||
*
|
||||
* @return The velocity of the collider.
|
||||
*/
|
||||
glm::vec3 getVelocity();
|
||||
|
||||
/**
|
||||
* Sets the collider type.
|
||||
*
|
||||
* @param colliderType The collider type.
|
||||
*/
|
||||
void setColliderType(ColliderType colliderType);
|
||||
|
||||
/**
|
||||
* Sets the velocity of the collider.
|
||||
*
|
||||
* @param velocity The velocity.
|
||||
*/
|
||||
void setVelocity(const glm::vec3 velocity);
|
||||
|
||||
/**
|
||||
* Adds a force to the collider.
|
||||
*
|
||||
* @param force Force to add.
|
||||
* @param inPoint Application point of the force.
|
||||
*/
|
||||
void addForce(
|
||||
const glm::vec3 force,
|
||||
const glm::vec3 inPoint = glm::vec3(0, 0, 0)
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "CubeCollider.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
std::shared_ptr<JPH::ShapeSettings> CubeCollider::getShapeSettings() {
|
||||
return std::make_shared<JPH::BoxShapeSettings>(
|
||||
JPH::Vec3(shape.x, shape.y, shape.z)
|
||||
);
|
||||
}
|
||||
|
||||
glm::vec3 CubeCollider::getShape() {
|
||||
return shape;
|
||||
}
|
||||
|
||||
void CubeCollider::setShape(const glm::vec3 &shape) {
|
||||
this->shape = shape;
|
||||
this->notifyShapeChanged();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "Collider.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class CubeCollider : public Collider {
|
||||
private:
|
||||
glm::vec3 shape = glm::vec3(1, 1, 1);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<JPH::ShapeSettings> getShapeSettings() override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns the shape of the cube collider.
|
||||
*
|
||||
* @return The shape of the cube collider.
|
||||
*/
|
||||
glm::vec3 getShape();
|
||||
|
||||
/**
|
||||
* Sets the shape of the cube collider.
|
||||
*
|
||||
* @param shape The shape of the cube collider.
|
||||
*/
|
||||
void setShape(const glm::vec3 &shape);
|
||||
};
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "SphereCollider.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
std::shared_ptr<JPH::ShapeSettings> SphereCollider::getShapeSettings() {
|
||||
return std::make_shared<JPH::SphereShapeSettings>(radius);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "Collider.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class SphereCollider : public Collider {
|
||||
protected:
|
||||
std::shared_ptr<JPH::ShapeSettings> getShapeSettings() override;
|
||||
|
||||
public:
|
||||
float radius = 1.0f;
|
||||
};
|
||||
}
|
||||
@@ -60,4 +60,7 @@ extern "C" {
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/Body/BodyActivationListener.h>
|
||||
#include <Jolt/Physics/Character/Character.h>
|
||||
#include <Jolt/Physics/Character/Character.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
@@ -25,21 +25,30 @@ void IGame::init() {
|
||||
renderHost->init(selfAsGame);
|
||||
|
||||
assetManager = std::make_shared<AssetManager>();
|
||||
assetManager->init();
|
||||
assetManager->init(selfAsGame);
|
||||
|
||||
localeManager = std::make_shared<LocaleManager>();
|
||||
localeManager->init(selfAsGame);
|
||||
|
||||
physicsManager = std::make_shared<PhysicsManager>();
|
||||
physicsManager->init(selfAsGame);
|
||||
#ifdef DAWN_ENABLE_PHYSICS
|
||||
physicsManager = std::make_shared<PhysicsManager>();
|
||||
physicsManager->init(selfAsGame);
|
||||
#endif
|
||||
|
||||
inputManager.init(selfAsGame);
|
||||
saveManager.init(selfAsGame);
|
||||
|
||||
this->initManagers();
|
||||
|
||||
auto initialScene = this->getInitialScene();
|
||||
nextFrameScene = std::make_shared<Scene>(selfAsGame, initialScene);
|
||||
// TEST
|
||||
auto scene = this->assetManager->get<SceneLoader>("test_scene.json");
|
||||
while(!this->assetManager->isEverythingLoaded()) {
|
||||
this->assetManager->update();
|
||||
}
|
||||
|
||||
// auto initialScene = this->getInitialScene();
|
||||
// nextFrameScene = std::make_shared<Scene>(selfAsGame, initialScene);
|
||||
nextFrameScene = scene->getScene();
|
||||
}
|
||||
|
||||
void IGame::deinit() {
|
||||
@@ -51,7 +60,10 @@ void IGame::deinit() {
|
||||
if(nextFrameScene) nextFrameScene->deinit();
|
||||
nextFrameScene = nullptr;
|
||||
|
||||
physicsManager = nullptr;
|
||||
#ifdef DAWN_ENABLE_PHYSICS
|
||||
physicsManager = nullptr;
|
||||
#endif
|
||||
|
||||
assetManager = nullptr;
|
||||
renderHost = nullptr;
|
||||
|
||||
@@ -71,7 +83,11 @@ void IGame::update() {
|
||||
|
||||
timeManager.update();
|
||||
if(currentScene) currentScene->update();
|
||||
physicsManager->update();
|
||||
|
||||
#ifdef DAWN_ENABLE_PHYSICS
|
||||
physicsManager->update();
|
||||
#endif
|
||||
|
||||
renderHost->update(this->getSelfAsGame());
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,11 @@ namespace Dawn {
|
||||
std::shared_ptr<RenderHost> renderHost;
|
||||
std::shared_ptr<AssetManager> assetManager;
|
||||
std::shared_ptr<LocaleManager> localeManager;
|
||||
std::shared_ptr<PhysicsManager> physicsManager;
|
||||
|
||||
#ifdef DAWN_ENABLE_PHYSICS
|
||||
std::shared_ptr<PhysicsManager> physicsManager;
|
||||
#endif
|
||||
|
||||
InputManager inputManager;
|
||||
TimeManager timeManager;
|
||||
SaveManager saveManager;
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "PhysicsManager.hpp"
|
||||
#include "game/Game.hpp"
|
||||
#include "scene/Scene.hpp"
|
||||
#include "component/physics/Collider.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
using namespace JPH;
|
||||
using namespace JPH::literals;
|
||||
|
||||
static void TraceImpl(const char *inFMT, ...) {
|
||||
// Format the message
|
||||
va_list list;
|
||||
va_start(list, inFMT);
|
||||
char buffer[1024];
|
||||
vsnprintf(buffer, sizeof(buffer), inFMT, list);
|
||||
va_end(list);
|
||||
|
||||
// Print to the TTY
|
||||
// std::cout << buffer << std::endl;
|
||||
}
|
||||
|
||||
#ifdef JPH_ENABLE_ASSERTS
|
||||
static bool AssertFailedImpl(
|
||||
const char *inExpression,
|
||||
const char *inMessage,
|
||||
const char *inFile,
|
||||
uint inLine
|
||||
) {
|
||||
// Print to the TTY
|
||||
std::cout << inFile << ":" << inLine << ": (" << inExpression << ") " << (inMessage != nullptr? inMessage : "") << std::endl;
|
||||
|
||||
// Breakpoint
|
||||
return true;
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace Layers {
|
||||
static constexpr ObjectLayer NON_MOVING = 0;
|
||||
static constexpr ObjectLayer MOVING = 1;
|
||||
static constexpr ObjectLayer NUM_LAYERS = 2;
|
||||
};
|
||||
|
||||
/// Class that determines if two object layers can collide
|
||||
class ObjectLayerPairFilterImpl : public ObjectLayerPairFilter {
|
||||
public:
|
||||
virtual bool ShouldCollide(
|
||||
ObjectLayer inObject1,
|
||||
ObjectLayer inObject2
|
||||
) const override {
|
||||
switch (inObject1) {
|
||||
case Layers::NON_MOVING:
|
||||
return inObject2 == Layers::MOVING;
|
||||
case Layers::MOVING:
|
||||
return true;
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace BroadPhaseLayers {
|
||||
static constexpr BroadPhaseLayer NON_MOVING(0);
|
||||
static constexpr BroadPhaseLayer MOVING(1);
|
||||
static constexpr uint NUM_LAYERS(2);
|
||||
};
|
||||
|
||||
|
||||
class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface {
|
||||
public:
|
||||
BPLayerInterfaceImpl() {
|
||||
mObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING;
|
||||
mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING;
|
||||
}
|
||||
|
||||
virtual uint GetNumBroadPhaseLayers() const override {
|
||||
return BroadPhaseLayers::NUM_LAYERS;
|
||||
}
|
||||
|
||||
virtual BroadPhaseLayer GetBroadPhaseLayer(
|
||||
ObjectLayer inLayer
|
||||
) const override {
|
||||
JPH_ASSERT(inLayer < Layers::NUM_LAYERS);
|
||||
return mObjectToBroadPhase[inLayer];
|
||||
}
|
||||
|
||||
#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
|
||||
virtual const char * GetBroadPhaseLayerName(
|
||||
BroadPhaseLayer inLayer
|
||||
) const override {
|
||||
switch ((BroadPhaseLayer::Type)inLayer) {
|
||||
case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING:
|
||||
return "NON_MOVING";
|
||||
case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING:
|
||||
return "MOVING";
|
||||
default:
|
||||
JPH_ASSERT(false); return "INVALID";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS];
|
||||
};
|
||||
|
||||
class ObjectVsBroadPhaseLayerFilterImpl : public ObjectVsBroadPhaseLayerFilter {
|
||||
public:
|
||||
virtual bool ShouldCollide(
|
||||
ObjectLayer inLayer1,
|
||||
BroadPhaseLayer inLayer2
|
||||
) const override {
|
||||
switch (inLayer1) {
|
||||
case Layers::NON_MOVING:
|
||||
return inLayer2 == BroadPhaseLayers::MOVING;
|
||||
case Layers::MOVING:
|
||||
return true;
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// An example contact listener
|
||||
class MyContactListener : public ContactListener {
|
||||
public:
|
||||
// See: ContactListener
|
||||
virtual ValidateResult OnContactValidate(
|
||||
const Body &inBody1,
|
||||
const Body &inBody2,
|
||||
RVec3Arg inBaseOffset,
|
||||
const CollideShapeResult &inCollisionResult
|
||||
) override {
|
||||
// std::cout << "Contact validate callback" << std::endl;
|
||||
return ValidateResult::AcceptAllContactsForThisBodyPair;
|
||||
}
|
||||
|
||||
virtual void OnContactAdded(
|
||||
const Body &inBody1,
|
||||
const Body &inBody2,
|
||||
const ContactManifold &inManifold,
|
||||
ContactSettings &ioSettings
|
||||
) override {
|
||||
// std::cout << "A contact was added" << std::endl;
|
||||
}
|
||||
|
||||
virtual void OnContactPersisted(
|
||||
const Body &inBody1,
|
||||
const Body &inBody2,
|
||||
const ContactManifold &inManifold,
|
||||
ContactSettings &ioSettings
|
||||
) override {
|
||||
// std::cout << "A contact was persisted" << std::endl;
|
||||
}
|
||||
|
||||
virtual void OnContactRemoved(
|
||||
const SubShapeIDPair &inSubShapePair
|
||||
) override {
|
||||
// std::cout << "A contact was removed" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
// An example activation listener
|
||||
class MyBodyActivationListener : public BodyActivationListener {
|
||||
public:
|
||||
virtual void OnBodyActivated(
|
||||
const BodyID &inBodyID,
|
||||
uint64 inBodyUserData
|
||||
) override {
|
||||
// std::cout << "A body got activated" << std::endl;
|
||||
}
|
||||
|
||||
virtual void OnBodyDeactivated(
|
||||
const BodyID &inBodyID,
|
||||
uint64 inBodyUserData
|
||||
) override {
|
||||
// std::cout << "A body went to sleep" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
const uint cMaxBodies = 1024;
|
||||
const uint cNumBodyMutexes = 0;
|
||||
const uint cMaxBodyPairs = 1024;
|
||||
const uint cMaxContactConstraints = 1024;
|
||||
BPLayerInterfaceImpl broadPhaseLayerInterface;
|
||||
ObjectVsBroadPhaseLayerFilterImpl objectVsBroadPhaseLayerFilter;
|
||||
ObjectLayerPairFilterImpl objectVsObjectLayerFilter;
|
||||
MyBodyActivationListener bodyActivationListener;
|
||||
MyContactListener contactListener;
|
||||
|
||||
std::shared_ptr<Game> PhysicsManager::getGame() {
|
||||
auto g = game.lock();
|
||||
assertNotNull(g, "Game is null");
|
||||
return g;
|
||||
}
|
||||
|
||||
BodyInterface & PhysicsManager::getBodyInterface() {
|
||||
return physicsSystem.GetBodyInterface();
|
||||
}
|
||||
|
||||
void PhysicsManager::init(const std::shared_ptr<Game> &game) {
|
||||
this->game = game;
|
||||
|
||||
RegisterDefaultAllocator();
|
||||
|
||||
Trace = TraceImpl;
|
||||
JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;)
|
||||
|
||||
Factory::sInstance = new Factory();
|
||||
RegisterTypes();
|
||||
|
||||
tempAllocator = std::make_shared<TempAllocatorImpl>(10 * 1024 * 1024);
|
||||
jobSystem = std::make_shared<JobSystemThreadPool>(
|
||||
cMaxPhysicsJobs,
|
||||
cMaxPhysicsBarriers,
|
||||
thread::hardware_concurrency() - 1
|
||||
);
|
||||
|
||||
physicsSystem.Init(
|
||||
cMaxBodies,
|
||||
cNumBodyMutexes,
|
||||
cMaxBodyPairs,
|
||||
cMaxContactConstraints,
|
||||
broadPhaseLayerInterface,
|
||||
objectVsBroadPhaseLayerFilter,
|
||||
objectVsObjectLayerFilter
|
||||
);
|
||||
|
||||
physicsSystem.SetBodyActivationListener(&bodyActivationListener);
|
||||
physicsSystem.SetContactListener(&contactListener);
|
||||
}
|
||||
|
||||
void PhysicsManager::update() {
|
||||
BodyInterface &bodyInterface = physicsSystem.GetBodyInterface();
|
||||
|
||||
const float cDeltaTime = getGame()->timeManager.delta;
|
||||
const int cCollisionSteps = 1;
|
||||
|
||||
auto scene = getGame()->getCurrentScene();
|
||||
if(scene == nullptr) return;
|
||||
|
||||
// // Now we're ready to simulate the body, keep simulating until it goes to sleep
|
||||
// uint step = 0;
|
||||
// if(!bodyInterface.IsActive(sphereId)) return;
|
||||
// // ++step;
|
||||
|
||||
// // Output current position and velocity of the sphere
|
||||
// RVec3 position = bodyInterface.GetCenterOfMassPosition(sphereId);
|
||||
// Vec3 velocity = bodyInterface.GetLinearVelocity(sphereId);
|
||||
|
||||
// Step the world
|
||||
physicsSystem.Update(
|
||||
cDeltaTime,
|
||||
cCollisionSteps,
|
||||
tempAllocator.get(),
|
||||
jobSystem.get()
|
||||
);
|
||||
|
||||
auto colliders = scene->findComponents<Collider>();
|
||||
for(auto &collider : colliders) {
|
||||
auto bodyId = collider->getBodyId();
|
||||
if(!bodyInterface.IsActive(bodyId)) continue;
|
||||
|
||||
auto pos = bodyInterface.GetCenterOfMassPosition(bodyId);
|
||||
auto rot = bodyInterface.GetRotation(bodyId);
|
||||
collider->getItem()->setLocalPosition(glm::vec3(pos.GetX(), pos.GetY(), pos.GetZ()));
|
||||
collider->getItem()->setLocalRotation(glm::quat(rot.GetW(), rot.GetX(), rot.GetY(), rot.GetZ()));
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsManager::~PhysicsManager() {
|
||||
BodyInterface &bodyInterface = physicsSystem.GetBodyInterface();
|
||||
|
||||
// bodyInterface.RemoveBody(sphereId);
|
||||
// bodyInterface.DestroyBody(sphereId);
|
||||
|
||||
// Remove and destroy the floor
|
||||
// bodyInterface.RemoveBody(floor->GetID());
|
||||
// bodyInterface.DestroyBody(floor->GetID());
|
||||
|
||||
tempAllocator = nullptr;
|
||||
jobSystem = nullptr;
|
||||
|
||||
UnregisterTypes();
|
||||
delete Factory::sInstance;
|
||||
Factory::sInstance = nullptr;
|
||||
|
||||
game.reset();
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "dawn.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class Game;
|
||||
|
||||
class PhysicsManager final {
|
||||
private:
|
||||
std::weak_ptr<Game> game;
|
||||
|
||||
protected:
|
||||
JPH::PhysicsSystem physicsSystem;
|
||||
std::shared_ptr<JPH::TempAllocatorImpl> tempAllocator;
|
||||
std::shared_ptr<JPH::JobSystemThreadPool> jobSystem;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Gets the game associated with the PhysicsManager.
|
||||
*
|
||||
* @return The game associated with the PhysicsManager.
|
||||
*/
|
||||
std::shared_ptr<Game> getGame();
|
||||
|
||||
/**
|
||||
* Gets the JoltPhysics body interface system.
|
||||
*
|
||||
* @return The JoltPhysics body interface system.
|
||||
*/
|
||||
JPH::BodyInterface & getBodyInterface();
|
||||
|
||||
/**
|
||||
* Initializes the PhysicsManager.
|
||||
*
|
||||
* @param game The game to initialize the PhysicsManager with.
|
||||
*/
|
||||
void init(const std::shared_ptr<Game> &game);
|
||||
|
||||
/**
|
||||
* Updates the PhysicsManager.
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* Deconstructs the PhysicsManager.
|
||||
*/
|
||||
~PhysicsManager();
|
||||
};
|
||||
}
|
||||
@@ -19,6 +19,9 @@ Scene::Scene(
|
||||
void Scene::init() {
|
||||
Scene &selfReference = *this;
|
||||
sceneInitializer(selfReference);
|
||||
sceneInitializer = [](Scene &scene) -> void {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
void Scene::deinit() {
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Dawn {
|
||||
class Scene final : public std::enable_shared_from_this<Scene> {
|
||||
private:
|
||||
std::weak_ptr<Game> game;
|
||||
const std::function<void(Scene&)> sceneInitializer;
|
||||
std::function<void(Scene&)> sceneInitializer;
|
||||
std::vector<std::shared_ptr<SceneItem>> sceneItems;
|
||||
std::vector<std::shared_ptr<SceneItem>> sceneItemsToRemove;
|
||||
bool_t paused = false;
|
||||
|
||||
@@ -70,6 +70,10 @@ std::shared_ptr<Game> SceneComponent::getGame() {
|
||||
return this->getScene()->getGame();
|
||||
}
|
||||
|
||||
void SceneComponent::load(const SceneComponentLoadContext &context) {
|
||||
// Override this method to load data from a JSON object.
|
||||
}
|
||||
|
||||
SceneComponent::~SceneComponent() {
|
||||
if(Flag::isOn<uint_fast8_t>(
|
||||
sceneComponentState,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "dawn.hpp"
|
||||
#include "assert/assert.hpp"
|
||||
|
||||
#define SCENE_COMPONENT_STATE_INIT 0x01
|
||||
#define SCENE_COMPONENT_STATE_DISPOSED 0x02
|
||||
@@ -13,6 +14,25 @@ namespace Dawn {
|
||||
class Game;
|
||||
class Scene;
|
||||
class SceneItem;
|
||||
class SceneComponent;
|
||||
class AssetLoader;
|
||||
|
||||
struct SceneComponentLoadContext {
|
||||
json data;
|
||||
std::shared_ptr<Scene> scene;
|
||||
std::unordered_map<std::string, std::shared_ptr<SceneItem>> items;
|
||||
std::unordered_map<std::string, std::shared_ptr<SceneComponent>> components;
|
||||
std::unordered_map<std::string, std::shared_ptr<AssetLoader>> assets;
|
||||
|
||||
template<class T>
|
||||
std::shared_ptr<T> getAsset(const std::string &j) const {
|
||||
auto it = assets.find(j);
|
||||
assertTrue(it != assets.end(), "Asset not found.");
|
||||
auto asset = std::dynamic_pointer_cast<T>(it->second);
|
||||
assertNotNull(asset, "Asset is not of the correct type.");
|
||||
return asset;
|
||||
}
|
||||
};
|
||||
|
||||
class SceneComponent : std::enable_shared_from_this<SceneComponent> {
|
||||
private:
|
||||
@@ -75,6 +95,14 @@ namespace Dawn {
|
||||
*/
|
||||
std::shared_ptr<Game> getGame();
|
||||
|
||||
/**
|
||||
* Load data from a JSON object. This is typically done during a scene
|
||||
* load.
|
||||
*
|
||||
* @param json JSON Data that this object needs to load.
|
||||
*/
|
||||
virtual void load(const SceneComponentLoadContext &context);
|
||||
|
||||
/**
|
||||
* Disposes this scene component.
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "scene/SceneItem.hpp"
|
||||
#include "scene/Scene.hpp"
|
||||
#include "util/JSON.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
@@ -65,6 +66,29 @@ void SceneItem::deinit() {
|
||||
this->components.clear();
|
||||
}
|
||||
|
||||
void SceneItem::load(const SceneComponentLoadContext &ctx) {
|
||||
// Transforms
|
||||
if(ctx.data.contains("position")) {
|
||||
this->setLocalPosition(JSON::vec3(ctx.data["position"]));
|
||||
}
|
||||
|
||||
if(ctx.data.contains("lookAt")) {
|
||||
auto &lookAt = ctx.data["lookAt"];
|
||||
glm::vec3 pos = glm::vec3(3, 3, 3);
|
||||
glm::vec3 look = glm::vec3(0, 0, 0);
|
||||
glm::vec3 up = glm::vec3(0, 1, 0);
|
||||
|
||||
if(lookAt.contains("position")) pos = JSON::vec3(lookAt["position"]);
|
||||
if(lookAt.contains("look")) look = JSON::vec3(lookAt["look"]);
|
||||
if(lookAt.contains("up")) up = JSON::vec3(lookAt["up"]);
|
||||
this->lookAt(pos, look, up);
|
||||
}
|
||||
|
||||
if(ctx.data.contains("scale")) {
|
||||
this->setLocalScale(JSON::vec3(ctx.data["scale"]));
|
||||
}
|
||||
}
|
||||
|
||||
void SceneItem::remove() {
|
||||
auto scene = getScene();
|
||||
if(!scene) return;
|
||||
|
||||
@@ -41,6 +41,15 @@ namespace Dawn {
|
||||
*/
|
||||
void deinit();
|
||||
|
||||
/**
|
||||
* Loads this scene item from the given context. Scene items are not
|
||||
* responsible for loading their components, this is handled by the scene
|
||||
* loader.
|
||||
*
|
||||
* @param ctx Context to load this scene item from.
|
||||
*/
|
||||
void load(const SceneComponentLoadContext &ctx);
|
||||
|
||||
/**
|
||||
* Returns the scene that this scene item belongs to.
|
||||
*
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
target_sources(${DAWN_TARGET_NAME}
|
||||
PRIVATE
|
||||
String.cpp
|
||||
JSON.cpp
|
||||
)
|
||||
77
src/dawn/util/JSON.cpp
Normal file
77
src/dawn/util/JSON.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#include "JSON.hpp"
|
||||
#include "assert/assert.hpp"
|
||||
|
||||
using namespace Dawn;
|
||||
|
||||
glm::vec3 JSON::vec3(const json &j) {
|
||||
if(j.type() == json::value_t::array) {
|
||||
return glm::vec3(j[0].get<float_t>(), j[1].get<float_t>(), j[2].get<float_t>());
|
||||
} else if(j.type() == json::value_t::object) {
|
||||
assertTrue(j.contains("x"), "Missing x in vec3");
|
||||
assertTrue(j.contains("y"), "Missing y in vec3");
|
||||
assertTrue(j.contains("z"), "Missing z in vec3");
|
||||
|
||||
return glm::vec3(
|
||||
j["x"].get<float_t>(),
|
||||
j["y"].get<float_t>(),
|
||||
j["z"].get<float_t>()
|
||||
);
|
||||
} else {
|
||||
assertUnreachable("Invalid JSON type for vec3");
|
||||
}
|
||||
}
|
||||
|
||||
struct Color JSON::color(const json &j) {
|
||||
if(j.type() == json::value_t::array) {
|
||||
return {
|
||||
j[0].get<float_t>(),
|
||||
j[1].get<float_t>(),
|
||||
j[2].get<float_t>(),
|
||||
j[3].get<float_t>()
|
||||
};
|
||||
|
||||
} else if(j.type() == json::value_t::object) {
|
||||
float_t r, g, b, a = 1.0f;
|
||||
|
||||
if(j.contains("r")) {
|
||||
r = j["r"].get<float_t>();
|
||||
} else if(j.contains("red")) {
|
||||
r = j["red"].get<float_t>();
|
||||
} else {
|
||||
assertTrue(j.contains("red"), "Missing red in color");
|
||||
}
|
||||
|
||||
if(j.contains("g")) {
|
||||
g = j["g"].get<float_t>();
|
||||
} else if(j.contains("green")) {
|
||||
g = j["green"].get<float_t>();
|
||||
} else {
|
||||
assertTrue(j.contains("green"), "Missing green in color");
|
||||
}
|
||||
|
||||
if(j.contains("b")) {
|
||||
b = j["b"].get<float_t>();
|
||||
} else if(j.contains("blue")) {
|
||||
b = j["blue"].get<float_t>();
|
||||
} else {
|
||||
assertTrue(j.contains("blue"), "Missing blue in color");
|
||||
}
|
||||
|
||||
if(j.contains("a")) {
|
||||
a = j["a"].get<float_t>();
|
||||
} else if(j.contains("alpha")) {
|
||||
a = j["alpha"].get<float_t>();
|
||||
}
|
||||
|
||||
return { r, g, b, a };
|
||||
} else if(j.type() == json::value_t::string) {
|
||||
return Color::fromString(j.get<std::string>());
|
||||
} else {
|
||||
assertUnreachable("Invalid JSON type for color");
|
||||
}
|
||||
}
|
||||
29
src/dawn/util/JSON.hpp
Normal file
29
src/dawn/util/JSON.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2024 Dominic Masters
|
||||
//
|
||||
// This software is released under the MIT License.
|
||||
// https://opensource.org/licenses/MIT
|
||||
|
||||
#pragma once
|
||||
#include "dawn.hpp"
|
||||
#include "display/Color.hpp"
|
||||
|
||||
namespace Dawn {
|
||||
class JSON final {
|
||||
public:
|
||||
/**
|
||||
* Parses the given JSON string as a vec3.
|
||||
*
|
||||
* @param j JSON obj to parse.
|
||||
* @return Parsed vec3.
|
||||
*/
|
||||
static glm::vec3 vec3(const json &j);
|
||||
|
||||
/**
|
||||
* Parses the given JSON string as a color.
|
||||
*
|
||||
* @param j JSON obj to parse.
|
||||
* @return Parsed color.
|
||||
*/
|
||||
static struct Color color(const json &j);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user