Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,14 @@ set(PHYSICS_SOURCES

set(SCENE_SOURCES
src/Scene/Camera.cpp
src/Scene/ComponentRegistry.cpp
src/Scene/FirstPersonController.cpp
src/Scene/FreeLookCameraControllerComponent.cpp
src/Scene/GameObject.cpp
src/Scene/MaterialComponent.cpp
src/Scene/MeshComponent.cpp
src/Scene/SceneBase.cpp
src/Scene/SceneSerializer.cpp
src/Scene/Scene.cpp
src/Scene/TransformComponent.cpp
)
Expand Down
10 changes: 10 additions & 0 deletions include/private/Assets/InternalGeometry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ inline Sleak::MeshData GetPlaneMesh(float width, float height, int subdivisionsX
}
}

mesh.source = {"Plane", {width, height, float(subdivisionsX),
float(subdivisionsY)}};
return mesh;
}

Expand Down Expand Up @@ -109,6 +111,7 @@ inline Sleak::MeshData GetCubeMesh() {
16, 17, 18, 16, 18, 19,
// Bottom face
20, 21, 22, 20, 22, 23};
mesh.source = {"Cube", {}};
return mesh;
}

Expand Down Expand Up @@ -143,6 +146,7 @@ inline Sleak::MeshData GetSphereMesh(int stacks = 16, int slices = 16) {
mesh.indices.add(first + 1);
}
}
mesh.source = {"Sphere", {float(stacks), float(slices)}};
return mesh;
}

Expand Down Expand Up @@ -207,6 +211,7 @@ inline Sleak::MeshData GetCylinderMesh(int segments, float height, float radius)
mesh.indices.add(base + 3);
}

mesh.source = {"Cylinder", {float(segments), height, radius}};
return mesh;
}

Expand Down Expand Up @@ -263,6 +268,8 @@ inline Sleak::MeshData GetCapsuleMesh(int segments, int rings, float height, flo
}
}

mesh.source = {"Capsule",
{float(segments), float(rings), height, radius}};
return mesh;
}

Expand Down Expand Up @@ -309,6 +316,8 @@ inline Sleak::MeshData GetTorusMesh(int segments, int rings, float innerRadius,
}
}

mesh.source = {"Torus", {float(segments), float(rings), innerRadius,
outerRadius}};
return mesh;
}

Expand Down Expand Up @@ -349,5 +358,6 @@ inline Sleak::MeshData GetPyramidMesh() {
mesh.indices.add(4);
mesh.indices.add(1);

mesh.source = {"Pyramid", {}};
return mesh;
}
5 changes: 5 additions & 0 deletions include/public/Core/GameObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ namespace Sleak {
return GetComponent<T>() != nullptr;
}

/// Every attached component, in the order they were added.
const List<RefPtr<Component>>& GetComponents() const {
return Components;
}

/// Initializes the object and its components; called once before the first Update.
virtual void Initialize();
virtual void Update(float deltaTime);
Expand Down
62 changes: 62 additions & 0 deletions include/public/Core/SceneSerializer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#ifndef _SCENE_SERIALIZER_HPP_
#define _SCENE_SERIALIZER_HPP_

#include <Core/OSDef.hpp>
#include <FileSystem/Serializable.hpp>
#include <string>

namespace Sleak {

class SceneBase;
class GameObject;

/// Saves a scene's GameObject hierarchy to JSON, YAML or binary and loads it
/// back into another scene.
///
/// Each object stores its name, tag, active flag, Camera or Light settings,
/// and the components ComponentRegistry knows about. Meshes are stored as
/// the primitive that built them, and objects created by ModelLoader as the
/// model path, so a model's generated children are rebuilt by reloading it.
/// Anything that cannot be stored (a hand-built mesh, an unregistered
/// component) is skipped with a warning naming the object.
///
/// @code{.cpp}
/// Sleak::SceneSerializer(*this).Save("assets/scenes/level1.json");
///
/// Sleak::SceneSerializer loader(*otherScene);
/// if (!loader.Load("assets/scenes/level1.json")) { /* logged */ }
/// @endcode
/// @see ComponentRegistry, Serializable
/// @ingroup scene
class ENGINE_API SceneSerializer {
public:
/// Version written to every scene file.
static constexpr int kVersion = 1;

explicit SceneSerializer(SceneBase& scene) : m_scene(scene) {}

/// Writes the scene to path, format picked from the extension. Logs and
/// returns false on failure.
bool Save(const std::string& path) const;
/// Adds the objects stored in path to the scene, keeping existing ones.
/// Logs and returns false on failure, leaving the scene untouched.
bool Load(const std::string& path);

/// Builds the scene document in memory.
SerialValue SaveToValue() const;
/// Loads from an in-memory scene document.
bool LoadFromValue(const SerialValue& document);

/// Serializes one object and its children.
static SerialValue SerializeObject(const GameObject& object);
/// Builds an object tree from SerializeObject output. The caller owns it
/// until it is added to a scene. Returns nullptr on malformed data.
static GameObject* DeserializeObject(const SerialValue& data);

private:
SceneBase& m_scene;
};

} // namespace Sleak

