commit e2e748925336779ea49a9f2ef84eea86ffe124e8 Author: Ludovic 'Archivist' Lagouardette Date: Thu Dec 21 01:03:15 2023 +0100 first draft of the engine diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..72b18fd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cmake-build-*/ \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..34368da --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..5fca962 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c8d753c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.24) +project(Ant) +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_CXX_STANDARD 23) + +add_subdirectory(Engine) + diff --git a/Engine/CMakeLists.txt b/Engine/CMakeLists.txt new file mode 100644 index 0000000..8be8fcc --- /dev/null +++ b/Engine/CMakeLists.txt @@ -0,0 +1,27 @@ +Include(FetchContent) + +FetchContent_Declare( + RayLib + GIT_REPOSITORY D:/AntiJeu/cmake-build-debug/_deps/raylib-src + GIT_TAG 4.5.0 + GIT_PROGRESS TRUE +) +FetchContent_MakeAvailable(RayLib) + +add_executable(Engine + src/main.cpp + include/entity.h + include/transform.h + include/component.h + include/scene.h + include/render_state.h + src/render_state.cpp + src/scene.cpp + include/entities/ui_fps_entity.h + src/ui_fps_entity.cpp + include/entities/background_entity.h + src/background_entity.cpp + include/components/sprite_component.h src/sprite_component.cpp) +target_include_directories(Engine PUBLIC include) +target_link_libraries(Engine PUBLIC raylib) +file(COPY assets/apple.png DESTINATION assets/apple.png) \ No newline at end of file diff --git a/Engine/assets/apple.png b/Engine/assets/apple.png new file mode 100644 index 0000000..3e78773 Binary files /dev/null and b/Engine/assets/apple.png differ diff --git a/Engine/include/component.h b/Engine/include/component.h new file mode 100644 index 0000000..82615e4 --- /dev/null +++ b/Engine/include/component.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace ant::core { + struct entity; + struct component { + virtual std::string name() = 0; + virtual void initialize() {} + virtual void render() {} + virtual void update(std::chrono::duration) {} + [[nodiscard]] auto owner() -> std::shared_ptr { + return m_owner.lock(); + } + private: + std::weak_ptr m_owner; + friend struct entity; + }; +} \ No newline at end of file diff --git a/Engine/include/components/sprite_component.h b/Engine/include/components/sprite_component.h new file mode 100644 index 0000000..e43aec6 --- /dev/null +++ b/Engine/include/components/sprite_component.h @@ -0,0 +1,19 @@ +#pragma once +#include "component.h" +#include +#include "raylib.h" +#include "transform.h" + +namespace ant::components::graphical { + struct sprite_component : public ant::core::component { + sprite_component() {} + sprite_component(const std::string& str, int layer = 0, ant::core::transform<> reposition = {{0,0},0}, ant::core::Vec2<> scale = {1,1}); + std::string name() override {return "sprite_component";} + void render() override; + private: + Texture2D current_texture; + ant::core::transform<> transform; + ant::core::Vec2<> m_scale; + int m_layer; + }; +} \ No newline at end of file diff --git a/Engine/include/entities/background_entity.h b/Engine/include/entities/background_entity.h new file mode 100644 index 0000000..ef5c2f5 --- /dev/null +++ b/Engine/include/entities/background_entity.h @@ -0,0 +1,18 @@ +#pragma once + +#include "entity.h" +#include "raylib.h" + +namespace ant::entities::utilities { + + struct background_entity : public ant::core::entity { + background_entity(const std::shared_ptr& parent, Color color) + : ant::core::entity(parent) + , m_color(color) + {} + void render() final; + private: + Color m_color; + }; + +} \ No newline at end of file diff --git a/Engine/include/entities/ui_fps_entity.h b/Engine/include/entities/ui_fps_entity.h new file mode 100644 index 0000000..064777b --- /dev/null +++ b/Engine/include/entities/ui_fps_entity.h @@ -0,0 +1,14 @@ +#pragma once + +#include "entity.h" + +namespace ant::entities::utilities { + + struct ui_fps_entity : public ant::core::entity { + ui_fps_entity(const std::shared_ptr& parent) + : ant::core::entity(parent) + {} + void render() final; + }; + +} \ No newline at end of file diff --git a/Engine/include/entity.h b/Engine/include/entity.h new file mode 100644 index 0000000..a6926f0 --- /dev/null +++ b/Engine/include/entity.h @@ -0,0 +1,139 @@ +#pragma once +#include +#include +#include +#include +#include +#include "transform.h" +#include "component.h" + +namespace ant::core { + struct entity { + [[nodiscard]] auto transform() const -> ant::core::transform { + return m_transform; + } + auto transform() -> ant::core::transform& { + return m_transform; + } + [[nodiscard]] auto world_transform() const -> ant::core::transform { + ant::core::transform ret{{0,0},0}; + if(auto parent = m_parent.lock(); parent) { + ret = parent->world_transform(); + } + auto transform_pivot = transform(); + if(ret.angle != 0.0f) { + transform_pivot.position.X *= std::cos(ret.angle*std::numbers::pi/180); + transform_pivot.position.Y *= std::sin(ret.angle*std::numbers::pi/180); + } + return ret + transform_pivot; + } + [[nodiscard]] auto parent() const -> std::shared_ptr { + return m_parent.lock(); + } + [[nodiscard]] auto is_visible() const -> bool { + if(auto parent = m_parent.lock(); parent){ + if(not parent->is_visible()) { + return false; + } + } + return m_is_visible; + } + [[nodiscard]] auto visibility_flag() const -> bool { + return m_is_visible; + } + [[nodiscard]] auto find_components(std::string name) const -> std::vector> { + decltype(m_components) ret; + auto source = m_components | std::views::filter([&](const auto& elem) {return name == elem->name();}); + std::ranges::copy(source, std::back_inserter(ret)); + return ret; + } + [[nodiscard]] auto components_list() const -> const std::vector>& { + return m_components; + } + [[nodiscard]] auto components_list() -> std::vector>& { + return m_components; + } + template ComponentType, typename... ARGS> + auto add_component(ARGS&&... constructor_args) -> std::shared_ptr { + auto component = std::make_shared(std::forward(constructor_args)...); + auto self = m_self.lock(); + component->m_owner = self; + m_components.push_back(component); + component->initialize(); + return self; + } + auto set_visible(bool visible) -> bool { + return std::exchange(m_is_visible, visible); + } + virtual void initialize() {} + virtual void render() { + if(not visibility_flag()) return; + for(auto& child : m_children | std::views::filter([](const std::shared_ptr& elem){return elem->visibility_flag();})) { + child->render(); + } + for(auto& component : m_components) { + component->render(); + } + } + virtual void update(std::chrono::duration delta_time) { + for(auto& child : m_children) { + child->update(delta_time); + } + for(auto& component : m_components) { + component->update(delta_time); + } + } + + entity(const std::shared_ptr& parent) + : m_is_visible(true) + , m_parent(parent) + , m_children() + , m_components() + , m_transform{.position = {0,0}, .angle = 0} + {} + + entity() + : m_is_visible(true) + , m_parent(std::shared_ptr(nullptr)) + , m_children() + , m_components() + , m_transform{.position = {0,0}, .angle = 0} + {} + private: + bool m_is_visible; + std::weak_ptr m_parent; + std::weak_ptr m_self; + std::vector> m_children; + std::vector> m_components; + ant::core::transform m_transform; + + template EntityType> + friend auto make_entity(const std::shared_ptr& parent, auto&&... ARGS) -> std::shared_ptr; + + template EntityType> + friend auto make_entity(auto&&... ARGS) -> std::shared_ptr; + }; + + template EntityType> + [[nodiscard]] auto make_entity(const std::shared_ptr& parent, auto&&... ARGS) -> std::shared_ptr { + auto object = std::make_shared(std::forward(ARGS)...); + const std::shared_ptr& entity_router = object; + entity_router->m_parent = parent; + entity_router->m_self = object; + if(parent) { + parent->m_children.push_back(object); + } + object->initialize(); + return object; + } + + template EntityType> + [[nodiscard]] auto make_entity(auto&&... ARGS) -> std::shared_ptr { + auto object = std::make_shared(std::shared_ptr(nullptr), std::forward(ARGS)...); + const std::shared_ptr& entity_router = object; + entity_router->m_parent = std::shared_ptr(nullptr); + entity_router->m_self = object; + object->initialize(); + return object; + } +} \ No newline at end of file diff --git a/Engine/include/render_state.h b/Engine/include/render_state.h new file mode 100644 index 0000000..ff19375 --- /dev/null +++ b/Engine/include/render_state.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace ant::render { + enum class layer_pos { + start, + any, + end + }; + void schedule_in_frame(int layer, std::function fn, layer_pos pos = layer_pos::any); + void schedule_in_frame(int layer, auto fn, layer_pos pos = layer_pos::any) { + schedule_in_frame(layer, std::function{std::move(fn)}, pos); + } + void render_all(); +} \ No newline at end of file diff --git a/Engine/include/scene.h b/Engine/include/scene.h new file mode 100644 index 0000000..71f60a8 --- /dev/null +++ b/Engine/include/scene.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include +#include +#include "entity.h" + +namespace ant::core { + struct scene { + static std::shared_ptr current; + std::vector> root_nodes; + + void update(std::chrono::duration delta_time) { + for (auto &elem: root_nodes) { + elem->update(delta_time); + } + } + void render() { + for (auto &elem: root_nodes | std::views::filter([](const std::shared_ptr& elem){return elem->visibility_flag();})) { + elem->render(); + } + } + }; +} \ No newline at end of file diff --git a/Engine/include/transform.h b/Engine/include/transform.h new file mode 100644 index 0000000..ac8ec6f --- /dev/null +++ b/Engine/include/transform.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +namespace ant::core { + template + struct Vec2 { + T X; + T Y; + }; + + template + auto operator+(const Vec2& lhs, const Vec2& rhs) -> Vec2{ + return {.X = lhs.X + rhs.X, .Y = lhs.Y + rhs.Y}; + } + + template + struct transform { + Vec2 position; + float angle; + }; + + template + auto operator+(const transform& lhs, const transform& rhs) -> transform{ + return {.position = lhs.position + rhs.position, .angle = std::fmod(lhs.angle + rhs.angle, 180.0f)}; + } +} \ No newline at end of file diff --git a/Engine/src/background_entity.cpp b/Engine/src/background_entity.cpp new file mode 100644 index 0000000..b811390 --- /dev/null +++ b/Engine/src/background_entity.cpp @@ -0,0 +1,7 @@ +#include "entities/background_entity.h" +#include "render_state.h" +#include "raylib.h" + +void ant::entities::utilities::background_entity::render() { + ant::render::schedule_in_frame(-255, [color = m_color](){ ClearBackground(color);}); +} \ No newline at end of file diff --git a/Engine/src/main.cpp b/Engine/src/main.cpp new file mode 100644 index 0000000..ac43445 --- /dev/null +++ b/Engine/src/main.cpp @@ -0,0 +1,39 @@ +#include "raylib.h" +#include "scene.h" +#include "render_state.h" +#include "entities/ui_fps_entity.h" +#include "entities/background_entity.h" +#include "components/sprite_component.h" + +using namespace std::string_literals; + +int main() { + InitWindow(800, 600, "Example"); + ant::core::scene::current = std::make_shared(); + ant::core::scene::current->root_nodes.push_back(ant::core::make_entity()); + ant::core::scene::current->root_nodes.push_back(ant::core::make_entity()); + ant::core::scene::current->root_nodes.back() + ->add_component( + "Engine/assets/apple.png"s, + 0, + ant::core::transform<>{{560/2, 640/2}, 60}, + ant::core::Vec2<>{0.5,0.5} + ); + ant::core::scene::current->root_nodes.back()->transform().position = {250,250}; + ant::core::scene::current->root_nodes.back()->transform().angle = 120; + ant::core::scene::current->root_nodes.push_back(ant::core::make_entity(BLACK)); + auto ref = std::chrono::steady_clock::now(); + std::chrono::duration frame_time = ref - ref; + while(not WindowShouldClose()) { + ant::core::scene::current->update(frame_time); + ant::core::scene::current->render(); + BeginDrawing(); + ant::render::render_all(); + EndDrawing(); + auto new_time = std::chrono::steady_clock::now(); + frame_time = new_time - ref; + ref = new_time; + } + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/Engine/src/render_state.cpp b/Engine/src/render_state.cpp new file mode 100644 index 0000000..46dc3f6 --- /dev/null +++ b/Engine/src/render_state.cpp @@ -0,0 +1,31 @@ +#include +#include "render_state.h" + +struct render_thunk { + int prio; + ant::render::layer_pos pos = ant::render::layer_pos::any; + std::function op; +}; + +static bool operator<(const render_thunk& lhs, const render_thunk& rhs) { + if(lhs.prio < rhs.prio) { + if(lhs.pos > rhs.pos) { + return false; + } else { + return true; + } + } + return false; +} +static std::priority_queue renders; + +void ant::render::schedule_in_frame(int layer, std::function fn, ant::render::layer_pos pos) { + renders.push({layer, pos, std::move(fn)}); +} + +void ant::render::render_all() { + while(not renders.empty()) { + renders.top().op(); + renders.pop(); + } +} diff --git a/Engine/src/scene.cpp b/Engine/src/scene.cpp new file mode 100644 index 0000000..f6b191c --- /dev/null +++ b/Engine/src/scene.cpp @@ -0,0 +1,3 @@ +#include "scene.h" + +std::shared_ptr ant::core::scene::current; \ No newline at end of file diff --git a/Engine/src/sprite_component.cpp b/Engine/src/sprite_component.cpp new file mode 100644 index 0000000..74c6616 --- /dev/null +++ b/Engine/src/sprite_component.cpp @@ -0,0 +1,41 @@ +#include "components/sprite_component.h" +#include "entity.h" +#include "render_state.h" + +ant::components::graphical::sprite_component::sprite_component(const std::string& str, int layer, ant::core::transform<> reposition, ant::core::Vec2<> scale) +// TODO: Textures should be managed by an asset manager +: current_texture(LoadTexture(str.c_str())) +, transform(reposition) +, m_scale(scale) +, m_layer(layer) +{} + +void ant::components::graphical::sprite_component::render() { + Rectangle source = { + .x = 0, + .y = 0, + .width = static_cast(current_texture.width), + .height = static_cast(current_texture.height) + }; + auto draw_position = owner()->world_transform(); + draw_position = draw_position + transform; + Rectangle dest = { + .x = draw_position.position.X-transform.position.X, + .y = draw_position.position.Y-transform.position.Y, + .width = static_cast(current_texture.width) * m_scale.X, + .height = static_cast(current_texture.height) * m_scale.Y + }; + ant::render::schedule_in_frame(m_layer, [current_texture = this->current_texture, dest, transform = this->transform, source, draw_position, m_scale = this->m_scale](){ + DrawTexturePro( + current_texture, + source, + dest, + Vector2{ + transform.position.X * m_scale.X, + transform.position.Y * m_scale.Y + }, + draw_position.angle, + WHITE + ); + }); +} \ No newline at end of file diff --git a/Engine/src/ui_fps_entity.cpp b/Engine/src/ui_fps_entity.cpp new file mode 100644 index 0000000..bbae939 --- /dev/null +++ b/Engine/src/ui_fps_entity.cpp @@ -0,0 +1,10 @@ +#include "entities/ui_fps_entity.h" +#include "render_state.h" +#include "raylib.h" + +void ant::entities::utilities::ui_fps_entity::render() { + entity::render(); + ant::render::schedule_in_frame(255, [coords = this->transform().position](){ + DrawFPS(static_cast(coords.X),static_cast(coords.Y)); + }); +} \ No newline at end of file