diff --git a/CMakeLists.txt b/CMakeLists.txt index e69a6ab..cfe1c96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ find_package(OpenGL REQUIRED) find_package(fmt CONFIG REQUIRED) find_package(spdlog CONFIG REQUIRED) find_package(assimp CONFIG REQUIRED) +find_package(Bullet REQUIRED) # Link sources file(GLOB_RECURSE source_list src/*.cpp) @@ -49,6 +50,7 @@ target_link_libraries(simpleengine PUBLIC GLEW::GLEW) target_link_libraries(simpleengine PUBLIC glfw) target_link_libraries(simpleengine PUBLIC ${GLM_LIBRARIES}) target_link_libraries(simpleengine PUBLIC ${OPENGL_LIBRARIES}) +target_link_libraries(simpleengine PUBLIC ${BULLET_LIBRARIES}) if(WIN32) target_link_libraries(simpleengine PUBLIC assimp::assimp) target_link_libraries(simpleengine PUBLIC fmt::fmt) @@ -68,6 +70,7 @@ target_include_directories(simpleengine PUBLIC ${OPENGL_INCLUDE_DIR}) target_include_directories(simpleengine PUBLIC ${GLM_INCLUDE_DIRS}) target_include_directories(simpleengine PUBLIC ${STB_INCLUDE_DIR}) target_include_directories(simpleengine PUBLIC ${ENTT_INCLUDE_DIR}) +target_include_directories(simpleengine PUBLIC ${BULLET_INCLUDE_DIRS}) # Add examples as a target if the user has them enabled if (SIMPLE_ENGINE_BUILD_EXAMPLES) diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index fcef86b..0baf8a0 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -1,7 +1,10 @@ #include "simpleengine/camera.h" +#include "simpleengine/ecs/component/box_collider_component.h" #include "simpleengine/ecs/component/mesh_component.h" +#include "simpleengine/ecs/component/rigid_body_component.h" #include "simpleengine/ecs/component/transform_component.h" #include "simpleengine/ecs/entity.h" +#include "simpleengine/ecs/registry.h" #include "simpleengine/gfx/light.h" #include "simpleengine/gfx/material.h" #include "simpleengine/gfx/mesh.h" @@ -9,7 +12,7 @@ #include "simpleengine/gfx/renderer.h" #include "simpleengine/gfx/texture.h" #include "simpleengine/vector.h" -#include +#include #include #include #include @@ -18,15 +21,20 @@ #include #include #include -#include #include +#include #include #include +#include + +#include "entt/entity/fwd.hpp" #include #include #include +#include + #include #include #include @@ -34,6 +42,8 @@ #include #include +#include + namespace se = simpleengine; class FPSCounterEvent : public se::Renderable { @@ -91,7 +101,7 @@ public: if (current_time - last_frame_time_render >= 1.0) { double ms_per_frame = 1000 / (double)frame_count_render; - SE_DEBUG("performance", "Render: {}fps, {}ms/frame", frame_count_render, ms_per_frame); + SE_DEBUG("performance", "Render: {}fps, {:.2f}ms/frame", frame_count_render, ms_per_frame); SE_DEBUG("performance", "-------------------------------"); frame_count_render = 0; last_frame_time_render += 1.0; @@ -107,17 +117,24 @@ int main(int argc, char *argv[]) { // Load core shaders from SimpleEngine resources se::gfx::shaders::Core3dShader core_shader; - auto camera = std::make_shared(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0)); + // Create an entity registry + auto registry = std::make_shared(); + + auto camera = std::make_shared(game.get_window(), core_shader, 70, glm::vec3(-6, 0, 0)); + //game.add_event(camera); // Create a renderer auto renderer = std::make_shared(game.get_window(), core_shader, camera); renderer->initialize(); // Create a Scene and give it the renderer - auto scene = std::make_shared(renderer, camera); - //game.add_event(scene); + auto scene = std::make_shared(registry, renderer, camera); game.add_renderable(scene); + // Create a Physics System for handling the physics + auto physics_sys = std::make_shared(registry); + game.add_event(physics_sys); + /* se::ecs::Entity other_e = scene->create_entity(); other_e.add_component("examples/dev_testing/resources/transparent_window.fbx", se::gfx::ModelProcessingFlags::MdlProcFlag_TRANSPARENT); @@ -134,11 +151,16 @@ int main(int argc, char *argv[]) { transform_comp.translate(4.f, 0.f, 0.f); */ se::ecs::Entity brick_e = scene->create_entity(); - brick_e.add_component("examples/dev_testing/resources/bricks/bricks.fbx"); - brick_e.add_component(); - auto &brick_transf = brick_e.add_component(); - brick_transf.translate(6.f, 0.f, 0.f); - //brick_transf.translate(6.f, -0.5f, 1.f); + brick_e.add_component("examples/dev_testing/resources/bricks/bricks.fbx"); + brick_e.add_component(glm::vec3(6.f, 6.f, 0.f)); + brick_e.add_component(1.f, 1.f, 1.f); + brick_e.add_component(1.f, se::Vectorf(6.f, 6.f, 0.1f)); + + se::ecs::Entity floor = scene->create_entity(); + floor.add_component("examples/dev_testing/resources/ground/ground.fbx"); + floor.add_component(glm::vec3(6.f, -6.f, 0.f), glm::vec3(0.f), glm::vec3(1.f, 1.f, 1.f)); + floor.add_component(1.f, 1.f, 1.f); + floor.add_component(0.f, se::Vectorf(6.f, -6.f, 0.f)); auto light = std::make_shared(core_shader, glm::vec3(0.f, 0.f, 0.f), glm::vec3(1.f, 1.f, 1.f)); game.add_event(light); diff --git a/include/simpleengine/ecs/component/box_collider_component.h b/include/simpleengine/ecs/component/box_collider_component.h new file mode 100644 index 0000000..be29d75 --- /dev/null +++ b/include/simpleengine/ecs/component/box_collider_component.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../../physics/collision/box_shape.h" + +namespace simpleengine::ecs { + /** + * @brief A component that contains a Box collision shape. + * + */ + class BoxColliderComponent { + public: + physics::collision::BoxShape box_shape; + + BoxColliderComponent(physics::collision::BoxShape box) : box_shape(box) { + + } + + /** + * @brief Create a BoxShape with the extent of the box. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param x_extent The extent of the box in the x direction. + * @param y_extent The extent of the box in the y direction. + * @param z_extent The extent of the box in the z direction. + * + */ + BoxColliderComponent(const float& x_extent, const float& y_extent, const float& z_extent) : box_shape(x_extent, y_extent, z_extent) { + + } + + /** + * @brief Create a BoxShape with the extent of the box. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param extent The extent of the box. + * + */ + BoxColliderComponent(const simpleengine::Vectorf& extent) : box_shape(extent) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/capsule_collider_component.h b/include/simpleengine/ecs/component/capsule_collider_component.h new file mode 100644 index 0000000..6e49ff4 --- /dev/null +++ b/include/simpleengine/ecs/component/capsule_collider_component.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../../physics/collision/capsule_shape.h" +#include "../../vector.h" + +namespace simpleengine::ecs { + /** + * @brief A component that contains a capsule collision shape. + * + */ + class CapsuleColliderComponent { + public: + physics::collision::CapsuleShape capsule_shape; + + CapsuleColliderComponent(physics::collision::CapsuleShape capsule) : capsule_shape(capsule) { + + } + + CapsuleColliderComponent(const float& radius, const float& height) : capsule_shape(radius, height) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/collision_shape_component.h b/include/simpleengine/ecs/component/collision_shape_component.h new file mode 100644 index 0000000..837c549 --- /dev/null +++ b/include/simpleengine/ecs/component/collision_shape_component.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../../physics/collision/collision_shape.h" + +namespace simpleengine::ecs { + /** + * @brief A component that contains a collision shape. + * + */ + class CollisionShapeComponent { + public: + physics::collision::CollisionShape* collision_shape; + + CollisionShapeComponent(physics::collision::CollisionShape* shape) : collision_shape(shape) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/cone_collider_component.h b/include/simpleengine/ecs/component/cone_collider_component.h new file mode 100644 index 0000000..f41068a --- /dev/null +++ b/include/simpleengine/ecs/component/cone_collider_component.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../../physics/collision/cone_shape.h" +#include "../../vector.h" + +namespace simpleengine::ecs { + /** + * @brief A component that contains a cone collision shape. + * + */ + class ConeColliderComponent { + public: + physics::collision::ConeShape cone_shape; + + ConeColliderComponent(physics::collision::ConeShape cone) : cone_shape(cone) { + + } + + ConeColliderComponent(float radius, float height) : cone_shape(radius, height) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/cylinder_collider_component.h b/include/simpleengine/ecs/component/cylinder_collider_component.h new file mode 100644 index 0000000..423f3ec --- /dev/null +++ b/include/simpleengine/ecs/component/cylinder_collider_component.h @@ -0,0 +1,45 @@ +#pragma once + +#include "../../physics/collision/cylinder_shape.h" +#include "../../vector.h" + +namespace simpleengine::ecs { + /** + * @brief A component that contains a cylinder collision shape. + * + */ + class CylinderColliderComponent { + public: + physics::collision::CylinderShape cylinder_shape; + + CylinderColliderComponent(physics::collision::CylinderShape cylinder) : cylinder_shape(cylinder) { + + } + + /** + * @brief Create a cylinder collider with the extent of the cylinder shape. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param x_extent The extent of the cylinder shape in the x direction. + * @param y_extent The extent of the cylinder shape in the y direction. + * @param z_extent The extent of the cylinder shape in the z direction. + * + */ + CylinderColliderComponent(const float& x_extent, const float& y_extent, const float& z_extent) : cylinder_shape(x_extent, y_extent, z_extent) { + + } + + /** + * @brief Create a CylinderShape with the extent of the cylinder shape. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param extent The extent of the cylinder. + * + */ + CylinderColliderComponent(const simpleengine::Vectorf& extent) : cylinder_shape(extent) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/mesh_component.h b/include/simpleengine/ecs/component/mesh_component.h index 6e2bf15..e14633c 100644 --- a/include/simpleengine/ecs/component/mesh_component.h +++ b/include/simpleengine/ecs/component/mesh_component.h @@ -6,7 +6,7 @@ #include #include -namespace simpleengine { +namespace simpleengine::ecs { /** * @brief A component that contains a Mesh that will be rendered. * diff --git a/include/simpleengine/ecs/component/model_component.h b/include/simpleengine/ecs/component/model_component.h index cac09de..e792290 100644 --- a/include/simpleengine/ecs/component/model_component.h +++ b/include/simpleengine/ecs/component/model_component.h @@ -5,7 +5,7 @@ #include #include -namespace simpleengine { +namespace simpleengine::ecs { /** * @brief A component that contains a Model that will be rendered. * diff --git a/include/simpleengine/ecs/component/rigid_body_component.h b/include/simpleengine/ecs/component/rigid_body_component.h new file mode 100644 index 0000000..7b613de --- /dev/null +++ b/include/simpleengine/ecs/component/rigid_body_component.h @@ -0,0 +1,41 @@ +#pragma once + +#include "../../gfx/model.h" +#include "simpleengine/vector.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace simpleengine::physics { + class PhysicsSystem; +} + +namespace simpleengine::ecs { + /** + * @brief A component that contains a Model that will be rendered. + * + */ + class RigidBodyComponent { + friend simpleengine::physics::PhysicsSystem; + private: + float mass; + bool is_dynamic; + + // TODO: Free + btDefaultMotionState* motion_state; + btCollisionShape* col_shape; + public: + //bool initialized = false; + btRigidBody* rigid_body; + + RigidBodyComponent(float mass, simpleengine::Vectorf start_origin) : mass(mass), is_dynamic(mass != 0.f), rigid_body(nullptr) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/rotating_component.h b/include/simpleengine/ecs/component/rotating_component.h index ab33827..7dee920 100644 --- a/include/simpleengine/ecs/component/rotating_component.h +++ b/include/simpleengine/ecs/component/rotating_component.h @@ -2,7 +2,7 @@ #include -namespace simpleengine { +namespace simpleengine::ecs { /** * @brief A component that will rotate the transform every frame. * diff --git a/include/simpleengine/ecs/component/sphere_collider_component.h b/include/simpleengine/ecs/component/sphere_collider_component.h new file mode 100644 index 0000000..c16da4f --- /dev/null +++ b/include/simpleengine/ecs/component/sphere_collider_component.h @@ -0,0 +1,22 @@ +#pragma once + +#include "../../physics/collision/sphere_shape.h" + +namespace simpleengine::ecs { + /** + * @brief A component that contains a Sphere collision shape. + * + */ + class SphereColliderComponent { + public: + physics::collision::SphereShape sphere_shape; + + SphereColliderComponent(physics::collision::SphereShape sphere) : sphere_shape(sphere) { + + } + + SphereColliderComponent(const float& radius) : sphere_shape(radius) { + + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/component/transform_component.h b/include/simpleengine/ecs/component/transform_component.h index 9a15519..5f5f00f 100644 --- a/include/simpleengine/ecs/component/transform_component.h +++ b/include/simpleengine/ecs/component/transform_component.h @@ -1,26 +1,42 @@ #pragma once -//#include "simpleengine/scene.h" +#include #include +#include #include -namespace simpleengine { +namespace simpleengine::ecs { + namespace system { + class SceneSystem; + } + /** * @brief A component that contains a Mesh that will be rendered. * */ class TransformComponent { - friend class Scene; - private: + friend simpleengine::ecs::system::SceneSystem; + public: // This is the transform from the last render loop. The renderer uses this for frame interprelation glm::mat4 last_transform_matrix; - public: glm::mat4 transform_matrix; TransformComponent() : transform_matrix(1.f), last_transform_matrix(1.f) { } + TransformComponent(glm::vec3 initial_position, glm::vec3 initial_rotation = glm::vec3(0.f), glm::vec3 initial_scale = glm::vec3(1.f)) + : transform_matrix(1.f), last_transform_matrix(1.f) { + + translate(initial_position); + + rotate_x(initial_rotation.x); + rotate_y(initial_rotation.y); + rotate_z(initial_rotation.z); + + scale(initial_scale); + } + TransformComponent(glm::mat4 transform_matrix) : transform_matrix(transform_matrix), last_transform_matrix(1.f) { } @@ -45,6 +61,38 @@ namespace simpleengine { return lhs; } + virtual void decompose_matrix(glm::vec3& pos, glm::quat& rot, glm::vec3& scale) const { + pos = transform_matrix[3]; + for(int i = 0; i < 3; i++) { + scale[i] = glm::length(glm::vec3(transform_matrix[i])); + } + + const glm::mat3 rotMtx(glm::vec3(transform_matrix[0]) / scale[0], + glm::vec3(transform_matrix[1]) / scale[1], glm::vec3(transform_matrix[2]) / scale[2]); + rot = glm::quat_cast(rotMtx); + } + + virtual glm::vec3 get_pos() const { + return transform_matrix[3]; + } + + virtual glm::vec3 get_scale() const { + glm::vec3 scale; + for(int i = 0; i < 3; i++) { + scale[i] = glm::length(glm::vec3(transform_matrix[i])); + } + + return scale; + } + + virtual glm::quat get_rotation_quat() const { + glm::vec3 scale = get_scale(); + + const glm::mat3 rot_mtx(glm::vec3(transform_matrix[0]) / scale[0], + glm::vec3(transform_matrix[1]) / scale[1], glm::vec3(transform_matrix[2]) / scale[2]); + return glm::quat_cast(rot_mtx); + } + virtual void combine_transform(const glm::mat4& transform_matrix) { this->transform_matrix *= transform_matrix; } @@ -57,9 +105,9 @@ namespace simpleengine { transform_matrix = glm::translate(transform_matrix, glm::vec3(x, y, z)); } - /* virtual void translate(const glm::vec3& vec) { + virtual void translate(const glm::vec3& vec) { transform_matrix = glm::translate(transform_matrix, vec); - } */ + } virtual glm::mat4 rotation_matrix(float degrees, glm::vec3 rotation_axis) const { return glm::rotate(transform_matrix, glm::radians(degrees), rotation_axis); diff --git a/include/simpleengine/ecs/registry.h b/include/simpleengine/ecs/registry.h new file mode 100644 index 0000000..0399d63 --- /dev/null +++ b/include/simpleengine/ecs/registry.h @@ -0,0 +1,22 @@ +#pragma once + +#include "entt/signal/fwd.hpp" +#include + +namespace simpleengine::ecs { + class Entity; + + class Registry { + entt::registry inner; + //entt::dispatcher dispatcher; + public: + Registry(); + + entt::registry& get_inner(); + Entity create_entity(); + }; + + /* struct NewEntityEvent { + Entity& entity; + }; */ +} \ No newline at end of file diff --git a/include/simpleengine/ecs/system/scene_system.h b/include/simpleengine/ecs/system/scene_system.h new file mode 100644 index 0000000..a49279f --- /dev/null +++ b/include/simpleengine/ecs/system/scene_system.h @@ -0,0 +1,33 @@ +#pragma once + +#include "system.h" + +namespace simpleengine { + // fwd decl + class Camera; + namespace gfx { + class Renderer; + } + + namespace ecs { + class Entity; + class Registry; + + namespace system { + class SceneSystem : public System { + protected: + std::shared_ptr renderer; + std::shared_ptr camera; + public: + SceneSystem(std::shared_ptr entity_registry, std::shared_ptr renderer, std::shared_ptr camera); + + ecs::Entity create_entity(); + + virtual void update(const float& delta_time) override; + virtual void input_update(const float& delta_time) override; + virtual void render(const float& interpolate_alpha, const float& frame_time) override; + virtual void destroy() override; + }; + } + } +} \ No newline at end of file diff --git a/include/simpleengine/ecs/system/system.h b/include/simpleengine/ecs/system/system.h new file mode 100644 index 0000000..5f0e392 --- /dev/null +++ b/include/simpleengine/ecs/system/system.h @@ -0,0 +1,24 @@ +#pragma once + +#include "../../renderable.h" + +namespace simpleengine::ecs { + class Entity; + class Registry; + + namespace system { + class System : public simpleengine::Renderable { + protected: + std::shared_ptr entity_registry; + public: + System(std::shared_ptr entity_registry); + + ecs::Entity create_entity(); + + virtual void update(const float& delta_time) = 0; + virtual void input_update(const float& delta_time) = 0; + virtual void render(const float& interpolate_alpha, const float& frame_time) = 0; + virtual void destroy() = 0; + }; + } +} \ No newline at end of file diff --git a/include/simpleengine/event/event.h b/include/simpleengine/event/event.h index 35d09b7..edeeabc 100644 --- a/include/simpleengine/event/event.h +++ b/include/simpleengine/event/event.h @@ -7,9 +7,6 @@ namespace simpleengine { class Event : public simpleengine::Destructable { public: - Event() = default; - virtual ~Event() = default; - /** * @brief The update function with fixed-timestep. * diff --git a/include/simpleengine/physics/collision/box_shape.h b/include/simpleengine/physics/collision/box_shape.h new file mode 100644 index 0000000..4885df0 --- /dev/null +++ b/include/simpleengine/physics/collision/box_shape.h @@ -0,0 +1,58 @@ +#pragma once + +#include "../../vector.h" +#include + +namespace simpleengine::physics::collision { + class BoxShape { + btBoxShape inner; + public: + BoxShape(btBoxShape inner) : inner(inner) { + + } + + /** + * @brief Create a BoxShape with the extent of the box. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param x_extent The extent of the box in the x direction. + * @param y_extent The extent of the box in the y direction. + * @param z_extent The extent of the box in the z direction. + * + */ + BoxShape(const float& x_extent, const float& y_extent, const float& z_extent) : inner(btVector3(btScalar(x_extent), btScalar(y_extent), btScalar(z_extent))) { + + } + + /** + * @brief Create a BoxShape with the extent of the box. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param extent The extent of the box. + * + */ + BoxShape(const simpleengine::Vectorf& extent) : BoxShape(extent.x(), extent.y(), extent.z()) { + + } + + /** + * @brief Get the inner bullet box shape object as a pointer. + * + * @return btBoxShape* + */ + btBoxShape* get_inner_ptr() { + return &inner; + } + + /** + * @brief Get the inner bullet box shape object as a reference. + * + * @return btBoxShape& + */ + btBoxShape& get_inner() { + return inner; + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/physics/collision/capsule_shape.h b/include/simpleengine/physics/collision/capsule_shape.h new file mode 100644 index 0000000..78ce2d5 --- /dev/null +++ b/include/simpleengine/physics/collision/capsule_shape.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace simpleengine::physics::collision { + class CapsuleShape { + btCapsuleShape inner; + public: + CapsuleShape(btCapsuleShape inner) : inner(inner) { + + } + + CapsuleShape(const float& radius, const float& height) : inner(radius, height) { + + } + + /** + * @brief Get the inner bullet capsule shape object as a pointer. + * + * @return btCapsuleShape* + */ + btCapsuleShape* get_inner_ptr() { + return &inner; + } + + /** + * @brief Get the inner bullet capsule shape object as a reference. + * + * @return btCapsuleShape& + */ + btCapsuleShape& get_inner() { + return inner; + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/physics/collision/collision_shape.h b/include/simpleengine/physics/collision/collision_shape.h new file mode 100644 index 0000000..6f9fb57 --- /dev/null +++ b/include/simpleengine/physics/collision/collision_shape.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace simpleengine::physics::collision { + class CollisionShape { + btCollisionShape* inner; + public: + CollisionShape() = default; + + CollisionShape(btCollisionShape* inner) : inner(inner) { + + } + + btCollisionShape* get_inner() { + return inner; + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/physics/collision/cone_shape.h b/include/simpleengine/physics/collision/cone_shape.h new file mode 100644 index 0000000..f86622c --- /dev/null +++ b/include/simpleengine/physics/collision/cone_shape.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +namespace simpleengine::physics::collision { + class ConeShape { + btConeShape inner; + public: + ConeShape(btConeShape inner) : inner(std::move(inner)) { + + } + + ConeShape(float radius, float height) : inner(radius, height) { + + } + + /** + * @brief Get the inner bullet cone shape object as a pointer. + * + * @return btConeShape* + */ + btConeShape* get_inner_ptr() { + return &inner; + } + + /** + * @brief Get the inner bullet cone shape object as a reference. + * + * @return btConeShape& + */ + btConeShape& get_inner() { + return inner; + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/physics/collision/cylinder_shape.h b/include/simpleengine/physics/collision/cylinder_shape.h new file mode 100644 index 0000000..31a8d6e --- /dev/null +++ b/include/simpleengine/physics/collision/cylinder_shape.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include "vector.h" + +namespace simpleengine::physics::collision { + class CylinderShape { + btCylinderShape inner; + public: + CylinderShape(btCylinderShape inner) : inner(inner) { + + } + + /** + * @brief Create a CylinderShape with the extent of the cylinder shape. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param x_extent The extent of the cylinder shape in the x direction. + * @param y_extent The extent of the cylinder shape in the y direction. + * @param z_extent The extent of the cylinder shape in the z direction. + * + */ + CylinderShape(const float& x_extent, const float& y_extent, const float& z_extent) : inner(btVector3(btScalar(x_extent), btScalar(y_extent), btScalar(z_extent))) { + + } + + /** + * @brief Create a CylinderShape with the extent of the cylinder shape. + * + * @note These extents are in half-extents since that's what the underlying Physics engine uses (Bullet). + * + * @param extent The extent of the cylinder. + * + */ + CylinderShape(const simpleengine::Vectorf& extent) : CylinderShape(extent.x(), extent.y(), extent.z()) { + + } + + /** + * @brief Get the inner bullet cylinder shape object as a pointer. + * + * @return btCylinderShape* + */ + btCylinderShape* get_inner_ptr() { + return &inner; + } + + /** + * @brief Get the inner bullet cylinder shape object as a reference. + * + * @return btCylinderShape& + */ + btCylinderShape& get_inner() { + return inner; + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/physics/collision/sphere_shape.h b/include/simpleengine/physics/collision/sphere_shape.h new file mode 100644 index 0000000..ecc349e --- /dev/null +++ b/include/simpleengine/physics/collision/sphere_shape.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace simpleengine::physics::collision { + class SphereShape { + btSphereShape inner; + public: + SphereShape(btSphereShape inner) : inner(inner) { + + } + + SphereShape(const float& radius) : inner(btScalar(radius)) { + + } + + /** + * @brief Get the inner bullet sphere shape object as a pointer. + * + * @return btSphereShape* + */ + btSphereShape* get_inner_ptr() { + return &inner; + } + + /** + * @brief Get the inner bullet sphere shape object as a reference. + * + * @return btSphereShape& + */ + btSphereShape& get_inner() { + return inner; + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/physics/physics_system.h b/include/simpleengine/physics/physics_system.h new file mode 100644 index 0000000..a9f05f2 --- /dev/null +++ b/include/simpleengine/physics/physics_system.h @@ -0,0 +1,69 @@ +#pragma once + +#include "../vector.h" +#include "../ecs/entity.h" +#include "../log/logger.h" +#include "../ecs/system/system.h" + +#include + +#include +#include + +class btDefaultCollisionConfiguration; +class btCollisionDispatcher; +class btBroadphaseInterface; +class btSequentialImpulseConstraintSolver; +class btDiscreteDynamicsWorld; +class btRigidBody; +class btCollisionShape; + +namespace simpleengine { + namespace ecs { + class Registry; + class TransformComponent; + class RigidBodyComponent; + } + + namespace physics { + class PhysicsSystem : public simpleengine::ecs::system::System { + protected: + std::unique_ptr collision_configuration; + std::unique_ptr col_dispatcher; + std::unique_ptr overlapping_pair_cache; + std::unique_ptr solver; + std::unique_ptr dynamics_world; + + simpleengine::log::Logger logger; + public: + simpleengine::Vectorf gravity_vector; + bool should_simulate; + + PhysicsSystem(std::shared_ptr entity_registry); + ~PhysicsSystem(); + + void set_y_gravity(const float& gravity); + + /** + * @brief Add a *manually* constructed rigid body to the Physics System. + * + * @note Do not add a rigid body from a rigid body component. This will be added automatically by the Physics System. + * + * @param rigid_body The rigid body that *manually* created. + */ + void add_rigid_body(btRigidBody* rigid_body); + + virtual void update(const float& delta_time) override; + + // Empty implementations + virtual void input_update(const float& delta_time) override {} + virtual void render(const float& interpolate_alpha, const float& frame_time) override {} + virtual void destroy() override {} + protected: + // Try get a collision shape from an entities components. + btCollisionShape* try_get_collision_shape(const entt::entity& entity); + /// Initialize a rigid body component. + inline bool init_rigid_body_component(const entt::entity& entity, simpleengine::ecs::TransformComponent& transform_comp, simpleengine::ecs::RigidBodyComponent& rigid_body_comp); + }; + } +} \ No newline at end of file diff --git a/include/simpleengine/scene.h b/include/simpleengine/scene.h deleted file mode 100644 index 56820dd..0000000 --- a/include/simpleengine/scene.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "camera.h" -#include "entt/entity/fwd.hpp" -#include "gfx/mesh.h" -#include "event/event.h" -#include "renderable.h" -#include "simpleengine/gfx/renderer.h" - -#include - -#include -#include -#include - -#include - -namespace simpleengine { - namespace ecs { - class Entity; - } - - //class Scene : public simpleengine::Event { - class Scene : public simpleengine::Renderable { - protected: - entt::registry registry; - std::shared_ptr renderer; - - // Last transform matrixes for all entities. - std::unordered_map last_transforms; - - std::shared_ptr camera; - public: - Scene(std::shared_ptr renderer, std::shared_ptr camera); - - ecs::Entity create_entity(); - - virtual void update(const float& delta_time) override; - virtual void input_update(const float& delta_time) override; - virtual void render(const float& interpolate_alpha, const float& frame_time) override; - - virtual void destroy() override; - }; -} \ No newline at end of file diff --git a/include/simpleengine/vector.h b/include/simpleengine/vector.h index 53bc814..d0a21dc 100644 --- a/include/simpleengine/vector.h +++ b/include/simpleengine/vector.h @@ -29,15 +29,15 @@ namespace simpleengine { return inner_vec; } - VectorType& x() { + const VectorType& x() const { return inner_vec.x; } - VectorType& y() { + const VectorType& y() const { return inner_vec.y; } - VectorType& z() { + const VectorType& z() const { return inner_vec.z; } diff --git a/shell.nix b/shell.nix index 0412ece..0246176 100644 --- a/shell.nix +++ b/shell.nix @@ -13,5 +13,6 @@ pkgs.mkShell { glm assimp spdlog + bullet ]; } \ No newline at end of file diff --git a/src/ecs/registry.cpp b/src/ecs/registry.cpp new file mode 100644 index 0000000..7a1dd70 --- /dev/null +++ b/src/ecs/registry.cpp @@ -0,0 +1,16 @@ +#include "ecs/registry.h" +#include "ecs/entity.h" + +namespace simpleengine::ecs { + Registry::Registry() { + + } + + entt::registry& Registry::get_inner() { + return inner; + } + + Entity Registry::create_entity() { + return ecs::Entity(inner, inner.create()); + } +} \ No newline at end of file diff --git a/src/scene.cpp b/src/ecs/system/scene_system.cpp similarity index 53% rename from src/scene.cpp rename to src/ecs/system/scene_system.cpp index e53d8ae..14290a6 100644 --- a/src/scene.cpp +++ b/src/ecs/system/scene_system.cpp @@ -1,44 +1,51 @@ -#include "scene.h" -#include "ecs/component/mesh_component.h" -#include "ecs/component/model_component.h" +#include "gfx/renderer.h" +#include "ecs/system/scene_system.h" #include "ecs/component/transform_component.h" #include "ecs/component/rotating_component.h" #include "ecs/entity.h" -#include "gfx/renderer.h" -#include "log/logger.h" +#include "ecs/registry.h" +#include "ecs/component/model_component.h" +#include "ecs/component/mesh_component.h" #include #include #include -namespace simpleengine { - Scene::Scene(std::shared_ptr renderer, std::shared_ptr camera) : renderer(renderer), camera(camera) { +#include +#include + +using namespace simpleengine::ecs; + +namespace simpleengine::ecs::system { + + SceneSystem::SceneSystem(std::shared_ptr entity_registry, std::shared_ptr renderer, std::shared_ptr camera) + : System(entity_registry), renderer(renderer), camera(camera) { } - ecs::Entity Scene::create_entity() { - return ecs::Entity(registry, registry.create()); + ecs::Entity SceneSystem::create_entity() { + return entity_registry->create_entity(); } - void Scene::input_update(const float& delta_time) { + void SceneSystem::input_update(const float& delta_time) { camera->input_update(delta_time); // Update camera input } - void Scene::update(const float& delta_time) { + void SceneSystem::update(const float& delta_time) { // Update the last transform matrix - registry.view().each([this, &delta_time](TransformComponent& transform) { + entity_registry->get_inner().view().each([this, &delta_time](TransformComponent& transform) { transform.last_transform_matrix = transform.transform_matrix; }); // Rotate the model - registry.view().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) { + /* registry->view().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) { transform.rotate(rotating.rate * delta_time, rotating.rotation_axis); - }); + }); */ } - void Scene::render(const float& interpolate_alpha, const float& frame_time) { + void SceneSystem::render(const float& interpolate_alpha, const float& frame_time) { // Is there a way these can be grouped? - registry.view().each([this](TransformComponent& transform, ModelComponent& model_component) { + entity_registry->get_inner().view().each([this](TransformComponent& transform, ModelComponent& model_component) { for (auto& mesh : model_component.model.meshes) { auto rendering_type = gfx::RenderingType::RendType_OPAQUE; if (mesh.material) { @@ -49,7 +56,7 @@ namespace simpleengine { } }); - registry.view().each([this](TransformComponent& transform, MeshComponent& mesh_component) { + entity_registry->get_inner().view().each([this](TransformComponent& transform, MeshComponent& mesh_component) { auto rendering_type = gfx::RenderingType::RendType_OPAQUE; if (mesh_component.mesh.material) { rendering_type = mesh_component.mesh.material->rendering_type; @@ -61,15 +68,15 @@ namespace simpleengine { renderer->render(interpolate_alpha, frame_time); } - void Scene::destroy() { + void SceneSystem::destroy() { SE_DEBUG("scene", "Destroying Scene..."); - registry.view().each([this](ModelComponent& model_component) { + entity_registry->get_inner().view().each([this](ModelComponent& model_component) { for (auto& mesh : model_component.model.meshes) { mesh.destroy(); } }); - registry.view().each([this](MeshComponent& mesh_component) { + entity_registry->get_inner().view().each([this](MeshComponent& mesh_component) { mesh_component.mesh.destroy(); }); } diff --git a/src/ecs/system/system.cpp b/src/ecs/system/system.cpp new file mode 100644 index 0000000..7c19633 --- /dev/null +++ b/src/ecs/system/system.cpp @@ -0,0 +1,13 @@ +#include "ecs/system/system.h" +#include "ecs/entity.h" +#include "ecs/registry.h" + +namespace simpleengine::ecs::system { + System::System(std::shared_ptr entity_registry) : entity_registry(entity_registry) { + + } + + ecs::Entity System::create_entity() { + return entity_registry->create_entity(); + } +} \ No newline at end of file diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index 5056df2..19390d7 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -2,6 +2,7 @@ #include "gfx/mesh.h" #include "gfx/vao.h" #include "renderable.h" +#include "gfx/shader.h" #include "ecs/component/mesh_component.h" #include "ecs/component/model_component.h" diff --git a/src/physics/physics_system.cpp b/src/physics/physics_system.cpp new file mode 100644 index 0000000..60f71a6 --- /dev/null +++ b/src/physics/physics_system.cpp @@ -0,0 +1,130 @@ +#include "ecs/component/box_collider_component.h" +#include "ecs/component/sphere_collider_component.h" +#include "ecs/component/cylinder_collider_component.h" +#include "ecs/component/cone_collider_component.h" +#include "ecs/component/capsule_collider_component.h" +#include "ecs/component/rigid_body_component.h" +#include "ecs/system/system.h" +#include "entt/entity/fwd.hpp" +#include "physics/physics_system.h" +#include "ecs/component/transform_component.h" +#include "ecs/entity.h" +#include "ecs/registry.h" +#include "game.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace simpleengine::physics { + PhysicsSystem::PhysicsSystem(std::shared_ptr entity_registry) : simpleengine::ecs::system::System(entity_registry), + logger(log::LoggerManager::create_logger("physics")), should_simulate(true), gravity_vector(0, -8, 0) { + collision_configuration = std::make_unique(); + col_dispatcher = std::make_unique(collision_configuration.get()); + overlapping_pair_cache = std::make_unique(); + solver = std::make_unique(); + + dynamics_world = std::make_unique(col_dispatcher.get(), overlapping_pair_cache.get(), solver.get(), collision_configuration.get()); + dynamics_world->setGravity(btVector3(gravity_vector.x(), gravity_vector.y(), gravity_vector.z())); + } + + PhysicsSystem::~PhysicsSystem() { + + } + + void PhysicsSystem::set_y_gravity(const float& gravity) { + this->gravity_vector.set_y(gravity); + } + + void PhysicsSystem::add_rigid_body(btRigidBody* rigid_body) { + dynamics_world->addRigidBody(rigid_body); + } + + void PhysicsSystem::update(const float& delta_time) { + if (should_simulate) { + dynamics_world->stepSimulation(delta_time, 10); + + entity_registry->get_inner().view() + .each([this](const entt::entity& entity, simpleengine::ecs::TransformComponent& transform_comp, simpleengine::ecs::RigidBodyComponent& rigid_body_comp) { + // Initialize the rigid body component if it hasn't already been initialized. + init_rigid_body_component(entity, transform_comp, rigid_body_comp); + + btRigidBody* rigid_body = rigid_body_comp.rigid_body; + btTransform trans; + if (rigid_body_comp.rigid_body->getMotionState()) { + rigid_body->getMotionState()->getWorldTransform(trans); + + trans.getOpenGLMatrix(glm::value_ptr(transform_comp.transform_matrix)); + } + }); + } + } + + btCollisionShape* PhysicsSystem::try_get_collision_shape(const entt::entity& entity) { + if (auto box_col = entity_registry->get_inner().try_get(entity)) { + return box_col->box_shape.get_inner_ptr(); + } else if (auto sphere_col = entity_registry->get_inner().try_get(entity)) { + return sphere_col->sphere_shape.get_inner_ptr(); + } else if (auto cylinder_col = entity_registry->get_inner().try_get(entity)) { + return cylinder_col->cylinder_shape.get_inner_ptr(); + } else if (auto cone_col = entity_registry->get_inner().try_get(entity)) { + return cone_col->cone_shape.get_inner_ptr(); + } else if (auto capsule_col = entity_registry->get_inner().try_get(entity)) { + return capsule_col->capsule_shape.get_inner_ptr(); + } + + return nullptr; + } + + bool PhysicsSystem::init_rigid_body_component(const entt::entity& entity, simpleengine::ecs::TransformComponent& transform_comp, simpleengine::ecs::RigidBodyComponent& rigid_body_comp) { + if (!rigid_body_comp.rigid_body) { + // Try to get the collision shape + btCollisionShape* collision_shape = try_get_collision_shape(entity); + if (!collision_shape) { + logger.warn("Entity with a rigid body component is missing a collider! The entities rigid body component will be removed!"); + + if (entity_registry->get_inner().remove(entity) == 0) { + logger.warn("Failed to remove rigid body component from the entity!"); + } + + return true; + } + + btTransform start_transform; + start_transform.setIdentity(); + + glm::vec3 origin = transform_comp.get_pos(); + start_transform.setOrigin(btVector3(origin.x, origin.y, origin.z)); + + glm::quat rot = transform_comp.get_rotation_quat(); + start_transform.setRotation(btQuaternion(rot.x, rot.y, rot.z, rot.w)); + + rigid_body_comp.col_shape = collision_shape; + + btVector3 local_inertia; + if (rigid_body_comp.is_dynamic) { + // update collision shape + rigid_body_comp.col_shape->calculateLocalInertia(rigid_body_comp.mass, local_inertia); + } + + rigid_body_comp.motion_state = new btDefaultMotionState(start_transform); + btRigidBody::btRigidBodyConstructionInfo rb_info(rigid_body_comp.mass, rigid_body_comp.motion_state, rigid_body_comp.col_shape, local_inertia); + rigid_body_comp.rigid_body = new btRigidBody(rb_info); + + this->add_rigid_body(rigid_body_comp.rigid_body); + + logger.debug("Initialized rigid body component"); + return true; + } + + return false; + } +} \ No newline at end of file