Solved TODOs
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -28,7 +28,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO this should be replaced with a matrix
|
||||
float m_Zoom = 1.0f;
|
||||
WorldPos m_Pan;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,48 +17,36 @@ 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 ¢er,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -73,48 +61,35 @@ 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 ¢er,
|
||||
float radius) override {
|
||||
vector_wptr output_vec{};
|
||||
|
||||
Get(output_vec, center, radius);
|
||||
@@ -122,18 +97,16 @@ public:
|
||||
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>();
|
||||
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 ¢er,
|
||||
float radius) {
|
||||
output_vec.clear();
|
||||
const WorldPos corner_1 = 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_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)
|
||||
{
|
||||
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);
|
||||
[&](const std::weak_ptr<T> &w) {
|
||||
return !w.owner_before(item) &&
|
||||
!item.owner_before(w);
|
||||
}),
|
||||
vec.end());
|
||||
// add new weak ptr to the map
|
||||
@@ -208,30 +162,24 @@ public:
|
||||
}
|
||||
|
||||
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 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;
|
||||
@@ -240,17 +188,16 @@ private:
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))));
|
||||
|
||||
Reference in New Issue
Block a user