Solved TODOs

This commit is contained in:
Jan Mrna
2025-11-04 19:46:33 +01:00
parent a029c416d5
commit 55aa2217cf
8 changed files with 72 additions and 150 deletions

View File

@@ -4,7 +4,7 @@
#include <stack> #include <stack>
#include <stdexcept> #include <stdexcept>
namespace array { // TODO rename to container or something namespace array {
template <typename U> template <typename U>
concept Deletable = requires(U u) { concept Deletable = requires(U u) {
@@ -148,7 +148,6 @@ public:
private: private:
pair_t *m_Pool = nullptr; pair_t *m_Pool = nullptr;
// TODO use unique_ptr
std::stack<size_t> m_FreeIdx; std::stack<size_t> m_FreeIdx;
size_t m_Capacity = 0; size_t m_Capacity = 0;

View File

@@ -28,7 +28,6 @@ public:
} }
private: private:
// TODO this should be replaced with a matrix
float m_Zoom = 1.0f; float m_Zoom = 1.0f;
WorldPos m_Pan; WorldPos m_Pan;
}; };

View File

@@ -63,16 +63,12 @@ void GameLoop::Draw() {
} }
} }
// TODO rethink coupling and dependencies in the game loop class
void GameLoop::Run() { void GameLoop::Run() {
LOG_INFO("Running the game"); LOG_INFO("Running the game");
while (!m_Game->IsExitRequested()) { while (!m_Game->IsExitRequested()) {
m_Game->HandleActions(m_UserInput->GetActions()); m_Game->HandleActions(m_UserInput->GetActions());
m_Game->UpdateWorld(); m_Game->UpdateWorld();
// TODO measure fps, draw only if delay for target fps was reached
// or create a separate thread for drawing
m_Window->ClearWindow(); m_Window->ClearWindow();
Draw(); Draw();
m_Window->Flush(); m_Window->Flush();

View File

@@ -7,16 +7,11 @@
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <iostream> #include <iostream>
#include <numbers>
#include <numeric> #include <numeric>
#include <ranges> #include <ranges>
#include <utility> #include <utility>
#ifdef _WIN32
#include <numbers>
#define M_PI std::numbers::pi
// TODO use std::numbers::pi instead of M_PI
#endif
template <typename T> template <typename T>
requires std::floating_point<T> requires std::floating_point<T>
static inline bool equalEpsilon(const T &a, const T &b) { static inline bool equalEpsilon(const T &a, const T &b) {
@@ -174,8 +169,6 @@ public:
vec &a = *this; vec &a = *this;
auto b = std::views::repeat(scalar); auto b = std::views::repeat(scalar);
std::ranges::transform(a.m_Array, b, a.m_Array.begin(), std::divides{}); std::ranges::transform(a.m_Array, b, a.m_Array.begin(), std::divides{});
// TODO check all of this, could be done better with views instead of
// ranges?
return a; return a;
} }

View File

@@ -23,7 +23,6 @@ PathFindingDemo::PathFindingDemo(int width, int height) : m_Map(width, height) {
PathFindingDemo::~PathFindingDemo() { LOG_DEBUG("."); } PathFindingDemo::~PathFindingDemo() { LOG_DEBUG("."); }
void PathFindingDemo::AddEntity(std::shared_ptr<Entity> e) { void PathFindingDemo::AddEntity(std::shared_ptr<Entity> e) {
// TODO emplace_back
m_Entities.push_back(e); m_Entities.push_back(e);
} }
@@ -71,11 +70,6 @@ void PathFindingDemo::CreateMap() {
AddEntity(p); AddEntity(p);
} }
} }
// select everything - TODO this is just temporary for testing
for (const auto &entity : m_Entities) {
m_SelectedEntities.push_back(entity);
}
} }
WorldPos PathFindingDemo::GetRandomPosition() const { WorldPos PathFindingDemo::GetRandomPosition() const {
@@ -121,8 +115,6 @@ void PathFindingDemo::UpdateWorld() {
entity->SetActualVelocity(velocity * tile_velocity_coeff); entity->SetActualVelocity(velocity * tile_velocity_coeff);
for (const auto &collision : GetEntityCollisions()) { for (const auto &collision : GetEntityCollisions()) {
// TODO this loop is quite "hot", is it good idea to use weak_ptr and
// promote it?
auto weak_A = std::get<0>(collision); auto weak_A = std::get<0>(collision);
auto weak_B = std::get<1>(collision); auto weak_B = std::get<1>(collision);
auto A = weak_A.lock(); auto A = weak_A.lock();
@@ -136,8 +128,6 @@ void PathFindingDemo::UpdateWorld() {
// LOG_DEBUG("Collision: A is ", A, ", B is ", B); // LOG_DEBUG("Collision: A is ", A, ", B is ", B);
auto AB = B->GetPosition() - A->GetPosition(); auto AB = B->GetPosition() - A->GetPosition();
A->ZeroActualVelocityInDirection(AB); A->ZeroActualVelocityInDirection(AB);
// handle logic
// TODO
} }
// update the position // update the position
@@ -211,7 +201,6 @@ void PathFindingDemo::DeselectEntities() {
void PathFindingDemo::SelectEntitiesInRectangle(WorldPos A, WorldPos B) { void PathFindingDemo::SelectEntitiesInRectangle(WorldPos A, WorldPos B) {
DeselectEntities(); DeselectEntities();
// TODO use colliders for this
auto [x_min, x_max] = std::minmax(A.x(), B.x()); auto [x_min, x_max] = std::minmax(A.x(), B.x());
auto [y_min, y_max] = std::minmax(A.y(), B.y()); auto [y_min, y_max] = std::minmax(A.y(), B.y());
for (const auto &entity : m_Entities) { for (const auto &entity : m_Entities) {

View File

@@ -1,14 +1,14 @@
#pragma once #pragma once
#include <vector>
#include <memory>
#include <cstdlib>
#include <cmath>
#include <algorithm> #include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iterator> #include <iterator>
#include <memory>
#include <vector>
#include "math.hpp"
#include "log.hpp" #include "log.hpp"
#include "math.hpp"
template <typename T> template <typename T>
concept HasPosition = requires(T t, WorldPos pos) { concept HasPosition = requires(T t, WorldPos pos) {
@@ -17,48 +17,36 @@ concept HasPosition = requires(T t, WorldPos pos) {
}; };
template <typename T> template <typename T>
concept HasCollisions = requires(T t) { requires HasPosition<T>
t.Dummy(); // TODO
};
template <typename T> class IPositionalContainer {
requires HasPosition<T>
class IPositionalContainer
{
public: public:
virtual ~IPositionalContainer() = default; virtual ~IPositionalContainer() = default;
virtual bool Add(std::shared_ptr<T> t) = 0; virtual bool Add(std::shared_ptr<T> t) = 0;
virtual std::vector<std::weak_ptr<T>> Get(const WorldPos& p, float radius) = 0; virtual std::vector<std::weak_ptr<T>> Get(const WorldPos &p,
float radius) = 0;
virtual void UpdateAll() = 0; virtual void UpdateAll() = 0;
virtual void Update(std::shared_ptr<T> item) = 0; virtual void Update(std::shared_ptr<T> item) = 0;
}; };
template <typename T> template <typename T>
class IColliderContainer : public IPositionalContainer<T> class IColliderContainer : public IPositionalContainer<T> {
{
public: public:
virtual std::vector<std::weak_ptr<T>> GetCollisions() = 0; virtual std::vector<std::weak_ptr<T>> GetCollisions() = 0;
}; };
template <typename T> template <typename T> class SimpleContainer : IPositionalContainer<T> {
class SimpleContainer : IPositionalContainer<T>
{
public: public:
bool Add(std::shared_ptr<T> t) override bool Add(std::shared_ptr<T> t) override {
{
m_Items.push_back(t); m_Items.push_back(t);
return true; return true;
} }
std::vector<std::weak_ptr<T>> Get(const WorldPos& center, float radius) override std::vector<std::weak_ptr<T>> Get(const WorldPos &center,
{ float radius) override {
std::vector<std::weak_ptr<T>> matched_items; std::vector<std::weak_ptr<T>> matched_items;
for (const auto& item : m_Items) for (const auto &item : m_Items) {
{ if (center.DistanceTo(item->GetPosition()) < radius) {
if (center.DistanceTo(item->GetPosition()) < radius)
{
matched_items.push_back(item); matched_items.push_back(item);
} }
} }
@@ -73,48 +61,35 @@ private:
std::vector<std::shared_ptr<T>> m_Items; std::vector<std::shared_ptr<T>> m_Items;
}; };
template <class T> class PositionalContainer : IPositionalContainer<T> {
template <class T>
class PositionalContainer : IPositionalContainer<T>
{
public: public:
PositionalContainer(const WorldSize &size, size_t chunks)
PositionalContainer(const WorldSize& size, size_t chunks) : : m_GridSize{size}, m_GridStep{size / chunks}, m_ChunksPerAxis{chunks} {
m_GridSize{size},
m_GridStep{size / chunks},
m_ChunksPerAxis{chunks}
{
LOG_INFO("Size: ", m_GridSize, " step: ", m_GridStep); LOG_INFO("Size: ", m_GridSize, " step: ", m_GridStep);
m_Grid.reserve(chunks); m_Grid.reserve(chunks);
for (size_t i = 0; i < chunks; i++) for (size_t i = 0; i < chunks; i++) {
{
m_Grid.emplace_back(chunks); m_Grid.emplace_back(chunks);
for (size_t j = 0; j < chunks; j++) for (size_t j = 0; j < chunks; j++) {
{
m_Grid[i][j].reserve(16); m_Grid[i][j].reserve(16);
} }
} }
} }
// calling Add on object that is already in the container is UB // calling Add on object that is already in the container is UB
bool Add(std::shared_ptr<T> item) override bool Add(std::shared_ptr<T> item) override {
{ const auto &world_pos = item->GetPosition();
const auto& world_pos = item->GetPosition(); if (!CheckBounds(world_pos)) {
if (!CheckBounds(world_pos))
{
return false; return false;
} }
m_Items.push_back(item); m_Items.push_back(item);
auto coords = GetCoords(world_pos); auto coords = GetCoords(world_pos);
m_Grid[coords.x()][coords.y()].push_back(item); m_Grid[coords.x()][coords.y()].push_back(item);
m_ReverseGridLookup[item] = coords; m_ReverseGridLookup[item] = coords;
// TODO should we call Update instead?
//Update(item);
return true; return true;
} }
std::vector<std::weak_ptr<T>> Get(const WorldPos& center, float radius) override std::vector<std::weak_ptr<T>> Get(const WorldPos &center,
{ float radius) override {
vector_wptr output_vec{}; vector_wptr output_vec{};
Get(output_vec, center, radius); Get(output_vec, center, radius);
@@ -122,18 +97,16 @@ public:
return output_vec; return output_vec;
} }
void Get(std::vector<std::weak_ptr<T>>& output_vec, const WorldPos& corner, const WorldSize& size) void Get(std::vector<std::weak_ptr<T>> &output_vec, const WorldPos &corner,
{ const WorldSize &size) {
const WorldSize half_size = size / 2.0f; const WorldSize half_size = size / 2.0f;
const WorldPos center = corner + half_size.ChangeTag<WorldPos>(); const WorldPos center = corner + half_size.ChangeTag<WorldPos>();
float radius = half_size.x(); float radius = half_size.x();
Get(output_vec, center, radius); Get(output_vec, center, radius);
} }
// TODO add those Get methods to the interface void Get(std::vector<std::weak_ptr<T>> &output_vec, const WorldPos &center,
float radius) {
void Get(std::vector<std::weak_ptr<T>>& output_vec, const WorldPos& center, float radius)
{
output_vec.clear(); output_vec.clear();
const WorldPos corner_1 = center + radius; const WorldPos corner_1 = center + radius;
const WorldPos corner_2 = center - radius; const WorldPos corner_2 = center - radius;
@@ -149,58 +122,39 @@ public:
size_t y_min = static_cast<size_t>(std::floor(y_min_f)); size_t y_min = static_cast<size_t>(std::floor(y_min_f));
size_t y_max = static_cast<size_t>(std::ceil(y_max_f)); size_t y_max = static_cast<size_t>(std::ceil(y_max_f));
for (size_t x = x_min; x <= x_max; x++) for (size_t x = x_min; x <= x_max; x++) {
{ for (size_t y = y_min; y <= y_max; y++) {
for (size_t y = y_min; y <= y_max; y++) if (!CheckBounds(x, y)) {
{
if (!CheckBounds(x, y))
{
continue; continue;
} }
#if 0 for (auto item_wptr : m_Grid[x][y]) {
// TODO this is approx 2x faster, but inserts items outside of radius; if (auto shared = item_wptr.lock()) {
// We can use this is a Get(rectangle) function if (center.DistanceTo(shared->GetPosition()) < radius) {
std::ranges::copy(m_Grid[x][y], std::back_inserter(output_vec));
#else
for (auto item_wptr : m_Grid[x][y])
{
if (auto shared = item_wptr.lock())
{
if (center.DistanceTo(shared->GetPosition()) < radius)
{
output_vec.push_back(item_wptr); output_vec.push_back(item_wptr);
} }
} }
} }
#endif
} }
} }
} }
void UpdateAll() override void UpdateAll() override {
{ for (auto ptr : m_Items) {
for (auto ptr : m_Items)
{
// TODO is this efficient? Maybe use const ref?
Update(ptr); Update(ptr);
} }
} }
void Update(std::shared_ptr<T> item) override void Update(std::shared_ptr<T> item) override {
{
coord_type current_coords = GetCoords(item->GetPosition()); coord_type current_coords = GetCoords(item->GetPosition());
coord_type last_known_coords = m_ReverseGridLookup[item]; coord_type last_known_coords = m_ReverseGridLookup[item];
if (current_coords == last_known_coords) if (current_coords == last_known_coords) {
{
return; return;
} }
vector_wptr& vec = m_Grid[last_known_coords.x()][last_known_coords.y()]; vector_wptr &vec = m_Grid[last_known_coords.x()][last_known_coords.y()];
// remove the old weak ptr from the map // remove the old weak ptr from the map
vec.erase(std::remove_if(vec.begin(), vec.end(), vec.erase(std::remove_if(vec.begin(), vec.end(),
[&](const std::weak_ptr<T>& w) [&](const std::weak_ptr<T> &w) {
{ return !w.owner_before(item) &&
return !w.owner_before(item) && !item.owner_before(w); !item.owner_before(w);
}), }),
vec.end()); vec.end());
// add new weak ptr to the map // add new weak ptr to the map
@@ -208,30 +162,24 @@ public:
} }
private: private:
using coord_type = vec<size_t, 2>; using coord_type = vec<size_t, 2>;
using vector_wptr = std::vector<std::weak_ptr<T>>; using vector_wptr = std::vector<std::weak_ptr<T>>;
using grid_type = std::vector<std::vector<vector_wptr>>; using grid_type = std::vector<std::vector<vector_wptr>>;
coord_type GetCoords(const WorldPos &wp) coord_type GetCoords(const WorldPos &wp) {
{
auto coord_float = wp / m_GridStep.ChangeTag<WorldPos>(); auto coord_float = wp / m_GridStep.ChangeTag<WorldPos>();
return coord_type{ return coord_type{static_cast<size_t>(coord_float.x()),
static_cast<size_t>(coord_float.x()), static_cast<size_t>(coord_float.y())};
static_cast<size_t>(coord_float.y())
};
} }
bool CheckBounds(size_t x, size_t y) const bool CheckBounds(size_t x, size_t y) const {
{
bool x_in_bounds = x < m_Grid.size(); bool x_in_bounds = x < m_Grid.size();
bool y_in_bounds = y < m_Grid.size(); bool y_in_bounds = y < m_Grid.size();
return x_in_bounds && y_in_bounds; return x_in_bounds && y_in_bounds;
} }
bool CheckBounds(const WorldPos& pos) const bool CheckBounds(const WorldPos &pos) const {
{ auto [x, y] = pos;
auto [x,y] = pos;
bool x_in_bounds = 0.0f < x && x < m_GridSize.x(); bool x_in_bounds = 0.0f < x && x < m_GridSize.x();
bool y_in_bounds = 0.0f < y && y < m_GridSize.y(); bool y_in_bounds = 0.0f < y && y < m_GridSize.y();
return x_in_bounds && y_in_bounds; return x_in_bounds && y_in_bounds;
@@ -240,17 +188,16 @@ private:
WorldSize m_GridSize; WorldSize m_GridSize;
WorldSize m_GridStep; WorldSize m_GridStep;
size_t m_ChunksPerAxis; size_t m_ChunksPerAxis;
// TODO it would be better to have vector<T> - contiguous memory, more cache-friendly?
std::vector<std::shared_ptr<T>> m_Items; std::vector<std::shared_ptr<T>> m_Items;
grid_type m_Grid; grid_type m_Grid;
// normal lookup: WorldPos -> coord_type -> vector_wptr -> std::shared_ptr<T> // normal lookup: WorldPos -> coord_type -> vector_wptr -> std::shared_ptr<T>
// reverse lookup: std::shared_ptr<T> -> vector_wptr -> coord_type // reverse lookup: std::shared_ptr<T> -> vector_wptr -> coord_type
// we need the reverse lookup because T.GetPosition() may change and we need to delete // we need the reverse lookup because T.GetPosition() may change and we need
// the old weak_ptr from vector_wptr (without iterating through all of them). // to delete the old weak_ptr from vector_wptr (without iterating through all
// Also it might be useful to have T -> location lookup // of them). Also it might be useful to have T -> location lookup Note: hash
// Note: hash of std::shared_ptr<T> may give us trouble if we free the memory and new one points // of std::shared_ptr<T> may give us trouble if we free the memory and new one
// to the same location, maybe it would be better to hash the object itself? // points to the same location, maybe it would be better to hash the object
// TODO how about using counting bloom filter for this? // itself? NOTE how about using counting bloom filter for this?
std::unordered_map<std::shared_ptr<T>, coord_type> m_ReverseGridLookup; std::unordered_map<std::shared_ptr<T>, coord_type> m_ReverseGridLookup;
}; };

View File

@@ -117,8 +117,7 @@ const std::vector<UserAction> &UserInput::GetActions() {
} else if (mouse_events.contains(event.type)) { } else if (mouse_events.contains(event.type)) {
GetActions_mouse(event); GetActions_mouse(event);
} else { } else {
// TODO uncomment, for now too much noise LOG_WARNING("Action not processed");
// LOG_WARNING("Action not processed");
} }
} }
return m_Actions; return m_Actions;

View File

@@ -110,7 +110,7 @@ void Window::DrawCircle(const WindowPos &position, float radius, uint8_t R,
int cy = static_cast<int>(position.y()); int cy = static_cast<int>(position.y());
SDL_SetRenderDrawColor(m_Renderer.get(), R, G, B, 255); SDL_SetRenderDrawColor(m_Renderer.get(), R, G, B, 255);
for (int i = 0; i < 360; ++i) { for (int i = 0; i < 360; ++i) {
double a = i * M_PI / 180.0; double a = i * std::numbers::pi / 180.0;
SDL_RenderPoint(m_Renderer.get(), SDL_RenderPoint(m_Renderer.get(),
cx + static_cast<int>(std::round(radius * std::cos(a))), cx + static_cast<int>(std::round(radius * std::cos(a))),
cy + static_cast<int>(std::round(radius * std::sin(a)))); cy + static_cast<int>(std::round(radius * std::sin(a))));