feat: added viewport transformation
parent
e84bdf5a48
commit
6610b847b8
|
@ -31,6 +31,7 @@ void AssetDB::load(std::string asset_name) {
|
||||||
void AssetDB::index_assets() {
|
void AssetDB::index_assets() {
|
||||||
SDL_Log("Indexing assets");
|
SDL_Log("Indexing assets");
|
||||||
for(std::filesystem::directory_entry const &itr : std::filesystem::recursive_directory_iterator("resources")) {
|
for(std::filesystem::directory_entry const &itr : std::filesystem::recursive_directory_iterator("resources")) {
|
||||||
|
SDL_Log("Indexing %s", itr.path().c_str());
|
||||||
if(itr.is_directory())
|
if(itr.is_directory())
|
||||||
continue;
|
continue;
|
||||||
else if(!itr.path().has_extension())
|
else if(!itr.path().has_extension())
|
||||||
|
|
|
@ -99,6 +99,10 @@ AssetDB &CanvasEngine::get_assets() {
|
||||||
return this->assets;
|
return this->assets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CollisionWorld &CanvasEngine::get_collision_world() {
|
||||||
|
return this->collision_world;
|
||||||
|
}
|
||||||
|
|
||||||
InputMap &CanvasEngine::get_input_map() {
|
InputMap &CanvasEngine::get_input_map() {
|
||||||
return this->input_map;
|
return this->input_map;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +136,7 @@ void CanvasEngine::process_event(SDL_Event const &evt) {
|
||||||
|
|
||||||
void CanvasEngine::tick(double delta_time) {
|
void CanvasEngine::tick(double delta_time) {
|
||||||
this->level->propagate_tick(delta_time);
|
this->level->propagate_tick(delta_time);
|
||||||
this->physics_world.check_collisions();
|
this->collision_world.check_collisions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasEngine::draw(SDL_Renderer *render) {
|
void CanvasEngine::draw(SDL_Renderer *render) {
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
#ifndef CANVAS_ENGINE_HPP
|
#ifndef CANVAS_ENGINE_HPP
|
||||||
#define CANVAS_ENGINE_HPP
|
#define CANVAS_ENGINE_HPP
|
||||||
|
|
||||||
|
#include "assets/asset_db.hpp"
|
||||||
#include "core/collision.hpp"
|
#include "core/collision.hpp"
|
||||||
#include "core/level.hpp"
|
#include "core/level.hpp"
|
||||||
#include "node.hpp"
|
|
||||||
#include "assets/asset_db.hpp"
|
|
||||||
#include "input/input_map.hpp"
|
#include "input/input_map.hpp"
|
||||||
#include <SDL2/SDL_stdinc.h>
|
#include "node.hpp"
|
||||||
#include <ctime>
|
|
||||||
#include <cassert>
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <SDL2/SDL_events.h>
|
#include <SDL2/SDL_events.h>
|
||||||
#include <SDL2/SDL_render.h>
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <SDL2/SDL_stdinc.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
|
@ -22,7 +22,7 @@ private:
|
||||||
SDL_Renderer *render{nullptr}; //!< primary application window's renderer
|
SDL_Renderer *render{nullptr}; //!< primary application window's renderer
|
||||||
SDL_Texture *render_target{nullptr};
|
SDL_Texture *render_target{nullptr};
|
||||||
AssetDB assets{};
|
AssetDB assets{};
|
||||||
CollisionWorld physics_world{};
|
CollisionWorld collision_world{};
|
||||||
InputMap input_map{}; //!< map of inputs to input callback objects
|
InputMap input_map{}; //!< map of inputs to input callback objects
|
||||||
std::unique_ptr<Level> level;
|
std::unique_ptr<Level> level;
|
||||||
Uint64 last_frame_start_time{}; //!< time at start of last frame
|
Uint64 last_frame_start_time{}; //!< time at start of last frame
|
||||||
|
@ -42,6 +42,7 @@ public:
|
||||||
void request_close();
|
void request_close();
|
||||||
void set_target_delta_time(double target);
|
void set_target_delta_time(double target);
|
||||||
AssetDB &get_assets();
|
AssetDB &get_assets();
|
||||||
|
CollisionWorld &get_collision_world();
|
||||||
InputMap &get_input_map();
|
InputMap &get_input_map();
|
||||||
SDL_Renderer *get_render();
|
SDL_Renderer *get_render();
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
#include "collision_shape.hpp"
|
#include "collision_shape.hpp"
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
|
CollidableNode::CollidableNode(std::string const &name, CollisionMask layers, CollisionMask mask)
|
||||||
|
: ce::Node2D(name) {
|
||||||
|
this->layers = layers;
|
||||||
|
this->mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
void CollidableNode::_tick(double const &delta) {
|
void CollidableNode::_tick(double const &delta) {
|
||||||
// find nodes that were overlapping last frame but not this frame
|
// find nodes that were overlapping last frame but not this frame
|
||||||
std::map<CollidableNode*, unsigned>::iterator iterator{this->overlapped_nodes.begin()};
|
std::map<CollidableNode*, unsigned>::iterator iterator{this->overlapped_nodes.begin()};
|
||||||
|
@ -23,4 +29,20 @@ void CollidableNode::add_overlap(CollisionShape *shape, CollisionShape *other_sh
|
||||||
this->shape_overlaps.insert(other_shape);
|
this->shape_overlaps.insert(other_shape);
|
||||||
++this->overlapped_nodes[other_shape->get_owner()];
|
++this->overlapped_nodes[other_shape->get_owner()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollidableNode::set_mask(CollisionMask mask) {
|
||||||
|
this->mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionMask CollidableNode::get_mask() const {
|
||||||
|
return this->mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollidableNode::set_layers(CollisionMask layers) {
|
||||||
|
this->layers = layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
CollisionMask CollidableNode::get_layers() const {
|
||||||
|
return this->layers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,27 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
|
typedef uint64_t CollisionMask;
|
||||||
class CollisionShape;
|
class CollisionShape;
|
||||||
|
|
||||||
class CollidableNode : public Node2D {
|
class CollidableNode : public Node2D {
|
||||||
std::set<CollisionShape*> shape_overlaps{};
|
std::set<CollisionShape*> shape_overlaps{};
|
||||||
std::map<CollidableNode*, unsigned> overlapped_nodes{};
|
std::map<CollidableNode*, unsigned> overlapped_nodes{};
|
||||||
|
CollisionMask mask{~0x0u /* all layers by default */};
|
||||||
|
CollisionMask layers{0x1u /* only the first layer is enabled by default */};
|
||||||
public:
|
public:
|
||||||
// collision(local_shape, other_node, other_shape)
|
// collision(local_shape, other_node, other_shape)
|
||||||
Signal<CollisionShape*, CollidableNode*, CollisionShape*> overlap_enter{};
|
Signal<CollisionShape*, CollidableNode*, CollisionShape*> overlap_enter{};
|
||||||
Signal<CollidableNode*> overlap_exit{};
|
Signal<CollidableNode*> overlap_exit{};
|
||||||
public:
|
public:
|
||||||
|
CollidableNode(std::string const &name, CollisionMask layers, CollisionMask mask);
|
||||||
virtual void _tick(double const &delta) override;
|
virtual void _tick(double const &delta) override;
|
||||||
void add_overlap(CollisionShape *shape, CollisionShape *other);
|
void add_overlap(CollisionShape *shape, CollisionShape *other);
|
||||||
|
|
||||||
|
void set_mask(CollisionMask mask);
|
||||||
|
CollisionMask get_mask() const;
|
||||||
|
void set_layers(CollisionMask layers);
|
||||||
|
CollisionMask get_layers() const;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "collision_shape.hpp"
|
#include "collision_shape.hpp"
|
||||||
#include "collidable_node.hpp"
|
#include "collidable_node.hpp"
|
||||||
|
#include "core/canvas_engine.hpp"
|
||||||
#include <SDL2/SDL_log.h>
|
#include <SDL2/SDL_log.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -24,14 +25,17 @@ Shape Shape::make_box(float h_extent, float v_extent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
CollisionShape::CollisionShape(std::string const &name, Node *owner, Shape shape)
|
CollisionShape::CollisionShape(std::string const &name, Node *owner, Shape shape)
|
||||||
: Node2D(name) {
|
: Node2D(name)
|
||||||
|
, world{CanvasEngine::get_singleton()->get_collision_world()} {
|
||||||
this->shape = shape;
|
this->shape = shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionShape::_added(Node *parent) {
|
void CollisionShape::_added() {
|
||||||
|
Node *parent{this->get_parent()};
|
||||||
while(parent != nullptr) {
|
while(parent != nullptr) {
|
||||||
if(CollidableNode *as_collidable{dynamic_cast<CollidableNode*>(parent)}) {
|
if(CollidableNode *as_collidable{dynamic_cast<CollidableNode*>(parent)}) {
|
||||||
this->owner = as_collidable;
|
this->owner = as_collidable;
|
||||||
|
this->register_with_world();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,13 +43,20 @@ void CollisionShape::_added(Node *parent) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
abort();
|
abort();
|
||||||
#endif
|
#endif
|
||||||
|
if(this->is_registered)
|
||||||
|
this->deregister_with_world();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollisionShape::_removed() {
|
||||||
|
if(this->is_registered)
|
||||||
|
this->deregister_with_world();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollisionShape::can_collide(CollisionShape const *lhs, CollisionShape const *rhs) {
|
bool CollisionShape::can_collide(CollisionShape const *lhs, CollisionShape const *rhs) {
|
||||||
return lhs->owner != nullptr && rhs->owner != nullptr
|
return lhs->owner != nullptr && rhs->owner != nullptr
|
||||||
&& lhs->owner != rhs->owner
|
&& lhs->owner != rhs->owner
|
||||||
&& ((lhs->mask & rhs->layers) != 0x0u
|
&& ((lhs->get_mask() & rhs->get_layers()) != 0x0u
|
||||||
|| (rhs->mask & lhs->layers) != 0x0u);
|
|| (rhs->get_mask() & lhs->get_layers()) != 0x0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CollisionShape::shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs) {
|
bool CollisionShape::shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs) {
|
||||||
|
@ -110,19 +121,21 @@ Shape const &CollisionShape::get_shape() const {
|
||||||
return this->shape;
|
return this->shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionShape::set_mask(CollisionMask mask) {
|
CollisionMask CollisionShape::get_layers() const {
|
||||||
this->mask = mask;
|
return this->owner ? this->owner->get_layers() : 0x0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
CollisionMask CollisionShape::get_mask() const {
|
CollisionMask CollisionShape::get_mask() const {
|
||||||
return this->mask;
|
return this->owner ? this->owner->get_mask() : 0x0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollisionShape::set_layers(CollisionMask layers) {
|
void CollisionShape::register_with_world() {
|
||||||
this->layers = layers;
|
this->world.add_collision_shape(this);
|
||||||
|
this->is_registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CollisionMask CollisionShape::get_layers() const {
|
void CollisionShape::deregister_with_world() {
|
||||||
return this->layers;
|
this->world.remove_collision_shape(this);
|
||||||
|
this->is_registered = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
#ifndef COLLISION_SHAPE_HPP
|
#ifndef COLLISION_SHAPE_HPP
|
||||||
#define COLLISION_SHAPE_HPP
|
#define COLLISION_SHAPE_HPP
|
||||||
|
|
||||||
#include "core/math/transform.hpp"
|
#include "collidable_node.hpp"
|
||||||
|
#include "math/transform.hpp"
|
||||||
#include "node2d.hpp"
|
#include "node2d.hpp"
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
class CollidableNode;
|
|
||||||
class CollisionWorld;
|
class CollisionWorld;
|
||||||
typedef uint64_t CollisionMask;
|
|
||||||
|
|
||||||
struct ShapeCircle { float radius; };
|
struct ShapeCircle { float radius; };
|
||||||
struct ShapeAABB { float h_extent, v_extent; };
|
struct ShapeAABB { float h_extent, v_extent; };
|
||||||
|
@ -27,13 +26,13 @@ struct Shape {
|
||||||
class CollisionShape : public Node2D {
|
class CollisionShape : public Node2D {
|
||||||
CollidableNode *owner{nullptr};
|
CollidableNode *owner{nullptr};
|
||||||
Shape shape{.shape=Shape::CIRCLE, .circle={.radius=1.f}};
|
Shape shape{.shape=Shape::CIRCLE, .circle={.radius=1.f}};
|
||||||
CollisionWorld *world{nullptr};
|
CollisionWorld &world;
|
||||||
CollisionMask mask{~0x0u /* all layers by default */};
|
bool is_registered{false};
|
||||||
CollisionMask layers{0x1u /* only the first layer is enabled by default */};
|
|
||||||
public:
|
public:
|
||||||
CollisionShape(std::string const &name, Node *owner, Shape shape);
|
CollisionShape(std::string const &name, Node *owner, Shape shape);
|
||||||
|
|
||||||
virtual void _added(Node *parent) override;
|
virtual void _added() override;
|
||||||
|
virtual void _removed() override;
|
||||||
|
|
||||||
static bool can_collide(CollisionShape const *lhs, CollisionShape const *rhs);
|
static bool can_collide(CollisionShape const *lhs, CollisionShape const *rhs);
|
||||||
static bool shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs);
|
static bool shapes_overlap(CollisionShape const *lhs, CollisionShape const *rhs);
|
||||||
|
@ -44,10 +43,11 @@ public:
|
||||||
CollidableNode *get_owner() const;
|
CollidableNode *get_owner() const;
|
||||||
void set_shape(Shape const &shape);
|
void set_shape(Shape const &shape);
|
||||||
Shape const &get_shape() const;
|
Shape const &get_shape() const;
|
||||||
void set_mask(CollisionMask mask);
|
|
||||||
CollisionMask get_mask() const;
|
|
||||||
void set_layers(CollisionMask layers);
|
|
||||||
CollisionMask get_layers() const;
|
CollisionMask get_layers() const;
|
||||||
|
CollisionMask get_mask() const;
|
||||||
|
private:
|
||||||
|
void register_with_world();
|
||||||
|
void deregister_with_world();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,32 @@
|
||||||
#include "level.hpp"
|
#include "level.hpp"
|
||||||
|
#include "core/math/transform.hpp"
|
||||||
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
|
void Level::deinstantiate() {
|
||||||
|
this->root.reset();
|
||||||
|
}
|
||||||
|
|
||||||
void Level::propagate_tick(double const &delta_time) {
|
void Level::propagate_tick(double const &delta_time) {
|
||||||
this->root->propagate_tick(delta_time);
|
this->root->propagate_tick(delta_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Level::propagate_draw(SDL_Renderer *render) {
|
void Level::propagate_draw(SDL_Renderer *render) {
|
||||||
this->root->propagate_draw(render);
|
int w, h;
|
||||||
|
SDL_Window *window{SDL_RenderGetWindow(render)};
|
||||||
|
SDL_GetWindowSize(window, &w, &h);
|
||||||
|
ce::Transform const screen_transform{
|
||||||
|
.position = ce::Vecf::ZERO,
|
||||||
|
.rotation = 0.f,
|
||||||
|
.scale = ce::Vecf::ONE * float(w),
|
||||||
|
};
|
||||||
|
float const ratio{float(h)/float(w)};
|
||||||
|
this->root->propagate_draw(render, Transform().translated({
|
||||||
|
0.5f / this->view_transform.scale.x,
|
||||||
|
0.5f / this->view_transform.scale.y * ratio
|
||||||
|
}) * this->view_transform * screen_transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node *Level::get_root() {
|
Node *Level::get_root() {
|
||||||
|
|
|
@ -7,14 +7,22 @@ namespace ce {
|
||||||
class Level {
|
class Level {
|
||||||
protected:
|
protected:
|
||||||
Node::OwnedPtr root{nullptr};
|
Node::OwnedPtr root{nullptr};
|
||||||
|
ce::Transform view_transform{
|
||||||
|
.position = ce::Vecf::ZERO,
|
||||||
|
.rotation = 0.f,
|
||||||
|
.scale = {1.f/10.f, 1.f/10.f},
|
||||||
|
};
|
||||||
public:
|
public:
|
||||||
virtual ~Level() = default;
|
virtual ~Level() = default;
|
||||||
virtual void instantiate() = 0;
|
virtual void instantiate() = 0;
|
||||||
|
void deinstantiate();
|
||||||
void propagate_tick(double const &delta_time);
|
void propagate_tick(double const &delta_time);
|
||||||
void propagate_draw(SDL_Renderer *render);
|
void propagate_draw(SDL_Renderer *render);
|
||||||
|
|
||||||
Node *get_root();
|
Node *get_root();
|
||||||
bool is_instantiated() const;
|
bool is_instantiated() const;
|
||||||
|
void set_view_transform(ce::Transform transform);
|
||||||
|
ce::Transform get_view_transform() const;
|
||||||
protected:
|
protected:
|
||||||
Node::OwnedPtr &get_owned_root();
|
Node::OwnedPtr &get_owned_root();
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,24 @@ void Transform::scale_by(Vecf const &factors) {
|
||||||
this->scale.scale(factors);
|
this->scale.scale(factors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transform Transform::translated(Vecf const &translation) const {
|
||||||
|
Transform out{*this};
|
||||||
|
out.translate_by(translation);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::rotated(float rotation) const {
|
||||||
|
Transform out{*this};
|
||||||
|
out.rotate_by(rotation);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform Transform::scaled(Vecf const &factors) const {
|
||||||
|
Transform out{*this};
|
||||||
|
out.scale_by(factors);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
Vecf Transform::right() const {
|
Vecf Transform::right() const {
|
||||||
return Vecf::RIGHT.rotated(this->rotation);
|
return Vecf::RIGHT.rotated(this->rotation);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +42,7 @@ Vecf Transform::up() const {
|
||||||
|
|
||||||
Transform operator *(Transform const &lhs, Transform const &rhs) {
|
Transform operator *(Transform const &lhs, Transform const &rhs) {
|
||||||
Transform t{
|
Transform t{
|
||||||
.position = rhs.position + lhs.position.rotated(rhs.rotation),
|
.position = rhs.position + lhs.position.rotated(rhs.rotation).scaled(rhs.scale),
|
||||||
.rotation = rhs.rotation + lhs.rotation,
|
.rotation = rhs.rotation + lhs.rotation,
|
||||||
.scale = lhs.scale.scaled(rhs.scale)
|
.scale = lhs.scale.scaled(rhs.scale)
|
||||||
};
|
};
|
||||||
|
@ -33,9 +51,7 @@ Transform operator *(Transform const &lhs, Transform const &rhs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform &operator *=(Transform &lhs, Transform const &rhs) {
|
Transform &operator *=(Transform &lhs, Transform const &rhs) {
|
||||||
lhs.position += lhs.position.rotated(rhs.rotation);
|
lhs = lhs * rhs;
|
||||||
lhs.rotation += rhs.rotation;
|
|
||||||
lhs.scale.scale(rhs.scale);
|
|
||||||
assert(lhs.scale.x != 0.f || lhs.scale.y != 0.f); // !!!
|
assert(lhs.scale.x != 0.f || lhs.scale.y != 0.f); // !!!
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,13 @@ struct Transform {
|
||||||
Vecf position{0.f, 0.f};
|
Vecf position{0.f, 0.f};
|
||||||
double rotation{0.0};
|
double rotation{0.0};
|
||||||
Vecf scale{1.f, 1.f};
|
Vecf scale{1.f, 1.f};
|
||||||
|
|
||||||
void translate_by(Vecf const &translation);
|
void translate_by(Vecf const &translation);
|
||||||
void rotate_by(double rotation);
|
void rotate_by(double rotation);
|
||||||
void scale_by(Vecf const &factors);
|
void scale_by(Vecf const &factors);
|
||||||
|
Transform translated(Vecf const &translation) const;
|
||||||
|
Transform rotated(float rotation) const;
|
||||||
|
Transform scaled(Vecf const &factors) const;
|
||||||
|
|
||||||
Vecf right() const;
|
Vecf right() const;
|
||||||
Vecf up() const;
|
Vecf up() const;
|
||||||
|
|
|
@ -80,9 +80,12 @@ void Vecf::scale(Vecf const &factors) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Vecf Vecf::scaled(Vecf const &factors) const {
|
Vecf Vecf::scaled(Vecf const &factors) const {
|
||||||
return Vecf(this->x * factors.x, this->y * factors.y);
|
return Vecf{this->x * factors.x, this->y * factors.y};
|
||||||
}
|
}
|
||||||
|
|
||||||
Vecf const Vecf::RIGHT{1.f, 0.f};
|
Vecf const Vecf::RIGHT{1.f, 0.f};
|
||||||
Vecf const Vecf::UP{0.f, 1.f};
|
Vecf const Vecf::UP{0.f, 1.f};
|
||||||
|
Vecf const Vecf::ONE{1.f, 1.f};
|
||||||
|
Vecf const Vecf::ZERO{0.f, 0.f};
|
||||||
|
Vecf const Vecf::POSITIVE_INFINITY{INFINITY, INFINITY};
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,9 @@ struct Vecf {
|
||||||
|
|
||||||
static Vecf const RIGHT;
|
static Vecf const RIGHT;
|
||||||
static Vecf const UP;
|
static Vecf const UP;
|
||||||
|
static Vecf const ONE;
|
||||||
|
static Vecf const POSITIVE_INFINITY;
|
||||||
|
static Vecf const ZERO;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "node.hpp"
|
#include "node.hpp"
|
||||||
#include <SDL2/SDL_assert.h>
|
#include <SDL2/SDL_assert.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ void Node::add_child(Node::OwnedPtr &child) {
|
||||||
Node *added{this->children.insert({child->get_name(), std::move(child)}).first->second.get()};
|
Node *added{this->children.insert({child->get_name(), std::move(child)}).first->second.get()};
|
||||||
added->parent = this;
|
added->parent = this;
|
||||||
this->child_added.invoke(added);
|
this->child_added.invoke(added);
|
||||||
added->_added(this);
|
added->propagate_added();
|
||||||
}
|
}
|
||||||
// specialize to skip dynamic_cast
|
// specialize to skip dynamic_cast
|
||||||
template <> Node *Node::get_child<Node>(std::string const &name) {
|
template <> Node *Node::get_child<Node>(std::string const &name) {
|
||||||
|
@ -68,6 +69,19 @@ bool Node::is_ticking() const {
|
||||||
return this->tick;
|
return this->tick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ce::Level *Node::get_level() const {
|
||||||
|
return this->level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Node::set_level(ce::Level *level) {
|
||||||
|
// parent level needs to match
|
||||||
|
assert(this->parent == nullptr || this->parent->get_level() == level);
|
||||||
|
this->level = level;
|
||||||
|
for(std::pair<std::string const, std::unique_ptr<ce::Node>> &pair : this->children) {
|
||||||
|
pair.second->set_level(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Node::OwnedPtr> Node::remove_child(Node *node) {
|
std::optional<Node::OwnedPtr> Node::remove_child(Node *node) {
|
||||||
if(this != node->get_parent())
|
if(this != node->get_parent())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -84,7 +98,7 @@ std::optional<Node::OwnedPtr> Node::remove_child(Node *node) {
|
||||||
Node::OwnedPtr owned{std::move(child->second)};
|
Node::OwnedPtr owned{std::move(child->second)};
|
||||||
this->children.erase(child);
|
this->children.erase(child);
|
||||||
// notify both former child and any listeners that the child has been removed
|
// notify both former child and any listeners that the child has been removed
|
||||||
owned->_removed();
|
owned->propagate_removed();
|
||||||
this->child_removed.invoke(child->second.get());
|
this->child_removed.invoke(child->second.get());
|
||||||
// return optional containing owned pointer to former child
|
// return optional containing owned pointer to former child
|
||||||
return std::optional(std::move(owned));
|
return std::optional(std::move(owned));
|
||||||
|
@ -117,18 +131,24 @@ void Node::propagate_post_tick() {
|
||||||
pair.second->propagate_post_tick();
|
pair.second->propagate_post_tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Node::propagate_added() {
|
||||||
|
this->_added();
|
||||||
|
for(std::pair<std::string const, Node::OwnedPtr> &pair : this->children)
|
||||||
|
pair.second->propagate_added();
|
||||||
|
}
|
||||||
|
|
||||||
void Node::propagate_removed() {
|
void Node::propagate_removed() {
|
||||||
for(std::pair<std::string const, Node::OwnedPtr> &pair : this->children)
|
for(std::pair<std::string const, Node::OwnedPtr> &pair : this->children)
|
||||||
pair.second->propagate_removed();
|
pair.second->propagate_removed();
|
||||||
this->_removed();
|
this->_removed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node::propagate_draw(SDL_Renderer *render) {
|
void Node::propagate_draw(SDL_Renderer *render, ce::Transform const &view_transform) {
|
||||||
this->_update_transform();
|
this->_update_transform();
|
||||||
if(this->visible) {
|
if(this->visible) {
|
||||||
this->_draw(render);
|
this->_draw(render, view_transform);
|
||||||
for(std::pair<std::string const, Node::OwnedPtr> &pair : this->children)
|
for(std::pair<std::string const, Node::OwnedPtr> &pair : this->children)
|
||||||
pair.second->propagate_draw(render);
|
pair.second->propagate_draw(render, view_transform);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
#ifndef CANVAS_NODE_HPP
|
#ifndef CANVAS_NODE_HPP
|
||||||
#define CANVAS_NODE_HPP
|
#define CANVAS_NODE_HPP
|
||||||
|
|
||||||
|
#include "core/math/transform.hpp"
|
||||||
#include "core/signal.hpp"
|
#include "core/signal.hpp"
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
struct SDL_Renderer;
|
struct SDL_Renderer;
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
|
class Level;
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
public:
|
public:
|
||||||
friend class Level;
|
friend class Level;
|
||||||
|
@ -22,6 +25,7 @@ private:
|
||||||
ChildrenMap children{};
|
ChildrenMap children{};
|
||||||
bool visible{true};
|
bool visible{true};
|
||||||
bool tick{true};
|
bool tick{true};
|
||||||
|
ce::Level *level{nullptr};
|
||||||
public:
|
public:
|
||||||
Signal<> destroyed{}; //!< Signal invoked by the destructor
|
Signal<> destroyed{}; //!< Signal invoked by the destructor
|
||||||
Signal<Node*> child_removed{}; //!< Signal invoked when a child is removed.
|
Signal<Node*> child_removed{}; //!< Signal invoked when a child is removed.
|
||||||
|
@ -30,11 +34,11 @@ public:
|
||||||
Node(std::string name);
|
Node(std::string name);
|
||||||
virtual ~Node();
|
virtual ~Node();
|
||||||
protected:
|
protected:
|
||||||
virtual void _added(Node *parent [[maybe_unused]]) {} //!< called the moment after the object is added as a child to another node
|
virtual void _added() {} //!< called the moment after the object is added as a child to another node
|
||||||
virtual void _first_tick() {} //!< called the first frame this object is active
|
virtual void _first_tick() {} //!< called the first frame this object is active
|
||||||
virtual void _tick(double const &delta_time [[maybe_unused]]) {} //!< called every frame
|
virtual void _tick(double const &delta_time [[maybe_unused]]) {} //!< called every frame
|
||||||
virtual void _removed() {} //!< called the moment before the object is removed as a child to another node
|
virtual void _removed() {} //!< called the moment before the object is removed as a child to another node
|
||||||
virtual void _draw(SDL_Renderer *render [[maybe_unused]]) {}
|
virtual void _draw(SDL_Renderer *render [[maybe_unused]], ce::Transform const &view_transform [[maybe_unused]]) {}
|
||||||
virtual void _update_transform() {}
|
virtual void _update_transform() {}
|
||||||
public:
|
public:
|
||||||
template <class TNode> TNode *get_child(std::string const &name); //!< get a non-owning pointer to a child
|
template <class TNode> TNode *get_child(std::string const &name); //!< get a non-owning pointer to a child
|
||||||
|
@ -50,18 +54,22 @@ public:
|
||||||
bool is_visible() const;
|
bool is_visible() const;
|
||||||
void set_tick(bool value);
|
void set_tick(bool value);
|
||||||
bool is_ticking() const;
|
bool is_ticking() const;
|
||||||
|
ce::Level *get_level() const;
|
||||||
private:
|
private:
|
||||||
|
void set_level(ce::Level *level);
|
||||||
std::optional<Node::OwnedPtr> remove_child(Node *child); //!< remove a child, the caller now owns the pointer
|
std::optional<Node::OwnedPtr> remove_child(Node *child); //!< remove a child, the caller now owns the pointer
|
||||||
void propagate_tick(double const &delta_time);
|
void propagate_tick(double const &delta_time);
|
||||||
void propagate_post_tick();
|
void propagate_post_tick();
|
||||||
|
void propagate_added();
|
||||||
void propagate_removed();
|
void propagate_removed();
|
||||||
void propagate_draw(SDL_Renderer *render);
|
void propagate_draw(SDL_Renderer *render, ce::Transform const &view_transform);
|
||||||
bool rename_child(std::string const &old_name, std::string const &new_name);
|
bool rename_child(std::string const &old_name, std::string const &new_name);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class TNode> TNode *Node::get_child(std::string const &name) {
|
template <class TNode> TNode *Node::get_child(std::string const &name) {
|
||||||
return children.contains(name) ? dynamic_cast<TNode*>(children.at(name).get()) : nullptr;
|
return children.contains(name) ? dynamic_cast<TNode*>(children.at(name).get()) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TNode, typename... Args> TNode *Node::create_child(Args... cargs) {
|
template <class TNode, typename... Args> TNode *Node::create_child(Args... cargs) {
|
||||||
OwnedPtr owned{std::make_unique<TNode>(cargs...)};
|
OwnedPtr owned{std::make_unique<TNode>(cargs...)};
|
||||||
TNode *referenced{dynamic_cast<TNode*>(owned.get())};
|
TNode *referenced{dynamic_cast<TNode*>(owned.get())};
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
namespace ce {
|
namespace ce {
|
||||||
Node2D::Node2D(std::string name) : Node(name) {}
|
Node2D::Node2D(std::string name) : Node(name) {}
|
||||||
|
|
||||||
void Node2D::_added(Node *parent) {
|
void Node2D::_added() {
|
||||||
this->parent_node2d = dynamic_cast<Node2D*>(parent);
|
this->parent_node2d = dynamic_cast<Node2D*>(this->get_parent());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Node2D::_update_transform() {
|
void Node2D::_update_transform() {
|
||||||
|
|
|
@ -11,7 +11,7 @@ class Node2D : public Node {
|
||||||
Node2D *parent_node2d{nullptr};
|
Node2D *parent_node2d{nullptr};
|
||||||
public:
|
public:
|
||||||
Node2D(std::string name);
|
Node2D(std::string name);
|
||||||
virtual void _added(Node *parent) override;
|
virtual void _added() override;
|
||||||
Node2D *get_parent_node2d() const;
|
Node2D *get_parent_node2d() const;
|
||||||
virtual void _update_transform() override;
|
virtual void _update_transform() override;
|
||||||
void set_transform(Transform const &transform);
|
void set_transform(Transform const &transform);
|
||||||
|
|
|
@ -2,16 +2,18 @@
|
||||||
#include "core/canvas_engine.hpp"
|
#include "core/canvas_engine.hpp"
|
||||||
#include "core/math/transform.hpp"
|
#include "core/math/transform.hpp"
|
||||||
#include <SDL2/SDL_render.h>
|
#include <SDL2/SDL_render.h>
|
||||||
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace ce {
|
namespace ce {
|
||||||
Sprite::Sprite(std::string name, std::string texture)
|
Sprite::Sprite(std::string name, std::string texture)
|
||||||
: Node2D(name) {
|
: Node2D(name) {
|
||||||
std::optional<std::shared_ptr<Texture>> asset = CanvasEngine::get_singleton()->get_assets().get_asset<Texture>(texture);
|
std::optional<std::shared_ptr<Texture>> asset{CanvasEngine::get_singleton()->get_assets().get_asset<Texture>(texture)};
|
||||||
|
if(asset.has_value())
|
||||||
this->texture = asset.value();
|
this->texture = asset.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sprite::_draw(SDL_Renderer *render) {
|
void Sprite::_draw(SDL_Renderer *render, ce::Transform const &view_transform) {
|
||||||
if(this->texture == nullptr) {
|
if(this->texture == nullptr) {
|
||||||
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "No texture assigned");
|
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "No texture assigned");
|
||||||
this->set_visible(false);
|
this->set_visible(false);
|
||||||
|
@ -19,9 +21,10 @@ void Sprite::_draw(SDL_Renderer *render) {
|
||||||
}
|
}
|
||||||
int w, h;
|
int w, h;
|
||||||
SDL_QueryTexture(this->texture->get(), NULL, NULL, &w, &h);
|
SDL_QueryTexture(this->texture->get(), NULL, NULL, &w, &h);
|
||||||
Transform transform{this->get_global_transform()};
|
Transform transform{this->get_global_transform() * view_transform};
|
||||||
assert(transform.scale.x != 0 && transform.scale.y != 0); // !!!
|
assert(transform.scale.x != 0 && transform.scale.y != 0); // !!!
|
||||||
float fw{w * transform.scale.x}, fh{h * transform.scale.y};
|
float fw{transform.scale.x * (w > h ? float(w) / float(h) : 1.f)};
|
||||||
|
float fh{transform.scale.y * (w < h ? float(h) / float(w) : 1.f)};
|
||||||
assert(fw != 0.f && fh != 0.f);
|
assert(fw != 0.f && fh != 0.f);
|
||||||
//float fw(w), fh(h);
|
//float fw(w), fh(h);
|
||||||
SDL_Rect src{0, 0, w, h};
|
SDL_Rect src{0, 0, w, h};
|
||||||
|
|
|
@ -9,7 +9,7 @@ class Sprite : public Node2D {
|
||||||
std::shared_ptr<Texture> texture{nullptr};
|
std::shared_ptr<Texture> texture{nullptr};
|
||||||
public:
|
public:
|
||||||
Sprite(std::string name, std::string texture);
|
Sprite(std::string name, std::string texture);
|
||||||
virtual void _draw(SDL_Renderer *render) override;
|
virtual void _draw(SDL_Renderer *render, ce::Transform const &view_transform) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
#include "level_1.hpp"
|
#include "level_1.hpp"
|
||||||
#include "core/node.hpp"
|
#include "core/node.hpp"
|
||||||
#include "test_node.hpp"
|
#include "core/node2d.hpp"
|
||||||
|
#include "core/sprite.hpp"
|
||||||
|
#include "player.hpp"
|
||||||
|
|
||||||
void Level1::instantiate() {
|
void Level1::instantiate() {
|
||||||
ce::Node::OwnedPtr &root_ptr{this->get_owned_root()};
|
root.reset(new ce::Node2D("root"));
|
||||||
root_ptr.reset(new ce::Node("root"));
|
root->create_child<ce::Sprite>("background", "background")->set_global_transform({
|
||||||
ce::Node::OwnedPtr node{std::make_unique<TestNode>()};
|
.position = ce::Vecf::ZERO,
|
||||||
root->add_child(node);
|
.rotation = 0.f,
|
||||||
|
.scale = ce::Vecf::ONE * 10.f
|
||||||
|
});
|
||||||
|
root->create_child<Player>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#include "core/canvas_engine.hpp"
|
#include "core/canvas_engine.hpp"
|
||||||
#include "level_1.hpp"
|
#include "level_1.hpp"
|
||||||
|
|
||||||
int main(int argc [[maybe_unused]], char* argv [[maybe_unused]][]) {
|
|
||||||
ce::CanvasEngine engine{};
|
ce::CanvasEngine engine{};
|
||||||
|
|
||||||
|
int main(int argc [[maybe_unused]], char* argv [[maybe_unused]][]) {
|
||||||
std::unique_ptr<ce::Level> level{std::make_unique<Level1>()};
|
std::unique_ptr<ce::Level> level{std::make_unique<Level1>()};
|
||||||
engine.run(level);
|
engine.run(level);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#include "player.hpp"
|
||||||
|
#include "core/collidable_node.hpp"
|
||||||
|
#include "core/collision_shape.hpp"
|
||||||
|
#include "core/math/transform.hpp"
|
||||||
|
#include "core/sprite.hpp"
|
||||||
|
|
||||||
|
Player::Player()
|
||||||
|
: ce::CollidableNode("player", 0x1u, 0x1u) {
|
||||||
|
this->sprite = this->create_child<ce::Sprite>("bike", "bike");
|
||||||
|
this->shape = this->create_child<ce::CollisionShape>("PlayerShape", this, ce::Shape::make_box(5.f, 10.f));
|
||||||
|
ce::Transform trans{this->get_global_transform()};
|
||||||
|
this->set_global_transform(trans);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::_tick(double const &delta) {
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef PLAYER_HPP
|
||||||
|
#define PLAYER_HPP
|
||||||
|
|
||||||
|
#include "core/collidable_node.hpp"
|
||||||
|
|
||||||
|
namespace ce {
|
||||||
|
class CollisionShape;
|
||||||
|
class Sprite;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Player : public ce::CollidableNode {
|
||||||
|
ce::CollisionShape *shape{nullptr};
|
||||||
|
ce::Sprite *sprite{nullptr};
|
||||||
|
public:
|
||||||
|
Player();
|
||||||
|
virtual void _tick(double const &delta) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !PLAYER_HPP
|
|
@ -5,7 +5,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
TestNode::TestNode()
|
TestNode::TestNode()
|
||||||
: ce::Node2D("TestNode") {
|
: ce::CollidableNode("TestNode", 0x1u, 0x1u) {
|
||||||
this->sprite = this->create_child<ce::Sprite>("neocat", "neocat");
|
this->sprite = this->create_child<ce::Sprite>("neocat", "neocat");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@ void TestNode::_tick(double const &delta) {
|
||||||
this->set_transform(trans);
|
this->set_transform(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestNode::_draw(SDL_Renderer *render) {
|
void TestNode::_draw(SDL_Renderer *render, ce::Transform const &view_transform) {
|
||||||
ce::Node2D::_draw(render);
|
|
||||||
ce::Transform trans{this->get_global_transform()};
|
ce::Transform trans{this->get_global_transform()};
|
||||||
SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
|
SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
|
||||||
SDL_FRect rect{trans.position.x-101.f, trans.position.y-101.f, 202.f, 202.f};
|
SDL_FRect rect{trans.position.x-101.f, trans.position.y-101.f, 202.f, 202.f};
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
#ifndef TEST_NODE_HPP
|
#ifndef TEST_NODE_HPP
|
||||||
#define TEST_NODE_HPP
|
#define TEST_NODE_HPP
|
||||||
|
|
||||||
#include "core/node2d.hpp"
|
#include "core/collidable_node.hpp"
|
||||||
|
#include "core/collision_shape.hpp"
|
||||||
#include "core/sprite.hpp"
|
#include "core/sprite.hpp"
|
||||||
|
|
||||||
class TestNode : public ce::Node2D {
|
class TestNode : public ce::CollidableNode {
|
||||||
private:
|
private:
|
||||||
ce::Sprite *sprite{nullptr};
|
ce::Sprite *sprite{nullptr};
|
||||||
|
ce::CollisionShape *shape{nullptr};
|
||||||
public:
|
public:
|
||||||
TestNode();
|
TestNode();
|
||||||
virtual void _first_tick() override;
|
virtual void _first_tick() override;
|
||||||
virtual void _tick(double const &delta) override;
|
virtual void _tick(double const &delta) override;
|
||||||
virtual void _draw(SDL_Renderer *render) override;
|
virtual void _draw(SDL_Renderer *render, ce::Transform const &view_transform) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !TEST_NODE_HPP
|
#endif // !TEST_NODE_HPP
|
||||||
|
|
Loading…
Reference in New Issue