#endif // _SCENE_SERIALIZER_HPP_
6 changes: 6 additions & 0 deletions include/public/ECS/Component.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Sleak {
class GameObject;
class ISerializationContext;
/// Base for behavior attached to a GameObject. Owner deletes it via
/// GameObject::RemoveComponent(), never directly.
///
Expand Down Expand Up @@ -73,6 +74,11 @@ namespace Sleak {
virtual void OnEnable() {}
virtual void OnDisable() {}

/// Writes state for SceneSerializer. Saves nothing by default.
virtual void Serialize(ISerializationContext& context) const {}
/// Restores what Serialize wrote. Does nothing by default.
virtual void Deserialize(const ISerializationContext& context) {}

GameObject* GetOwner() {
return owner;
}
Expand Down
95 changes: 95 additions & 0 deletions include/public/ECS/ComponentRegistry.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#ifndef _COMPONENT_REGISTRY_HPP_
#define _COMPONENT_REGISTRY_HPP_

#include <Core/GameObject.hpp>
#include <Core/OSDef.hpp>
#include <ECS/Component.hpp>
#include <FileSystem/Serializable.hpp>
#include <functional>
#include <string>
#include <typeindex>
#include <typeinfo>
#include <vector>

namespace Sleak {

/// Maps component type names to factories so SceneSerializer can save and
/// rebuild components it knows nothing about at compile time.
///
/// Engine components are registered on first use. Game components opt in by
/// overriding Component::Serialize/Deserialize and calling Register<T>() once
/// at startup, or by passing their own functions when the constructor needs
/// more than the owner.
///
/// @code{.cpp}
/// Sleak::ComponentRegistry::Register<SpinComponent>("Spin");
/// @endcode
/// @ingroup scene
class ENGINE_API ComponentRegistry {
public:
/// Adds the component to owner from saved data and returns it.
using CreateFn = std::function<Component*(GameObject& owner,
const ISerializationContext&)>;
/// Finds the existing component of this type on owner, or nullptr.
using FindFn = std::function<Component*(GameObject& owner)>;
/// Writes the component's data. Returning false skips the component.
using SaveFn =
std::function<bool(const Component&, ISerializationContext&)>;
/// Applies saved data to a component that already exists.
using LoadFn =
std::function<void(Component&, const ISerializationContext&)>;

/// One registered component type.
struct Entry {
std::string name;
std::type_index type;
CreateFn create;
FindFn find;
SaveFn save;
LoadFn load;
};

/// Registers a type with explicit functions. A second registration of the
/// same name or type replaces the first.
static void Register(const std::string& name, std::type_index type,
CreateFn create, FindFn find, SaveFn save,
LoadFn load);

/// Registers T using its (GameObject*) constructor and its
/// Serialize/Deserialize overrides.
template <typename T>
static void Register(const std::string& name) {
static_assert(std::is_base_of_v<Component, T>,
"T must derive from Component!");
Register(
name, typeid(T),
[](GameObject& owner, const ISerializationContext& data) {
owner.AddComponent<T>();
Component* c = owner.GetComponent<T>();
if (c) c->Deserialize(data);
return c;
},
[](GameObject& owner) -> Component* {
return owner.GetComponent<T>();
},
[](const Component& c, ISerializationContext& data) {
c.Serialize(data);
return true;
},
[](Component& c, const ISerializationContext& data) {
c.Deserialize(data);
});
}

/// Entry for a saved type name, nullptr when unknown.
static const Entry* Find(const std::string& name);
/// Entry for the dynamic type of component, nullptr when unregistered.
static const Entry* Find(const Component& component);

private:
static std::vector<Entry>& Entries();
};

} // namespace Sleak

#endif // _COMPONENT_REGISTRY_HPP_
6 changes: 6 additions & 0 deletions include/public/ECS/Components/MeshComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <Math/AABB.hpp>
#include <Memory/ObjectPtr.hpp>
#include <Memory/RefPtr.hpp>
#include <Runtime/MeshData.hpp>

namespace Sleak {
class MeshData;
Expand Down Expand Up @@ -49,6 +50,10 @@ namespace Sleak {
// Local-space bounds, transformed by the owner each Update.
void SetCullBoundsLocal(const Math::AABB& bounds);

/// Primitive this mesh was generated from, empty when it was not.
const MeshSource& GetSource() const { return m_source; }
void SetSource(const MeshSource& source) { m_source = source; }

private:
RefPtr<RenderEngine::BufferBase> VertexBuffer{};
RefPtr<RenderEngine::BufferBase> IndexBuffer{};
Expand All @@ -61,6 +66,7 @@ namespace Sleak {
bool m_hasCullBounds = false;
Math::AABB m_localBounds{};
bool m_hasLocalBounds = false;
MeshSource m_source;

};
}
Expand Down
32 changes: 32 additions & 0 deletions include/public/ECS/Components/ModelSourceComponent.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef _MODEL_SOURCE_COMPONENT_HPP_
#define _MODEL_SOURCE_COMPONENT_HPP_

#include <ECS/Component.hpp>
#include <Runtime/ModelLoader.hpp>
#include <string>

namespace Sleak {

/// Marks the root object ModelLoader built, remembering the file and options
/// so SceneSerializer can store a reference instead of the generated meshes.
/// @ingroup scene
class ModelSourceComponent : public Component {
public:
ModelSourceComponent(GameObject* owner, const std::string& path,
const ModelLoadOptions& options = {})
: Component(owner), m_path(path), m_options(options) {}

bool Initialize() override { return true; }
void Update(float) override {}

const std::string& GetPath() const { return m_path; }
const ModelLoadOptions& GetOptions() const { return m_options; }

private:
std::string m_path;
ModelLoadOptions m_options;
};

} // namespace Sleak

#endif // _MODEL_SOURCE_COMPONENT_HPP_
2 changes: 1 addition & 1 deletion include/public/ECS/Components/TransformComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ namespace Sleak {
Vector3D position = Vector3D(0.0f, 0.0f, 0.0f);
Quaternion rotation = Quaternion();
Vector3D scale = Vector3D(1.0f, 1.0f, 1.0f);
TransformMatrix* Transform;
TransformMatrix* Transform = nullptr;

private:
RefPtr<RenderEngine::BufferBase> ConstantBuffer;
Expand Down
3 changes: 3 additions & 0 deletions include/public/Runtime/Material.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ namespace Sleak {
void SetShader(RenderEngine::Shader* shader);
void SetShader(const std::string& shaderPath);
RenderEngine::Shader* GetShader() const;
/// Path given to SetShader(path), empty when set from a Shader pointer.
const std::string& GetShaderPath() const { return m_shaderPath; }

// Texture slots: each pair of Set overloads takes either an owned Texture* or loads by path
void SetDiffuseTexture(Texture* texture);
Expand Down Expand Up @@ -203,6 +205,7 @@ namespace Sleak {

// Shader
ObjectPtr<RenderEngine::Shader> m_shader;
std::string m_shaderPath;

// GPU constant buffer (slot 1)
ObjectPtr<RenderEngine::BufferBase> m_materialBuffer;
Expand Down
11 changes: 11 additions & 0 deletions include/public/Runtime/MeshData.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include <Utility/Container/List.hpp>

namespace Sleak {
Expand Down Expand Up @@ -98,11 +100,20 @@ namespace Sleak {
Sleak::List<Vertex> vertices;
};

/// Names the built-in primitive a mesh was generated from, so it can be
/// rebuilt on load. Empty for imported and hand-built meshes.
/// @ingroup rendering
struct MeshSource {
std::string primitive;
std::vector<float> params;
};

/// CPU-side vertex and index buffers for one mesh, ready for upload.
/// @ingroup rendering
struct MeshData {
VertexGroup vertices;
IndexGroup indices;
MeshSource source;
};

}
Expand Down
7 changes: 7 additions & 0 deletions include/public/Runtime/Texture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ class Texture {
virtual float GetLodBias() const { return 0.0f; }

virtual uint64_t GetImGuiTextureID() const { return 0; }

/// File the texture was loaded from, empty for textures built in memory.
const std::string& GetSourcePath() const { return m_sourcePath; }
void SetSourcePath(const std::string& path) { m_sourcePath = path; }

private:
std::string m_sourcePath;
};

} // namespace Sleak
Expand Down
Loading
Loading