@ -0,0 +1 @@ | |||
cmake-build-*/ |
@ -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 |
@ -0,0 +1,4 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> | |||
</project> |
@ -0,0 +1,8 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="ProjectModuleManager"> | |||
<modules> | |||
<module fileurl="file://$PROJECT_DIR$/.idea/ProcurementOS.iml" filepath="$PROJECT_DIR$/.idea/ProcurementOS.iml" /> | |||
</modules> | |||
</component> | |||
</project> |
@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project version="4"> | |||
<component name="VcsDirectoryMappings"> | |||
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/raylib-src" vcs="Git" /> | |||
</component> | |||
</project> |
@ -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) | |||
@ -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) |
@ -0,0 +1,19 @@ | |||
#pragma once | |||
#include <chrono> | |||
namespace ant::core { | |||
struct entity; | |||
struct component { | |||
virtual std::string name() = 0; | |||
virtual void initialize() {} | |||
virtual void render() {} | |||
virtual void update(std::chrono::duration<double>) {} | |||
[[nodiscard]] auto owner() -> std::shared_ptr<entity> { | |||
return m_owner.lock(); | |||
} | |||
private: | |||
std::weak_ptr<entity> m_owner; | |||
friend struct entity; | |||
}; | |||
} |
@ -0,0 +1,19 @@ | |||
#pragma once | |||
#include "component.h" | |||
#include <string> | |||
#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; | |||
}; | |||
} |
@ -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<entity>& parent, Color color) | |||
: ant::core::entity(parent) | |||
, m_color(color) | |||
{} | |||
void render() final; | |||
private: | |||
Color m_color; | |||
}; | |||
} |
@ -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<entity>& parent) | |||
: ant::core::entity(parent) | |||
{} | |||
void render() final; | |||
}; | |||
} |
@ -0,0 +1,139 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <vector> | |||
#include <chrono> | |||
#include <utility> | |||
#include <ranges> | |||
#include "transform.h" | |||
#include "component.h" | |||
namespace ant::core { | |||
struct entity { | |||
[[nodiscard]] auto transform() const -> ant::core::transform<float> { | |||
return m_transform; | |||
} | |||
auto transform() -> ant::core::transform<float>& { | |||
return m_transform; | |||
} | |||
[[nodiscard]] auto world_transform() const -> ant::core::transform<float> { | |||
ant::core::transform<float> 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<entity> { | |||
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<std::shared_ptr<component>> { | |||
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<std::shared_ptr<component>>& { | |||
return m_components; | |||
} | |||
[[nodiscard]] auto components_list() -> std::vector<std::shared_ptr<component>>& { | |||
return m_components; | |||
} | |||
template<std::derived_from<component> ComponentType, typename... ARGS> | |||
auto add_component(ARGS&&... constructor_args) -> std::shared_ptr<entity> { | |||
auto component = std::make_shared<ComponentType>(std::forward<ARGS>(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<entity>& elem){return elem->visibility_flag();})) { | |||
child->render(); | |||
} | |||
for(auto& component : m_components) { | |||
component->render(); | |||
} | |||
} | |||
virtual void update(std::chrono::duration<double> 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<entity>& 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<entity>(nullptr)) | |||
, m_children() | |||
, m_components() | |||
, m_transform{.position = {0,0}, .angle = 0} | |||
{} | |||
private: | |||
bool m_is_visible; | |||
std::weak_ptr<entity> m_parent; | |||
std::weak_ptr<entity> m_self; | |||
std::vector<std::shared_ptr<entity>> m_children; | |||
std::vector<std::shared_ptr<component>> m_components; | |||
ant::core::transform<float> m_transform; | |||
template<std::derived_from<entity> EntityType> | |||
friend auto make_entity(const std::shared_ptr<entity>& parent, auto&&... ARGS) -> std::shared_ptr<EntityType>; | |||
template<std::derived_from<entity> EntityType> | |||
friend auto make_entity(auto&&... ARGS) -> std::shared_ptr<EntityType>; | |||
}; | |||
template<std::derived_from<entity> EntityType> | |||
[[nodiscard]] auto make_entity(const std::shared_ptr<entity>& parent, auto&&... ARGS) -> std::shared_ptr<EntityType> { | |||
auto object = std::make_shared<EntityType>(std::forward<decltype(ARGS)>(ARGS)...); | |||
const std::shared_ptr<entity>& 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<std::derived_from<entity> EntityType> | |||
[[nodiscard]] auto make_entity(auto&&... ARGS) -> std::shared_ptr<EntityType> { | |||
auto object = std::make_shared<EntityType>(std::shared_ptr<entity>(nullptr), std::forward<decltype(ARGS)>(ARGS)...); | |||
const std::shared_ptr<entity>& entity_router = object; | |||
entity_router->m_parent = std::shared_ptr<entity>(nullptr); | |||
entity_router->m_self = object; | |||
object->initialize(); | |||
return object; | |||
} | |||
} |
@ -0,0 +1,16 @@ | |||
#pragma once | |||
#include <functional> | |||
namespace ant::render { | |||
enum class layer_pos { | |||
start, | |||
any, | |||
end | |||
}; | |||
void schedule_in_frame(int layer, std::function<void()> 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<void()>{std::move(fn)}, pos); | |||
} | |||
void render_all(); | |||
} |
@ -0,0 +1,24 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <vector> | |||
#include <chrono> | |||
#include "entity.h" | |||
namespace ant::core { | |||
struct scene { | |||
static std::shared_ptr<scene> current; | |||
std::vector<std::shared_ptr<entity>> root_nodes; | |||
void update(std::chrono::duration<double> 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<entity>& elem){return elem->visibility_flag();})) { | |||
elem->render(); | |||
} | |||
} | |||
}; | |||
} |
@ -0,0 +1,27 @@ | |||
#pragma once | |||
#include <cmath> | |||
#include <numbers> | |||
namespace ant::core { | |||
template<typename T = float> | |||
struct Vec2 { | |||
T X; | |||
T Y; | |||
}; | |||
template<typename T> | |||
auto operator+(const Vec2<T>& lhs, const Vec2<T>& rhs) -> Vec2<T>{ | |||
return {.X = lhs.X + rhs.X, .Y = lhs.Y + rhs.Y}; | |||
} | |||
template<typename T = float> | |||
struct transform { | |||
Vec2<T> position; | |||
float angle; | |||
}; | |||
template<typename T> | |||
auto operator+(const transform<T>& lhs, const transform<T>& rhs) -> transform<T>{ | |||
return {.position = lhs.position + rhs.position, .angle = std::fmod<float>(lhs.angle + rhs.angle, 180.0f)}; | |||
} | |||
} |
@ -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);}); | |||
} |
@ -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>(); | |||
ant::core::scene::current->root_nodes.push_back(ant::core::make_entity<ant::entities::utilities::ui_fps_entity>()); | |||
ant::core::scene::current->root_nodes.push_back(ant::core::make_entity<ant::core::entity>()); | |||
ant::core::scene::current->root_nodes.back() | |||
->add_component<ant::components::graphical::sprite_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<ant::entities::utilities::background_entity>(BLACK)); | |||
auto ref = std::chrono::steady_clock::now(); | |||
std::chrono::duration<double> 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; | |||
} |
@ -0,0 +1,31 @@ | |||
#include <queue> | |||
#include "render_state.h" | |||
struct render_thunk { | |||
int prio; | |||
ant::render::layer_pos pos = ant::render::layer_pos::any; | |||
std::function<void()> 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<render_thunk> renders; | |||
void ant::render::schedule_in_frame(int layer, std::function<void()> 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(); | |||
} | |||
} |
@ -0,0 +1,3 @@ | |||
#include "scene.h" | |||
std::shared_ptr<ant::core::scene> ant::core::scene::current; |
@ -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<float>(current_texture.width), | |||
.height = static_cast<float>(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<float>(current_texture.width) * m_scale.X, | |||
.height = static_cast<float>(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 | |||
); | |||
}); | |||
} |
@ -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<int>(coords.X),static_cast<int>(coords.Y)); | |||
}); | |||
} |