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

View File

@@ -28,7 +28,6 @@ public:
}
private:
// TODO this should be replaced with a matrix
float m_Zoom = 1.0f;
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() {
LOG_INFO("Running the game");
while (!m_Game->IsExitRequested()) {
m_Game->HandleActions(m_UserInput->GetActions());
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();
Draw();
m_Window->Flush();

View File

@@ -7,16 +7,11 @@
#include <functional>
#include <initializer_list>
#include <iostream>
#include <numbers>
#include <numeric>
#include <ranges>
#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>
requires std::floating_point<T>
static inline bool equalEpsilon(const T &a, const T &b) {
@@ -174,8 +169,6 @@ public:
vec &a = *this;
auto b = std::views::repeat(scalar);
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;
}

View File

@@ -23,7 +23,6 @@ PathFindingDemo::PathFindingDemo(int width, int height) : m_Map(width, height) {
PathFindingDemo::~PathFindingDemo() { LOG_DEBUG("."); }
void PathFindingDemo::AddEntity(std::shared_ptr<Entity> e) {
// TODO emplace_back
m_Entities.push_back(e);
}
@@ -71,11 +70,6 @@ void PathFindingDemo::CreateMap() {
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 {
@@ -121,8 +115,6 @@ void PathFindingDemo::UpdateWorld() {
entity->SetActualVelocity(velocity * tile_velocity_coeff);
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_B = std::get<1>(collision);
auto A = weak_A.lock();
@@ -136,8 +128,6 @@ void PathFindingDemo::UpdateWorld() {
// LOG_DEBUG("Collision: A is ", A, ", B is ", B);
auto AB = B->GetPosition() - A->GetPosition();
A->ZeroActualVelocityInDirection(AB);
// handle logic
// TODO
}
// update the position
@@ -211,7 +201,6 @@ void PathFindingDemo::DeselectEntities() {
void PathFindingDemo::SelectEntitiesInRectangle(WorldPos A, WorldPos B) {
DeselectEntities();
// TODO use colliders for this
auto [x_min, x_max] = std::minmax(A.x(), B.x());
auto [y_min, y_max] = std::minmax(A.y(), B.y());
for (const auto &entity : m_Entities) {

View File

@@ -1,14 +1,14 @@
#pragma once
#include <vector>
#include <memory>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iterator>
#include <memory>
#include <vector>
#include "math.hpp"
#include "log.hpp"
#include "math.hpp"
template <typename T>
concept HasPosition = requires(T t, WorldPos pos) {
@@ -17,54 +17,42 @@ concept HasPosition = requires(T t, WorldPos pos) {
};
template <typename T>
concept HasCollisions = requires(T t) {
t.Dummy(); // TODO
};
requires HasPosition<T>
template <typename T>
requires HasPosition<T>
class IPositionalContainer
{
class IPositionalContainer {
public:
virtual ~IPositionalContainer() = default;
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 Update(std::shared_ptr<T> item) = 0;
};
template <typename T>
class IColliderContainer : public IPositionalContainer<T>
{
class IColliderContainer : public IPositionalContainer<T> {
public:
virtual std::vector<std::weak_ptr<T>> GetCollisions() = 0;
};
template <typename T>
class SimpleContainer : IPositionalContainer<T>
{
template <typename T> class SimpleContainer : IPositionalContainer<T> {
public:
bool Add(std::shared_ptr<T> t) override
{
bool Add(std::shared_ptr<T> t) override {
m_Items.push_back(t);
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;
for (const auto& item : m_Items)
{
if (center.DistanceTo(item->GetPosition()) < radius)
{
for (const auto &item : m_Items) {
if (center.DistanceTo(item->GetPosition()) < radius) {
matched_items.push_back(item);
}
}
return matched_items;
}
// no update needed here, as we have no smart lookup scheme
void UpdateAll() override {}
void Update(std::shared_ptr<T>) override {}
@@ -73,70 +61,55 @@ private:
std::vector<std::shared_ptr<T>> m_Items;
};
template <class T>
class PositionalContainer : IPositionalContainer<T>
{
template <class T> class PositionalContainer : IPositionalContainer<T> {
public:
PositionalContainer(const WorldSize& size, size_t chunks) :
m_GridSize{size},
m_GridStep{size / chunks},
m_ChunksPerAxis{chunks}
{
PositionalContainer(const WorldSize &size, size_t chunks)
: m_GridSize{size}, m_GridStep{size / chunks}, m_ChunksPerAxis{chunks} {
LOG_INFO("Size: ", m_GridSize, " step: ", m_GridStep);
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);
for (size_t j = 0; j < chunks; j++)
{
for (size_t j = 0; j < chunks; j++) {
m_Grid[i][j].reserve(16);
}
}
}
// calling Add on object that is already in the container is UB
bool Add(std::shared_ptr<T> item) override
{
const auto& world_pos = item->GetPosition();
if (!CheckBounds(world_pos))
{
bool Add(std::shared_ptr<T> item) override {
const auto &world_pos = item->GetPosition();
if (!CheckBounds(world_pos)) {
return false;
}
m_Items.push_back(item);
auto coords = GetCoords(world_pos);
m_Grid[coords.x()][coords.y()].push_back(item);
m_ReverseGridLookup[item] = coords;
// TODO should we call Update instead?
//Update(item);
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{};
Get(output_vec, center, radius);
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 WorldPos center = corner + half_size.ChangeTag<WorldPos>();
const WorldPos center = corner + half_size.ChangeTag<WorldPos>();
float radius = half_size.x();
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();
const WorldPos corner_1 = center + radius;
const WorldPos corner_2 = center - radius;
const WorldPos corner_1 = center + radius;
const WorldPos corner_2 = center - radius;
const auto A = GetCoords(corner_1);
const auto B = GetCoords(corner_2);
@@ -149,108 +122,82 @@ public:
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));
for (size_t x = x_min; x <= x_max; x++)
{
for (size_t y = y_min; y <= y_max; y++)
{
if (!CheckBounds(x, y))
{
for (size_t x = x_min; x <= x_max; x++) {
for (size_t y = y_min; y <= y_max; y++) {
if (!CheckBounds(x, y)) {
continue;
}
#if 0
// TODO this is approx 2x faster, but inserts items outside of radius;
// We can use this is a Get(rectangle) function
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)
{
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);
}
}
}
#endif
}
}
}
void UpdateAll() override
{
for (auto ptr : m_Items)
{
// TODO is this efficient? Maybe use const ref?
void UpdateAll() override {
for (auto ptr : m_Items) {
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 last_known_coords = m_ReverseGridLookup[item];
if (current_coords == last_known_coords)
{
coord_type last_known_coords = m_ReverseGridLookup[item];
if (current_coords == last_known_coords) {
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
vec.erase(std::remove_if(vec.begin(), vec.end(),
[&](const std::weak_ptr<T>& w)
{
return !w.owner_before(item) && !item.owner_before(w);
}),
vec.end());
[&](const std::weak_ptr<T> &w) {
return !w.owner_before(item) &&
!item.owner_before(w);
}),
vec.end());
// add new weak ptr to the map
m_Grid[current_coords.x()][current_coords.y()].push_back(item);
}
private:
using coord_type = vec<size_t, 2>;
using vector_wptr = std::vector<std::weak_ptr<T>>;
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>();
return coord_type{
static_cast<size_t>(coord_float.x()),
static_cast<size_t>(coord_float.y())
};
return coord_type{static_cast<size_t>(coord_float.x()),
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 y_in_bounds = y < m_Grid.size();
return x_in_bounds && y_in_bounds;
}
bool CheckBounds(const WorldPos& pos) const
{
auto [x,y] = pos;
bool x_in_bounds = 0.0f < x && x < m_GridSize.x();
bool y_in_bounds = 0.0f < y && y < m_GridSize.y();
bool CheckBounds(const WorldPos &pos) const {
auto [x, y] = pos;
bool x_in_bounds = 0.0f < x && x < m_GridSize.x();
bool y_in_bounds = 0.0f < y && y < m_GridSize.y();
return x_in_bounds && y_in_bounds;
}
WorldSize m_GridSize;
WorldSize m_GridStep;
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;
grid_type m_Grid;
// normal lookup: WorldPos -> coord_type -> vector_wptr -> std::shared_ptr<T>
// 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
// the old weak_ptr from vector_wptr (without iterating through all of them).
// Also it might be useful to have T -> location lookup
// Note: hash of std::shared_ptr<T> may give us trouble if we free the memory and new one points
// to the same location, maybe it would be better to hash the object itself?
// TODO how about using counting bloom filter for this?
// we need the reverse lookup because T.GetPosition() may change and we need
// to delete the old weak_ptr from vector_wptr (without iterating through all
// of them). Also it might be useful to have T -> location lookup Note: hash
// of std::shared_ptr<T> may give us trouble if we free the memory and new one
// points to the same location, maybe it would be better to hash the object
// itself? NOTE how about using counting bloom filter for this?
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)) {
GetActions_mouse(event);
} else {
// TODO uncomment, for now too much noise
// LOG_WARNING("Action not processed");
LOG_WARNING("Action not processed");
}
}
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());
SDL_SetRenderDrawColor(m_Renderer.get(), R, G, B, 255);
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(),
cx + static_cast<int>(std::round(radius * std::cos(a))),
cy + static_cast<int>(std::round(radius * std::sin(a))));