From 77be863f4f15d2481a64e4e8dadb4060a6e4e590 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 11 Feb 2018 12:34:52 -0500 Subject: Implemented map rendering and basic collision Only wall and platform collision currently works, and map edges are not currently implemented. --- src/systems/mapping.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 src/systems/mapping.cpp (limited to 'src/systems/mapping.cpp') diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp new file mode 100644 index 0000000..8723e16 --- /dev/null +++ b/src/systems/mapping.cpp @@ -0,0 +1,143 @@ +#include "mapping.h" +#include "components/mappable.h" +#include "game.h" +#include "consts.h" + +#include + +template +inline void addBoundary( + Storage& boundaries, + int axis, + int lower, + int upper, + MappableComponent::Boundary::Type type) +{ + boundaries.emplace(std::piecewise_construct, + std::tie(axis), + std::tie(axis, lower, upper, type)); +} + +void MappingSystem::render(Texture& texture) +{ + auto entities = game_.getEntityManager().getEntitiesWithComponents< + MappableComponent>(); + + for (id_type entity : entities) + { + auto& mappable = game_.getEntityManager(). + getComponent(entity); + + const Map& map = game_.getWorld().getMap(mappable.getMapId()); + + for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) + { + int x = i % MAP_WIDTH; + int y = i / MAP_WIDTH; + int tile = map.getTiles()[i]; + + if (tile > 0) + { + Rectangle dst { + x * TILE_WIDTH, + y * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + Rectangle src { + (tile % TILESET_COLS) * TILE_WIDTH, + (tile / TILESET_COLS) * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + texture.blit(mappable.getTileset(), std::move(src), std::move(dst)); + } + } + + int startX = ((GAME_WIDTH / TILE_WIDTH) / 2) - (map.getTitle().size() / 2); + for (size_t i = 0; i < map.getTitle().size(); i++) + { + Rectangle src { + (map.getTitle()[i] % FONT_COLS) * TILE_WIDTH, + (map.getTitle()[i] / FONT_COLS) * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + Rectangle dst { + (startX + static_cast(i)) * TILE_WIDTH, + 24 * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + texture.blit(mappable.getFont(), std::move(src), std::move(dst)); + } + } +} + +void MappingSystem::loadMap(size_t mapId) +{ + id_type mapEntity = game_.getEntityManager().emplaceEntity(); + + auto& mappable = game_.getEntityManager(). + emplaceComponent(mapEntity, + Texture("res/tiles.png"), + Texture("res/font.bmp")); + + mappable.setMapId(mapId); + + const Map& map = game_.getWorld().getMap(mappable.getMapId()); + + for (size_t i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) + { + size_t x = i % MAP_WIDTH; + size_t y = i / MAP_WIDTH; + int tile = map.getTiles()[i]; + + if ((tile >= 5) && (tile <= 7)) + { + addBoundary( + mappable.getDownBoundaries(), + y * TILE_HEIGHT, + x * TILE_WIDTH, + (x + 1) * TILE_WIDTH, + MappableComponent::Boundary::Type::platform); + } else if ((tile > 0) && (tile < 28)) + { + addBoundary( + mappable.getRightBoundaries(), + x * TILE_WIDTH, + y * TILE_HEIGHT, + (y+1) * TILE_HEIGHT, + MappableComponent::Boundary::Type::wall); + + addBoundary( + mappable.getLeftBoundaries(), + (x+1) * TILE_WIDTH, + y * TILE_HEIGHT, + (y+1) * TILE_HEIGHT, + MappableComponent::Boundary::Type::wall); + + addBoundary( + mappable.getDownBoundaries(), + y * TILE_HEIGHT, + x * TILE_WIDTH, + (x+1) * TILE_WIDTH, + MappableComponent::Boundary::Type::wall); + + addBoundary( + mappable.getUpBoundaries(), + (y+1) * TILE_HEIGHT, + x * TILE_WIDTH, + (x+1) * TILE_WIDTH, + MappableComponent::Boundary::Type::wall); + } else if (tile == 42) + { + addBoundary( + mappable.getDownBoundaries(), + y * TILE_HEIGHT, + x * TILE_WIDTH, + (x+1) * TILE_WIDTH, + MappableComponent::Boundary::Type::danger); + } + } +} -- cgit 1.4.1 From 5cc80ec58ea5bd66456f6f5286fa5f26d3fe702b Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Mon, 12 Feb 2018 16:39:49 -0500 Subject: Abstracted behavior related to "orientable" entities A lot of the stuff that ControllingSystem did to control the player character was moved into the new OrientingSystem. This is so that the player, or any player-like entities, can also be controlled by AI, with the underlying behavior being delegated in the same way as if the player were being controlled by the user. Fixed the issue where, if the player were blocked while moving horizontally, they would remain blocked even if vertical movement were to remove the collision. Fixed cases of the player animating incorrectly after performing certain movements. --- CMakeLists.txt | 1 + src/components/droppable.h | 24 ----- src/components/orientable.h | 45 +++++++++ src/components/ponderable.h | 17 +--- src/consts.h | 10 +- src/game.cpp | 4 +- src/systems/controlling.cpp | 129 +++--------------------- src/systems/controlling.h | 8 -- src/systems/mapping.cpp | 2 - src/systems/orienting.cpp | 236 ++++++++++++++++++++++++++++++++++++++++++++ src/systems/orienting.h | 35 +++++++ src/systems/pondering.cpp | 89 +++++++++-------- 12 files changed, 393 insertions(+), 207 deletions(-) delete mode 100644 src/components/droppable.h create mode 100644 src/systems/orienting.cpp create mode 100644 src/systems/orienting.h (limited to 'src/systems/mapping.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index f918156..39b1bbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ add_executable(Aromatherapy src/systems/pondering.cpp src/systems/animating.cpp src/systems/mapping.cpp + src/systems/orienting.cpp ) target_link_libraries(Aromatherapy ${ALL_LIBS}) install(TARGETS Aromatherapy RUNTIME DESTINATION ${BIN_DIR}) diff --git a/src/components/droppable.h b/src/components/droppable.h deleted file mode 100644 index 722c139..0000000 --- a/src/components/droppable.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef DROPPABLE_H_5DB254EF -#define DROPPABLE_H_5DB254EF - -#include "component.h" - -class DroppableComponent : public Component { -public: - - inline bool isDroppable() const - { - return droppable_; - } - - inline void setDroppable(bool can) - { - droppable_ = can; - } - -private: - - bool droppable_ = false; -}; - -#endif /* end of include guard: DROPPABLE_H_5DB254EF */ diff --git a/src/components/orientable.h b/src/components/orientable.h index 8f56912..e356b78 100644 --- a/src/components/orientable.h +++ b/src/components/orientable.h @@ -6,6 +6,18 @@ class OrientableComponent : public Component { public: + enum class WalkState { + still, + left, + right + }; + + enum class DropState { + none, + ready, + active + }; + inline bool isFacingRight() const { return facingRight_; @@ -16,9 +28,42 @@ public: facingRight_ = v; } + inline WalkState getWalkState() const + { + return walkState_; + } + + inline void setWalkState(WalkState v) + { + walkState_ = v; + } + + inline bool isJumping() const + { + return jumping_; + } + + inline void setJumping(bool v) + { + jumping_ = v; + } + + inline DropState getDropState() const + { + return dropState_; + } + + inline void setDropState(DropState v) + { + dropState_ = v; + } + private: bool facingRight_ = false; + WalkState walkState_ = WalkState::still; + bool jumping_ = false; + DropState dropState_ = DropState::none; }; #endif /* end of include guard: ORIENTABLE_H_EDB6C4A1 */ diff --git a/src/components/ponderable.h b/src/components/ponderable.h index ac759b6..e21cbab 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h @@ -11,13 +11,6 @@ public: freefalling }; - enum class State { - grounded, - jumping, - falling, - dropping - }; - PonderableComponent(Type type) : type_(type) { } @@ -67,14 +60,14 @@ public: accelY_ = v; } - inline State getState() const + inline bool isGrounded() const { - return state_; + return grounded_; } - inline void setState(State arg) + inline void setGrounded(bool v) { - state_ = arg; + grounded_ = v; } private: @@ -84,7 +77,7 @@ private: double accelX_ = 0.0; double accelY_ = 0.0; Type type_ = Type::vacuumed; - State state_ = State::grounded; + bool grounded_ = false; }; #endif /* end of include guard: TANGIBLE_H_746DB3EE */ diff --git a/src/consts.h b/src/consts.h index a6c9985..581018d 100644 --- a/src/consts.h +++ b/src/consts.h @@ -14,7 +14,13 @@ const int FONT_COLS = 16; const int FRAMES_PER_SECOND = 60; const double SECONDS_PER_FRAME = 1.0 / FRAMES_PER_SECOND; -#define JUMP_VELOCITY(h, l) (-2 * (h) / (l)) -#define JUMP_GRAVITY(h, l) (2 * ((h) / (l)) / (l)) +#define CALC_VELOCITY(h, l) (-2 * (h) / (l)) +#define CALC_GRAVITY(h, l) (2 * ((h) / (l)) / (l)) + +const double NORMAL_GRAVITY = CALC_GRAVITY(TILE_HEIGHT*3.5, 0.233); +const double JUMP_GRAVITY = CALC_GRAVITY(TILE_HEIGHT*4.5, 0.3); +const double JUMP_VELOCITY = CALC_VELOCITY(TILE_HEIGHT*4.5, 0.3); + +const double WALK_SPEED = 90; #endif diff --git a/src/game.cpp b/src/game.cpp index 7cbe7e0..39bb3f1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2,13 +2,13 @@ #include "components/animatable.h" #include "components/transformable.h" #include "components/controllable.h" -#include "components/droppable.h" #include "components/ponderable.h" #include "components/orientable.h" #include "systems/controlling.h" #include "systems/pondering.h" #include "systems/animating.h" #include "systems/mapping.h" +#include "systems/orienting.h" #include "animation.h" #include "renderer.h" #include "consts.h" @@ -33,6 +33,7 @@ Game::Game( world_("res/maps.xml") { systemManager_.emplaceSystem(*this); + systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); @@ -58,7 +59,6 @@ Game::Game( player, PonderableComponent::Type::freefalling); - entityManager_.emplaceComponent(player); entityManager_.emplaceComponent(player); entityManager_.emplaceComponent(player); diff --git a/src/systems/controlling.cpp b/src/systems/controlling.cpp index fa09d11..e1609bd 100644 --- a/src/systems/controlling.cpp +++ b/src/systems/controlling.cpp @@ -1,14 +1,8 @@ #include "controlling.h" #include "game.h" #include "components/controllable.h" -#include "components/ponderable.h" -#include "components/animatable.h" -#include "components/droppable.h" #include "components/orientable.h" -#include "systems/animating.h" -#include "direction.h" -#include "muxer.h" -#include "consts.h" +#include "systems/orienting.h" void ControllingSystem::tick(double) { @@ -19,9 +13,6 @@ void ControllingSystem::tick(double) auto entities = game_.getEntityManager().getEntitiesWithComponents< ControllableComponent, - PonderableComponent, - AnimatableComponent, - DroppableComponent, OrientableComponent>(); for (auto entity : entities) @@ -29,6 +20,8 @@ void ControllingSystem::tick(double) auto& controllable = game_.getEntityManager(). getComponent(entity); + auto& orienting = game_.getSystemManager().getSystem(); + if (action == GLFW_PRESS) { if (key == controllable.getLeftKey()) @@ -37,7 +30,7 @@ void ControllingSystem::tick(double) if (!controllable.isFrozen()) { - walkLeft(entity); + orienting.moveLeft(entity); } } else if (key == controllable.getRightKey()) { @@ -45,19 +38,19 @@ void ControllingSystem::tick(double) if (!controllable.isFrozen()) { - walkRight(entity); + orienting.moveRight(entity); } } else if (key == controllable.getJumpKey()) { if (!controllable.isFrozen()) { - jump(entity); + orienting.jump(entity); } } else if (key == controllable.getDropKey()) { if (!controllable.isFrozen()) { - drop(entity, true); + orienting.drop(entity); } } } else if (action == GLFW_RELEASE) @@ -70,9 +63,9 @@ void ControllingSystem::tick(double) { if (controllable.isHoldingRight()) { - walkRight(entity); + orienting.moveRight(entity); } else { - stopWalking(entity); + orienting.stopWalking(entity); } } } else if (key == controllable.getRightKey()) @@ -83,22 +76,22 @@ void ControllingSystem::tick(double) { if (controllable.isHoldingLeft()) { - walkLeft(entity); + orienting.moveLeft(entity); } else { - stopWalking(entity); + orienting.stopWalking(entity); } } } else if (key == controllable.getDropKey()) { if (!controllable.isFrozen()) { - drop(entity, false); + orienting.stopDropping(entity); } } else if (key == controllable.getJumpKey()) { if (!controllable.isFrozen()) { - stopJumping(entity); + orienting.stopJumping(entity); } } } @@ -112,99 +105,3 @@ void ControllingSystem::input(int key, int action) { actions_.push(std::make_pair(key, action)); } - -void ControllingSystem::walkLeft(id_type entity) -{ - auto& ponderable = game_.getEntityManager().getComponent(entity); - auto& orientable = game_.getEntityManager().getComponent(entity); - - orientable.setFacingRight(false); - ponderable.setVelocityX(-90); - - auto& animating = game_.getSystemManager().getSystem(); - - if (ponderable.getState() == PonderableComponent::State::grounded) - { - animating.startAnimation(entity, "walkingLeft"); - } else { - animating.startAnimation(entity, "stillLeft"); - } -} - -void ControllingSystem::walkRight(id_type entity) -{ - auto& ponderable = game_.getEntityManager().getComponent(entity); - auto& orientable = game_.getEntityManager().getComponent(entity); - - orientable.setFacingRight(true); - ponderable.setVelocityX(90); - - auto& animating = game_.getSystemManager().getSystem(); - - if (ponderable.getState() == PonderableComponent::State::grounded) - { - animating.startAnimation(entity, "walkingRight"); - } else { - animating.startAnimation(entity, "stillRight"); - } -} - -void ControllingSystem::stopWalking(id_type entity) -{ - auto& ponderable = game_.getEntityManager().getComponent(entity); - auto& orientable = game_.getEntityManager().getComponent(entity); - - ponderable.setVelocityX(0); - - if (ponderable.getState() == PonderableComponent::State::grounded) - { - auto& animating = game_.getSystemManager().getSystem(); - - if (orientable.isFacingRight()) - { - animating.startAnimation(entity, "stillRight"); - } else { - animating.startAnimation(entity, "stillLeft"); - } - } -} - -void ControllingSystem::jump(id_type entity) -{ - auto& ponderable = game_.getEntityManager().getComponent(entity); - - if (ponderable.getState() == PonderableComponent::State::grounded) - { - playSound("res/Randomize87.wav", 0.25); - - ponderable.setVelocityY(JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3)); - ponderable.setAccelY(JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3)); - ponderable.setState(PonderableComponent::State::jumping); - } -} - -void ControllingSystem::stopJumping(id_type entity) -{ - auto& ponderable = game_.getEntityManager().getComponent(entity); - - if (ponderable.getState() == PonderableComponent::State::jumping) - { - ponderable.setAccelY(JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233)); - ponderable.setState(PonderableComponent::State::falling); - } -} - -void ControllingSystem::drop(id_type entity, bool start) -{ - auto& droppable = game_.getEntityManager().getComponent(entity); - auto& ponderable = game_.getEntityManager().getComponent(entity); - - if (start && (ponderable.getState() == PonderableComponent::State::grounded)) - { - ponderable.setState(PonderableComponent::State::dropping); - } else if ((!start) && (ponderable.getState() == PonderableComponent::State::dropping)) - { - ponderable.setState(PonderableComponent::State::grounded); - } - droppable.setDroppable(start); -} diff --git a/src/systems/controlling.h b/src/systems/controlling.h index 1f1e8a0..01ed7a0 100644 --- a/src/systems/controlling.h +++ b/src/systems/controlling.h @@ -3,7 +3,6 @@ #include "system.h" #include -#include "entity_manager.h" class ControllingSystem : public System { public: @@ -17,13 +16,6 @@ public: private: - void walkLeft(id_type entity); - void walkRight(id_type entity); - void stopWalking(id_type entity); - void jump(id_type entity); - void stopJumping(id_type entity); - void drop(id_type entity, bool start); - std::queue> actions_; }; diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index 8723e16..5b63ded 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -3,8 +3,6 @@ #include "game.h" #include "consts.h" -#include - template inline void addBoundary( Storage& boundaries, diff --git a/src/systems/orienting.cpp b/src/systems/orienting.cpp new file mode 100644 index 0000000..187bebc --- /dev/null +++ b/src/systems/orienting.cpp @@ -0,0 +1,236 @@ +#include "orienting.h" +#include "game.h" +#include "components/orientable.h" +#include "components/ponderable.h" +#include "systems/animating.h" +#include "consts.h" +#include "muxer.h" + +void OrientingSystem::tick(double) +{ + auto entities = game_.getEntityManager().getEntitiesWithComponents< + OrientableComponent, + PonderableComponent>(); + + for (id_type entity : entities) + { + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + switch (orientable.getWalkState()) + { + case OrientableComponent::WalkState::still: + { + ponderable.setVelocityX(0); + + break; + } + + case OrientableComponent::WalkState::left: + { + ponderable.setVelocityX(-WALK_SPEED); + + break; + } + + case OrientableComponent::WalkState::right: + { + ponderable.setVelocityX(WALK_SPEED); + + break; + } + } + + if (orientable.isJumping() && (ponderable.getVelocityY() > 0)) + { + orientable.setJumping(false); + } + } +} + +void OrientingSystem::moveLeft(id_type entity) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + orientable.setFacingRight(false); + orientable.setWalkState(OrientableComponent::WalkState::left); + + auto& animating = game_.getSystemManager().getSystem(); + if (ponderable.isGrounded()) + { + animating.startAnimation(entity, "walkingLeft"); + } else { + animating.startAnimation(entity, "stillLeft"); + } +} + +void OrientingSystem::moveRight(id_type entity) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + orientable.setFacingRight(true); + orientable.setWalkState(OrientableComponent::WalkState::right); + + auto& animating = game_.getSystemManager().getSystem(); + if (ponderable.isGrounded()) + { + animating.startAnimation(entity, "walkingRight"); + } else { + animating.startAnimation(entity, "stillRight"); + } +} + +void OrientingSystem::stopWalking(id_type entity) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + orientable.setWalkState(OrientableComponent::WalkState::still); + + if (ponderable.isGrounded()) + { + auto& animating = game_.getSystemManager().getSystem(); + + if (orientable.isFacingRight()) + { + animating.startAnimation(entity, "stillRight"); + } else { + animating.startAnimation(entity, "stillLeft"); + } + } +} + +void OrientingSystem::jump(id_type entity) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + if (ponderable.isGrounded()) + { + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + orientable.setJumping(true); + + playSound("res/Randomize87.wav", 0.25); + + ponderable.setVelocityY(JUMP_VELOCITY); + ponderable.setAccelY(JUMP_GRAVITY); + + auto& animating = game_.getSystemManager().getSystem(); + if (orientable.isFacingRight()) + { + animating.startAnimation(entity, "stillRight"); + } else { + animating.startAnimation(entity, "stillLeft"); + } + } +} + +void OrientingSystem::stopJumping(id_type entity) +{ + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + if (orientable.isJumping()) + { + orientable.setJumping(false); + + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + ponderable.setAccelY(NORMAL_GRAVITY); + } +} + +void OrientingSystem::land(id_type entity) +{ + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + auto& animating = game_.getSystemManager().getSystem(); + + switch (orientable.getWalkState()) + { + case OrientableComponent::WalkState::still: + { + if (orientable.isFacingRight()) + { + animating.startAnimation(entity, "stillRight"); + } else { + animating.startAnimation(entity, "stillLeft"); + } + + break; + } + + case OrientableComponent::WalkState::left: + { + animating.startAnimation(entity, "walkingLeft"); + + break; + } + + case OrientableComponent::WalkState::right: + { + animating.startAnimation(entity, "walkingRight"); + + break; + } + } +} + +void OrientingSystem::startFalling(id_type entity) +{ + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + auto& animating = game_.getSystemManager().getSystem(); + + if (orientable.isFacingRight()) + { + animating.startAnimation(entity, "stillRight"); + } else { + animating.startAnimation(entity, "stillLeft"); + } +} + +void OrientingSystem::drop(id_type entity) +{ + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + if (ponderable.isGrounded() + && (orientable.getDropState() == OrientableComponent::DropState::none)) + { + orientable.setDropState(OrientableComponent::DropState::ready); + } +} + +void OrientingSystem::stopDropping(id_type entity) +{ + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + if (orientable.getDropState() == OrientableComponent::DropState::ready) + { + orientable.setDropState(OrientableComponent::DropState::none); + } +} diff --git a/src/systems/orienting.h b/src/systems/orienting.h new file mode 100644 index 0000000..4ded612 --- /dev/null +++ b/src/systems/orienting.h @@ -0,0 +1,35 @@ +#ifndef ORIENTING_H_099F0C23 +#define ORIENTING_H_099F0C23 + +#include "system.h" + +class OrientingSystem : public System { +public: + + OrientingSystem(Game& game) : System(game) + { + } + + void tick(double dt); + + void moveLeft(id_type entity); + + void moveRight(id_type entity); + + void stopWalking(id_type entity); + + void jump(id_type entity); + + void stopJumping(id_type entity); + + void land(id_type entity); + + void startFalling(id_type entity); + + void drop(id_type entity); + + void stopDropping(id_type entity); + +}; + +#endif /* end of include guard: ORIENTING_H_099F0C23 */ diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 26a6f56..4a165b1 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -2,7 +2,9 @@ #include "game.h" #include "components/ponderable.h" #include "components/transformable.h" -#include "components/droppable.h" +#include "components/orientable.h" +#include "components/mappable.h" +#include "systems/orienting.h" #include "consts.h" void PonderingSystem::tick(double dt) @@ -37,10 +39,8 @@ void PonderingSystem::tick(double dt) double newX = oldX + ponderable.getVelocityX() * dt; double newY = oldY + ponderable.getVelocityY() * dt; - if (ponderable.getVelocityY() > 0.0) - { - ponderable.setState(PonderableComponent::State::falling); - } + bool oldGrounded = ponderable.isGrounded(); + ponderable.setGrounded(false); for (id_type mapEntity : maps) { @@ -64,8 +64,6 @@ void PonderingSystem::tick(double dt) newY, it->first, it->second.getType()); - - break; } } } else if (newX > oldX) @@ -86,8 +84,6 @@ void PonderingSystem::tick(double dt) newY, it->first, it->second.getType()); - - break; } } } @@ -109,8 +105,6 @@ void PonderingSystem::tick(double dt) newY, it->first, it->second.getType()); - - break; } } } else if (newY > oldY) @@ -131,8 +125,6 @@ void PonderingSystem::tick(double dt) newY, it->first, it->second.getType()); - - break; } } } @@ -141,6 +133,31 @@ void PonderingSystem::tick(double dt) // Move transformable.setX(newX); transformable.setY(newY); + + // Perform cleanup for orientable entites + if (game_.getEntityManager().hasComponent(entity)) + { + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + // Handle changes in groundedness + if (ponderable.isGrounded() != oldGrounded) + { + if (ponderable.isGrounded()) + { + game_.getSystemManager().getSystem().land(entity); + } else { + game_.getSystemManager(). + getSystem().startFalling(entity); + } + } + + // Complete dropping, if necessary + if (orientable.getDropState() == OrientableComponent::DropState::active) + { + orientable.setDropState(OrientableComponent::DropState::none); + } + } } } @@ -153,8 +170,7 @@ void PonderingSystem::initializeBody( if (type == PonderableComponent::Type::freefalling) { - ponderable.setAccelY(JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233)); - ponderable.setState(PonderableComponent::State::falling); + ponderable.setAccelY(NORMAL_GRAVITY); } } @@ -172,6 +188,8 @@ void PonderingSystem::processCollision( auto& transformable = game_.getEntityManager(). getComponent(entity); + bool touchedGround = false; + switch (type) { case MappableComponent::Boundary::Type::wall: @@ -204,13 +222,7 @@ void PonderingSystem::processCollision( case Direction::down: { - newY = axis - transformable.getH(); - ponderable.setVelocityY(0.0); - - if (ponderable.getState() == PonderableComponent::State::falling) - { - ponderable.setState(PonderableComponent::State::grounded); - } + touchedGround = true; break; } @@ -221,31 +233,19 @@ void PonderingSystem::processCollision( case MappableComponent::Boundary::Type::platform: { - if (game_.getEntityManager().hasComponent(entity)) + if (game_.getEntityManager().hasComponent(entity)) { - auto& droppable = game_.getEntityManager(). - getComponent(entity); + auto& orientable = game_.getEntityManager(). + getComponent(entity); - if (droppable.isDroppable()) + if (orientable.getDropState() != OrientableComponent::DropState::none) { - droppable.setDroppable(false); + orientable.setDropState(OrientableComponent::DropState::active); } else { - newY = axis - transformable.getH(); - ponderable.setVelocityY(0.0); - - if (ponderable.getState() == PonderableComponent::State::falling) - { - ponderable.setState(PonderableComponent::State::grounded); - } + touchedGround = true; } } else { - newY = axis - transformable.getH(); - ponderable.setVelocityY(0.0); - - if (ponderable.getState() == PonderableComponent::State::falling) - { - ponderable.setState(PonderableComponent::State::grounded); - } + touchedGround = true; } break; @@ -258,4 +258,11 @@ void PonderingSystem::processCollision( break; } } + + if (touchedGround) + { + newY = axis - transformable.getH(); + ponderable.setVelocityY(0.0); + ponderable.setGrounded(true); + } } -- cgit 1.4.1 From ed08b673c50b076042d8f0c49501372168142764 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Fri, 16 Feb 2018 16:04:32 -0500 Subject: Refactored renderer Renderer is basically now more C++'y, as it makes more use of classes (a lot of GL types have been wrapped), and the renderer itself is now a class. The monitor mesh is also now indexed. Tweaked the NTSC artifacting after inadvertently fixing a bug with the way the image was loaded. --- CMakeLists.txt | 6 +- shaders/final.fragment | 4 +- src/algorithms.h | 12 - src/animation.h | 2 +- src/components/controllable.h | 2 +- src/components/mappable.h | 2 +- src/entity_manager.h | 2 +- src/game.cpp | 19 +- src/game.h | 11 +- src/main.cpp | 17 +- src/renderer.cpp | 862 ------------------------------------------ src/renderer.h | 37 -- src/renderer/gl.h | 7 + src/renderer/mesh.cpp | 109 ++++++ src/renderer/mesh.h | 62 +++ src/renderer/renderer.cpp | 635 +++++++++++++++++++++++++++++++ src/renderer/renderer.h | 114 ++++++ src/renderer/shader.cpp | 84 ++++ src/renderer/shader.h | 58 +++ src/renderer/texture.cpp | 124 ++++++ src/renderer/texture.h | 52 +++ src/renderer/wrappers.h | 273 +++++++++++++ src/systems/animating.cpp | 3 +- src/systems/animating.h | 2 +- src/systems/mapping.cpp | 12 +- src/util.cpp | 30 ++ src/util.h | 41 ++ 27 files changed, 1631 insertions(+), 951 deletions(-) delete mode 100644 src/algorithms.h delete mode 100644 src/renderer.cpp delete mode 100644 src/renderer.h create mode 100644 src/renderer/gl.h create mode 100644 src/renderer/mesh.cpp create mode 100644 src/renderer/mesh.h create mode 100644 src/renderer/renderer.cpp create mode 100644 src/renderer/renderer.h create mode 100644 src/renderer/shader.cpp create mode 100644 src/renderer/shader.h create mode 100644 src/renderer/texture.cpp create mode 100644 src/renderer/texture.h create mode 100644 src/renderer/wrappers.h create mode 100644 src/util.cpp create mode 100644 src/util.h (limited to 'src/systems/mapping.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 22bbc1a..3e7bcb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,12 +49,16 @@ link_directories( add_executable(Aromatherapy src/main.cpp - src/renderer.cpp src/muxer.cpp src/entity_manager.cpp src/game.cpp src/animation.cpp src/world.cpp + src/util.cpp + src/renderer/renderer.cpp + src/renderer/mesh.cpp + src/renderer/shader.cpp + src/renderer/texture.cpp src/systems/controlling.cpp src/systems/pondering.cpp src/systems/animating.cpp diff --git a/shaders/final.fragment b/shaders/final.fragment index 9a39597..2e38f38 100644 --- a/shaders/final.fragment +++ b/shaders/final.fragment @@ -15,8 +15,8 @@ const float Tuning_Dimming = 0.0; const float Tuning_Satur = 1.13; const float Tuning_ReflScalar = 0.3; const float Tuning_Barrel = 0;//0.12; -const float Tuning_Scanline_Brightness = 0.5;//0.45; -const float Tuning_Scanline_Opacity = 0.75;//0.55; +const float Tuning_Scanline_Brightness = 0.55; +const float Tuning_Scanline_Opacity = 0.55; const float Tuning_Diff_Brightness = 0.75; const float Tuning_Spec_Brightness = 0.5;//0.35; const float Tuning_Spec_Power = 50.0; diff --git a/src/algorithms.h b/src/algorithms.h deleted file mode 100644 index 80e3e27..0000000 --- a/src/algorithms.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef ALGORITHMS_H_1DDC517E -#define ALGORITHMS_H_1DDC517E - -template< typename ContainerT, typename PredicateT > -void erase_if( ContainerT& items, const PredicateT& predicate ) { - for( auto it = items.begin(); it != items.end(); ) { - if( predicate(*it) ) it = items.erase(it); - else ++it; - } -}; - -#endif /* end of include guard: ALGORITHMS_H_1DDC517E */ diff --git a/src/animation.h b/src/animation.h index 50446d0..58df616 100644 --- a/src/animation.h +++ b/src/animation.h @@ -1,7 +1,7 @@ #ifndef ANIMATION_H_74EB0901 #define ANIMATION_H_74EB0901 -#include "renderer.h" +#include "renderer/texture.h" #include #include #include diff --git a/src/components/controllable.h b/src/components/controllable.h index fa78c8b..1b12985 100644 --- a/src/components/controllable.h +++ b/src/components/controllable.h @@ -2,7 +2,7 @@ #define CONTROLLABLE_H_4E0B85B4 #include "component.h" -#include "renderer.h" +#include "renderer/gl.h" class ControllableComponent : public Component { public: diff --git a/src/components/mappable.h b/src/components/mappable.h index 7530919..2dbab77 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h @@ -3,7 +3,7 @@ #include #include "component.h" -#include "renderer.h" +#include "renderer/texture.h" #include "map.h" class MappableComponent : public Component { diff --git a/src/entity_manager.h b/src/entity_manager.h index 7fd82fc..65fa6ca 100644 --- a/src/entity_manager.h +++ b/src/entity_manager.h @@ -7,7 +7,7 @@ #include #include #include "component.h" -#include "algorithms.h" +#include "util.h" class EntityManager { private: diff --git a/src/game.cpp b/src/game.cpp index 1182689..228ff23 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -10,12 +10,11 @@ #include "systems/mapping.h" #include "systems/orienting.h" #include "animation.h" -#include "renderer.h" #include "consts.h" void key_callback(GLFWwindow* window, int key, int, int action, int) { - Game& game = *((Game*) glfwGetWindowUserPointer(window)); + Game& game = *static_cast(glfwGetWindowUserPointer(window)); if ((action == GLFW_PRESS) && (key == GLFW_KEY_ESCAPE)) { @@ -27,10 +26,7 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) game.systemManager_.input(key, action); } -Game::Game( - GLFWwindow* window) : - window_(window), - world_("res/maps.xml") +Game::Game() : world_("res/maps.xml") { systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); @@ -65,8 +61,8 @@ Game::Game( systemManager_.getSystem().loadMap(world_.getStartingMapId()); glfwSwapInterval(1); - glfwSetWindowUserPointer(window_, this); - glfwSetKeyCallback(window_, key_callback); + glfwSetWindowUserPointer(renderer_.getWindow().getHandle(), this); + glfwSetKeyCallback(renderer_.getWindow().getHandle(), key_callback); } void Game::execute() @@ -76,7 +72,8 @@ void Game::execute() double accumulator = 0.0; Texture texture(GAME_WIDTH, GAME_HEIGHT); - while (!(shouldQuit_ || glfwWindowShouldClose(window_))) + while (!(shouldQuit_ || + glfwWindowShouldClose(renderer_.getWindow().getHandle()))) { double currentTime = glfwGetTime(); double frameTime = currentTime - lastTime; @@ -93,8 +90,8 @@ void Game::execute() } // Render - texture.fill(texture.entirety(), 0, 0, 0); + renderer_.fill(texture, texture.entirety(), 0, 0, 0); systemManager_.render(texture); - texture.renderScreen(); + renderer_.renderScreen(texture); } } diff --git a/src/game.h b/src/game.h index 346d67e..43e08da 100644 --- a/src/game.h +++ b/src/game.h @@ -1,18 +1,23 @@ #ifndef GAME_H_1014DDC9 #define GAME_H_1014DDC9 -#include "renderer.h" #include "entity_manager.h" #include "system_manager.h" #include "world.h" +#include "renderer/renderer.h" class Game { public: - Game(GLFWwindow* window); + Game(); void execute(); + inline Renderer& getRenderer() + { + return renderer_; + } + inline EntityManager& getEntityManager() { return entityManager_; @@ -37,7 +42,7 @@ public: private: - GLFWwindow* const window_; + Renderer renderer_; EntityManager entityManager_; SystemManager systemManager_; World world_; diff --git a/src/main.cpp b/src/main.cpp index d51da7d..ddbc15f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,27 +1,14 @@ -#include -#include -#include -#include "renderer.h" #include "muxer.h" #include "game.h" int main() { - srand(time(NULL)); - - GLFWwindow* window = initRenderer(); - glfwSwapInterval(1); - initMuxer(); - // Put this in a block so game goes out of scope before we destroy the renderer - { - Game game {window}; - game.execute(); - } + Game game; + game.execute(); destroyMuxer(); - destroyRenderer(); return 0; } diff --git a/src/renderer.cpp b/src/renderer.cpp deleted file mode 100644 index f840180..0000000 --- a/src/renderer.cpp +++ /dev/null @@ -1,862 +0,0 @@ -#include "renderer.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "consts.h" - -// include stb_image -#define STB_IMAGE_IMPLEMENTATION -#define STBI_ONLY_PNG -#define STBI_ONLY_BMP -#include "stb_image.h" - -static bool rendererInitialized = false; - -static GLFWwindow* window; - -static GLuint generic_framebuffer; // The framebuffer -static GLuint bloom_framebuffer; -static GLuint bloom_depthbuffer; -static int buffer_width = 1024; -static int buffer_height = 768; - -static GLuint ntscShader; // The NTSC shader -static GLuint finalShader; // The passthrough shader -static GLuint blitShader; // The blitting shader -static GLuint fillShader; // The fill shader -static GLuint bloom1Shader; -static GLuint bloom2Shader; - -// The buffers for the NTSC rendering process -static GLuint renderedTexBufs[2]; -static int curBuf; -static GLuint artifactsTex; -static GLuint scanlinesTex; -static GLuint preBloomTex; -static GLuint bloomPassTex1; -static GLuint bloomPassTex2; - -// The VAO -static GLuint VertexArrayID; - -// A plane that fills the renderbuffer -static GLuint quad_vertexbuffer; - -// Buffers for the mesh -static GLuint mesh_vertexbuffer; -static GLuint mesh_uvbuffer; -static GLuint mesh_normalbuffer; -static int mesh_numvertices; - -GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path) -{ - // Create the shaders - GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); - GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); - - // Read the Vertex Shader code from the file - std::string VertexShaderCode; - std::ifstream VertexShaderStream(vertex_file_path, std::ios::in); - if(VertexShaderStream.is_open()) - { - std::string Line = ""; - while(getline(VertexShaderStream, Line)) - VertexShaderCode += "\n" + Line; - VertexShaderStream.close(); - } - - // Read the Fragment Shader code from the file - std::string FragmentShaderCode; - std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in); - if(FragmentShaderStream.is_open()){ - std::string Line = ""; - while(getline(FragmentShaderStream, Line)) - FragmentShaderCode += "\n" + Line; - FragmentShaderStream.close(); - } - - GLint Result = GL_FALSE; - int InfoLogLength; - - // Compile Vertex Shader - printf("Compiling shader : %s\n", vertex_file_path); - char const * VertexSourcePointer = VertexShaderCode.c_str(); - glShaderSource(VertexShaderID, 1, &VertexSourcePointer , nullptr); - glCompileShader(VertexShaderID); - - // Check Vertex Shader - glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); - glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); - std::vector VertexShaderErrorMessage(InfoLogLength); - glGetShaderInfoLog(VertexShaderID, InfoLogLength, nullptr, &VertexShaderErrorMessage[0]); - fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]); - - // Compile Fragment Shader - printf("Compiling shader : %s\n", fragment_file_path); - char const * FragmentSourcePointer = FragmentShaderCode.c_str(); - glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , nullptr); - glCompileShader(FragmentShaderID); - - // Check Fragment Shader - glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); - glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); - std::vector FragmentShaderErrorMessage(InfoLogLength); - glGetShaderInfoLog(FragmentShaderID, InfoLogLength, nullptr, &FragmentShaderErrorMessage[0]); - fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]); - - // Link the program - fprintf(stdout, "Linking program\n"); - GLuint ProgramID = glCreateProgram(); - glAttachShader(ProgramID, VertexShaderID); - glAttachShader(ProgramID, FragmentShaderID); - glLinkProgram(ProgramID); - - // Check the program - glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); - glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); - std::vector ProgramErrorMessage( glm::max(InfoLogLength, int(1)) ); - glGetProgramInfoLog(ProgramID, InfoLogLength, nullptr, &ProgramErrorMessage[0]); - fprintf(stdout, "%s\n", &ProgramErrorMessage[0]); - - glDeleteShader(VertexShaderID); - glDeleteShader(FragmentShaderID); - - return ProgramID; -} - -void flipImageData(unsigned char* data, int width, int height, int comps) -{ - unsigned char* data_copy = (unsigned char*) malloc(width*height*comps*sizeof(unsigned char)); - memcpy(data_copy, data, width*height*comps); - - int row_size = width * comps; - - for (int i=0;i& out_vertices, std::vector& out_uvs, std::vector& out_normals) -{ - out_vertices.clear(); - out_uvs.clear(); - out_normals.clear(); - - FILE* file = fopen(filename, "r"); - if (file == nullptr) - { - fprintf(stderr, "Could not open mesh file %s\n", filename); - exit(1); - } - - std::vector temp_vertices; - std::vector temp_uvs; - std::vector temp_normals; - - for (;;) - { - char lineHeader[256]; - int res = fscanf(file, "%s", lineHeader); - if (res == EOF) - { - break; - } - - if (!strncmp(lineHeader, "v", 2)) - { - glm::vec3 vertex; - fscanf(file, "%f %f %f\n", &vertex.x,&vertex.y,&vertex.z); - temp_vertices.push_back(vertex); - } else if (!strncmp(lineHeader, "vt", 3)) - { - glm::vec2 uv; - fscanf(file, "%f %f\n", &uv.x, &uv.y); - temp_uvs.push_back(uv); - } else if (!strncmp(lineHeader, "vn", 3)) - { - glm::vec3 normal; - fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z); - temp_normals.push_back(normal); - } else if (!strncmp(lineHeader, "f", 2)) - { - int vertexIDs[3], uvIDs[3], normalIDs[3]; - fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIDs[0], &uvIDs[0], &normalIDs[0], &vertexIDs[1], &uvIDs[1], &normalIDs[1], &vertexIDs[2], &uvIDs[2], &normalIDs[2]); - - for (int i=0; i<3; i++) - { - out_vertices.push_back(temp_vertices[vertexIDs[i] - 1]); - out_uvs.push_back(temp_uvs[uvIDs[i] - 1]); - out_normals.push_back(temp_normals[normalIDs[i] - 1]); - } - } - } -} - -void setFramebufferSize(GLFWwindow* w, int width, int height) -{ - buffer_width = width; - buffer_height = height; - - glDeleteFramebuffers(1, &bloom_framebuffer); - glDeleteRenderbuffers(1, &bloom_depthbuffer); - - glGenFramebuffers(1, &bloom_framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, bloom_framebuffer); - GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT1}; - glDrawBuffers(1, DrawBuffers); - - glGenRenderbuffers(1, &bloom_depthbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, bloom_depthbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bloom_depthbuffer); - - glDeleteTextures(1, &preBloomTex); - glDeleteTextures(1, &bloomPassTex1); - glDeleteTextures(1, &bloomPassTex2); - - glGenTextures(1, &preBloomTex); - glBindTexture(GL_TEXTURE_2D, preBloomTex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glGenTextures(1, &bloomPassTex1); - glBindTexture(GL_TEXTURE_2D, bloomPassTex1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width/4, height/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glGenTextures(1, &bloomPassTex2); - glBindTexture(GL_TEXTURE_2D, bloomPassTex2); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width/4, height/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -GLFWwindow* initRenderer() -{ - if (rendererInitialized) - { - fprintf(stderr, "Renderer already initialized\n"); - exit(-1); - } - - // Initialize GLFW - if (!glfwInit()) - { - fprintf(stderr, "Failed to initialize GLFW\n"); - exit(-1); - } - - glfwWindowHint(GLFW_SAMPLES, 4); // 4x antialiasing - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want version 3.3 - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Mac requires this - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - - // Create a window - window = glfwCreateWindow(1024, 768, "Aromatherapy", nullptr, nullptr); - if (window == nullptr) - { - fprintf(stderr, "Failed to open GLFW window\n"); - glfwTerminate(); - exit(-1); - } - - glfwMakeContextCurrent(window); - glewExperimental = true; // Needed in core profile - if (glewInit() != GLEW_OK) - { - fprintf(stderr, "Failed to initialize GLEW\n"); - exit(-1); - } - - glfwSetFramebufferSizeCallback(window, &setFramebufferSize); - - // Set up vertex array object - glGenVertexArrays(1, &VertexArrayID); - glBindVertexArray(VertexArrayID); - - // Enable depth testing - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - - // Enable blending - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // Set up the framebuffer - glGenFramebuffers(1, &generic_framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer); - GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; - glDrawBuffers(1, DrawBuffers); - - glGenFramebuffers(1, &bloom_framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, bloom_framebuffer); - GLenum DrawBuffers2[1] = {GL_COLOR_ATTACHMENT1}; - glDrawBuffers(1, DrawBuffers2); - - glfwGetFramebufferSize(window, &buffer_width, &buffer_height); - - glGenRenderbuffers(1, &bloom_depthbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, bloom_depthbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, buffer_width, buffer_height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, bloom_depthbuffer); - - // Set up the NTSC rendering buffers - glGenTextures(2, renderedTexBufs); - glBindTexture(GL_TEXTURE_2D, renderedTexBufs[0]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GAME_WIDTH, GAME_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glBindTexture(GL_TEXTURE_2D, renderedTexBufs[1]); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GAME_WIDTH, GAME_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Set up bloom rendering buffers - glGenTextures(1, &preBloomTex); - glBindTexture(GL_TEXTURE_2D, preBloomTex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, buffer_width, buffer_height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glGenTextures(1, &bloomPassTex1); - glBindTexture(GL_TEXTURE_2D, bloomPassTex1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, buffer_width/4, buffer_height/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glGenTextures(1, &bloomPassTex2); - glBindTexture(GL_TEXTURE_2D, bloomPassTex2); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, buffer_width/4, buffer_height/4, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - curBuf = 0; - - // Load the mesh! - std::vector mesh_vertices; - std::vector mesh_uvs; - std::vector mesh_normals; - - loadMesh("res/monitor-old.obj", mesh_vertices, mesh_uvs, mesh_normals); - - mesh_numvertices = mesh_vertices.size(); - - glGenBuffers(1, &mesh_vertexbuffer); - glBindBuffer(GL_ARRAY_BUFFER, mesh_vertexbuffer); - glBufferData(GL_ARRAY_BUFFER, mesh_vertices.size() * sizeof(glm::vec3), mesh_vertices.data(), GL_STATIC_DRAW); - - glGenBuffers(1, &mesh_uvbuffer); - glBindBuffer(GL_ARRAY_BUFFER, mesh_uvbuffer); - glBufferData(GL_ARRAY_BUFFER, mesh_uvs.size() * sizeof(glm::vec2), mesh_uvs.data(), GL_STATIC_DRAW); - - glGenBuffers(1, &mesh_normalbuffer); - glBindBuffer(GL_ARRAY_BUFFER, mesh_normalbuffer); - glBufferData(GL_ARRAY_BUFFER, mesh_normals.size() * sizeof(glm::vec3), mesh_normals.data(), GL_STATIC_DRAW); - - // Load the vertices of a flat surface - GLfloat g_quad_vertex_buffer_data[] = { - -1.0f, -1.0f, 0.0f, - 1.0f, -1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, - -1.0f, 1.0f, 0.0f, - 1.0f, -1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - }; - - glGenBuffers(1, &quad_vertexbuffer); - glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(g_quad_vertex_buffer_data), g_quad_vertex_buffer_data, GL_STATIC_DRAW); - - glGenTextures(1, &artifactsTex); - glBindTexture(GL_TEXTURE_2D, artifactsTex); - int atdw, atdh; - unsigned char* artifactsTex_data = stbi_load("res/artifacts.bmp", &atdw, &atdh, 0, 3); - flipImageData(artifactsTex_data, atdw, atdh, 3); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, atdw, atdh, 0, GL_RGB, GL_UNSIGNED_BYTE, artifactsTex_data); - stbi_image_free(artifactsTex_data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - glGenerateMipmap(GL_TEXTURE_2D); - - glGenTextures(1, &scanlinesTex); - glBindTexture(GL_TEXTURE_2D, scanlinesTex); - int stdw, stdh; - unsigned char* scanlinesTex_data = stbi_load("res/scanlines_333.bmp", &stdw, &stdh, 0, 3); - flipImageData(scanlinesTex_data, stdw, stdh, 3); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, atdw, atdh, 0, GL_RGB, GL_UNSIGNED_BYTE, scanlinesTex_data); - stbi_image_free(scanlinesTex_data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); - glGenerateMipmap(GL_TEXTURE_2D); - - // Load the shaders - ntscShader = LoadShaders("shaders/ntsc.vertex", "shaders/ntsc.fragment"); - finalShader = LoadShaders("shaders/final.vertex", "shaders/final.fragment"); - blitShader = LoadShaders("shaders/blit.vertex", "shaders/blit.fragment"); - fillShader = LoadShaders("shaders/fill.vertex", "shaders/fill.fragment"); - bloom1Shader = LoadShaders("shaders/bloom1.vertex", "shaders/bloom1.fragment"); - bloom2Shader = LoadShaders("shaders/bloom2.vertex", "shaders/bloom2.fragment"); - - rendererInitialized = true; - - return window; -} - -void destroyRenderer() -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - // Delete the plane buffer - glDeleteBuffers(1, &quad_vertexbuffer); - glDeleteBuffers(1, &mesh_vertexbuffer); - glDeleteBuffers(1, &mesh_uvbuffer); - glDeleteBuffers(1, &mesh_normalbuffer); - - // Delete the shaders - glDeleteProgram(ntscShader); - glDeleteProgram(finalShader); - glDeleteProgram(blitShader); - glDeleteProgram(fillShader); - glDeleteProgram(bloom1Shader); - glDeleteProgram(bloom2Shader); - - // Delete the NTSC rendering buffers - glDeleteTextures(2, renderedTexBufs); - glDeleteTextures(1, &artifactsTex); - glDeleteTextures(1, &scanlinesTex); - glDeleteTextures(1, &preBloomTex); - glDeleteTextures(1, &bloomPassTex1); - glDeleteTextures(1, &bloomPassTex2); - - // Delete the framebuffer - glDeleteRenderbuffers(1, &bloom_depthbuffer); - glDeleteFramebuffers(1, &bloom_framebuffer); - glDeleteFramebuffers(1, &generic_framebuffer); - - // Delete the VAO - glDeleteVertexArrays(1, &VertexArrayID); - - // Kill the window - glfwTerminate(); - - rendererInitialized = false; -} - -Texture::Texture(int width, int height) -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - this->width = width; - this->height = height; - - glGenTextures(1, &texID); - glBindTexture(GL_TEXTURE_2D, texID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); -} - -Texture::Texture(const char* filename) -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - glGenTextures(1, &texID); - glBindTexture(GL_TEXTURE_2D, texID); - unsigned char* data = stbi_load(filename, &width, &height, 0, 4); - flipImageData(data, width, height, 4); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - stbi_image_free(data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -Texture::Texture(const Texture& tex) -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - width = tex.width; - height = tex.height; - - unsigned char* data = (unsigned char*) malloc(4 * width * height); - glBindTexture(GL_TEXTURE_2D, tex.texID); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - - glGenTextures(1, &texID); - glBindTexture(GL_TEXTURE_2D, texID); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - - free(data); -} - -Texture::Texture(Texture&& tex) : Texture(0, 0) -{ - swap(*this, tex); -} - -Texture::~Texture() -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - glDeleteTextures(1, &texID); -} - -Texture& Texture::operator= (Texture tex) -{ - swap(*this, tex); - - return *this; -} - -void swap(Texture& tex1, Texture& tex2) -{ - std::swap(tex1.width, tex2.width); - std::swap(tex1.height, tex2.height); - std::swap(tex1.texID, tex2.texID); -} - -void Texture::fill(Rectangle dstrect, int r, int g, int b) -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - // Target the framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texID, 0); - - // Set up the vertex attributes - GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0; - GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0); - GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0; - GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0); - - GLfloat vertexbuffer_data[] = { - minx, miny, - maxx, miny, - maxx, maxy, - minx, miny, - minx, maxy, - maxx, maxy - }; - GLuint vertexbuffer; - glGenBuffers(1, &vertexbuffer); - glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertexbuffer_data), vertexbuffer_data, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); - - glViewport(0, 0, width, height); - glClear(GL_DEPTH_BUFFER_BIT); - glUseProgram(fillShader); - glUniform3f(glGetUniformLocation(fillShader, "vecColor"), r / 255.0, g / 255.0, b / 255.0); - - glDrawArrays(GL_TRIANGLES, 0, 6); - - glDisableVertexAttribArray(0); - glDeleteBuffers(1, &vertexbuffer); -} - -void Texture::blit(const Texture& srctex, Rectangle srcrect, Rectangle dstrect, double alpha) -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - alpha = glm::clamp(alpha, 0.0, 1.0); - - // Target the framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texID, 0); - - // Set up the vertex attributes - GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0; - GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0); - GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0; - GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0); - - GLfloat vertexbuffer_data[] = { - minx, miny, - maxx, miny, - minx, maxy, - maxx, maxy - }; - GLuint vertexbuffer; - glGenBuffers(1, &vertexbuffer); - glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertexbuffer_data), vertexbuffer_data, GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); - - GLfloat minu = (GLfloat) srcrect.x / srctex.width; - GLfloat minv = 1 - ((GLfloat) srcrect.y / srctex.height); - GLfloat maxu = (GLfloat) (srcrect.x + srcrect.w) / srctex.width; - GLfloat maxv = 1 - ((GLfloat) (srcrect.y + srcrect.h) / srctex.height); - - GLfloat texcoordbuffer_data[] = { - minu, minv, - maxu, minv, - minu, maxv, - maxu, maxv - }; - GLuint texcoordbuffer; - glGenBuffers(1, &texcoordbuffer); - glBindBuffer(GL_ARRAY_BUFFER, texcoordbuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(texcoordbuffer_data), texcoordbuffer_data, GL_STATIC_DRAW); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); - - // Set up the shader - glUseProgram(blitShader); - glClear(GL_DEPTH_BUFFER_BIT); - glViewport(0, 0, width, height); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, srctex.texID); - glUniform1i(glGetUniformLocation(blitShader, "srctex"), 0); - glUniform1f(glGetUniformLocation(blitShader, "alpha"), alpha); - - // Blit! - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - // Unload everything - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(0); - glDeleteBuffers(1, &texcoordbuffer); - glDeleteBuffers(1, &vertexbuffer); -} - -void bloomPass1(GLuint srcTex, GLuint dstTex, bool horizontal, glm::vec2 srcRes, glm::vec2 dstRes) -{ - glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dstTex, 0); - glViewport(0,0,dstRes.x,dstRes.y); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glUseProgram(bloom1Shader); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, srcTex); - glUniform1i(glGetUniformLocation(bloom1Shader, "inTex"), 0); - - glm::vec2 offset = glm::vec2(0.0); - if (horizontal) - { - offset.x = 1.2/srcRes.x; - } else { - offset.y = 1.2/srcRes.y; - } - - glUniform2f(glGetUniformLocation(bloom1Shader, "offset"), offset.x, offset.y); - - glEnableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); - glDrawArrays(GL_TRIANGLES, 0, 6); - glDisableVertexAttribArray(0); -} - -void Texture::renderScreen() const -{ - if (!rendererInitialized) - { - fprintf(stderr, "Renderer not initialized\n"); - exit(-1); - } - - // First we're going to composite our frame with the previous frame - // We start by setting up the framebuffer - glBindFramebuffer(GL_FRAMEBUFFER, generic_framebuffer); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, renderedTexBufs[curBuf], 0); - - // Set up the shader - glViewport(0,0,GAME_WIDTH,GAME_HEIGHT); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glUseProgram(ntscShader); - - // Use the current frame texture, nearest neighbor and clamped to edge - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texID); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glUniform1i(glGetUniformLocation(ntscShader, "curFrameSampler"), 0); - - // Use the previous frame composite texture, nearest neighbor and clamped to edge - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, renderedTexBufs[(curBuf + 1) % 2]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glUniform1i(glGetUniformLocation(ntscShader, "prevFrameSampler"), 1); - - // Load the NTSC artifact texture - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, artifactsTex); - glUniform1i(glGetUniformLocation(ntscShader, "NTSCArtifactSampler"), 2); - glUniform1f(glGetUniformLocation(ntscShader, "NTSCLerp"), curBuf * 1.0); - - if ((rand() % 60) == 0) - { - // Change the 0.0 to a 1.0 or a 10.0 for a glitchy effect! - glUniform1f(glGetUniformLocation(ntscShader, "Tuning_NTSC"), 0.0); - } else { - glUniform1f(glGetUniformLocation(ntscShader, "Tuning_NTSC"), 0.0); - } - - // Render our composition - glEnableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); - glDrawArrays(GL_TRIANGLES, 0, 6); - glDisableVertexAttribArray(0); - - // We're going to render the screen now - glBindFramebuffer(GL_FRAMEBUFFER, bloom_framebuffer); - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, preBloomTex, 0); - glViewport(0,0,buffer_width,buffer_height); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glUseProgram(finalShader); - - // Use the composited frame texture, linearly filtered and filling in black for the border - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, renderedTexBufs[curBuf]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - float border_color[] = {0.0f, 0.0f, 0.0f, 1.0f}; - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color); - glGenerateMipmap(GL_TEXTURE_2D); - glUniform1i(glGetUniformLocation(finalShader, "rendertex"), 0); - - // Use the scanlines texture - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, scanlinesTex); - glUniform1i(glGetUniformLocation(finalShader, "scanlinestex"), 1); - - // Initialize the MVP matrices - glm::mat4 p_matrix = glm::perspective(glm::radians(25.0f), (float) buffer_width / (float) buffer_height, 0.1f, 100.0f); - glm::mat4 v_matrix = glm::lookAt(glm::vec3(3.75,0,0), glm::vec3(0,0,0), glm::vec3(0,1,0)); - glm::mat4 m_matrix = glm::mat4(1.0); - glm::mat4 mvp_matrix = p_matrix * v_matrix * m_matrix; - - glUniformMatrix4fv(glGetUniformLocation(finalShader, "MVP"), 1, GL_FALSE, &mvp_matrix[0][0]); - glUniformMatrix4fv(glGetUniformLocation(finalShader, "worldMat"), 1, GL_FALSE, &m_matrix[0][0]); - glUniform2f(glGetUniformLocation(finalShader, "resolution"), buffer_width, buffer_height); - glUniform1f(glGetUniformLocation(finalShader, "iGlobalTime"), glfwGetTime()); - glUniform3f(glGetUniformLocation(finalShader, "frameColor"), 0.76f, 0.78f, 0.81f); - - glEnableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, mesh_vertexbuffer); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); - - glEnableVertexAttribArray(1); - glBindBuffer(GL_ARRAY_BUFFER, mesh_normalbuffer); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); - - glEnableVertexAttribArray(2); - glBindBuffer(GL_ARRAY_BUFFER, mesh_uvbuffer); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); - - glDrawArrays(GL_TRIANGLES, 0, mesh_numvertices); - glDisableVertexAttribArray(2); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(0); - - // First pass of bloom! - glm::vec2 buffer_size = glm::vec2(buffer_width, buffer_height); - bloomPass1(preBloomTex, bloomPassTex1, true, buffer_size, buffer_size / 4.0f); - bloomPass1(bloomPassTex1, bloomPassTex2, false, buffer_size / 4.0f, buffer_size / 4.0f); - - // Do the second pass of bloom and render to screen - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(0, 0, buffer_width, buffer_height); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glUseProgram(bloom2Shader); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, preBloomTex); - glUniform1i(glGetUniformLocation(bloom2Shader, "clearTex"), 0); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, bloomPassTex2); - glUniform1i(glGetUniformLocation(bloom2Shader, "blurTex"), 1); - - glUniform1f(glGetUniformLocation(bloom2Shader, "iGlobalTime"), glfwGetTime()); - - glEnableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, quad_vertexbuffer); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); - glDrawArrays(GL_TRIANGLES, 0, 6); - glDisableVertexAttribArray(0); - - glfwSwapBuffers(window); - - curBuf = (curBuf + 1) % 2; -} - -Rectangle Texture::entirety() const -{ - return {0, 0, width, height}; -} diff --git a/src/renderer.h b/src/renderer.h deleted file mode 100644 index 6dccf7a..0000000 --- a/src/renderer.h +++ /dev/null @@ -1,37 +0,0 @@ -#include -#include - -#ifndef RENDERER_H -#define RENDERER_H - -struct Rectangle { - int x; - int y; - int w; - int h; -}; - -class Texture { - public: - Texture(int width, int height); - Texture(const char* file); - Texture(const Texture& tex); - Texture(Texture&& tex); - ~Texture(); - Texture& operator= (Texture tex); - friend void swap(Texture& tex1, Texture& tex2); - void fill(Rectangle loc, int r, int g, int b); - void blit(const Texture& src, Rectangle srcrect, Rectangle dstrect, double alpha = 1.0); - void renderScreen() const; - Rectangle entirety() const; - - private: - GLuint texID; - int width; - int height; -}; - -GLFWwindow* initRenderer(); -void destroyRenderer(); - -#endif diff --git a/src/renderer/gl.h b/src/renderer/gl.h new file mode 100644 index 0000000..4e98c42 --- /dev/null +++ b/src/renderer/gl.h @@ -0,0 +1,7 @@ +#ifndef GL_H_3EE4A268 +#define GL_H_3EE4A268 + +#include +#include + +#endif /* end of include guard: GL_H_3EE4A268 */ diff --git a/src/renderer/mesh.cpp b/src/renderer/mesh.cpp new file mode 100644 index 0000000..b06b723 --- /dev/null +++ b/src/renderer/mesh.cpp @@ -0,0 +1,109 @@ +#include "mesh.h" +#include +#include +#include +#include +#include "util.h" + +Mesh::Mesh(std::string filename) +{ + std::ifstream meshfile(filename); + if (!meshfile.is_open()) + { + throw std::invalid_argument("Could not open mesh file"); + } + + std::vector tempVertices; + std::vector tempUvs; + std::vector tempNormals; + + std::vector outVertices; + std::vector outUvs; + std::vector outNormals; + std::map elementIds; + std::vector indices; + + while (meshfile) + { + std::string linetype; + meshfile >> linetype; + + if (linetype == "v") + { + glm::vec3 vertex; + meshfile >> vertex.x >> vertex.y >> vertex.z; + + tempVertices.push_back(std::move(vertex)); + } else if (linetype == "vt") + { + glm::vec2 uv; + meshfile >> uv.x >> uv.y; + + tempUvs.push_back(std::move(uv)); + } else if (linetype == "vn") + { + glm::vec3 normal; + meshfile >> normal.x >> normal.y >> normal.z; + + tempNormals.push_back(std::move(normal)); + } else if (linetype == "f") + { + element elements[3]; + + meshfile + >> elements[0].vertexId >> chlit('/') + >> elements[0].uvId >> chlit('/') + >> elements[0].normalId + >> elements[1].vertexId >> chlit('/') + >> elements[1].uvId >> chlit('/') + >> elements[1].normalId + >> elements[2].vertexId >> chlit('/') + >> elements[2].uvId >> chlit('/') + >> elements[2].normalId; + + for (size_t i = 0; i < 3; i++) + { + if (!elementIds.count(elements[i])) + { + elementIds[elements[i]] = outVertices.size(); + + outVertices.push_back(tempVertices[elements[i].vertexId - 1]); + outUvs.push_back(tempUvs[elements[i].uvId - 1]); + outNormals.push_back(tempNormals[elements[i].normalId - 1]); + } + + indices.push_back(elementIds[elements[i]]); + } + } + } + + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer_.getId()); + glBufferData( + GL_ARRAY_BUFFER, + outVertices.size() * sizeof(glm::vec3), + outVertices.data(), + GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, uvBuffer_.getId()); + glBufferData( + GL_ARRAY_BUFFER, + outUvs.size() * sizeof(glm::vec2), + outUvs.data(), + GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, normalBuffer_.getId()); + glBufferData( + GL_ARRAY_BUFFER, + outNormals.size() * sizeof(glm::vec3), + outNormals.data(), + GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer_.getId()); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + indices.size() * sizeof(unsigned short), + indices.data(), + GL_STATIC_DRAW); + + indexCount_ = indices.size(); +} diff --git a/src/renderer/mesh.h b/src/renderer/mesh.h new file mode 100644 index 0000000..06bf65d --- /dev/null +++ b/src/renderer/mesh.h @@ -0,0 +1,62 @@ +#ifndef MESH_H_76B72E12 +#define MESH_H_76B72E12 + +#include +#include "gl.h" +#include "wrappers.h" + +class Mesh { +public: + + Mesh(std::string filename); + + Mesh(const Mesh& other) = delete; + Mesh& operator=(const Mesh& other) = delete; + + inline GLuint getVertexBufferId() const + { + return vertexBuffer_.getId(); + } + + inline GLuint getUvBufferId() const + { + return uvBuffer_.getId(); + } + + inline GLuint getNormalBufferId() const + { + return normalBuffer_.getId(); + } + + inline GLuint getIndexBufferId() const + { + return indexBuffer_.getId(); + } + + inline size_t getIndexCount() const + { + return indexCount_; + } + +private: + + struct element { + size_t vertexId; + size_t uvId; + size_t normalId; + + bool operator<(const element& other) const + { + return std::tie(vertexId, uvId, normalId) < + std::tie(other.vertexId, other.uvId, other.normalId); + } + }; + + GLBuffer vertexBuffer_; + GLBuffer uvBuffer_; + GLBuffer normalBuffer_; + GLBuffer indexBuffer_; + size_t indexCount_; +}; + +#endif /* end of include guard: MESH_H_76B72E12 */ diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp new file mode 100644 index 0000000..6eef2f3 --- /dev/null +++ b/src/renderer/renderer.cpp @@ -0,0 +1,635 @@ +#include "renderer.h" +#include "consts.h" +#include "game.h" +#include +#include "texture.h" + +// include stb_image +#define STB_IMAGE_IMPLEMENTATION +#define STBI_ONLY_PNG +#define STBI_ONLY_BMP +#include "stb_image.h" + +void setFramebufferSize(GLFWwindow* w, int width, int height) +{ + Game& game = *static_cast(glfwGetWindowUserPointer(w)); + Renderer& renderer = game.getRenderer(); + + renderer.width_ = width; + renderer.height_ = height; + + renderer.bloomFb_ = {}; + renderer.bloomDepth_ = {}; + renderer.preBloomTex_ = {}; + renderer.bloomPassTex1_ = {}; + renderer.bloomPassTex2_ = {}; + + renderer.initializeFramebuffers(); +} + +bool Renderer::singletonInitialized_ = false; + +Renderer::Window::Window() +{ + // Initialize GLFW + if (!glfwInit()) + { + throw std::runtime_error("Failed to initialize GLFW"); + } + + glfwWindowHint(GLFW_SAMPLES, 4); // 4x antialiasing + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // We want version 3.3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Mac requires this + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + + // Create a window + window_ = glfwCreateWindow(1024, 768, "Aromatherapy", nullptr, nullptr); + if (window_ == nullptr) + { + throw std::runtime_error("Failed to open GLFW window"); + } + + glfwMakeContextCurrent(window_); + + glewExperimental = true; // Needed in core profile + if (glewInit() != GLEW_OK) + { + throw std::runtime_error("Failed to initialize GLEW"); + } + + glfwSetFramebufferSizeCallback(window_, &setFramebufferSize); +} + +Renderer::Window::~Window() +{ + glfwTerminate(); +} + +Renderer::Renderer() : + monitor_("res/monitor-old.obj"), + ntscShader_("ntsc"), + finalShader_("final"), + blitShader_("blit"), + fillShader_("fill"), + bloom1Shader_("bloom1"), + bloom2Shader_("bloom2") +{ + if (singletonInitialized_) + { + throw std::logic_error("Singleton renderer already initialized"); + } + + singletonInitialized_ = true; + + // Set up vertex array object + glBindVertexArray(vao_.getId()); + + // Enable depth testing + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + + // Enable blending + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Set up the rendering buffers and textures + glfwGetFramebufferSize(window_.getHandle(), &width_, &height_); + + initializeFramebuffers(); + + // Load the vertices of a flat surface + GLfloat g_quad_vertex_buffer_data[] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 1.0f, 1.0f, 0.0f, + }; + + glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId()); + glBufferData( + GL_ARRAY_BUFFER, + sizeof(GLfloat) * 18, + g_quad_vertex_buffer_data, + GL_STATIC_DRAW); + + // Load NTSC artifacts + int atdw, atdh; + unsigned char* artifactsData = + stbi_load("res/artifacts.bmp", &atdw, &atdh, 0, 3); + + flipImageData(artifactsData, atdw, atdh, 3); + + glBindTexture(GL_TEXTURE_2D, artifactsTex_.getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + atdw, + atdh, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + artifactsData); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri( + GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST_MIPMAP_NEAREST); + + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(artifactsData); + + // Load NTSC scanlines + unsigned char* scanlinesData = + stbi_load("res/scanlines_333.bmp", &atdw, &atdh, 0, 3); + + flipImageData(scanlinesData, atdw, atdh, 3); + + glBindTexture(GL_TEXTURE_2D, scanlinesTex_.getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + atdw, + atdh, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + scanlinesData); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri( + GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST_MIPMAP_NEAREST); + + glGenerateMipmap(GL_TEXTURE_2D); + stbi_image_free(scanlinesData); +} + +void Renderer::initializeFramebuffers() +{ + // Set up the framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId()); + GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0}; + glDrawBuffers(1, DrawBuffers); + + // Set up the bloom framebuffer and depthbuffer + glBindFramebuffer(GL_FRAMEBUFFER, bloomFb_.getId()); + GLenum DrawBuffers2[1] = {GL_COLOR_ATTACHMENT1}; + glDrawBuffers(1, DrawBuffers2); + + glBindRenderbuffer(GL_RENDERBUFFER, bloomDepth_.getId()); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width_, height_); + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + bloomDepth_.getId()); + + // Set up the NTSC rendering buffers + glBindTexture(GL_TEXTURE_2D, renderPages_[0].getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + GAME_WIDTH, + GAME_HEIGHT, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, renderPages_[1].getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + GAME_WIDTH, + GAME_HEIGHT, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Set up bloom rendering buffers + glBindTexture(GL_TEXTURE_2D, preBloomTex_.getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + width_, + height_, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, bloomPassTex1_.getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + width_ / 4, + height_ / 4, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, bloomPassTex2_.getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB, + width_ / 4, + height_ / 4, + 0, + GL_RGB, + GL_UNSIGNED_BYTE, + 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +Renderer::~Renderer() +{ + singletonInitialized_ = false; +} + +void Renderer::fill(Texture& tex, Rectangle dstrect, int r, int g, int b) +{ + // Target the framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId()); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex.getId(), 0); + + // Set up the vertex attributes + int width = tex.getWidth(); + int height = tex.getHeight(); + + GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0; + GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0); + GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0; + GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0); + + GLfloat vertexData[] = { + minx, miny, + maxx, miny, + maxx, maxy, + minx, miny, + minx, maxy, + maxx, maxy + }; + + GLBuffer vertexBuffer; + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getId()); + glBufferData( + GL_ARRAY_BUFFER, + sizeof(GLfloat) * 12, + vertexData, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + glViewport(0, 0, tex.getWidth(), tex.getHeight()); + glClear(GL_DEPTH_BUFFER_BIT); + + fillShader_.use(); + glUniform3f( + fillShader_.getUniformLocation("vecColor"), + r / 255.0, + g / 255.0, + b / 255.0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableVertexAttribArray(0); +} + +void Renderer::blit( + const Texture& src, + Texture& dst, + Rectangle srcrect, + Rectangle dstrect, + double alpha) +{ + alpha = glm::clamp(alpha, 0.0, 1.0); + + // Target the framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId()); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dst.getId(), 0); + + // Set up the vertex attributes + int width = dst.getWidth(); + int height = dst.getHeight(); + + GLfloat minx = (GLfloat) dstrect.x / width * 2.0 - 1.0; + GLfloat miny = -((GLfloat) dstrect.y / height * 2.0 - 1.0); + GLfloat maxx = (GLfloat) (dstrect.x + dstrect.w) / width * 2.0 - 1.0; + GLfloat maxy = -((GLfloat) (dstrect.y + dstrect.h) / height * 2.0 - 1.0); + + GLfloat vertexData[] = { + minx, miny, + maxx, miny, + minx, maxy, + maxx, maxy + }; + + GLBuffer vertexBuffer; + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.getId()); + glBufferData( + GL_ARRAY_BUFFER, + sizeof(GLfloat) * 8, + vertexData, + GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + GLfloat minu = (GLfloat) srcrect.x / src.getWidth(); + GLfloat minv = 1 - ((GLfloat) srcrect.y / src.getHeight()); + GLfloat maxu = (GLfloat) (srcrect.x + srcrect.w) / src.getWidth(); + GLfloat maxv = 1 - ((GLfloat) (srcrect.y + srcrect.h) / src.getHeight()); + + GLfloat uvData[] = { + minu, minv, + maxu, minv, + minu, maxv, + maxu, maxv + }; + + GLBuffer uvBuffer; + glBindBuffer(GL_ARRAY_BUFFER, uvBuffer.getId()); + glBufferData( + GL_ARRAY_BUFFER, + sizeof(GLfloat) * 8, + uvData, + GL_STATIC_DRAW); + + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + // Set up the shader + blitShader_.use(); + glClear(GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, dst.getWidth(), dst.getHeight()); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, src.getId()); + glUniform1i(blitShader_.getUniformLocation("srctex"), 0); + glUniform1f(blitShader_.getUniformLocation("alpha"), alpha); + + // Blit! + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + + // Unload everything + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); +} + +void Renderer::bloomPass1( + const GLTexture& src, + GLTexture& dst, + bool horizontal, + glm::vec2 srcRes, + glm::vec2 dstRes) +{ + glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId()); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dst.getId(), 0); + glViewport(0,0,dstRes.x,dstRes.y); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + bloom1Shader_.use(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, src.getId()); + glUniform1i(bloom1Shader_.getUniformLocation("inTex"), 0); + + glm::vec2 offset = glm::vec2(0.0); + if (horizontal) + { + offset.x = 1.2/srcRes.x; + } else { + offset.y = 1.2/srcRes.y; + } + + glUniform2f(bloom1Shader_.getUniformLocation("offset"), offset.x, offset.y); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId()); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + glDrawArrays(GL_TRIANGLES, 0, 6); + glDisableVertexAttribArray(0); +} + +void Renderer::renderScreen(const Texture& tex) +{ + // First we're going to composite our frame with the previous frame + // We start by setting up the framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, genericFb_.getId()); + glFramebufferTexture( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + renderPages_[curBuf_].getId(), + 0); + + // Set up the shader + glViewport(0,0,GAME_WIDTH,GAME_HEIGHT); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + ntscShader_.use(); + + // Use the current frame texture, nearest neighbor and clamped to edge + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex.getId()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glUniform1i(ntscShader_.getUniformLocation("curFrameSampler"), 0); + + // Use the previous frame composite texture, nearest neighbor and clamped to + // edge + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, renderPages_[(curBuf_ + 1) % 2].getId()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glUniform1i(ntscShader_.getUniformLocation("prevFrameSampler"), 1); + + // Load the NTSC artifact texture + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, artifactsTex_.getId()); + glUniform1i(ntscShader_.getUniformLocation("NTSCArtifactSampler"), 2); + glUniform1f(ntscShader_.getUniformLocation("NTSCLerp"), curBuf_ * 1.0); + + // Change the 0.0 to a 1.0 or a 10.0 for a glitchy effect! + glUniform1f(ntscShader_.getUniformLocation("Tuning_NTSC"), 0.0); + + // Render our composition + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId()); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + glDrawArrays(GL_TRIANGLES, 0, 6); + glDisableVertexAttribArray(0); + + // We're going to render the screen now + glBindFramebuffer(GL_FRAMEBUFFER, bloomFb_.getId()); + glFramebufferTexture( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT1, + preBloomTex_.getId(), + 0); + + glViewport(0,0,width_,height_); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + finalShader_.use(); + + // Use the composited frame texture, linearly filtered and filling in black + // for the border + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, renderPages_[curBuf_].getId()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + + float borderColor[] = {0.0f, 0.0f, 0.0f, 1.0f}; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + + glGenerateMipmap(GL_TEXTURE_2D); + glUniform1i(finalShader_.getUniformLocation("rendertex"), 0); + + // Use the scanlines texture + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, scanlinesTex_.getId()); + glUniform1i(finalShader_.getUniformLocation("scanlinestex"), 1); + + // Initialize the MVP matrices + glm::mat4 p_matrix = glm::perspective( + glm::radians(25.0f), + static_cast(width_) / static_cast(height_), + 0.1f, + 100.0f); + + glm::mat4 v_matrix = glm::lookAt( + glm::vec3(3.75,0,0), // Camera + glm::vec3(0,0,0), // Center + glm::vec3(0,1,0)); // Up + + glm::mat4 m_matrix = glm::mat4(1.0); + glm::mat4 mvp_matrix = p_matrix * v_matrix * m_matrix; + + glUniformMatrix4fv( + finalShader_.getUniformLocation("MVP"), + 1, + GL_FALSE, + &mvp_matrix[0][0]); + + glUniformMatrix4fv( + finalShader_.getUniformLocation("worldMat"), + 1, + GL_FALSE, + &m_matrix[0][0]); + + glUniform2f(finalShader_.getUniformLocation("resolution"), width_, height_); + glUniform1f(finalShader_.getUniformLocation("iGlobalTime"), glfwGetTime()); + + glUniform3f( + finalShader_.getUniformLocation("frameColor"), + 0.76f, + 0.78f, + 0.81f); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, monitor_.getVertexBufferId()); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, monitor_.getNormalBufferId()); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + + glEnableVertexAttribArray(2); + glBindBuffer(GL_ARRAY_BUFFER, monitor_.getUvBufferId()); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, nullptr); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, monitor_.getIndexBufferId()); + glDrawElements( + GL_TRIANGLES, + monitor_.getIndexCount(), + GL_UNSIGNED_SHORT, + nullptr); + + glDisableVertexAttribArray(2); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(0); + + // First pass of bloom! + glm::vec2 bufferSize = glm::vec2(width_, height_); + + bloomPass1( + preBloomTex_, + bloomPassTex1_, + true, + bufferSize, + bufferSize / 4.0f); + + bloomPass1( + bloomPassTex1_, + bloomPassTex2_, + false, + bufferSize / 4.0f, + bufferSize / 4.0f); + + // Do the second pass of bloom and render to screen + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, width_, height_); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + bloom2Shader_.use(); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, preBloomTex_.getId()); + glUniform1i(bloom2Shader_.getUniformLocation("clearTex"), 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, bloomPassTex2_.getId()); + glUniform1i(bloom2Shader_.getUniformLocation("blurTex"), 1); + + glUniform1f(bloom2Shader_.getUniformLocation("iGlobalTime"), glfwGetTime()); + + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, quadBuffer_.getId()); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); + glDrawArrays(GL_TRIANGLES, 0, 6); + glDisableVertexAttribArray(0); + + glfwSwapBuffers(window_.getHandle()); + + curBuf_ = (curBuf_ + 1) % 2; +} diff --git a/src/renderer/renderer.h b/src/renderer/renderer.h new file mode 100644 index 0000000..0b10af5 --- /dev/null +++ b/src/renderer/renderer.h @@ -0,0 +1,114 @@ +#ifndef RENDERER_H +#define RENDERER_H + +#include "gl.h" +#include "wrappers.h" +#include "mesh.h" +#include "shader.h" +#include + +class Texture; +struct Rectangle; + +class Renderer { +public: + + class Window { + public: + + Window(); + + Window(const Window& other) = delete; + Window& operator=(const Window& other) = delete; + + ~Window(); + + inline GLFWwindow* getHandle() + { + return window_; + } + + private: + + GLFWwindow* window_; + }; + + static inline bool isSingletonInitialized() + { + return singletonInitialized_; + } + + Renderer(); + + Renderer(const Renderer& other) = delete; + Renderer& operator=(const Renderer& other) = delete; + + ~Renderer(); + + inline Window& getWindow() + { + return window_; + } + + void fill( + Texture& tex, + Rectangle loc, + int r, + int g, + int b); + + void blit( + const Texture& src, + Texture& dst, + Rectangle srcrect, + Rectangle dstrect, + double alpha = 1.0); + + void renderScreen(const Texture& tex); + +private: + + friend void setFramebufferSize(GLFWwindow* w, int width, int height); + + void initializeFramebuffers(); + + void bloomPass1( + const GLTexture& src, + GLTexture& dst, + bool horizontal, + glm::vec2 srcRes, + glm::vec2 dstRes); + + static bool singletonInitialized_; + + Window window_; + GLVertexArray vao_; + + GLFramebuffer genericFb_; + GLFramebuffer bloomFb_; + GLRenderbuffer bloomDepth_; + + GLTexture renderPages_[2]; + GLTexture preBloomTex_; + GLTexture bloomPassTex1_; + GLTexture bloomPassTex2_; + + Mesh monitor_; + GLBuffer quadBuffer_; + + GLTexture artifactsTex_; + GLTexture scanlinesTex_; + + Shader ntscShader_; + Shader finalShader_; + Shader blitShader_; + Shader fillShader_; + Shader bloom1Shader_; + Shader bloom2Shader_; + + size_t curBuf_ = 0; + int width_; + int height_; +}; + +#endif diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp new file mode 100644 index 0000000..735fc22 --- /dev/null +++ b/src/renderer/shader.cpp @@ -0,0 +1,84 @@ +#include "shader.h" +#include +#include +#include "util.h" + +Shader::Shader(std::string name) +{ + GLShader vertexShader(GL_VERTEX_SHADER); + GLShader fragmentShader(GL_FRAGMENT_SHADER); + + std::ifstream vertexFile("shaders/" + name + ".vertex"); + std::ifstream fragmentFile("shaders/" + name + ".fragment"); + + std::string vertexCode(slurp(vertexFile)); + std::string fragmentCode(slurp(fragmentFile)); + + const char* vertexCodePtr = vertexCode.c_str(); + const char* fragmentCodePtr = fragmentCode.c_str(); + + glShaderSource(vertexShader.getId(), 1, &vertexCodePtr, nullptr); + glShaderSource(fragmentShader.getId(), 1, &fragmentCodePtr, nullptr); + + glCompileShader(vertexShader.getId()); + glCompileShader(fragmentShader.getId()); + +#ifdef DEBUG + GLint result = GL_FALSE; + int infoLogLength; + + glGetShaderiv(vertexShader.getId(), GL_COMPILE_STATUS, &result); + + if (result == GL_FALSE) + { + glGetShaderiv(vertexShader.getId(), GL_INFO_LOG_LENGTH, &infoLogLength); + + std::vector errMsg(infoLogLength); + glGetShaderInfoLog( + vertexShader.getId(), + infoLogLength, + nullptr, + errMsg.data()); + + throw std::gl_error("Could not compile shader", errMsg.data()); + } + + glGetShaderiv(fragmentShader.getId(), GL_COMPILE_STATUS, &result); + + if (result == GL_FALSE) + { + glGetShaderiv(fragmentShader.getId(), GL_INFO_LOG_LENGTH, &infoLogLength); + + std::vector errMsg(infoLogLength); + glGetShaderInfoLog( + fragmentShader.getId(), + infoLogLength, + nullptr, + errMsg.data()); + + throw std::gl_error("Could not compile shader", errMsg.data()); + } +#endif + + glAttachShader(program_.getId(), vertexShader.getId()); + glAttachShader(program_.getId(), fragmentShader.getId()); + glLinkProgram(program_.getId()); + +#ifdef DEBUG + glGetProgramiv(program_.getId(), GL_LINK_STATUS, &result); + + if (result == GL_FALSE) + { + glGetProgramiv(program_.getId(), GL_INFO_LOG_LENGTH, &infoLogLength); + + std::vector errMsg(infoLogLength); + glGetProgramInfoLog( + program_.getId(), + infoLogLength, + nullptr, + errMsg.data()); + + throw std::gl_error("Could not link shader program", errMsg.data()); + } +#endif +} diff --git a/src/renderer/shader.h b/src/renderer/shader.h new file mode 100644 index 0000000..d2c673c --- /dev/null +++ b/src/renderer/shader.h @@ -0,0 +1,58 @@ +#ifndef SHADER_H_25115B63 +#define SHADER_H_25115B63 + +#include +#include +#include "gl.h" +#include "wrappers.h" + +class gl_error : public std::logic_error { +public: + + gl_error( + const char* msg, + std::string info) : + std::logic_error(msg), + info_(std::move(info)) + { + } + + gl_error( + std::string& msg, + std::string info) : + std::logic_error(msg), + info_(std::move(info)) + { + } + + inline const std::string& getInfo() const + { + return info_; + } + +private: + + std::string info_; +}; + +class Shader { +public: + + Shader(std::string name); + + inline void use() + { + glUseProgram(program_.getId()); + } + + inline GLint getUniformLocation(const GLchar* name) + { + return glGetUniformLocation(program_.getId(), name); + } + +private: + + GLProgram program_; +}; + +#endif /* end of include guard: SHADER_H_25115B63 */ diff --git a/src/renderer/texture.cpp b/src/renderer/texture.cpp new file mode 100644 index 0000000..2728665 --- /dev/null +++ b/src/renderer/texture.cpp @@ -0,0 +1,124 @@ +#include "texture.h" +#include +#include "renderer.h" +#include "util.h" + +// include stb_image +#define STBI_ONLY_PNG +#define STBI_ONLY_BMP +#include "stb_image.h" + +Texture::Texture( + int width, + int height) : + width_(width), + height_(height) +{ + if (!Renderer::isSingletonInitialized()) + { + throw std::logic_error("Renderer needs to be initialized"); + } + + glBindTexture(GL_TEXTURE_2D, texture_.getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width_, + height_, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); +} + +Texture::Texture(const char* filename) +{ + if (!Renderer::isSingletonInitialized()) + { + throw std::logic_error("Renderer needs to be initialized"); + } + + glBindTexture(GL_TEXTURE_2D, texture_.getId()); + unsigned char* data = stbi_load(filename, &width_, &height_, 0, 4); + flipImageData(data, width_, height_, 4); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width_, + height_, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + data); + + stbi_image_free(data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +Texture::Texture( + const Texture& tex) : + width_(tex.width_), + height_(tex.height_) +{ + if (!Renderer::isSingletonInitialized()) + { + throw std::logic_error("Renderer needs to be initialized"); + } + + unsigned char* data = new unsigned char[4 * width_ * height_]; + glBindTexture(GL_TEXTURE_2D, tex.getId()); + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + + glBindTexture(GL_TEXTURE_2D, texture_.getId()); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + width_, + height_, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + data); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + + delete[] data; +} + +Texture::Texture(Texture&& tex) : Texture(0, 0) +{ + swap(*this, tex); +} + +Texture& Texture::operator= (Texture tex) +{ + swap(*this, tex); + + return *this; +} + +void swap(Texture& tex1, Texture& tex2) +{ + std::swap(tex1.width_, tex2.width_); + std::swap(tex1.height_, tex2.height_); + std::swap(tex1.texture_, tex2.texture_); +} + +Rectangle Texture::entirety() const +{ + return {0, 0, width_, height_}; +} diff --git a/src/renderer/texture.h b/src/renderer/texture.h new file mode 100644 index 0000000..3aa8773 --- /dev/null +++ b/src/renderer/texture.h @@ -0,0 +1,52 @@ +#ifndef TEXTURE_H_84EC6DF6 +#define TEXTURE_H_84EC6DF6 + +#include "wrappers.h" + +struct Rectangle { + int x; + int y; + int w; + int h; +}; + +class Texture { +public: + + Texture(int width, int height); + + Texture(const char* file); + + Texture(const Texture& tex); + + Texture(Texture&& tex); + + Texture& operator= (Texture tex); + + friend void swap(Texture& tex1, Texture& tex2); + + Rectangle entirety() const; + + inline GLuint getId() const + { + return texture_.getId(); + } + + inline int getWidth() const + { + return width_; + } + + inline int getHeight() const + { + return height_; + } + +private: + + GLTexture texture_; + int width_; + int height_; +}; + +#endif /* end of include guard: TEXTURE_H_84EC6DF6 */ diff --git a/src/renderer/wrappers.h b/src/renderer/wrappers.h new file mode 100644 index 0000000..c6edc11 --- /dev/null +++ b/src/renderer/wrappers.h @@ -0,0 +1,273 @@ +#ifndef WRAPPERS_H_1EE0965B +#define WRAPPERS_H_1EE0965B + +#include "gl.h" +#include + +class GLVertexArray { +public: + + GLVertexArray() + { + glGenVertexArrays(1, &id_); + } + + GLVertexArray(const GLVertexArray& other) = delete; + GLVertexArray& operator=(const GLVertexArray& other) = delete; + + GLVertexArray(GLVertexArray&& other) : GLVertexArray() + { + std::swap(id_, other.id_); + } + + GLVertexArray& operator=(GLVertexArray&& other) + { + std::swap(id_, other.id_); + + return *this; + } + + ~GLVertexArray() + { + glDeleteVertexArrays(1, &id_); + } + + inline GLuint getId() const + { + return id_; + } + +private: + + GLuint id_; +}; + +class GLFramebuffer { +public: + + GLFramebuffer() + { + glGenFramebuffers(1, &id_); + } + + GLFramebuffer(const GLFramebuffer& other) = delete; + GLFramebuffer& operator=(const GLFramebuffer& other) = delete; + + GLFramebuffer(GLFramebuffer&& other) : GLFramebuffer() + { + std::swap(id_, other.id_); + } + + GLFramebuffer& operator=(GLFramebuffer&& other) + { + std::swap(id_, other.id_); + + return *this; + } + + ~GLFramebuffer() + { + glDeleteFramebuffers(1, &id_); + } + + inline GLuint getId() const + { + return id_; + } + +private: + + GLuint id_; +}; + +class GLRenderbuffer { +public: + + GLRenderbuffer() + { + glGenRenderbuffers(1, &id_); + } + + GLRenderbuffer(const GLRenderbuffer& other) = delete; + GLRenderbuffer& operator=(const GLRenderbuffer& other) = delete; + + GLRenderbuffer(GLRenderbuffer&& other) : GLRenderbuffer() + { + std::swap(id_, other.id_); + } + + GLRenderbuffer& operator=(GLRenderbuffer&& other) + { + std::swap(id_, other.id_); + + return *this; + } + + ~GLRenderbuffer() + { + glDeleteRenderbuffers(1, &id_); + } + + inline GLuint getId() const + { + return id_; + } + +private: + + GLuint id_; +}; + +class GLBuffer { +public: + + GLBuffer() + { + glGenBuffers(1, &id_); + } + + GLBuffer(const GLBuffer& other) = delete; + GLBuffer& operator=(const GLBuffer& other) = delete; + + GLBuffer(GLBuffer&& other) : GLBuffer() + { + std::swap(id_, other.id_); + } + + GLBuffer& operator=(GLBuffer&& other) + { + std::swap(id_, other.id_); + + return *this; + } + + ~GLBuffer() + { + glDeleteBuffers(1, &id_); + } + + inline GLuint getId() const + { + return id_; + } + +private: + + GLuint id_; +}; + +class GLTexture { +public: + + GLTexture() + { + glGenTextures(1, &id_); + } + + GLTexture(const GLTexture& other) = delete; + GLTexture& operator=(const GLTexture& other) = delete; + + GLTexture(GLTexture&& other) : GLTexture() + { + std::swap(id_, other.id_); + } + + GLTexture& operator=(GLTexture&& other) + { + std::swap(id_, other.id_); + + return *this; + } + + ~GLTexture() + { + glDeleteTextures(1, &id_); + } + + inline GLuint getId() const + { + return id_; + } + +private: + + GLuint id_; +}; + +class GLShader { +public: + + GLShader(GLenum type) + { + id_ = glCreateShader(type); + } + + GLShader(const GLShader& other) = delete; + GLShader& operator=(const GLShader& other) = delete; + + GLShader(GLShader&& other) : GLShader(GL_VERTEX_SHADER) + { + std::swap(id_, other.id_); + } + + GLShader& operator=(GLShader&& other) + { + std::swap(id_, other.id_); + + return *this; + } + + ~GLShader() + { + glDeleteShader(id_); + } + + inline GLuint getId() const + { + return id_; + } + +private: + + GLuint id_; +}; + +class GLProgram { +public: + + GLProgram() + { + id_ = glCreateProgram(); + } + + GLProgram(const GLProgram& other) = delete; + GLProgram& operator=(const GLProgram& other) = delete; + + GLProgram(GLProgram&& other) : GLProgram() + { + std::swap(id_, other.id_); + } + + GLProgram& operator=(GLProgram&& other) + { + std::swap(id_, other.id_); + + return *this; + } + + ~GLProgram() + { + glDeleteProgram(id_); + } + + inline GLuint getId() const + { + return id_; + } + +private: + + GLuint id_; +}; + +#endif /* end of include guard: WRAPPERS_H_1EE0965B */ diff --git a/src/systems/animating.cpp b/src/systems/animating.cpp index 91fe925..22224c9 100644 --- a/src/systems/animating.cpp +++ b/src/systems/animating.cpp @@ -51,8 +51,9 @@ void AnimatingSystem::render(Texture& texture) transform.getH()}; const AnimationSet& aset = sprite.getAnimationSet(); - texture.blit( + game_.getRenderer().blit( aset.getTexture(), + texture, aset.getFrameRect(sprite.getFrame()), dstrect); } diff --git a/src/systems/animating.h b/src/systems/animating.h index d6a89a5..548bff1 100644 --- a/src/systems/animating.h +++ b/src/systems/animating.h @@ -3,7 +3,7 @@ #include "system.h" #include -#include "renderer.h" +#include "renderer/texture.h" class AnimatingSystem : public System { public: diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index 5b63ded..120a27a 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -48,7 +48,11 @@ void MappingSystem::render(Texture& texture) TILE_WIDTH, TILE_HEIGHT}; - texture.blit(mappable.getTileset(), std::move(src), std::move(dst)); + game_.getRenderer().blit( + mappable.getTileset(), + texture, + std::move(src), + std::move(dst)); } } @@ -67,7 +71,11 @@ void MappingSystem::render(Texture& texture) TILE_WIDTH, TILE_HEIGHT}; - texture.blit(mappable.getFont(), std::move(src), std::move(dst)); + game_.getRenderer().blit( + mappable.getFont(), + texture, + std::move(src), + std::move(dst)); } } } diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..f0c39fd --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,30 @@ +#include "util.h" + +std::string slurp(std::ifstream& in) +{ + std::stringstream sstr; + sstr << in.rdbuf(); + return sstr.str(); +} + +void flipImageData( + unsigned char* data, + int width, + int height, + int comps) +{ + unsigned char* dataCopy = new unsigned char[width * height * comps]; + memcpy(dataCopy, data, width * height * comps); + + int rowSize = width * comps; + + for (int i = 0; i < height; i++) + { + memcpy( + data + (rowSize * i), + dataCopy + (rowSize * (height - i - 1)), + rowSize); + } + + delete[] dataCopy; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..72cb0d3 --- /dev/null +++ b/src/util.h @@ -0,0 +1,41 @@ +#ifndef ALGORITHMS_H_1DDC517E +#define ALGORITHMS_H_1DDC517E + +#include +#include +#include + +template< typename ContainerT, typename PredicateT > +void erase_if( ContainerT& items, const PredicateT& predicate ) { + for( auto it = items.begin(); it != items.end(); ) { + if( predicate(*it) ) it = items.erase(it); + else ++it; + } +}; + +struct chlit +{ + chlit(char c) : c_(c) { } + char c_; +}; + +inline std::istream& operator>>(std::istream& is, chlit x) +{ + char c; + if (is >> c && c != x.c_) + { + is.setstate(std::iostream::failbit); + } + + return is; +} + +std::string slurp(std::ifstream& in); + +void flipImageData( + unsigned char* data, + int width, + int height, + int comps); + +#endif /* end of include guard: ALGORITHMS_H_1DDC517E */ -- cgit 1.4.1 From e16fb5be90c889c371cbb0ca2444735c2e12073c Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 18 Feb 2018 12:35:45 -0500 Subject: Implemented map adjacency This brings along with it the ability to move to different maps, for which the PlayingSystem and PlayableComponent were introduced. The PlayingSystem is a general overseer system that handles big picture stuff like initializing the player and changing maps. The PlayableComponent represents the player. While the ControllableComponent is also likely to always only be on the player entity, the two are distinct by separation of concerns. This also required a refactoring of how collisions are processed, because of a bug where the player can move to a new map when horizontal collisions are checked, and vertical collisions are skipped, causing the player to clip through the ground because the normal force was never handled. --- CMakeLists.txt | 2 + src/collision.cpp | 97 +++++++++++++ src/collision.h | 81 +++++++++++ src/components/mappable.h | 10 +- src/components/playable.h | 15 ++ src/consts.h | 2 +- src/entity_manager.h | 10 ++ src/game.cpp | 27 +--- src/map.h | 70 +++++++++- src/systems/mapping.cpp | 28 ++++ src/systems/playing.cpp | 98 +++++++++++++ src/systems/playing.h | 21 +++ src/systems/pondering.cpp | 347 +++++++++++++++++++++++++++++++--------------- src/systems/pondering.h | 11 -- src/world.cpp | 58 +++++++- 15 files changed, 717 insertions(+), 160 deletions(-) create mode 100644 src/collision.cpp create mode 100644 src/collision.h create mode 100644 src/components/playable.h create mode 100644 src/systems/playing.cpp create mode 100644 src/systems/playing.h (limited to 'src/systems/mapping.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e7bcb8..155063e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ add_executable(Aromatherapy src/animation.cpp src/world.cpp src/util.cpp + src/collision.cpp src/renderer/renderer.cpp src/renderer/mesh.cpp src/renderer/shader.cpp @@ -64,6 +65,7 @@ add_executable(Aromatherapy src/systems/animating.cpp src/systems/mapping.cpp src/systems/orienting.cpp + src/systems/playing.cpp ) set_property(TARGET Aromatherapy PROPERTY CXX_STANDARD 11) diff --git a/src/collision.cpp b/src/collision.cpp new file mode 100644 index 0000000..b747a90 --- /dev/null +++ b/src/collision.cpp @@ -0,0 +1,97 @@ +#include "collision.h" + +bool Collision::operator<(const Collision& other) const +{ + // Most important is the type of collision + if (type_ != other.type_) + { + return (static_cast(type_) > static_cast(other.type_)); + } + + // Next, categorize the collisions arbitrarily based on direction + if (dir_ != other.dir_) + { + return (static_cast(dir_) < static_cast(other.dir_)); + } + + // We want to process closer collisions first + if (axis_ != other.axis_) + { + switch (dir_) + { + case Direction::left: + case Direction::up: + { + return (axis_ < other.axis_); + } + + case Direction::right: + case Direction::down: + { + return (axis_ > other.axis_); + } + } + } + + // Order the remaining attributes arbitrarily + return std::tie(collider_, lower_, upper_) < + std::tie(other.collider_, other.lower_, other.upper_); +} + +bool Collision::isColliding( + double x, + double y, + int w, + int h) const +{ + int right = x + w; + int bottom = y + h; + + switch (dir_) + { + case Direction::left: + case Direction::right: + { + if (!((bottom > lower_) && (y < upper_))) + { + return false; + } + + break; + } + + case Direction::up: + case Direction::down: + { + if (!((right > lower_) && (x < upper_))) + { + return false; + } + + break; + } + } + + switch (dir_) + { + case Direction::left: + { + return (axis_ >= x); + } + + case Direction::right: + { + return (axis_ <= right); + } + + case Direction::up: + { + return (axis_ >= y); + } + + case Direction::down: + { + return (axis_ <= bottom); + } + } +} diff --git a/src/collision.h b/src/collision.h new file mode 100644 index 0000000..e5371f8 --- /dev/null +++ b/src/collision.h @@ -0,0 +1,81 @@ +#ifndef COLLISION_H_53D84877 +#define COLLISION_H_53D84877 + +#include "entity_manager.h" +#include "direction.h" + +class Collision { +public: + + using id_type = EntityManager::id_type; + + // Types are defined in descending priority order + enum class Type { + wall, + platform, + adjacency, + warp, + danger + }; + + Collision( + id_type collider, + Direction dir, + Type type, + int axis, + double lower, + double upper) : + collider_(collider), + dir_(dir), + type_(type), + axis_(axis), + lower_(lower), + upper_(upper) + { + } + + inline id_type getCollider() const + { + return collider_; + } + + inline Direction getDirection() const + { + return dir_; + } + + inline Type getType() const + { + return type_; + } + + inline int getAxis() const + { + return axis_; + } + + inline double getLower() const + { + return lower_; + } + + inline double getUpper() const + { + return upper_; + } + + bool operator<(const Collision& other) const; + + bool isColliding(double x, double y, int w, int h) const; + +private: + + id_type collider_; + Direction dir_; + Type type_; + int axis_; + double lower_; + double upper_; +}; + +#endif /* end of include guard: COLLISION_H_53D84877 */ diff --git a/src/components/mappable.h b/src/components/mappable.h index 2dbab77..633cdf4 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h @@ -4,6 +4,7 @@ #include #include "component.h" #include "renderer/texture.h" +#include "collision.h" #include "map.h" class MappableComponent : public Component { @@ -12,14 +13,7 @@ public: class Boundary { public: - enum class Type { - wall, - wrap, - teleport, - reverse, - platform, - danger - }; + using Type = Collision::Type; Boundary( double axis, diff --git a/src/components/playable.h b/src/components/playable.h new file mode 100644 index 0000000..a6e71b0 --- /dev/null +++ b/src/components/playable.h @@ -0,0 +1,15 @@ +#ifndef PLAYABLE_H_DDC566C3 +#define PLAYABLE_H_DDC566C3 + +#include "component.h" + +class PlayableComponent : public Component { +public: + + bool changingMap = false; + int newMapId = -1; + double newMapX = 0; + double newMapY = 0; +}; + +#endif /* end of include guard: PLAYABLE_H_DDC566C3 */ diff --git a/src/consts.h b/src/consts.h index 581018d..a065159 100644 --- a/src/consts.h +++ b/src/consts.h @@ -7,7 +7,7 @@ const int GAME_WIDTH = 320; const int GAME_HEIGHT = 200; const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH; const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT - 1; -const int WALL_GAP = 6; +const int WALL_GAP = 5; const int TILESET_COLS = 8; const int FONT_COLS = 16; diff --git a/src/entity_manager.h b/src/entity_manager.h index 65fa6ca..1e8d31c 100644 --- a/src/entity_manager.h +++ b/src/entity_manager.h @@ -26,6 +26,7 @@ private: database_type entities; std::vector slotAvailable; + std::set allEntities; std::map, std::set> cachedComponents; id_type nextEntityID = 0; @@ -59,12 +60,14 @@ public: // If the database is saturated, add a new element for the new entity. entities.emplace_back(); slotAvailable.push_back(false); + allEntities.insert(nextEntityID); return nextEntityID++; } else { // If there is an available slot in the database, use it. id_type id = nextEntityID++; slotAvailable[id] = false; + allEntities.insert(id); // Fast forward the next available slot pointer to an available slot. while ((nextEntityID < entities.size()) && !slotAvailable[nextEntityID]) @@ -89,6 +92,8 @@ public: cache.second.erase(entity); } + allEntities.erase(entity); + // Destroy the data entities[entity].components.clear(); @@ -202,6 +207,11 @@ public: return getEntitiesWithComponentsHelper(componentTypes); } + + const std::set& getEntities() const + { + return allEntities; + } }; template <> diff --git a/src/game.cpp b/src/game.cpp index 228ff23..f245e7c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -9,6 +9,7 @@ #include "systems/animating.h" #include "systems/mapping.h" #include "systems/orienting.h" +#include "systems/playing.h" #include "animation.h" #include "consts.h" @@ -28,36 +29,14 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) Game::Game() : world_("res/maps.xml") { + systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); - int player = entityManager_.emplaceEntity(); - - AnimationSet playerGraphics {"res/Starla.png", 10, 12, 6}; - playerGraphics.emplaceAnimation("stillLeft", 3, 1, 1); - playerGraphics.emplaceAnimation("stillRight", 0, 1, 1); - playerGraphics.emplaceAnimation("walkingLeft", 4, 2, 10); - playerGraphics.emplaceAnimation("walkingRight", 1, 2, 10); - - entityManager_.emplaceComponent( - player, - std::move(playerGraphics), - "stillLeft"); - - entityManager_.emplaceComponent( - player, - 203, 44, 10, 12); - - systemManager_.getSystem().initializeBody( - player, - PonderableComponent::Type::freefalling); - - entityManager_.emplaceComponent(player); - entityManager_.emplaceComponent(player); - + systemManager_.getSystem().initPlayer(); systemManager_.getSystem().loadMap(world_.getStartingMapId()); glfwSwapInterval(1); diff --git a/src/map.h b/src/map.h index 9177870..6fe1e62 100644 --- a/src/map.h +++ b/src/map.h @@ -10,13 +10,55 @@ class Map { public: + class Adjacent { + public: + + enum class Type { + wall, + wrap, + warp, + reverse + }; + + Adjacent( + Type type = Type::wall, + int mapId = -1) : + type_(type), + mapId_(mapId) + { + } + + inline Type getType() const + { + return type_; + } + + inline int getMapId() const + { + return mapId_; + } + + private: + + Type type_; + int mapId_; + }; + Map( int id, std::vector tiles, - std::string title) : + std::string title, + Adjacent leftAdjacent, + Adjacent rightAdjacent, + Adjacent upAdjacent, + Adjacent downAdjacent) : id_(id), tiles_(std::move(tiles)), - title_(std::move(title)) + title_(std::move(title)), + leftAdjacent_(std::move(leftAdjacent)), + rightAdjacent_(std::move(rightAdjacent)), + upAdjacent_(std::move(upAdjacent)), + downAdjacent_(std::move(downAdjacent)) { } @@ -35,11 +77,35 @@ public: return title_; } + inline const Adjacent& getLeftAdjacent() const + { + return leftAdjacent_; + } + + inline const Adjacent& getRightAdjacent() const + { + return rightAdjacent_; + } + + inline const Adjacent& getUpAdjacent() const + { + return upAdjacent_; + } + + inline const Adjacent& getDownAdjacent() const + { + return downAdjacent_; + } + private: int id_; std::vector tiles_; std::string title_; + Adjacent leftAdjacent_; + Adjacent rightAdjacent_; + Adjacent upAdjacent_; + Adjacent downAdjacent_; }; #endif /* end of include guard: MAP_H_74055FC0 */ diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index 120a27a..05167c1 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -93,6 +93,34 @@ void MappingSystem::loadMap(size_t mapId) const Map& map = game_.getWorld().getMap(mappable.getMapId()); + addBoundary( + mappable.getLeftBoundaries(), + -WALL_GAP, + 0, + MAP_HEIGHT * TILE_HEIGHT, + MappableComponent::Boundary::Type::adjacency); + + addBoundary( + mappable.getRightBoundaries(), + GAME_WIDTH + WALL_GAP, + 0, + MAP_HEIGHT * TILE_HEIGHT, + MappableComponent::Boundary::Type::adjacency); + + addBoundary( + mappable.getUpBoundaries(), + -WALL_GAP, + 0, + GAME_WIDTH, + MappableComponent::Boundary::Type::adjacency); + + addBoundary( + mappable.getDownBoundaries(), + MAP_HEIGHT * TILE_HEIGHT + WALL_GAP, + 0, + GAME_WIDTH, + MappableComponent::Boundary::Type::adjacency); + for (size_t i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) { size_t x = i % MAP_WIDTH; diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp new file mode 100644 index 0000000..2c6a419 --- /dev/null +++ b/src/systems/playing.cpp @@ -0,0 +1,98 @@ +#include "playing.h" +#include "game.h" +#include "components/transformable.h" +#include "components/animatable.h" +#include "components/playable.h" +#include "components/controllable.h" +#include "components/orientable.h" +#include "systems/mapping.h" +#include "systems/pondering.h" +#include "animation.h" + +void PlayingSystem::tick(double) +{ + // Check if we need to change the map + auto players = game_.getEntityManager().getEntitiesWithComponents< + PlayableComponent>(); + + for (id_type player : players) + { + auto& playable = game_.getEntityManager(). + getComponent(player); + + if (playable.changingMap) + { + // Change the map! + auto entities = game_.getEntityManager().getEntities(); + + for (id_type entity : entities) + { + if (entity != player) + { + game_.getEntityManager().deleteEntity(entity); + } + } + + game_.getSystemManager().getSystem(). + loadMap(playable.newMapId); + + auto& transformable = game_.getEntityManager(). + getComponent(player); + + transformable.setX(playable.newMapX); + transformable.setY(playable.newMapY); + + playable.changingMap = false; + + break; + } + } +} + +void PlayingSystem::initPlayer() +{ + id_type player = game_.getEntityManager().emplaceEntity(); + + AnimationSet playerGraphics {"res/Starla.png", 10, 12, 6}; + playerGraphics.emplaceAnimation("stillLeft", 3, 1, 1); + playerGraphics.emplaceAnimation("stillRight", 0, 1, 1); + playerGraphics.emplaceAnimation("walkingLeft", 4, 2, 10); + playerGraphics.emplaceAnimation("walkingRight", 1, 2, 10); + + game_.getEntityManager().emplaceComponent( + player, + std::move(playerGraphics), + "stillLeft"); + + game_.getEntityManager().emplaceComponent( + player, + 203, 44, 10, 12); + + game_.getSystemManager().getSystem().initializeBody( + player, + PonderableComponent::Type::freefalling); + + game_.getEntityManager().emplaceComponent(player); + game_.getEntityManager().emplaceComponent(player); + game_.getEntityManager().emplaceComponent(player); +} + +void PlayingSystem::changeMap( + size_t mapId, + double x, + double y) +{ + auto players = game_.getEntityManager().getEntitiesWithComponents< + PlayableComponent>(); + + for (id_type player : players) + { + auto& playable = game_.getEntityManager(). + getComponent(player); + + playable.changingMap = true; + playable.newMapId = mapId; + playable.newMapX = x; + playable.newMapY = y; + } +} diff --git a/src/systems/playing.h b/src/systems/playing.h new file mode 100644 index 0000000..c98a464 --- /dev/null +++ b/src/systems/playing.h @@ -0,0 +1,21 @@ +#ifndef PLAYING_H_70A54F7D +#define PLAYING_H_70A54F7D + +#include "system.h" + +class PlayingSystem : public System { +public: + + PlayingSystem(Game& game) : System(game) + { + } + + void tick(double dt); + + void initPlayer(); + + void changeMap(size_t mapId, double x, double y); + +}; + +#endif /* end of include guard: PLAYING_H_70A54F7D */ diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 4a165b1..2490dc9 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -1,11 +1,14 @@ #include "pondering.h" +#include #include "game.h" #include "components/ponderable.h" #include "components/transformable.h" #include "components/orientable.h" #include "components/mappable.h" #include "systems/orienting.h" +#include "systems/playing.h" #include "consts.h" +#include "collision.h" void PonderingSystem::tick(double dt) { @@ -42,6 +45,9 @@ void PonderingSystem::tick(double dt) bool oldGrounded = ponderable.isGrounded(); ponderable.setGrounded(false); + std::priority_queue collisions; + + // Find collisions for (id_type mapEntity : maps) { auto& mappable = game_.getEntityManager(). @@ -57,13 +63,13 @@ void PonderingSystem::tick(double dt) && (oldY < it->second.getUpper())) { // We have a collision! - processCollision( - entity, + collisions.emplace( + mapEntity, Direction::left, - newX, - newY, + it->second.getType(), it->first, - it->second.getType()); + it->second.getLower(), + it->second.getUpper()); } } } else if (newX > oldX) @@ -77,13 +83,13 @@ void PonderingSystem::tick(double dt) && (oldY < it->second.getUpper())) { // We have a collision! - processCollision( - entity, + collisions.emplace( + mapEntity, Direction::right, - newX, - newY, + it->second.getType(), it->first, - it->second.getType()); + it->second.getLower(), + it->second.getUpper()); } } } @@ -98,13 +104,13 @@ void PonderingSystem::tick(double dt) && (oldX < it->second.getUpper())) { // We have a collision! - processCollision( - entity, + collisions.emplace( + mapEntity, Direction::up, - newX, - newY, + it->second.getType(), it->first, - it->second.getType()); + it->second.getLower(), + it->second.getUpper()); } } } else if (newY > oldY) @@ -118,13 +124,221 @@ void PonderingSystem::tick(double dt) && (oldX < it->second.getUpper())) { // We have a collision! - processCollision( - entity, + collisions.emplace( + mapEntity, Direction::down, - newX, - newY, + it->second.getType(), it->first, - it->second.getType()); + it->second.getLower(), + it->second.getUpper()); + } + } + } + } + + // Process collisions in order of priority + while (!collisions.empty()) + { + Collision collision = collisions.top(); + collisions.pop(); + + // Make sure that they are still colliding + if (!collision.isColliding( + newX, + newY, + transformable.getW(), + transformable.getH())) + { + continue; + } + + bool touchedWall = false; + bool stopProcessing = false; + + switch (collision.getType()) + { + case Collision::Type::wall: + { + touchedWall = true; + + break; + } + + case Collision::Type::platform: + { + if (game_.getEntityManager(). + hasComponent(entity)) + { + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + if (orientable.getDropState() != + OrientableComponent::DropState::none) + { + orientable.setDropState(OrientableComponent::DropState::active); + } else { + touchedWall = true; + } + } else { + touchedWall = true; + } + + break; + } + + case Collision::Type::adjacency: + { + auto& mappable = game_.getEntityManager(). + getComponent(collision.getCollider()); + const Map& map = game_.getWorld().getMap(mappable.getMapId()); + auto& adj = [&] () -> const Map::Adjacent& { + switch (collision.getDirection()) + { + case Direction::left: return map.getLeftAdjacent(); + case Direction::right: return map.getRightAdjacent(); + case Direction::up: return map.getUpAdjacent(); + case Direction::down: return map.getDownAdjacent(); + } + }(); + + switch (adj.getType()) + { + case Map::Adjacent::Type::wall: + { + touchedWall = true; + + break; + } + + case Map::Adjacent::Type::wrap: + { + switch (collision.getDirection()) + { + case Direction::left: + { + newX = GAME_WIDTH + WALL_GAP - transformable.getW(); + + break; + } + + case Direction::right: + { + newX = -WALL_GAP; + + break; + } + + case Direction::up: + { + newY = MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - + transformable.getH(); + + break; + } + + case Direction::down: + { + newY = -WALL_GAP; + + break; + } + } + } + + case Map::Adjacent::Type::warp: + { + double warpX = newX; + double warpY = newY; + + switch (collision.getDirection()) + { + case Direction::left: + { + warpX = GAME_WIDTH + WALL_GAP - transformable.getW(); + + break; + } + + case Direction::right: + { + warpX = -WALL_GAP; + + break; + } + + case Direction::up: + { + warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.getH(); + + break; + } + + case Direction::down: + { + warpY = -WALL_GAP; + + break; + } + } + + game_.getSystemManager().getSystem(). + changeMap(adj.getMapId(), warpX, warpY); + + stopProcessing = true; + + break; + } + } + } + + default: + { + // Not yet implemented. + + break; + } + } + + if (stopProcessing) + { + break; + } + + if (touchedWall) + { + switch (collision.getDirection()) + { + case Direction::left: + { + newX = collision.getAxis(); + ponderable.setVelocityX(0.0); + + break; + } + + case Direction::right: + { + newX = collision.getAxis() - transformable.getW(); + ponderable.setVelocityX(0.0); + + break; + } + + case Direction::up: + { + newY = collision.getAxis(); + ponderable.setVelocityY(0.0); + + break; + } + + case Direction::down: + { + newY = collision.getAxis() - transformable.getH(); + ponderable.setVelocityY(0.0); + ponderable.setGrounded(true); + + break; } } } @@ -173,96 +387,3 @@ void PonderingSystem::initializeBody( ponderable.setAccelY(NORMAL_GRAVITY); } } - -void PonderingSystem::processCollision( - id_type entity, - Direction dir, - double& newX, - double& newY, - int axis, - MappableComponent::Boundary::Type type) -{ - auto& ponderable = game_.getEntityManager(). - getComponent(entity); - - auto& transformable = game_.getEntityManager(). - getComponent(entity); - - bool touchedGround = false; - - switch (type) - { - case MappableComponent::Boundary::Type::wall: - { - switch (dir) - { - case Direction::left: - { - newX = axis; - ponderable.setVelocityX(0.0); - - break; - } - - case Direction::right: - { - newX = axis - transformable.getW(); - ponderable.setVelocityX(0.0); - - break; - } - - case Direction::up: - { - newY = axis; - ponderable.setVelocityY(0.0); - - break; - } - - case Direction::down: - { - touchedGround = true; - - break; - } - } - - break; - } - - case MappableComponent::Boundary::Type::platform: - { - if (game_.getEntityManager().hasComponent(entity)) - { - auto& orientable = game_.getEntityManager(). - getComponent(entity); - - if (orientable.getDropState() != OrientableComponent::DropState::none) - { - orientable.setDropState(OrientableComponent::DropState::active); - } else { - touchedGround = true; - } - } else { - touchedGround = true; - } - - break; - } - - default: - { - // Not yet implemented. - - break; - } - } - - if (touchedGround) - { - newY = axis - transformable.getH(); - ponderable.setVelocityY(0.0); - ponderable.setGrounded(true); - } -} diff --git a/src/systems/pondering.h b/src/systems/pondering.h index a16622b..d70525b 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h @@ -2,7 +2,6 @@ #define PONDERING_H_F2530E0E #include "system.h" -#include "components/mappable.h" #include "components/ponderable.h" #include "direction.h" @@ -17,16 +16,6 @@ public: void initializeBody(id_type entity, PonderableComponent::Type type); -private: - - void processCollision( - id_type entity, - Direction dir, - double& newX, - double& newY, - int axis, - MappableComponent::Boundary::Type type); - }; #endif /* end of include guard: PONDERING_H_F2530E0E */ diff --git a/src/world.cpp b/src/world.cpp index 9b1e4f6..3b6bd41 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -63,6 +63,10 @@ World::World(std::string filename) xmlFree(key); std::vector mapTiles; + Map::Adjacent leftAdj; + Map::Adjacent rightAdj; + Map::Adjacent upAdj; + Map::Adjacent downAdj; for (xmlNodePtr mapNode = node->xmlChildrenNode; mapNode != nullptr; @@ -82,6 +86,54 @@ World::World(std::string filename) } xmlFree(key); + } else if (!xmlStrcmp( + mapNode->name, + reinterpret_cast("adjacent"))) + { + key = getProp(mapNode, "type"); + std::string adjTypeStr(reinterpret_cast(key)); + xmlFree(key); + + Map::Adjacent::Type adjType; + if (adjTypeStr == "wall") + { + adjType = Map::Adjacent::Type::wall; + } else if (adjTypeStr == "wrap") + { + adjType = Map::Adjacent::Type::wrap; + } else if (adjTypeStr == "warp") + { + adjType = Map::Adjacent::Type::warp; + } else if (adjTypeStr == "reverseWarp") + { + adjType = Map::Adjacent::Type::reverse; + } else { + throw std::logic_error("Invalid adjacency type"); + } + + key = getProp(mapNode, "map"); + int adjMapId = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(mapNode, "dir"); + std::string adjDir(reinterpret_cast(key)); + xmlFree(key); + + if (adjDir == "left") + { + leftAdj = {adjType, adjMapId}; + } else if (adjDir == "right") + { + rightAdj = {adjType, adjMapId}; + } else if (adjDir == "up") + { + upAdj = {adjType, adjMapId}; + } else if (adjDir == "down") + { + downAdj = {adjType, adjMapId}; + } else { + throw std::logic_error("Invalid adjacency direction"); + } } } @@ -91,7 +143,11 @@ World::World(std::string filename) std::forward_as_tuple( mapId, std::move(mapTiles), - std::move(mapTitle))); + std::move(mapTitle), + leftAdj, + rightAdj, + upAdj, + downAdj)); } } -- cgit 1.4.1 From e4e2f2d2a7b6a282b9618aa0004d9453936f43c6 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 18 Feb 2018 15:25:52 -0500 Subject: Added player death and event scheduling Also added ability to make sprites flicker, to freeze physics for an entity, and to freeze progression of a sprite's animation loop. --- CMakeLists.txt | 1 + res/maps.xml | 2 +- src/components/animatable.h | 33 +++++++++++++++++ src/components/playable.h | 9 +++++ src/components/ponderable.h | 22 ++++++++++++ src/components/schedulable.h | 21 +++++++++++ src/game.cpp | 2 ++ src/systems/animating.cpp | 19 ++++++++-- src/systems/controlling.cpp | 29 +++++++++++++++ src/systems/controlling.h | 5 +++ src/systems/mapping.cpp | 21 +++++++++++ src/systems/orienting.cpp | 18 ++++------ src/systems/playing.cpp | 85 +++++++++++++++++++++++++++++++++++++++++--- src/systems/playing.h | 9 ++++- src/systems/pondering.cpp | 16 +++++++++ src/systems/scheduling.cpp | 54 ++++++++++++++++++++++++++++ src/systems/scheduling.h | 22 ++++++++++++ 17 files changed, 348 insertions(+), 20 deletions(-) create mode 100644 src/components/schedulable.h create mode 100644 src/systems/scheduling.cpp create mode 100644 src/systems/scheduling.h (limited to 'src/systems/mapping.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 155063e..34246ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ add_executable(Aromatherapy src/systems/mapping.cpp src/systems/orienting.cpp src/systems/playing.cpp + src/systems/scheduling.cpp ) set_property(TARGET Aromatherapy PROPERTY CXX_STANDARD 11) diff --git a/res/maps.xml b/res/maps.xml index 9d855a2..c561912 100644 --- a/res/maps.xml +++ b/res/maps.xml @@ -1,5 +1,5 @@ -00,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,20,0,0,0,0,0,0,0,18,9,8,10,8,11,8,10,10,8,11,8,9,10,21,0, +00,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,20,0,0,0,0,0,0,0,18,9,8,10,8,11,8,10,10,8,11,8,9,10,21,0, 0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,8,8,17,0,0,0,0,0,0,0,0,0,0,0,0,0,22,21, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,8,8,8,8,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12, 0,19,0,0,0,0,0,0,0,0,0,0,0,18,8,8,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12, diff --git a/src/components/animatable.h b/src/components/animatable.h index ed0133e..ec72be0 100644 --- a/src/components/animatable.h +++ b/src/components/animatable.h @@ -51,12 +51,45 @@ public: animation_ = std::move(animation); } + inline bool isFlickering() const + { + return flickering_; + } + + inline void setFlickering(bool v) + { + flickering_ = v; + } + + inline size_t getFlickerTimer() const + { + return flickerTimer_; + } + + inline void setFlickerTimer(size_t v) + { + flickerTimer_ = v; + } + + inline bool isFrozen() const + { + return frozen_; + } + + inline void setFrozen(bool v) + { + frozen_ = v; + } + private: AnimationSet animationSet_; std::string animation_; size_t frame_ = 0; size_t countdown_ = 0; + bool flickering_ = false; + size_t flickerTimer_ = 0; + bool frozen_ = false; }; #endif /* end of include guard: SPRITE_RENDERABLE_H_D3AACBBF */ diff --git a/src/components/playable.h b/src/components/playable.h index a6e71b0..86a7ee7 100644 --- a/src/components/playable.h +++ b/src/components/playable.h @@ -2,14 +2,23 @@ #define PLAYABLE_H_DDC566C3 #include "component.h" +#include class PlayableComponent : public Component { public: + using MapChangeCallback = std::function; + bool changingMap = false; int newMapId = -1; double newMapX = 0; double newMapY = 0; + MapChangeCallback newMapCallback; + + int checkpointMapId = -1; + double checkpointX = 0; + double checkpointY = 0; + }; #endif /* end of include guard: PLAYABLE_H_DDC566C3 */ diff --git a/src/components/ponderable.h b/src/components/ponderable.h index e21cbab..78af25f 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h @@ -70,6 +70,26 @@ public: grounded_ = v; } + inline bool isFrozen() const + { + return frozen_; + } + + inline void setFrozen(bool v) + { + frozen_ = v; + } + + inline bool isCollidable() const + { + return collidable_; + } + + inline void setCollidable(bool v) + { + collidable_ = v; + } + private: double velX_ = 0.0; @@ -78,6 +98,8 @@ private: double accelY_ = 0.0; Type type_ = Type::vacuumed; bool grounded_ = false; + bool frozen_ = false; + bool collidable_ = true; }; #endif /* end of include guard: TANGIBLE_H_746DB3EE */ diff --git a/src/components/schedulable.h b/src/components/schedulable.h new file mode 100644 index 0000000..a92bbba --- /dev/null +++ b/src/components/schedulable.h @@ -0,0 +1,21 @@ +#ifndef SCHEDULABLE_H_1DA3FA2A +#define SCHEDULABLE_H_1DA3FA2A + +#include "component.h" +#include +#include +#include +#include "entity_manager.h" + +class SchedulableComponent : public Component { +public: + + using id_type = EntityManager::id_type; + + using Callback = std::function; + using Action = std::tuple; + + std::list actions; +}; + +#endif /* end of include guard: SCHEDULABLE_H_1DA3FA2A */ diff --git a/src/game.cpp b/src/game.cpp index f245e7c..3da23a3 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -10,6 +10,7 @@ #include "systems/mapping.h" #include "systems/orienting.h" #include "systems/playing.h" +#include "systems/scheduling.h" #include "animation.h" #include "consts.h" @@ -30,6 +31,7 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) Game::Game() : world_("res/maps.xml") { systemManager_.emplaceSystem(*this); + systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); diff --git a/src/systems/animating.cpp b/src/systems/animating.cpp index 22224c9..634af67 100644 --- a/src/systems/animating.cpp +++ b/src/systems/animating.cpp @@ -13,7 +13,10 @@ void AnimatingSystem::tick(double) auto& sprite = game_.getEntityManager(). getComponent(entity); - sprite.setCountdown(sprite.getCountdown() + 1); + if (!sprite.isFrozen()) + { + sprite.setCountdown(sprite.getCountdown() + 1); + } const Animation& anim = sprite.getAnimation(); if (sprite.getCountdown() >= anim.getDelay()) @@ -26,6 +29,11 @@ void AnimatingSystem::tick(double) sprite.setFrame(anim.getFirstFrame()); } } + + if (sprite.isFlickering()) + { + sprite.setFlickerTimer((sprite.getFlickerTimer() + 1) % 6); + } } } @@ -44,6 +52,12 @@ void AnimatingSystem::render(Texture& texture) auto& transform = game_.getEntityManager(). getComponent(entity); + double alpha = 1.0; + if (sprite.isFlickering() && (sprite.getFlickerTimer() < 3)) + { + alpha = 0.0; + } + Rectangle dstrect { static_cast(transform.getX()), static_cast(transform.getY()), @@ -55,7 +69,8 @@ void AnimatingSystem::render(Texture& texture) aset.getTexture(), texture, aset.getFrameRect(sprite.getFrame()), - dstrect); + dstrect, + alpha); } } diff --git a/src/systems/controlling.cpp b/src/systems/controlling.cpp index e1609bd..e65c09a 100644 --- a/src/systems/controlling.cpp +++ b/src/systems/controlling.cpp @@ -105,3 +105,32 @@ void ControllingSystem::input(int key, int action) { actions_.push(std::make_pair(key, action)); } + +void ControllingSystem::freeze(id_type entity) +{ + auto& controllable = game_.getEntityManager(). + getComponent(entity); + + controllable.setFrozen(true); +} + +void ControllingSystem::unfreeze(id_type entity) +{ + auto& controllable = game_.getEntityManager(). + getComponent(entity); + + if (controllable.isFrozen()) + { + controllable.setFrozen(false); + + auto& orienting = game_.getSystemManager().getSystem(); + + if (controllable.isHoldingLeft()) + { + orienting.moveLeft(entity); + } else if (controllable.isHoldingRight()) + { + orienting.moveRight(entity); + } + } +} diff --git a/src/systems/controlling.h b/src/systems/controlling.h index 01ed7a0..d6f0789 100644 --- a/src/systems/controlling.h +++ b/src/systems/controlling.h @@ -12,8 +12,13 @@ public: } void tick(double dt); + void input(int key, int action); + void freeze(id_type entity); + + void unfreeze(id_type entity); + private: std::queue> actions_; diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index 05167c1..a3a17ec 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -166,12 +166,33 @@ void MappingSystem::loadMap(size_t mapId) MappableComponent::Boundary::Type::wall); } else if (tile == 42) { + addBoundary( + mappable.getRightBoundaries(), + x * TILE_WIDTH, + y * TILE_HEIGHT, + (y+1) * TILE_HEIGHT, + MappableComponent::Boundary::Type::danger); + + addBoundary( + mappable.getLeftBoundaries(), + (x+1) * TILE_WIDTH, + y * TILE_HEIGHT, + (y+1) * TILE_HEIGHT, + MappableComponent::Boundary::Type::danger); + addBoundary( mappable.getDownBoundaries(), y * TILE_HEIGHT, x * TILE_WIDTH, (x+1) * TILE_WIDTH, MappableComponent::Boundary::Type::danger); + + addBoundary( + mappable.getUpBoundaries(), + (y+1) * TILE_HEIGHT, + x * TILE_WIDTH, + (x+1) * TILE_WIDTH, + MappableComponent::Boundary::Type::danger); } } } diff --git a/src/systems/orienting.cpp b/src/systems/orienting.cpp index 187bebc..2df8f87 100644 --- a/src/systems/orienting.cpp +++ b/src/systems/orienting.cpp @@ -93,24 +93,18 @@ void OrientingSystem::moveRight(id_type entity) void OrientingSystem::stopWalking(id_type entity) { - auto& ponderable = game_.getEntityManager(). - getComponent(entity); - auto& orientable = game_.getEntityManager(). getComponent(entity); orientable.setWalkState(OrientableComponent::WalkState::still); - if (ponderable.isGrounded()) - { - auto& animating = game_.getSystemManager().getSystem(); + auto& animating = game_.getSystemManager().getSystem(); - if (orientable.isFacingRight()) - { - animating.startAnimation(entity, "stillRight"); - } else { - animating.startAnimation(entity, "stillLeft"); - } + if (orientable.isFacingRight()) + { + animating.startAnimation(entity, "stillRight"); + } else { + animating.startAnimation(entity, "stillLeft"); } } diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp index 2c6a419..40d9706 100644 --- a/src/systems/playing.cpp +++ b/src/systems/playing.cpp @@ -7,13 +7,18 @@ #include "components/orientable.h" #include "systems/mapping.h" #include "systems/pondering.h" +#include "systems/orienting.h" +#include "systems/scheduling.h" +#include "systems/controlling.h" #include "animation.h" +#include "muxer.h" void PlayingSystem::tick(double) { // Check if we need to change the map auto players = game_.getEntityManager().getEntitiesWithComponents< - PlayableComponent>(); + PlayableComponent, + TransformableComponent>(); for (id_type player : players) { @@ -44,6 +49,12 @@ void PlayingSystem::tick(double) playable.changingMap = false; + if (playable.newMapCallback) + { + playable.newMapCallback(); + playable.newMapCallback = nullptr; + } + break; } } @@ -66,7 +77,10 @@ void PlayingSystem::initPlayer() game_.getEntityManager().emplaceComponent( player, - 203, 44, 10, 12); + game_.getWorld().getStartingX(), + game_.getWorld().getStartingY(), + 10, + 12); game_.getSystemManager().getSystem().initializeBody( player, @@ -74,13 +88,20 @@ void PlayingSystem::initPlayer() game_.getEntityManager().emplaceComponent(player); game_.getEntityManager().emplaceComponent(player); - game_.getEntityManager().emplaceComponent(player); + + auto& playable = game_.getEntityManager(). + emplaceComponent(player); + + playable.checkpointMapId = game_.getWorld().getStartingMapId(); + playable.checkpointX = game_.getWorld().getStartingX(); + playable.checkpointY = game_.getWorld().getStartingY(); } void PlayingSystem::changeMap( size_t mapId, double x, - double y) + double y, + PlayableComponent::MapChangeCallback callback) { auto players = game_.getEntityManager().getEntitiesWithComponents< PlayableComponent>(); @@ -94,5 +115,61 @@ void PlayingSystem::changeMap( playable.newMapId = mapId; playable.newMapX = x; playable.newMapY = y; + playable.newMapCallback = std::move(callback); + } +} + +void PlayingSystem::die() +{ + playSound("res/Hit_Hurt5.wav", 0.25); + + auto players = game_.getEntityManager().getEntitiesWithComponents< + OrientableComponent, + ControllableComponent, + AnimatableComponent, + PonderableComponent, + PlayableComponent>(); + + for (id_type player : players) + { + auto& animatable = game_.getEntityManager(). + getComponent(player); + + auto& ponderable = game_.getEntityManager(). + getComponent(player); + + auto& controlling = game_.getSystemManager().getSystem(); + controlling.freeze(player); + + animatable.setFrozen(true); + animatable.setFlickering(true); + ponderable.setFrozen(true); + ponderable.setCollidable(false); + + auto& scheduling = game_.getSystemManager().getSystem(); + + scheduling.schedule(player, 0.75, [&] (id_type player) { + auto& playable = game_.getEntityManager(). + getComponent(player); + + changeMap( + playable.checkpointMapId, + playable.checkpointX, + playable.checkpointY, + [&, player] () { + animatable.setFrozen(false); + animatable.setFlickering(false); + ponderable.setFrozen(false); + ponderable.setCollidable(true); + + // Reset the walk state, and then potentially let the + // ControllingSystem set it again. + auto& orienting = game_.getSystemManager(). + getSystem(); + orienting.stopWalking(player); + + controlling.unfreeze(player); + }); + }); } } diff --git a/src/systems/playing.h b/src/systems/playing.h index c98a464..ff16808 100644 --- a/src/systems/playing.h +++ b/src/systems/playing.h @@ -2,6 +2,7 @@ #define PLAYING_H_70A54F7D #include "system.h" +#include "components/playable.h" class PlayingSystem : public System { public: @@ -14,7 +15,13 @@ public: void initPlayer(); - void changeMap(size_t mapId, double x, double y); + void changeMap( + size_t mapId, + double x, + double y, + PlayableComponent::MapChangeCallback callback = nullptr); + + void die(); }; diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 2490dc9..d3601ac 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -27,6 +27,11 @@ void PonderingSystem::tick(double dt) auto& ponderable = game_.getEntityManager(). getComponent(entity); + if (ponderable.isFrozen()) + { + continue; + } + // Accelerate ponderable.setVelocityX( ponderable.getVelocityX() + ponderable.getAccelX() * dt); @@ -289,6 +294,17 @@ void PonderingSystem::tick(double dt) break; } } + + break; + } + + case Collision::Type::danger: + { + game_.getSystemManager().getSystem().die(); + + stopProcessing = true; + + break; } default: diff --git a/src/systems/scheduling.cpp b/src/systems/scheduling.cpp new file mode 100644 index 0000000..220efae --- /dev/null +++ b/src/systems/scheduling.cpp @@ -0,0 +1,54 @@ +#include "scheduling.h" +#include "game.h" +#include "components/schedulable.h" +#include "util.h" + +void SchedulingSystem::tick(double dt) +{ + auto entities = game_.getEntityManager().getEntitiesWithComponents< + SchedulableComponent>(); + + for (id_type entity : entities) + { + auto& schedulable = game_.getEntityManager(). + getComponent(entity); + + for (auto& action : schedulable.actions) + { + std::get<0>(action) -= dt; + + if (std::get<0>(action) < 0) + { + std::get<1>(action)(entity); + } + } + + erase_if(schedulable.actions, + [] (const SchedulableComponent::Action& action) { + return (std::get<0>(action) < 0); + }); + + if (schedulable.actions.empty()) + { + game_.getEntityManager().removeComponent(entity); + } + } +} + +void SchedulingSystem::schedule( + id_type entity, + double length, + std::function action) +{ + if (!game_.getEntityManager().hasComponent(entity)) + { + game_.getEntityManager().emplaceComponent(entity); + } + + auto& schedulable = game_.getEntityManager(). + getComponent(entity); + + schedulable.actions.emplace_back( + length, + std::move(action)); +} diff --git a/src/systems/scheduling.h b/src/systems/scheduling.h new file mode 100644 index 0000000..7950d62 --- /dev/null +++ b/src/systems/scheduling.h @@ -0,0 +1,22 @@ +#ifndef SCHEDULING_H_7B02E3E3 +#define SCHEDULING_H_7B02E3E3 + +#include "system.h" + +class SchedulingSystem : public System { +public: + + SchedulingSystem(Game& game) : System(game) + { + } + + void tick(double dt); + + void schedule( + id_type entity, + double length, + std::function action); + +}; + +#endif /* end of include guard: SCHEDULING_H_7B02E3E3 */ -- cgit 1.4.1 From 8016a7146fec3f6f43ca05723441750e5aae3d4d Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sat, 28 Apr 2018 09:22:44 -0400 Subject: Restructured the way the world is loaded The World class was removed and replaced by the RealizingSystem and RealizableComponent. The realizable entity is intended to be a singleton and to represent the world. The Map class was also removed and integrated into the MappableComponent. These changes are to facilitate implementation of map objects without needing special intermediary objects (including the Map class). Now, map entities are created as soon as the world is created, and map object entities will be as well. They will simply be deactivated while the map is not active. Multiple players are now slightly better supported, which will be important in the future. This will likely become inefficient as the world becomes bigger, and some sort of sector-loading process will have to be designed. This also reduces the usefulness of EntityManager's entity-searching capabilities (which are not the most efficiently implemented currently anyway), and will likely in the future require some added functionality to better search subsets of entities. A lot of the components were also rewritten to use bare member variables instead of accessor methods, as they never had special functionality and just took up space. These components were also documented. --- CMakeLists.txt | 2 +- src/components/animatable.h | 146 +++++++++-------- src/components/mappable.h | 208 ++++++++++++------------ src/components/playable.h | 28 ++-- src/components/ponderable.h | 151 ++++++++---------- src/components/realizable.h | 67 ++++++++ src/components/transformable.h | 79 +++------ src/game.cpp | 6 +- src/game.h | 7 - src/map.h | 111 ------------- src/systems/animating.cpp | 93 ++++++----- src/systems/animating.h | 2 + src/systems/mapping.cpp | 127 +++++++-------- src/systems/mapping.h | 2 +- src/systems/orienting.cpp | 22 +-- src/systems/playing.cpp | 220 ++++++++++++------------- src/systems/playing.h | 9 +- src/systems/pondering.cpp | 354 +++++++++++++++++++++++------------------ src/systems/pondering.h | 2 + src/systems/realizing.cpp | 321 +++++++++++++++++++++++++++++++++++++ src/systems/realizing.h | 43 +++++ src/world.cpp | 155 ------------------ src/world.h | 41 ----- 23 files changed, 1155 insertions(+), 1041 deletions(-) create mode 100644 src/components/realizable.h delete mode 100644 src/map.h create mode 100644 src/systems/realizing.cpp create mode 100644 src/systems/realizing.h delete mode 100644 src/world.cpp delete mode 100644 src/world.h (limited to 'src/systems/mapping.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 34246ad..81365c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,6 @@ add_executable(Aromatherapy src/entity_manager.cpp src/game.cpp src/animation.cpp - src/world.cpp src/util.cpp src/collision.cpp src/renderer/renderer.cpp @@ -67,6 +66,7 @@ add_executable(Aromatherapy src/systems/orienting.cpp src/systems/playing.cpp src/systems/scheduling.cpp + src/systems/realizing.cpp ) set_property(TARGET Aromatherapy PROPERTY CXX_STANDARD 11) diff --git a/src/components/animatable.h b/src/components/animatable.h index ec72be0..1a678ba 100644 --- a/src/components/animatable.h +++ b/src/components/animatable.h @@ -8,88 +8,86 @@ class AnimatableComponent : public Component { public: + /** + * Constructor for initializing the animation set, because it is not default + * constructible. + */ AnimatableComponent( - AnimationSet animationSet, - std::string animation) : - animationSet_(std::move(animationSet)), - animation_(std::move(animation)) + AnimationSet animationSet) : + animationSet(std::move(animationSet)) { } - inline size_t getFrame() const - { - return frame_; - } - - inline void setFrame(size_t v) - { - frame_ = v; - } - - inline size_t getCountdown() const - { - return countdown_; - } - - inline void setCountdown(size_t v) - { - countdown_ = v; - } - - inline const AnimationSet& getAnimationSet() const - { - return animationSet_; - } - + /** + * The animation set that this entity will use -- an object describing the + * different animations that can be used to render the entity. + * + * @managed_by RealizingSystem + */ + AnimationSet animationSet; + + /** + * The name of the currently active animation. + * + * @managed_by AnimatingSystem + */ + std::string animation; + + /** + * For prototypes, the name of the original animation. + * + * @managed_by RealizingSystem + */ + std::string origAnimation; + + /** + * Helper method for accessing the currently active animation. + */ inline const Animation& getAnimation() const { - return animationSet_.getAnimation(animation_); + return animationSet.getAnimation(animation); } - inline void setAnimation(std::string animation) - { - animation_ = std::move(animation); - } - - inline bool isFlickering() const - { - return flickering_; - } - - inline void setFlickering(bool v) - { - flickering_ = v; - } - - inline size_t getFlickerTimer() const - { - return flickerTimer_; - } - - inline void setFlickerTimer(size_t v) - { - flickerTimer_ = v; - } - - inline bool isFrozen() const - { - return frozen_; - } - - inline void setFrozen(bool v) - { - frozen_ = v; - } - -private: - - AnimationSet animationSet_; - std::string animation_; - size_t frame_ = 0; - size_t countdown_ = 0; - bool flickering_ = false; - size_t flickerTimer_ = 0; - bool frozen_ = false; + /** + * The frame of animation that is currently being rendered. + * + * @managed_by AnimatingSystem + */ + size_t frame = 0; + + /** + * The amount of time (in game frames) before the animation is advanced. + * + * @managed_by AnimatingSystem + */ + size_t countdown = 0; + + /** + * This option allows to give the sprite a "flickering" effect (as in, it is + * not rendered in some frames). + */ + bool flickering = false; + + /** + * Used for the flickering effect. + * + * @managed_by AnimatingSystem + */ + size_t flickerTimer = 0; + + /** + * If enabled, this will prevent the sprite's animation from progressing (but + * will not affect things such as placement on screen and flickering). + */ + bool frozen = false; + + /** + * If this flag is disabled, the entity will be ignored by the animating + * system. + * + * @managed_by RealizingSystem + */ + bool active = false; }; #endif /* end of include guard: SPRITE_RENDERABLE_H_D3AACBBF */ diff --git a/src/components/mappable.h b/src/components/mappable.h index 633cdf4..6f3d38e 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h @@ -2,14 +2,47 @@ #define MAPPABLE_H_0B0316FB #include +#include +#include +#include #include "component.h" #include "renderer/texture.h" #include "collision.h" -#include "map.h" +#include "entity_manager.h" class MappableComponent : public Component { public: + using id_type = EntityManager::id_type; + + /** + * Helper type that stores information about map adjacency. + */ + class Adjacent { + public: + + enum class Type { + wall, + wrap, + warp, + reverse + }; + + Adjacent( + Type type = Type::wall, + size_t mapId = 0) : + type(type), + mapId(mapId) + { + } + + Type type; + size_t mapId; + }; + + /** + * Helper type that stores information about collision boundaries. + */ class Boundary { public: @@ -20,121 +53,100 @@ public: double lower, double upper, Type type) : - axis_(axis), - lower_(lower), - upper_(upper), - type_(type) - { - } - - inline double getAxis() const - { - return axis_; - } - - inline double getLower() const - { - return lower_; - } - - inline double getUpper() const - { - return upper_; - } - - inline Type getType() const + axis(axis), + lower(lower), + upper(upper), + type(type) { - return type_; } - private: - - double axis_; - double lower_; - double upper_; - Type type_; + double axis; + double lower; + double upper; + Type type; }; - MappableComponent( - Texture tileset, - Texture font) : - tileset_(std::move(tileset)), - font_(std::move(font)) - { - } - + /** + * Helper types for efficient storage and lookup of collision boundaries. + */ using asc_boundaries_type = std::multimap< double, - Boundary, + const Boundary, std::less>; using desc_boundaries_type = std::multimap< double, - Boundary, + const Boundary, std::greater>; - inline size_t getMapId() const - { - return mapId_; - } - - inline void setMapId(size_t v) - { - mapId_ = v; - } - - inline desc_boundaries_type& getLeftBoundaries() - { - return leftBoundaries_; - } - - inline asc_boundaries_type& getRightBoundaries() - { - return rightBoundaries_; - } - - inline desc_boundaries_type& getUpBoundaries() - { - return upBoundaries_; - } - - inline asc_boundaries_type& getDownBoundaries() - { - return downBoundaries_; - } - - inline const Texture& getTileset() const - { - return tileset_; - } - - inline void setTileset(Texture v) - { - tileset_ = std::move(v); - } - - inline const Texture& getFont() const - { - return font_; - } - - inline void setFont(Texture v) + /** + * Constructor for initializing the tileset and font attributes, as they are + * not default constructible. + */ + MappableComponent( + Texture tileset, + Texture font) : + tileset(std::move(tileset)), + font(std::move(font)) { - font_ = std::move(v); } -private: - - size_t mapId_ = -1; - - desc_boundaries_type leftBoundaries_; - asc_boundaries_type rightBoundaries_; - desc_boundaries_type upBoundaries_; - asc_boundaries_type downBoundaries_; - Texture tileset_; - Texture font_; + /** + * The ID of the map in the world definition that this entity represents. + * + * @managed_by RealizingSystem + */ + size_t mapId; + + /** + * The title of the map, which is displayed at the bottom of the screen. + */ + std::string title; + + /** + * The map data. + * + * @managed_by RealizingSystem + */ + std::vector tiles; + + /** + * These objects describe the behavior of the four edges of the map. + * + * @managed_by RealizingSystem + */ + Adjacent leftAdjacent; + Adjacent rightAdjacent; + Adjacent upAdjacent; + Adjacent downAdjacent; + + /** + * Collision boundaries, for detecting when a ponderable entity is colliding + * with the environment. + * + * @managed_by MappingSystem + */ + desc_boundaries_type leftBoundaries; + asc_boundaries_type rightBoundaries; + desc_boundaries_type upBoundaries; + asc_boundaries_type downBoundaries; + + /** + * The list of entities representing the objects owned by the map. + * + * @managed_by RealizingSystem + */ + std::list objects; + + /** + * The tilesets for the map and the map name. + * + * TODO: These probably do not belong here. + */ + Texture tileset; + Texture font; }; #endif /* end of include guard: MAPPABLE_H_0B0316FB */ diff --git a/src/components/playable.h b/src/components/playable.h index 86a7ee7..94d4326 100644 --- a/src/components/playable.h +++ b/src/components/playable.h @@ -2,22 +2,30 @@ #define PLAYABLE_H_DDC566C3 #include "component.h" -#include +#include "entity_manager.h" class PlayableComponent : public Component { public: - using MapChangeCallback = std::function; + using id_type = EntityManager::id_type; - bool changingMap = false; - int newMapId = -1; - double newMapX = 0; - double newMapY = 0; - MapChangeCallback newMapCallback; + /** + * The entity ID of the map that the player is on. + * + * @managed_by PlayingSystem + */ + id_type mapId; - int checkpointMapId = -1; - double checkpointX = 0; - double checkpointY = 0; + /** + * The map ID and coordinates of the location that the player will spawn after + * dying. Note that the map ID here is a world description map ID, not an + * entity ID. + * + * @managed_by PlayingSystem + */ + size_t checkpointMapId; + double checkpointX; + double checkpointY; }; diff --git a/src/components/ponderable.h b/src/components/ponderable.h index 78af25f..fd7e775 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h @@ -6,100 +6,73 @@ class PonderableComponent : public Component { public: + /** + * List of different types of physical bodies. + * + * vacuumed - Default. + * freefalling - The body will be treated as if there were a downward force + * of gravity being exerted onto it. The body will also exhibit + * terminal velocity (that is, its downward velocity will be + * capped at a constant value). + */ enum class Type { vacuumed, freefalling }; - PonderableComponent(Type type) : type_(type) - { - } - - inline Type getType() const - { - return type_; - } - - inline double getVelocityX() const - { - return velX_; - } - - inline void setVelocityX(double v) - { - velX_ = v; - } - - inline double getVelocityY() const - { - return velY_; - } - - inline void setVelocityY(double v) - { - velY_ = v; - } - - inline double getAccelX() const - { - return accelX_; - } - - inline void setAccelX(double v) - { - accelX_ = v; - } - - inline double getAccelY() const - { - return accelY_; - } - - inline void setAccelY(double v) - { - accelY_ = v; - } - - inline bool isGrounded() const - { - return grounded_; - } - - inline void setGrounded(bool v) - { - grounded_ = v; - } - - inline bool isFrozen() const - { - return frozen_; - } - - inline void setFrozen(bool v) - { - frozen_ = v; - } - - inline bool isCollidable() const - { - return collidable_; - } - - inline void setCollidable(bool v) - { - collidable_ = v; - } - -private: - - double velX_ = 0.0; - double velY_ = 0.0; - double accelX_ = 0.0; - double accelY_ = 0.0; - Type type_ = Type::vacuumed; - bool grounded_ = false; - bool frozen_ = false; - bool collidable_ = true; + /** + * Constructor for initializing the body type, which is a constant. + */ + PonderableComponent(Type type) : type(type) + { + } + + /** + * The velocity of the body. + */ + double velX = 0.0; + double velY = 0.0; + + /** + * The acceleration of the body. + */ + double accelX = 0.0; + double accelY = 0.0; + + /** + * The type of physical body that the entity is meant to assume. The body will + * be acted upon differently based on this. See the enumeration above for more + * details. + * + * @managed_by PonderingSystem + */ + const Type type; + + /** + * Whether or not a freefalling body is in contact with the ground. + * + * @managed_by PonderingSystem + */ + bool grounded = false; + + /** + * If enabled, this will prevent the body from moving. + */ + bool frozen = false; + + /** + * If disabled, collision detection for this body will not be performed and + * other bodies will ignore it. + */ + bool collidable = true; + + /** + * If this flag is disabled, the entity will be ignored by the pondering + * system. + * + * @managed_by RealizingSystem + */ + bool active = false; }; #endif /* end of include guard: TANGIBLE_H_746DB3EE */ diff --git a/src/components/realizable.h b/src/components/realizable.h new file mode 100644 index 0000000..f6a7eb4 --- /dev/null +++ b/src/components/realizable.h @@ -0,0 +1,67 @@ +#ifndef REALIZABLE_H_36D8D71E +#define REALIZABLE_H_36D8D71E + +#include "component.h" +#include +#include +#include "entity_manager.h" + +class RealizableComponent : public Component { +public: + + using id_type = EntityManager::id_type; + + /** + * Path to the XML file containing the world definition. + * + * @managed_by RealizingSystem + */ + std::string worldFile; + + /** + * Starting map and player location for a new game. + * + * @managed_by RealizingSystem + */ + int startingMapId; + int startingX; + int startingY; + + /** + * The set of map entities loaded by this entity. It is only intended for + * there to be one realizable entity, so this should contain all loaded maps. + * The realizable entity has ownership of the loaded maps. + * + * @managed_by RealizingSystem + */ + std::set maps; + + /** + * A lookup table that translates a map ID to the entity representing that + * loaded map. + * + * @managed_by RealizingSystem + */ + std::map entityByMapId; + + /** + * The entity ID of the currently active map. + * + * @managed_by RealizingSystem + */ + id_type activeMap; + + /** + * Whether or not a map has been activated yet. + * + * @managed_by RealizingSystem + */ + bool hasActiveMap = false; + + /** + * The entity ID of the currently active player. + */ + id_type activePlayer; +}; + +#endif /* end of include guard: REALIZABLE_H_36D8D71E */ diff --git a/src/components/transformable.h b/src/components/transformable.h index 6ed2637..3296e49 100644 --- a/src/components/transformable.h +++ b/src/components/transformable.h @@ -6,64 +6,27 @@ class TransformableComponent : public Component { public: - TransformableComponent( - double x, - double y, - int w, - int h) : - x_(x), - y_(y), - w_(w), - h_(h) - { - } - - inline double getX() const - { - return x_; - } - - inline void setX(double v) - { - x_ = v; - } - - inline double getY() const - { - return y_; - } - - inline void setY(double v) - { - y_ = v; - } - - inline int getW() const - { - return w_; - } - - inline void setW(int v) - { - w_ = v; - } - - inline int getH() const - { - return h_; - } - - inline void setH(int v) - { - h_ = v; - } - -private: - - double x_; - double y_; - int w_; - int h_; + /** + * The coordinates of the entity. + */ + double x; + double y; + + /** + * The size of the entity. + */ + int w; + int h; + + /** + * For prototypes, the original coordinates and size of the entity. + * + * @managed_by RealizingSystem + */ + double origX; + double origY; + int origW; + int origH; }; #endif /* end of include guard: LOCATABLE_H_39E526CA */ diff --git a/src/game.cpp b/src/game.cpp index 3da23a3..b7dd200 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -11,6 +11,7 @@ #include "systems/orienting.h" #include "systems/playing.h" #include "systems/scheduling.h" +#include "systems/realizing.h" #include "animation.h" #include "consts.h" @@ -28,8 +29,9 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) game.systemManager_.input(key, action); } -Game::Game() : world_("res/maps.xml") +Game::Game() { + systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); @@ -38,8 +40,8 @@ Game::Game() : world_("res/maps.xml") systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); + systemManager_.getSystem().initSingleton("res/maps.xml"); systemManager_.getSystem().initPlayer(); - systemManager_.getSystem().loadMap(world_.getStartingMapId()); glfwSwapInterval(1); glfwSetWindowUserPointer(renderer_.getWindow().getHandle(), this); diff --git a/src/game.h b/src/game.h index 43e08da..92a67d9 100644 --- a/src/game.h +++ b/src/game.h @@ -3,7 +3,6 @@ #include "entity_manager.h" #include "system_manager.h" -#include "world.h" #include "renderer/renderer.h" class Game { @@ -28,11 +27,6 @@ public: return systemManager_; } - inline const World& getWorld() - { - return world_; - } - friend void key_callback( GLFWwindow* window, int key, @@ -45,7 +39,6 @@ private: Renderer renderer_; EntityManager entityManager_; SystemManager systemManager_; - World world_; bool shouldQuit_ = false; }; diff --git a/src/map.h b/src/map.h deleted file mode 100644 index 6fe1e62..0000000 --- a/src/map.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef MAP_H_74055FC0 -#define MAP_H_74055FC0 - -#include -#include -#include -#include -#include - -class Map { -public: - - class Adjacent { - public: - - enum class Type { - wall, - wrap, - warp, - reverse - }; - - Adjacent( - Type type = Type::wall, - int mapId = -1) : - type_(type), - mapId_(mapId) - { - } - - inline Type getType() const - { - return type_; - } - - inline int getMapId() const - { - return mapId_; - } - - private: - - Type type_; - int mapId_; - }; - - Map( - int id, - std::vector tiles, - std::string title, - Adjacent leftAdjacent, - Adjacent rightAdjacent, - Adjacent upAdjacent, - Adjacent downAdjacent) : - id_(id), - tiles_(std::move(tiles)), - title_(std::move(title)), - leftAdjacent_(std::move(leftAdjacent)), - rightAdjacent_(std::move(rightAdjacent)), - upAdjacent_(std::move(upAdjacent)), - downAdjacent_(std::move(downAdjacent)) - { - } - - inline size_t getId() const - { - return id_; - } - - inline const std::vector& getTiles() const - { - return tiles_; - } - - inline const std::string& getTitle() const - { - return title_; - } - - inline const Adjacent& getLeftAdjacent() const - { - return leftAdjacent_; - } - - inline const Adjacent& getRightAdjacent() const - { - return rightAdjacent_; - } - - inline const Adjacent& getUpAdjacent() const - { - return upAdjacent_; - } - - inline const Adjacent& getDownAdjacent() const - { - return downAdjacent_; - } - -private: - - int id_; - std::vector tiles_; - std::string title_; - Adjacent leftAdjacent_; - Adjacent rightAdjacent_; - Adjacent upAdjacent_; - Adjacent downAdjacent_; -}; - -#endif /* end of include guard: MAP_H_74055FC0 */ diff --git a/src/systems/animating.cpp b/src/systems/animating.cpp index 634af67..8543ba2 100644 --- a/src/systems/animating.cpp +++ b/src/systems/animating.cpp @@ -13,26 +13,29 @@ void AnimatingSystem::tick(double) auto& sprite = game_.getEntityManager(). getComponent(entity); - if (!sprite.isFrozen()) + if (sprite.active) { - sprite.setCountdown(sprite.getCountdown() + 1); - } - - const Animation& anim = sprite.getAnimation(); - if (sprite.getCountdown() >= anim.getDelay()) - { - sprite.setFrame(sprite.getFrame() + 1); - sprite.setCountdown(0); + if (!sprite.frozen) + { + sprite.countdown++; + } - if (sprite.getFrame() >= anim.getFirstFrame() + anim.getNumFrames()) + const Animation& anim = sprite.getAnimation(); + if (sprite.countdown >= anim.getDelay()) { - sprite.setFrame(anim.getFirstFrame()); + sprite.frame++; + sprite.countdown = 0; + + if (sprite.frame >= anim.getFirstFrame() + anim.getNumFrames()) + { + sprite.frame = anim.getFirstFrame(); + } } - } - if (sprite.isFlickering()) - { - sprite.setFlickerTimer((sprite.getFlickerTimer() + 1) % 6); + if (sprite.flickering) + { + sprite.flickerTimer = (sprite.flickerTimer + 1) % 6; + } } } } @@ -49,36 +52,52 @@ void AnimatingSystem::render(Texture& texture) auto& sprite = game_.getEntityManager(). getComponent(entity); - auto& transform = game_.getEntityManager(). - getComponent(entity); - - double alpha = 1.0; - if (sprite.isFlickering() && (sprite.getFlickerTimer() < 3)) + if (sprite.active) { - alpha = 0.0; - } + auto& transform = game_.getEntityManager(). + getComponent(entity); - Rectangle dstrect { - static_cast(transform.getX()), - static_cast(transform.getY()), - transform.getW(), - transform.getH()}; - - const AnimationSet& aset = sprite.getAnimationSet(); - game_.getRenderer().blit( - aset.getTexture(), - texture, - aset.getFrameRect(sprite.getFrame()), - dstrect, - alpha); + double alpha = 1.0; + if (sprite.flickering && (sprite.flickerTimer < 3)) + { + alpha = 0.0; + } + + Rectangle dstrect { + static_cast(transform.x), + static_cast(transform.y), + transform.w, + transform.h}; + + const AnimationSet& aset = sprite.animationSet; + game_.getRenderer().blit( + aset.getTexture(), + texture, + aset.getFrameRect(sprite.frame), + dstrect, + alpha); + } } } +void AnimatingSystem::initPrototype(id_type entity) +{ + auto& sprite = game_.getEntityManager(). + getComponent(entity); + + startAnimation(entity, sprite.origAnimation); + + sprite.countdown = 0; + sprite.flickering = false; + sprite.flickerTimer = 0; + sprite.frozen = false; +} + void AnimatingSystem::startAnimation(id_type entity, std::string animation) { auto& sprite = game_.getEntityManager(). getComponent(entity); - sprite.setAnimation(animation); - sprite.setFrame(sprite.getAnimation().getFirstFrame()); + sprite.animation = std::move(animation); + sprite.frame = sprite.getAnimation().getFirstFrame(); } diff --git a/src/systems/animating.h b/src/systems/animating.h index 548bff1..acc6191 100644 --- a/src/systems/animating.h +++ b/src/systems/animating.h @@ -16,6 +16,8 @@ public: void render(Texture& texture); + void initPrototype(id_type entity); + void startAnimation(id_type entity, std::string animation); }; diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index a3a17ec..af67aed 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -1,5 +1,7 @@ #include "mapping.h" #include "components/mappable.h" +#include "components/realizable.h" +#include "systems/realizing.h" #include "game.h" #include "consts.h" @@ -18,104 +20,95 @@ inline void addBoundary( void MappingSystem::render(Texture& texture) { - auto entities = game_.getEntityManager().getEntitiesWithComponents< - MappableComponent>(); + auto& realizable = game_.getEntityManager(). + getComponent( + game_.getSystemManager().getSystem().getSingleton()); - for (id_type entity : entities) - { - auto& mappable = game_.getEntityManager(). - getComponent(entity); + id_type map = realizable.activeMap; - const Map& map = game_.getWorld().getMap(mappable.getMapId()); + auto& mappable = game_.getEntityManager(). + getComponent(map); - for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) - { - int x = i % MAP_WIDTH; - int y = i / MAP_WIDTH; - int tile = map.getTiles()[i]; - - if (tile > 0) - { - Rectangle dst { - x * TILE_WIDTH, - y * TILE_HEIGHT, - TILE_WIDTH, - TILE_HEIGHT}; - - Rectangle src { - (tile % TILESET_COLS) * TILE_WIDTH, - (tile / TILESET_COLS) * TILE_HEIGHT, - TILE_WIDTH, - TILE_HEIGHT}; - - game_.getRenderer().blit( - mappable.getTileset(), - texture, - std::move(src), - std::move(dst)); - } - } + for (int i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) + { + int x = i % MAP_WIDTH; + int y = i / MAP_WIDTH; + int tile = mappable.tiles[i]; - int startX = ((GAME_WIDTH / TILE_WIDTH) / 2) - (map.getTitle().size() / 2); - for (size_t i = 0; i < map.getTitle().size(); i++) + if (tile > 0) { - Rectangle src { - (map.getTitle()[i] % FONT_COLS) * TILE_WIDTH, - (map.getTitle()[i] / FONT_COLS) * TILE_HEIGHT, + Rectangle dst { + x * TILE_WIDTH, + y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT}; - Rectangle dst { - (startX + static_cast(i)) * TILE_WIDTH, - 24 * TILE_HEIGHT, + Rectangle src { + (tile % TILESET_COLS) * TILE_WIDTH, + (tile / TILESET_COLS) * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT}; game_.getRenderer().blit( - mappable.getFont(), + mappable.tileset, texture, std::move(src), std::move(dst)); } } + + int startX = ((GAME_WIDTH / TILE_WIDTH) / 2) - (mappable.title.size() / 2); + + for (size_t i = 0; i < mappable.title.size(); i++) + { + Rectangle src { + (mappable.title[i] % FONT_COLS) * TILE_WIDTH, + (mappable.title[i] / FONT_COLS) * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + Rectangle dst { + (startX + static_cast(i)) * TILE_WIDTH, + 24 * TILE_HEIGHT, + TILE_WIDTH, + TILE_HEIGHT}; + + game_.getRenderer().blit( + mappable.font, + texture, + std::move(src), + std::move(dst)); + } } -void MappingSystem::loadMap(size_t mapId) +void MappingSystem::generateBoundaries(id_type mapEntity) { - id_type mapEntity = game_.getEntityManager().emplaceEntity(); - auto& mappable = game_.getEntityManager(). - emplaceComponent(mapEntity, - Texture("res/tiles.png"), - Texture("res/font.bmp")); - - mappable.setMapId(mapId); - - const Map& map = game_.getWorld().getMap(mappable.getMapId()); + getComponent(mapEntity); addBoundary( - mappable.getLeftBoundaries(), + mappable.leftBoundaries, -WALL_GAP, 0, MAP_HEIGHT * TILE_HEIGHT, MappableComponent::Boundary::Type::adjacency); addBoundary( - mappable.getRightBoundaries(), + mappable.rightBoundaries, GAME_WIDTH + WALL_GAP, 0, MAP_HEIGHT * TILE_HEIGHT, MappableComponent::Boundary::Type::adjacency); addBoundary( - mappable.getUpBoundaries(), + mappable.upBoundaries, -WALL_GAP, 0, GAME_WIDTH, MappableComponent::Boundary::Type::adjacency); addBoundary( - mappable.getDownBoundaries(), + mappable.downBoundaries, MAP_HEIGHT * TILE_HEIGHT + WALL_GAP, 0, GAME_WIDTH, @@ -125,12 +118,12 @@ void MappingSystem::loadMap(size_t mapId) { size_t x = i % MAP_WIDTH; size_t y = i / MAP_WIDTH; - int tile = map.getTiles()[i]; + int tile = mappable.tiles[i]; if ((tile >= 5) && (tile <= 7)) { addBoundary( - mappable.getDownBoundaries(), + mappable.downBoundaries, y * TILE_HEIGHT, x * TILE_WIDTH, (x + 1) * TILE_WIDTH, @@ -138,28 +131,28 @@ void MappingSystem::loadMap(size_t mapId) } else if ((tile > 0) && (tile < 28)) { addBoundary( - mappable.getRightBoundaries(), + mappable.rightBoundaries, x * TILE_WIDTH, y * TILE_HEIGHT, (y+1) * TILE_HEIGHT, MappableComponent::Boundary::Type::wall); addBoundary( - mappable.getLeftBoundaries(), + mappable.leftBoundaries, (x+1) * TILE_WIDTH, y * TILE_HEIGHT, (y+1) * TILE_HEIGHT, MappableComponent::Boundary::Type::wall); addBoundary( - mappable.getDownBoundaries(), + mappable.downBoundaries, y * TILE_HEIGHT, x * TILE_WIDTH, (x+1) * TILE_WIDTH, MappableComponent::Boundary::Type::wall); addBoundary( - mappable.getUpBoundaries(), + mappable.upBoundaries, (y+1) * TILE_HEIGHT, x * TILE_WIDTH, (x+1) * TILE_WIDTH, @@ -167,28 +160,28 @@ void MappingSystem::loadMap(size_t mapId) } else if (tile == 42) { addBoundary( - mappable.getRightBoundaries(), + mappable.rightBoundaries, x * TILE_WIDTH, y * TILE_HEIGHT, (y+1) * TILE_HEIGHT, MappableComponent::Boundary::Type::danger); addBoundary( - mappable.getLeftBoundaries(), + mappable.leftBoundaries, (x+1) * TILE_WIDTH, y * TILE_HEIGHT, (y+1) * TILE_HEIGHT, MappableComponent::Boundary::Type::danger); addBoundary( - mappable.getDownBoundaries(), + mappable.downBoundaries, y * TILE_HEIGHT, x * TILE_WIDTH, (x+1) * TILE_WIDTH, MappableComponent::Boundary::Type::danger); addBoundary( - mappable.getUpBoundaries(), + mappable.upBoundaries, (y+1) * TILE_HEIGHT, x * TILE_WIDTH, (x+1) * TILE_WIDTH, diff --git a/src/systems/mapping.h b/src/systems/mapping.h index 53d054b..3c47419 100644 --- a/src/systems/mapping.h +++ b/src/systems/mapping.h @@ -12,7 +12,7 @@ public: void render(Texture& texture); - void loadMap(size_t mapId); + void generateBoundaries(id_type mapEntity); }; diff --git a/src/systems/orienting.cpp b/src/systems/orienting.cpp index 2df8f87..206ebf6 100644 --- a/src/systems/orienting.cpp +++ b/src/systems/orienting.cpp @@ -24,27 +24,27 @@ void OrientingSystem::tick(double) { case OrientableComponent::WalkState::still: { - ponderable.setVelocityX(0); + ponderable.velX = 0.0; break; } case OrientableComponent::WalkState::left: { - ponderable.setVelocityX(-WALK_SPEED); + ponderable.velX = -WALK_SPEED; break; } case OrientableComponent::WalkState::right: { - ponderable.setVelocityX(WALK_SPEED); + ponderable.velX = WALK_SPEED; break; } } - if (orientable.isJumping() && (ponderable.getVelocityY() > 0)) + if (orientable.isJumping() && (ponderable.velY > 0)) { orientable.setJumping(false); } @@ -63,7 +63,7 @@ void OrientingSystem::moveLeft(id_type entity) orientable.setWalkState(OrientableComponent::WalkState::left); auto& animating = game_.getSystemManager().getSystem(); - if (ponderable.isGrounded()) + if (ponderable.grounded) { animating.startAnimation(entity, "walkingLeft"); } else { @@ -83,7 +83,7 @@ void OrientingSystem::moveRight(id_type entity) orientable.setWalkState(OrientableComponent::WalkState::right); auto& animating = game_.getSystemManager().getSystem(); - if (ponderable.isGrounded()) + if (ponderable.grounded) { animating.startAnimation(entity, "walkingRight"); } else { @@ -113,7 +113,7 @@ void OrientingSystem::jump(id_type entity) auto& ponderable = game_.getEntityManager(). getComponent(entity); - if (ponderable.isGrounded()) + if (ponderable.grounded) { auto& orientable = game_.getEntityManager(). getComponent(entity); @@ -122,8 +122,8 @@ void OrientingSystem::jump(id_type entity) playSound("res/Randomize87.wav", 0.25); - ponderable.setVelocityY(JUMP_VELOCITY); - ponderable.setAccelY(JUMP_GRAVITY); + ponderable.velY = JUMP_VELOCITY; + ponderable.accelY = JUMP_GRAVITY; auto& animating = game_.getSystemManager().getSystem(); if (orientable.isFacingRight()) @@ -147,7 +147,7 @@ void OrientingSystem::stopJumping(id_type entity) auto& ponderable = game_.getEntityManager(). getComponent(entity); - ponderable.setAccelY(NORMAL_GRAVITY); + ponderable.accelY = NORMAL_GRAVITY; } } @@ -211,7 +211,7 @@ void OrientingSystem::drop(id_type entity) auto& ponderable = game_.getEntityManager(). getComponent(entity); - if (ponderable.isGrounded() + if (ponderable.grounded && (orientable.getDropState() == OrientableComponent::DropState::none)) { orientable.setDropState(OrientableComponent::DropState::ready); diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp index 40d9706..b04f0cb 100644 --- a/src/systems/playing.cpp +++ b/src/systems/playing.cpp @@ -5,61 +5,17 @@ #include "components/playable.h" #include "components/controllable.h" #include "components/orientable.h" +#include "components/realizable.h" #include "systems/mapping.h" #include "systems/pondering.h" #include "systems/orienting.h" #include "systems/scheduling.h" #include "systems/controlling.h" +#include "systems/animating.h" +#include "systems/realizing.h" #include "animation.h" #include "muxer.h" -void PlayingSystem::tick(double) -{ - // Check if we need to change the map - auto players = game_.getEntityManager().getEntitiesWithComponents< - PlayableComponent, - TransformableComponent>(); - - for (id_type player : players) - { - auto& playable = game_.getEntityManager(). - getComponent(player); - - if (playable.changingMap) - { - // Change the map! - auto entities = game_.getEntityManager().getEntities(); - - for (id_type entity : entities) - { - if (entity != player) - { - game_.getEntityManager().deleteEntity(entity); - } - } - - game_.getSystemManager().getSystem(). - loadMap(playable.newMapId); - - auto& transformable = game_.getEntityManager(). - getComponent(player); - - transformable.setX(playable.newMapX); - transformable.setY(playable.newMapY); - - playable.changingMap = false; - - if (playable.newMapCallback) - { - playable.newMapCallback(); - playable.newMapCallback = nullptr; - } - - break; - } - } -} - void PlayingSystem::initPlayer() { id_type player = game_.getEntityManager().emplaceEntity(); @@ -72,15 +28,24 @@ void PlayingSystem::initPlayer() game_.getEntityManager().emplaceComponent( player, - std::move(playerGraphics), - "stillLeft"); + std::move(playerGraphics)); - game_.getEntityManager().emplaceComponent( + game_.getSystemManager().getSystem().startAnimation( player, - game_.getWorld().getStartingX(), - game_.getWorld().getStartingY(), - 10, - 12); + "stillLeft"); + + auto& realizing = game_.getSystemManager().getSystem(); + + auto& realizable = game_.getEntityManager(). + getComponent(realizing.getSingleton()); + + auto& transformable = game_.getEntityManager(). + emplaceComponent(player); + + transformable.x = realizable.startingX; + transformable.y = realizable.startingY; + transformable.w = 10; + transformable.h = 12; game_.getSystemManager().getSystem().initializeBody( player, @@ -92,84 +57,103 @@ void PlayingSystem::initPlayer() auto& playable = game_.getEntityManager(). emplaceComponent(player); - playable.checkpointMapId = game_.getWorld().getStartingMapId(); - playable.checkpointX = game_.getWorld().getStartingX(); - playable.checkpointY = game_.getWorld().getStartingY(); + playable.mapId = realizable.activeMap; + playable.checkpointMapId = realizable.startingMapId; + playable.checkpointX = realizable.startingX; + playable.checkpointY = realizable.startingY; + + realizing.enterActiveMap(player); + + realizable.activePlayer = player; } void PlayingSystem::changeMap( + id_type player, size_t mapId, double x, - double y, - PlayableComponent::MapChangeCallback callback) + double y) { - auto players = game_.getEntityManager().getEntitiesWithComponents< - PlayableComponent>(); + auto& playable = game_.getEntityManager(). + getComponent(player); - for (id_type player : players) + auto& transformable = game_.getEntityManager(). + getComponent(player); + + auto& animatable = game_.getEntityManager(). + getComponent(player); + + auto& ponderable = game_.getEntityManager(). + getComponent(player); + + auto& realizing = game_.getSystemManager().getSystem(); + + auto& realizable = game_.getEntityManager(). + getComponent(realizing.getSingleton()); + + id_type newMapEntity = realizable.entityByMapId[mapId]; + + if (playable.mapId != newMapEntity) { - auto& playable = game_.getEntityManager(). - getComponent(player); + if (playable.mapId == realizable.activeMap) + { + realizing.leaveActiveMap(player); + } else if (newMapEntity == realizable.activeMap) + { + realizing.enterActiveMap(player); + } - playable.changingMap = true; - playable.newMapId = mapId; - playable.newMapX = x; - playable.newMapY = y; - playable.newMapCallback = std::move(callback); + playable.mapId = newMapEntity; + } + + transformable.x = x; + transformable.y = y; + + if (realizable.activePlayer == player) + { + realizing.loadMap(newMapEntity); } } -void PlayingSystem::die() +void PlayingSystem::die(id_type player) { playSound("res/Hit_Hurt5.wav", 0.25); - auto players = game_.getEntityManager().getEntitiesWithComponents< - OrientableComponent, - ControllableComponent, - AnimatableComponent, - PonderableComponent, - PlayableComponent>(); + auto& animatable = game_.getEntityManager(). + getComponent(player); - for (id_type player : players) - { - auto& animatable = game_.getEntityManager(). - getComponent(player); - - auto& ponderable = game_.getEntityManager(). - getComponent(player); - - auto& controlling = game_.getSystemManager().getSystem(); - controlling.freeze(player); - - animatable.setFrozen(true); - animatable.setFlickering(true); - ponderable.setFrozen(true); - ponderable.setCollidable(false); - - auto& scheduling = game_.getSystemManager().getSystem(); - - scheduling.schedule(player, 0.75, [&] (id_type player) { - auto& playable = game_.getEntityManager(). - getComponent(player); - - changeMap( - playable.checkpointMapId, - playable.checkpointX, - playable.checkpointY, - [&, player] () { - animatable.setFrozen(false); - animatable.setFlickering(false); - ponderable.setFrozen(false); - ponderable.setCollidable(true); - - // Reset the walk state, and then potentially let the - // ControllingSystem set it again. - auto& orienting = game_.getSystemManager(). - getSystem(); - orienting.stopWalking(player); - - controlling.unfreeze(player); - }); - }); - } + auto& ponderable = game_.getEntityManager(). + getComponent(player); + + auto& controlling = game_.getSystemManager().getSystem(); + controlling.freeze(player); + + animatable.frozen = true; + animatable.flickering = true; + ponderable.frozen = true; + ponderable.collidable = false; + + auto& scheduling = game_.getSystemManager().getSystem(); + + scheduling.schedule(player, 0.75, [&] (id_type player) { + auto& playable = game_.getEntityManager(). + getComponent(player); + + changeMap( + player, + playable.checkpointMapId, + playable.checkpointX, + playable.checkpointY); + + animatable.frozen = false; + animatable.flickering = false; + ponderable.frozen = false; + ponderable.collidable = true; + + // Reset the walk state, and then potentially let the + // ControllingSystem set it again. + auto& orienting = game_.getSystemManager().getSystem(); + orienting.stopWalking(player); + + controlling.unfreeze(player); + }); } diff --git a/src/systems/playing.h b/src/systems/playing.h index ff16808..9ba403b 100644 --- a/src/systems/playing.h +++ b/src/systems/playing.h @@ -2,7 +2,6 @@ #define PLAYING_H_70A54F7D #include "system.h" -#include "components/playable.h" class PlayingSystem : public System { public: @@ -11,17 +10,15 @@ public: { } - void tick(double dt); - void initPlayer(); void changeMap( + id_type player, size_t mapId, double x, - double y, - PlayableComponent::MapChangeCallback callback = nullptr); + double y); - void die(); + void die(id_type player); }; diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 02d5cfc..4ae6176 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -5,149 +5,153 @@ #include "components/transformable.h" #include "components/orientable.h" #include "components/mappable.h" +#include "components/realizable.h" +#include "components/playable.h" #include "systems/orienting.h" #include "systems/playing.h" +#include "systems/realizing.h" #include "consts.h" #include "collision.h" void PonderingSystem::tick(double dt) { + auto& realizable = game_.getEntityManager(). + getComponent( + game_.getSystemManager().getSystem().getSingleton()); + + id_type mapEntity = realizable.activeMap; + + auto& mappable = game_.getEntityManager(). + getComponent(mapEntity); + auto entities = game_.getEntityManager().getEntitiesWithComponents< PonderableComponent, TransformableComponent>(); - auto maps = game_.getEntityManager().getEntitiesWithComponents< - MappableComponent>(); - for (id_type entity : entities) { - auto& transformable = game_.getEntityManager(). - getComponent(entity); - auto& ponderable = game_.getEntityManager(). getComponent(entity); - if (ponderable.isFrozen()) + if (!ponderable.active || ponderable.frozen) { continue; } - // Accelerate - ponderable.setVelocityX( - ponderable.getVelocityX() + ponderable.getAccelX() * dt); + auto& transformable = game_.getEntityManager(). + getComponent(entity); - ponderable.setVelocityY( - ponderable.getVelocityY() + ponderable.getAccelY() * dt); + // Accelerate + ponderable.velX += ponderable.accelX * dt; + ponderable.velY += ponderable.accelY * dt; - if ((ponderable.getType() == PonderableComponent::Type::freefalling) - && (ponderable.getVelocityY() > TERMINAL_VELOCITY)) + if ((ponderable.type == PonderableComponent::Type::freefalling) + && (ponderable.velY > TERMINAL_VELOCITY)) { - ponderable.setVelocityY(TERMINAL_VELOCITY); + ponderable.velY = TERMINAL_VELOCITY; } - const double oldX = transformable.getX(); - const double oldY = transformable.getY(); - const double oldRight = oldX + transformable.getW(); - const double oldBottom = oldY + transformable.getH(); + const double oldX = transformable.x; + const double oldY = transformable.y; + const double oldRight = oldX + transformable.w; + const double oldBottom = oldY + transformable.h; - double newX = oldX + ponderable.getVelocityX() * dt; - double newY = oldY + ponderable.getVelocityY() * dt; + double newX = oldX + ponderable.velX * dt; + double newY = oldY + ponderable.velY * dt; - bool oldGrounded = ponderable.isGrounded(); - ponderable.setGrounded(false); + bool oldGrounded = ponderable.grounded; + ponderable.grounded = false; std::priority_queue collisions; // Find collisions - for (id_type mapEntity : maps) + if (newX < oldX) { - auto& mappable = game_.getEntityManager(). - getComponent(mapEntity); - - if (newX < oldX) + for (auto it = mappable.leftBoundaries.lower_bound(oldX); + (it != std::end(mappable.leftBoundaries)) && (it->first >= newX); + it++) { - for (auto it = mappable.getLeftBoundaries().lower_bound(oldX); - (it != std::end(mappable.getLeftBoundaries())) && (it->first >= newX); - it++) + if ((oldBottom > it->second.lower) + && (oldY < it->second.upper)) { - if ((oldBottom > it->second.getLower()) - && (oldY < it->second.getUpper())) - { - // We have a collision! - collisions.emplace( - mapEntity, - Direction::left, - it->second.getType(), - it->first, - it->second.getLower(), - it->second.getUpper()); - } + // We have a collision! + collisions.emplace( + mapEntity, + Direction::left, + it->second.type, + it->first, + it->second.lower, + it->second.upper); } - } else if (newX > oldX) + } + } else if (newX > oldX) + { + for (auto it = mappable.rightBoundaries.lower_bound(oldRight); + (it != std::end(mappable.rightBoundaries)) + && (it->first <= (newX + transformable.w)); + it++) { - for (auto it = mappable.getRightBoundaries().lower_bound(oldRight); - (it != std::end(mappable.getRightBoundaries())) - && (it->first <= (newX + transformable.getW())); - it++) + if ((oldBottom > it->second.lower) + && (oldY < it->second.upper)) { - if ((oldBottom > it->second.getLower()) - && (oldY < it->second.getUpper())) - { - // We have a collision! - collisions.emplace( - mapEntity, - Direction::right, - it->second.getType(), - it->first, - it->second.getLower(), - it->second.getUpper()); - } + // We have a collision! + collisions.emplace( + mapEntity, + Direction::right, + it->second.type, + it->first, + it->second.lower, + it->second.upper); } } + } - if (newY < oldY) + if (newY < oldY) + { + for (auto it = mappable.upBoundaries.lower_bound(oldY); + (it != std::end(mappable.upBoundaries)) && (it->first >= newY); + it++) { - for (auto it = mappable.getUpBoundaries().lower_bound(oldY); - (it != std::end(mappable.getUpBoundaries())) && (it->first >= newY); - it++) + if ((oldRight > it->second.lower) + && (oldX < it->second.upper)) { - if ((oldRight > it->second.getLower()) - && (oldX < it->second.getUpper())) - { - // We have a collision! - collisions.emplace( - mapEntity, - Direction::up, - it->second.getType(), - it->first, - it->second.getLower(), - it->second.getUpper()); - } + // We have a collision! + collisions.emplace( + mapEntity, + Direction::up, + it->second.type, + it->first, + it->second.lower, + it->second.upper); } - } else if (newY > oldY) + } + } else if (newY > oldY) + { + for (auto it = mappable.downBoundaries.lower_bound(oldBottom); + (it != std::end(mappable.downBoundaries)) + && (it->first <= (newY + transformable.h)); + it++) { - for (auto it = mappable.getDownBoundaries().lower_bound(oldBottom); - (it != std::end(mappable.getDownBoundaries())) - && (it->first <= (newY + transformable.getH())); - it++) + if ((oldRight > it->second.lower) + && (oldX < it->second.upper)) { - if ((oldRight > it->second.getLower()) - && (oldX < it->second.getUpper())) - { - // We have a collision! - collisions.emplace( - mapEntity, - Direction::down, - it->second.getType(), - it->first, - it->second.getLower(), - it->second.getUpper()); - } + // We have a collision! + collisions.emplace( + mapEntity, + Direction::down, + it->second.type, + it->first, + it->second.lower, + it->second.upper); } } } // Process collisions in order of priority + bool adjacentlyWarping = false; + Direction adjWarpDir; + size_t adjWarpMapId; + while (!collisions.empty()) { Collision collision = collisions.top(); @@ -157,8 +161,8 @@ void PonderingSystem::tick(double dt) if (!collision.isColliding( newX, newY, - transformable.getW(), - transformable.getH())) + transformable.w, + transformable.h)) { continue; } @@ -201,33 +205,33 @@ void PonderingSystem::tick(double dt) { auto& mappable = game_.getEntityManager(). getComponent(collision.getCollider()); - const Map& map = game_.getWorld().getMap(mappable.getMapId()); - auto& adj = [&] () -> const Map::Adjacent& { + + auto& adj = [&] () -> const MappableComponent::Adjacent& { switch (collision.getDirection()) { - case Direction::left: return map.getLeftAdjacent(); - case Direction::right: return map.getRightAdjacent(); - case Direction::up: return map.getUpAdjacent(); - case Direction::down: return map.getDownAdjacent(); + case Direction::left: return mappable.leftAdjacent; + case Direction::right: return mappable.rightAdjacent; + case Direction::up: return mappable.upAdjacent; + case Direction::down: return mappable.downAdjacent; } }(); - switch (adj.getType()) + switch (adj.type) { - case Map::Adjacent::Type::wall: + case MappableComponent::Adjacent::Type::wall: { touchedWall = true; break; } - case Map::Adjacent::Type::wrap: + case MappableComponent::Adjacent::Type::wrap: { switch (collision.getDirection()) { case Direction::left: { - newX = GAME_WIDTH + WALL_GAP - transformable.getW(); + newX = GAME_WIDTH + WALL_GAP - transformable.w; break; } @@ -241,8 +245,7 @@ void PonderingSystem::tick(double dt) case Direction::up: { - newY = MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - - transformable.getH(); + newY = MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.h; break; } @@ -256,46 +259,22 @@ void PonderingSystem::tick(double dt) } } - case Map::Adjacent::Type::warp: + case MappableComponent::Adjacent::Type::warp: { - double warpX = newX; - double warpY = newY; - - switch (collision.getDirection()) + if (game_.getEntityManager(). + hasComponent(entity)) { - case Direction::left: - { - warpX = GAME_WIDTH + WALL_GAP - transformable.getW(); - - break; - } - - case Direction::right: - { - warpX = -WALL_GAP; - - break; - } - - case Direction::up: - { - warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.getH(); - - break; - } - - case Direction::down: - { - warpY = -WALL_GAP; - - break; - } + adjacentlyWarping = true; + adjWarpDir = collision.getDirection(); + adjWarpMapId = adj.mapId; } - game_.getSystemManager().getSystem(). - changeMap(adj.getMapId(), warpX, warpY); + break; + } - stopProcessing = true; + case MappableComponent::Adjacent::Type::reverse: + { + // TODO: not yet implemented. break; } @@ -306,7 +285,13 @@ void PonderingSystem::tick(double dt) case Collision::Type::danger: { - game_.getSystemManager().getSystem().die(); + if (game_.getEntityManager(). + hasComponent(entity)) + { + game_.getSystemManager().getSystem().die(entity); + + adjacentlyWarping = false; + } stopProcessing = true; @@ -333,15 +318,15 @@ void PonderingSystem::tick(double dt) case Direction::left: { newX = collision.getAxis(); - ponderable.setVelocityX(0.0); + ponderable.velX = 0.0; break; } case Direction::right: { - newX = collision.getAxis() - transformable.getW(); - ponderable.setVelocityX(0.0); + newX = collision.getAxis() - transformable.w; + ponderable.velX = 0.0; break; } @@ -349,16 +334,16 @@ void PonderingSystem::tick(double dt) case Direction::up: { newY = collision.getAxis(); - ponderable.setVelocityY(0.0); + ponderable.velY = 0.0; break; } case Direction::down: { - newY = collision.getAxis() - transformable.getH(); - ponderable.setVelocityY(0.0); - ponderable.setGrounded(true); + newY = collision.getAxis() - transformable.h; + ponderable.velY = 0.0; + ponderable.grounded = true; break; } @@ -367,8 +352,8 @@ void PonderingSystem::tick(double dt) } // Move - transformable.setX(newX); - transformable.setY(newY); + transformable.x = newX; + transformable.y = newY; // Perform cleanup for orientable entites if (game_.getEntityManager().hasComponent(entity)) @@ -377,9 +362,9 @@ void PonderingSystem::tick(double dt) getComponent(entity); // Handle changes in groundedness - if (ponderable.isGrounded() != oldGrounded) + if (ponderable.grounded != oldGrounded) { - if (ponderable.isGrounded()) + if (ponderable.grounded) { game_.getSystemManager().getSystem().land(entity); } else { @@ -394,6 +379,51 @@ void PonderingSystem::tick(double dt) orientable.setDropState(OrientableComponent::DropState::none); } } + + // Move to an adjacent map, if necessary + if (adjacentlyWarping) + { + double warpX = newX; + double warpY = newY; + + switch (adjWarpDir) + { + case Direction::left: + { + warpX = GAME_WIDTH + WALL_GAP - transformable.w; + + break; + } + + case Direction::right: + { + warpX = -WALL_GAP; + + break; + } + + case Direction::up: + { + warpY = MAP_HEIGHT * TILE_HEIGHT - transformable.h; + + break; + } + + case Direction::down: + { + warpY = -WALL_GAP; + + break; + } + } + + game_.getSystemManager().getSystem(). + changeMap( + entity, + adjWarpMapId, + warpX, + warpY); + } } } @@ -406,6 +436,20 @@ void PonderingSystem::initializeBody( if (type == PonderableComponent::Type::freefalling) { - ponderable.setAccelY(NORMAL_GRAVITY); + ponderable.accelY = NORMAL_GRAVITY; } } + +void PonderingSystem::initPrototype(id_type prototype) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(prototype); + + ponderable.velX = 0.0; + ponderable.velY = 0.0; + ponderable.accelX = 0.0; + ponderable.accelY = 0.0; + ponderable.grounded = false; + ponderable.frozen = false; + ponderable.collidable = true; +} diff --git a/src/systems/pondering.h b/src/systems/pondering.h index d70525b..58e6496 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h @@ -16,6 +16,8 @@ public: void initializeBody(id_type entity, PonderableComponent::Type type); + void initPrototype(id_type prototype); + }; #endif /* end of include guard: PONDERING_H_F2530E0E */ diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp new file mode 100644 index 0000000..09c38f3 --- /dev/null +++ b/src/systems/realizing.cpp @@ -0,0 +1,321 @@ +#include "realizing.h" +#include +#include +#include +#include "game.h" +#include "consts.h" +#include "components/realizable.h" +#include "components/mappable.h" +#include "components/animatable.h" +#include "components/playable.h" +#include "components/ponderable.h" +#include "components/transformable.h" +#include "systems/mapping.h" +#include "systems/animating.h" +#include "systems/pondering.h" + +inline xmlChar* getProp(xmlNodePtr node, const char* attr) +{ + xmlChar* key = xmlGetProp(node, reinterpret_cast(attr)); + if (key == nullptr) + { + throw std::invalid_argument("Error parsing world file"); + } + + return key; +} + +// TODO: neither the XML doc nor any of the emplaced entities are properly +// destroyed if this method throws an exception. +EntityManager::id_type RealizingSystem::initSingleton(std::string filename) +{ + id_type world = game_.getEntityManager().emplaceEntity(); + + auto& realizable = game_.getEntityManager(). + emplaceComponent(world); + + auto& mapping = game_.getSystemManager().getSystem(); + + xmlDocPtr doc = xmlParseFile(filename.c_str()); + if (doc == nullptr) + { + throw std::invalid_argument("Cannot find world file"); + } + + xmlNodePtr top = xmlDocGetRootElement(doc); + if (top == nullptr) + { + throw std::invalid_argument("Error parsing world file"); + } + + if (xmlStrcmp(top->name, reinterpret_cast("world"))) + { + throw std::invalid_argument("Error parsing world file"); + } + + xmlChar* key = nullptr; + + key = getProp(top, "startx"); + realizable.startingX = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(top, "starty"); + realizable.startingY = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(top, "startmap"); + realizable.startingMapId = atoi(reinterpret_cast(key)); + xmlFree(key); + + for (xmlNodePtr node = top->xmlChildrenNode; + node != nullptr; + node = node->next) + { + if (!xmlStrcmp(node->name, reinterpret_cast("map"))) + { + id_type map = game_.getEntityManager().emplaceEntity(); + + auto& mappable = game_.getEntityManager(). + emplaceComponent(map, + Texture("res/tiles.png"), + Texture("res/font.bmp")); + + key = getProp(node, "id"); + mappable.mapId = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(node, "title"); + mappable.title = reinterpret_cast(key); + xmlFree(key); + + for (xmlNodePtr mapNode = node->xmlChildrenNode; + mapNode != nullptr; + mapNode = mapNode->next) + { + if (!xmlStrcmp( + mapNode->name, + reinterpret_cast("environment"))) + { + key = xmlNodeGetContent(mapNode); + if (key == nullptr) + { + throw std::invalid_argument("Error parsing world file"); + } + + mappable.tiles.clear(); + mappable.tiles.push_back(atoi(strtok( + reinterpret_cast(key), + ",\n"))); + + for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++) + { + mappable.tiles.push_back(atoi(strtok(nullptr, ",\n"))); + } + + xmlFree(key); + } else if (!xmlStrcmp( + mapNode->name, + reinterpret_cast("adjacent"))) + { + key = getProp(mapNode, "type"); + std::string adjTypeStr(reinterpret_cast(key)); + xmlFree(key); + + MappableComponent::Adjacent::Type adjType; + if (adjTypeStr == "wall") + { + adjType = MappableComponent::Adjacent::Type::wall; + } else if (adjTypeStr == "wrap") + { + adjType = MappableComponent::Adjacent::Type::wrap; + } else if (adjTypeStr == "warp") + { + adjType = MappableComponent::Adjacent::Type::warp; + } else if (adjTypeStr == "reverseWarp") + { + adjType = MappableComponent::Adjacent::Type::reverse; + } else { + throw std::logic_error("Invalid adjacency type"); + } + + key = getProp(mapNode, "map"); + size_t adjMapId = atoi(reinterpret_cast(key)); + xmlFree(key); + + key = getProp(mapNode, "dir"); + std::string adjDir(reinterpret_cast(key)); + xmlFree(key); + + if (adjDir == "left") + { + mappable.leftAdjacent = {adjType, adjMapId}; + } else if (adjDir == "right") + { + mappable.rightAdjacent = {adjType, adjMapId}; + } else if (adjDir == "up") + { + mappable.upAdjacent = {adjType, adjMapId}; + } else if (adjDir == "down") + { + mappable.downAdjacent = {adjType, adjMapId}; + } else { + throw std::logic_error("Invalid adjacency direction"); + } + } + } + + mapping.generateBoundaries(map); + + realizable.maps.insert(map); + realizable.entityByMapId[mappable.mapId] = map; + } + } + + xmlFreeDoc(doc); + + loadMap(realizable.entityByMapId[realizable.startingMapId]); + + return world; +} + +EntityManager::id_type RealizingSystem::getSingleton() const +{ + std::set result = + game_.getEntityManager().getEntitiesWithComponents< + RealizableComponent>(); + + if (result.empty()) + { + throw std::logic_error("No realizable entity found"); + } else if (result.size() > 1) + { + throw std::logic_error("Multiple realizable entities found"); + } + + return *std::begin(result); +} + +void RealizingSystem::loadMap(id_type mapEntity) +{ + id_type world = getSingleton(); + + auto& realizable = game_.getEntityManager(). + getComponent(world); + + auto& animating = game_.getSystemManager().getSystem(); + auto& pondering = game_.getSystemManager().getSystem(); + + std::set players = + game_.getEntityManager().getEntitiesWithComponents< + PlayableComponent>(); + + if (realizable.hasActiveMap) + { + id_type oldMap = realizable.activeMap; + + auto& oldMappable = game_.getEntityManager(). + getComponent(oldMap); + + // Deactivate any map objects from the old map. + for (id_type prototype : oldMappable.objects) + { + leaveActiveMap(prototype); + } + + // Deactivate players that were on the old map. + for (id_type player : players) + { + auto& playable = game_.getEntityManager(). + getComponent(player); + + if (playable.mapId == oldMap) + { + leaveActiveMap(player); + } + } + } + + realizable.hasActiveMap = true; + realizable.activeMap = mapEntity; + + auto& mappable = game_.getEntityManager(). + getComponent(mapEntity); + + // Initialize the new map's objects. + for (id_type prototype : mappable.objects) + { + if (game_.getEntityManager(). + hasComponent(prototype)) + { + auto& transformable = game_.getEntityManager(). + getComponent(prototype); + + transformable.x = transformable.origX; + transformable.y = transformable.origY; + transformable.w = transformable.origW; + transformable.h = transformable.origH; + } + + if (game_.getEntityManager().hasComponent(prototype)) + { + animating.initPrototype(prototype); + } + + if (game_.getEntityManager().hasComponent(prototype)) + { + pondering.initPrototype(prototype); + } + + enterActiveMap(prototype); + } + + // Activate any players on the map. + for (id_type player : players) + { + auto& playable = game_.getEntityManager(). + getComponent(player); + + if (playable.mapId == mapEntity) + { + enterActiveMap(player); + } + } +} + +void RealizingSystem::enterActiveMap(id_type entity) +{ + if (game_.getEntityManager().hasComponent(entity)) + { + auto& animatable = game_.getEntityManager(). + getComponent(entity); + + animatable.active = true; + } + + if (game_.getEntityManager().hasComponent(entity)) + { + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + ponderable.active = true; + } +} + +void RealizingSystem::leaveActiveMap(id_type entity) +{ + if (game_.getEntityManager().hasComponent(entity)) + { + auto& animatable = game_.getEntityManager(). + getComponent(entity); + + animatable.active = false; + } + + if (game_.getEntityManager().hasComponent(entity)) + { + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + ponderable.active = false; + } +} diff --git a/src/systems/realizing.h b/src/systems/realizing.h new file mode 100644 index 0000000..c681892 --- /dev/null +++ b/src/systems/realizing.h @@ -0,0 +1,43 @@ +#ifndef REALIZING_H_6853748C +#define REALIZING_H_6853748C + +#include "system.h" + +class RealizingSystem : public System { +public: + + RealizingSystem(Game& game) : System(game) + { + } + + /** + * Creates the singleton realizable entity and initializes it with the + * provided world definition. + */ + id_type initSingleton(std::string filename); + + /** + * Helper method that returns the entity ID of the (assumed) singleton entity + * with a RealizableComponent. Throws an exception if the number of realizable + * entities is not exactly one. + */ + id_type getSingleton() const; + + /** + * Loads the given map. + */ + void loadMap(id_type mapEntity); + + /** + * Treats the given entity as part of the active map. + */ + void enterActiveMap(id_type entity); + + /** + * Stops treating the given entity as part of the active map. + */ + void leaveActiveMap(id_type entity); + +}; + +#endif /* end of include guard: REALIZING_H_6853748C */ diff --git a/src/world.cpp b/src/world.cpp deleted file mode 100644 index 3b6bd41..0000000 --- a/src/world.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "world.h" -#include -#include -#include -#include "consts.h" - -inline xmlChar* getProp(xmlNodePtr node, const char* attr) -{ - xmlChar* key = xmlGetProp(node, reinterpret_cast(attr)); - if (key == nullptr) - { - throw std::invalid_argument("Error parsing world file"); - } - - return key; -} - -World::World(std::string filename) -{ - xmlDocPtr doc = xmlParseFile(filename.c_str()); - if (doc == nullptr) - { - throw std::invalid_argument("Cannot find world file"); - } - - xmlNodePtr top = xmlDocGetRootElement(doc); - if (top == nullptr) - { - throw std::invalid_argument("Error parsing world file"); - } - - if (xmlStrcmp(top->name, reinterpret_cast("world"))) - { - throw std::invalid_argument("Error parsing world file"); - } - - xmlChar* key = nullptr; - - key = getProp(top, "startx"); - startX_ = atoi(reinterpret_cast(key)); - xmlFree(key); - - key = getProp(top, "starty"); - startY_ = atoi(reinterpret_cast(key)); - xmlFree(key); - - key = getProp(top, "startmap"); - startMap_ = atoi(reinterpret_cast(key)); - xmlFree(key); - - for (xmlNodePtr node = top->xmlChildrenNode; - node != nullptr; - node = node->next) - { - if (!xmlStrcmp(node->name, reinterpret_cast("map"))) - { - key = getProp(node, "id"); - size_t mapId = atoi(reinterpret_cast(key)); - xmlFree(key); - - key = getProp(node, "title"); - std::string mapTitle(reinterpret_cast(key)); - xmlFree(key); - - std::vector mapTiles; - Map::Adjacent leftAdj; - Map::Adjacent rightAdj; - Map::Adjacent upAdj; - Map::Adjacent downAdj; - - for (xmlNodePtr mapNode = node->xmlChildrenNode; - mapNode != nullptr; - mapNode = mapNode->next) - { - if (!xmlStrcmp( - mapNode->name, - reinterpret_cast("environment"))) - { - key = xmlNodeGetContent(mapNode); - - mapTiles.clear(); - mapTiles.push_back(atoi(strtok(reinterpret_cast(key), ",\n"))); - for (size_t i = 1; i < (MAP_WIDTH * MAP_HEIGHT); i++) - { - mapTiles.push_back(atoi(strtok(nullptr, ",\n"))); - } - - xmlFree(key); - } else if (!xmlStrcmp( - mapNode->name, - reinterpret_cast("adjacent"))) - { - key = getProp(mapNode, "type"); - std::string adjTypeStr(reinterpret_cast(key)); - xmlFree(key); - - Map::Adjacent::Type adjType; - if (adjTypeStr == "wall") - { - adjType = Map::Adjacent::Type::wall; - } else if (adjTypeStr == "wrap") - { - adjType = Map::Adjacent::Type::wrap; - } else if (adjTypeStr == "warp") - { - adjType = Map::Adjacent::Type::warp; - } else if (adjTypeStr == "reverseWarp") - { - adjType = Map::Adjacent::Type::reverse; - } else { - throw std::logic_error("Invalid adjacency type"); - } - - key = getProp(mapNode, "map"); - int adjMapId = atoi(reinterpret_cast(key)); - xmlFree(key); - - key = getProp(mapNode, "dir"); - std::string adjDir(reinterpret_cast(key)); - xmlFree(key); - - if (adjDir == "left") - { - leftAdj = {adjType, adjMapId}; - } else if (adjDir == "right") - { - rightAdj = {adjType, adjMapId}; - } else if (adjDir == "up") - { - upAdj = {adjType, adjMapId}; - } else if (adjDir == "down") - { - downAdj = {adjType, adjMapId}; - } else { - throw std::logic_error("Invalid adjacency direction"); - } - } - } - - maps_.emplace( - std::piecewise_construct, - std::forward_as_tuple(mapId), - std::forward_as_tuple( - mapId, - std::move(mapTiles), - std::move(mapTitle), - leftAdj, - rightAdj, - upAdj, - downAdj)); - } - } - - xmlFreeDoc(doc); -} diff --git a/src/world.h b/src/world.h deleted file mode 100644 index b88adf4..0000000 --- a/src/world.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef WORLD_H_153C698B -#define WORLD_H_153C698B - -#include -#include -#include "map.h" - -class World { -public: - - explicit World(std::string filename); - - inline const Map& getMap(size_t id) const - { - return maps_.at(id); - } - - inline size_t getStartingMapId() const - { - return startMap_; - } - - inline int getStartingX() const - { - return startX_; - } - - inline int getStartingY() const - { - return startY_; - } - -private: - - std::map maps_; - size_t startMap_; - int startX_; - int startY_; -}; - -#endif /* end of include guard: WORLD_H_153C698B */ -- cgit 1.4.1 From c00668c58b26325203cb6815bc3dedf1e7d7ac5e Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 29 Apr 2018 16:45:55 -0400 Subject: Added map object collision Collision checking in PonderingSystem was rewritten to work as follows: horizontal movement is step first, then vertical. In each step, the closest environmental boundary to the body is found on the axis of movement in the space traversed by the body. Then, if any map objects fall in the region between the body's old position and the environmental boundary (or body new position if no boundary was found), process collision with those bodies in increasing distance order, stopping if a collision stops movement short of where the next collision would take place. After this, process collision with all of the environmental boundaries at the axis distance found earlier, as long as movement hasn't stopped short. This is not the most optimal implementation, and there is a lot of code repetition, but it is a start and it works. All map objects currently function as walls. This fixes the bug where you could, with pixel-perfect precision, jump into the corner of a wall tile. The top of the hitbox for the spike tile was lowered by one pixel. This fixes a problem where if the player is halfway on a floor tile and halfway over a spike tile, the floor tile would not stop the spike tile from being processed, and the player would die. --- CMakeLists.txt | 1 - src/collision.cpp | 97 ----- src/collision.h | 81 ---- src/components/mappable.h | 4 +- src/components/ponderable.h | 16 + src/systems/mapping.cpp | 2 +- src/systems/pondering.cpp | 882 +++++++++++++++++++++++++++++++++----------- src/systems/pondering.h | 23 ++ src/systems/realizing.cpp | 4 + 9 files changed, 714 insertions(+), 396 deletions(-) delete mode 100644 src/collision.cpp delete mode 100644 src/collision.h (limited to 'src/systems/mapping.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 49a0384..cd652e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,6 @@ add_executable(Aromatherapy src/game.cpp src/animation.cpp src/util.cpp - src/collision.cpp src/renderer/renderer.cpp src/renderer/mesh.cpp src/renderer/shader.cpp diff --git a/src/collision.cpp b/src/collision.cpp deleted file mode 100644 index b747a90..0000000 --- a/src/collision.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "collision.h" - -bool Collision::operator<(const Collision& other) const -{ - // Most important is the type of collision - if (type_ != other.type_) - { - return (static_cast(type_) > static_cast(other.type_)); - } - - // Next, categorize the collisions arbitrarily based on direction - if (dir_ != other.dir_) - { - return (static_cast(dir_) < static_cast(other.dir_)); - } - - // We want to process closer collisions first - if (axis_ != other.axis_) - { - switch (dir_) - { - case Direction::left: - case Direction::up: - { - return (axis_ < other.axis_); - } - - case Direction::right: - case Direction::down: - { - return (axis_ > other.axis_); - } - } - } - - // Order the remaining attributes arbitrarily - return std::tie(collider_, lower_, upper_) < - std::tie(other.collider_, other.lower_, other.upper_); -} - -bool Collision::isColliding( - double x, - double y, - int w, - int h) const -{ - int right = x + w; - int bottom = y + h; - - switch (dir_) - { - case Direction::left: - case Direction::right: - { - if (!((bottom > lower_) && (y < upper_))) - { - return false; - } - - break; - } - - case Direction::up: - case Direction::down: - { - if (!((right > lower_) && (x < upper_))) - { - return false; - } - - break; - } - } - - switch (dir_) - { - case Direction::left: - { - return (axis_ >= x); - } - - case Direction::right: - { - return (axis_ <= right); - } - - case Direction::up: - { - return (axis_ >= y); - } - - case Direction::down: - { - return (axis_ <= bottom); - } - } -} diff --git a/src/collision.h b/src/collision.h deleted file mode 100644 index e5371f8..0000000 --- a/src/collision.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef COLLISION_H_53D84877 -#define COLLISION_H_53D84877 - -#include "entity_manager.h" -#include "direction.h" - -class Collision { -public: - - using id_type = EntityManager::id_type; - - // Types are defined in descending priority order - enum class Type { - wall, - platform, - adjacency, - warp, - danger - }; - - Collision( - id_type collider, - Direction dir, - Type type, - int axis, - double lower, - double upper) : - collider_(collider), - dir_(dir), - type_(type), - axis_(axis), - lower_(lower), - upper_(upper) - { - } - - inline id_type getCollider() const - { - return collider_; - } - - inline Direction getDirection() const - { - return dir_; - } - - inline Type getType() const - { - return type_; - } - - inline int getAxis() const - { - return axis_; - } - - inline double getLower() const - { - return lower_; - } - - inline double getUpper() const - { - return upper_; - } - - bool operator<(const Collision& other) const; - - bool isColliding(double x, double y, int w, int h) const; - -private: - - id_type collider_; - Direction dir_; - Type type_; - int axis_; - double lower_; - double upper_; -}; - -#endif /* end of include guard: COLLISION_H_53D84877 */ diff --git a/src/components/mappable.h b/src/components/mappable.h index 6f3d38e..e92074e 100644 --- a/src/components/mappable.h +++ b/src/components/mappable.h @@ -7,7 +7,7 @@ #include #include "component.h" #include "renderer/texture.h" -#include "collision.h" +#include "components/ponderable.h" #include "entity_manager.h" class MappableComponent : public Component { @@ -46,7 +46,7 @@ public: class Boundary { public: - using Type = Collision::Type; + using Type = PonderableComponent::Collision; Boundary( double axis, diff --git a/src/components/ponderable.h b/src/components/ponderable.h index fd7e775..5354f87 100644 --- a/src/components/ponderable.h +++ b/src/components/ponderable.h @@ -20,6 +20,17 @@ public: freefalling }; + /** + * List of different types of collidable surfaces. + */ + enum class Collision { + wall, + platform, + adjacency, + warp, + danger + }; + /** * Constructor for initializing the body type, which is a constant. */ @@ -66,6 +77,11 @@ public: */ bool collidable = true; + /** + * The effect that colliding with this body has. + */ + Collision colliderType = Collision::wall; + /** * If this flag is disabled, the entity will be ignored by the pondering * system. diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index af67aed..d78c8fe 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -175,7 +175,7 @@ void MappingSystem::generateBoundaries(id_type mapEntity) addBoundary( mappable.downBoundaries, - y * TILE_HEIGHT, + y * TILE_HEIGHT + 1, x * TILE_WIDTH, (x+1) * TILE_WIDTH, MappableComponent::Boundary::Type::danger); diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index 4ae6176..04b45a1 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -11,7 +11,6 @@ #include "systems/playing.h" #include "systems/realizing.h" #include "consts.h" -#include "collision.h" void PonderingSystem::tick(double dt) { @@ -56,304 +55,566 @@ void PonderingSystem::tick(double dt) const double oldRight = oldX + transformable.w; const double oldBottom = oldY + transformable.h; - double newX = oldX + ponderable.velX * dt; - double newY = oldY + ponderable.velY * dt; + CollisionResult result; + result.newX = oldX + ponderable.velX * dt; + result.newY = oldY + ponderable.velY * dt; bool oldGrounded = ponderable.grounded; ponderable.grounded = false; - std::priority_queue collisions; - - // Find collisions - if (newX < oldX) + // Find horizontal collisions. + if (result.newX < oldX) { - for (auto it = mappable.leftBoundaries.lower_bound(oldX); - (it != std::end(mappable.leftBoundaries)) && (it->first >= newX); - it++) + bool boundaryCollision = false; + auto it = mappable.leftBoundaries.lower_bound(oldX); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.leftBoundaries)) && + (it->first >= result.newX); + it++) { - if ((oldBottom > it->second.lower) - && (oldY < it->second.upper)) + // Check that the boundary is in range for the other axis. + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) { // We have a collision! - collisions.emplace( - mapEntity, - Direction::left, - it->second.type, - it->first, - it->second.lower, - it->second.upper); + boundaryCollision = true; + + break; } } - } else if (newX > oldX) - { - for (auto it = mappable.rightBoundaries.lower_bound(oldRight); - (it != std::end(mappable.rightBoundaries)) - && (it->first <= (newX + transformable.w)); - it++) + + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; + + for (id_type collider : entities) { - if ((oldBottom > it->second.lower) - && (oldY < it->second.upper)) + // Can't collide with self. + if (collider == entity) { - // We have a collision! - collisions.emplace( - mapEntity, - Direction::right, - it->second.type, - it->first, - it->second.lower, - it->second.upper); + continue; } - } - } - if (newY < oldY) - { - for (auto it = mappable.upBoundaries.lower_bound(oldY); - (it != std::end(mappable.upBoundaries)) && (it->first >= newY); - it++) - { - if ((oldRight > it->second.lower) - && (oldX < it->second.upper)) + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); + + // Only check objects that are active. + if (!colliderPonder.active) { - // We have a collision! - collisions.emplace( - mapEntity, - Direction::up, - it->second.type, - it->first, - it->second.lower, - it->second.upper); + continue; } - } - } else if (newY > oldY) - { - for (auto it = mappable.downBoundaries.lower_bound(oldBottom); - (it != std::end(mappable.downBoundaries)) - && (it->first <= (newY + transformable.h)); - it++) - { - if ((oldRight > it->second.lower) - && (oldX < it->second.upper)) + + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.x + colliderTrans.w > result.newX) && + // that it wasn't already colliding, + (colliderTrans.x + colliderTrans.w <= oldX) && + // that the position on the other axis is in range, + (colliderTrans.y + colliderTrans.h > oldY) && + (colliderTrans.y < oldBottom) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || + (colliderTrans.x + colliderTrans.w >= it->first))) { - // We have a collision! - collisions.emplace( - mapEntity, - Direction::down, - it->second.type, - it->first, - it->second.lower, - it->second.upper); + colliders.push_back(collider); } } - } - // Process collisions in order of priority - bool adjacentlyWarping = false; - Direction adjWarpDir; - size_t adjWarpMapId; + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); - while (!collisions.empty()) - { - Collision collision = collisions.top(); - collisions.pop(); - - // Make sure that they are still colliding - if (!collision.isColliding( - newX, - newY, - transformable.w, - transformable.h)) - { - continue; - } + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - bool touchedWall = false; - bool stopProcessing = false; + return (rightTrans.x < leftTrans.x); + }); - switch (collision.getType()) + for (id_type collider : colliders) { - case Collision::Type::wall: + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would still move into the potential collider. + if (colliderTrans.x + colliderTrans.w <= result.newX) { - touchedWall = true; + break; + } + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); + + processCollision( + entity, + collider, + Direction::left, + colliderPonder.colliderType, + colliderTrans.x + colliderTrans.w, + colliderTrans.y, + colliderTrans.y + colliderTrans.h, + result); + + if (result.stopProcessing) + { break; } + } - case Collision::Type::platform: + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) + { + double boundaryAxis = it->first; + + for (; + (it != std::end(mappable.leftBoundaries)) && + (it->first == boundaryAxis); + it++) { - if (game_.getEntityManager(). - hasComponent(entity)) + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) { - auto& orientable = game_.getEntityManager(). - getComponent(entity); - - if (orientable.getDropState() != - OrientableComponent::DropState::none) + processCollision( + entity, + mapEntity, + Direction::left, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) { - orientable.setDropState(OrientableComponent::DropState::active); - } else { - touchedWall = true; + break; } - } else { - touchedWall = true; } + } + } + } else if (result.newX > oldX) + { + bool boundaryCollision = false; + auto it = mappable.rightBoundaries.lower_bound(oldRight); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.rightBoundaries)) + && (it->first <= (result.newX + transformable.w)); + it++) + { + // Check that the boundary is in range for the other axis. + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) + { + // We have a collision! + boundaryCollision = true; break; } + } + + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; - case Collision::Type::adjacency: + for (id_type collider : entities) + { + // Can't collide with self. + if (collider == entity) { - auto& mappable = game_.getEntityManager(). - getComponent(collision.getCollider()); + continue; + } - auto& adj = [&] () -> const MappableComponent::Adjacent& { - switch (collision.getDirection()) - { - case Direction::left: return mappable.leftAdjacent; - case Direction::right: return mappable.rightAdjacent; - case Direction::up: return mappable.upAdjacent; - case Direction::down: return mappable.downAdjacent; - } - }(); + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - switch (adj.type) - { - case MappableComponent::Adjacent::Type::wall: - { - touchedWall = true; + // Only check objects that are active. + if (!colliderPonder.active) + { + continue; + } - break; - } + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.x < result.newX + transformable.w) && + // that it wasn't already colliding, + (colliderTrans.x >= oldRight) && + // that the position on the other axis is in range, + (colliderTrans.y + colliderTrans.h > oldY) && + (colliderTrans.y < oldBottom) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || (colliderTrans.x <= it->first))) + { + colliders.push_back(collider); + } + } - case MappableComponent::Adjacent::Type::wrap: - { - switch (collision.getDirection()) - { - case Direction::left: - { - newX = GAME_WIDTH + WALL_GAP - transformable.w; + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); - break; - } + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - case Direction::right: - { - newX = -WALL_GAP; + return (leftTrans.x < rightTrans.x); + }); - break; - } + for (id_type collider : colliders) + { + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); - case Direction::up: - { - newY = MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.h; + // Check if the entity would still move into the potential collider. + if (colliderTrans.x >= result.newX + transformable.w) + { + break; + } - break; - } + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - case Direction::down: - { - newY = -WALL_GAP; + processCollision( + entity, + collider, + Direction::right, + colliderPonder.colliderType, + colliderTrans.x, + colliderTrans.y, + colliderTrans.y + colliderTrans.h, + result); + + if (result.stopProcessing) + { + break; + } + } - break; - } - } - } + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) + { + double boundaryAxis = it->first; - case MappableComponent::Adjacent::Type::warp: + for (; + (it != std::end(mappable.rightBoundaries)) && + (it->first == boundaryAxis); + it++) + { + if ((oldBottom > it->second.lower) && (oldY < it->second.upper)) + { + processCollision( + entity, + mapEntity, + Direction::right, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) { - if (game_.getEntityManager(). - hasComponent(entity)) - { - adjacentlyWarping = true; - adjWarpDir = collision.getDirection(); - adjWarpMapId = adj.mapId; - } - break; } + } + } + } + } - case MappableComponent::Adjacent::Type::reverse: - { - // TODO: not yet implemented. + // Find vertical collisions + result.touchedWall = false; - break; - } - } + if ((!result.stopProcessing) && (result.newY < oldY)) + { + bool boundaryCollision = false; + auto it = mappable.upBoundaries.lower_bound(oldY); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.upBoundaries)) && + (it->first >= result.newY); + it++) + { + // Check that the boundary is in range for the other axis. + if ((result.newX + transformable.h > it->second.lower) && + (result.newX < it->second.upper)) + { + // We have a collision! + boundaryCollision = true; break; } + } - case Collision::Type::danger: + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; + + for (id_type collider : entities) + { + // Can't collide with self. + if (collider == entity) { - if (game_.getEntityManager(). - hasComponent(entity)) - { - game_.getSystemManager().getSystem().die(entity); + continue; + } - adjacentlyWarping = false; - } + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - stopProcessing = true; + // Only check objects that are active. + if (!colliderPonder.active) + { + continue; + } - break; + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.y + colliderTrans.h > result.newY) && + // that it wasn't already colliding, + (colliderTrans.y + colliderTrans.h <= oldY) && + // that the position on the other axis is in range, + (colliderTrans.x + colliderTrans.w > result.newX) && + (colliderTrans.x < result.newX + transformable.w) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || + (colliderTrans.y + colliderTrans.h >= it->first))) + { + colliders.push_back(collider); } + } + + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); + + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - default: + return (rightTrans.y < leftTrans.y); + }); + + for (id_type collider : colliders) + { + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would still move into the potential collider. + if (colliderTrans.y + colliderTrans.h <= result.newY) { - // Not yet implemented. + break; + } + + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); + processCollision( + entity, + collider, + Direction::up, + colliderPonder.colliderType, + colliderTrans.y + colliderTrans.h, + colliderTrans.x, + colliderTrans.x + colliderTrans.w, + result); + + if (result.stopProcessing) + { break; } } - if (stopProcessing) + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) { - break; + double boundaryAxis = it->first; + + for (; + (it != std::end(mappable.upBoundaries)) && + (it->first == boundaryAxis); + it++) + { + if ((result.newX + transformable.w > it->second.lower) && + (result.newX < it->second.upper)) + { + processCollision( + entity, + mapEntity, + Direction::up, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) + { + break; + } + } + } } + } else if ((!result.stopProcessing) && (result.newY > oldY)) + { + bool boundaryCollision = false; + auto it = mappable.downBoundaries.lower_bound(oldBottom); + + // Find the axis distance of the closest environmental boundary. + for (; + (it != std::end(mappable.downBoundaries)) + && (it->first <= (result.newY + transformable.h)); + it++) + { + // Check that the boundary is in range for the other axis. + if ((result.newX + transformable.w > it->second.lower) && + (result.newX < it->second.upper)) + { + // We have a collision! + boundaryCollision = true; - if (touchedWall) + break; + } + } + + // Find a list of potential colliders, sorted so that the closest is + // first. + std::vector colliders; + + for (id_type collider : entities) { - switch (collision.getDirection()) + // Can't collide with self. + if (collider == entity) { - case Direction::left: - { - newX = collision.getAxis(); - ponderable.velX = 0.0; + continue; + } - break; - } + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); - case Direction::right: - { - newX = collision.getAxis() - transformable.w; - ponderable.velX = 0.0; + // Only check objects that are active. + if (!colliderPonder.active) + { + continue; + } - break; - } + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would move into the potential collider, + if ((colliderTrans.y < result.newY + transformable.h) && + // that it wasn't already colliding, + (colliderTrans.y >= oldBottom) && + // that the position on the other axis is in range, + (colliderTrans.x + colliderTrans.w > result.newX) && + (colliderTrans.x < result.newX + transformable.w) && + // and that the collider is not farther away than the environmental + // boundary. + (!boundaryCollision || (colliderTrans.y <= it->first))) + { + colliders.push_back(collider); + } + } - case Direction::up: - { - newY = collision.getAxis(); - ponderable.velY = 0.0; + std::sort( + std::begin(colliders), + std::end(colliders), + [&] (id_type left, id_type right) { + auto& leftTrans = game_.getEntityManager(). + getComponent(left); - break; - } + auto& rightTrans = game_.getEntityManager(). + getComponent(right); - case Direction::down: - { - newY = collision.getAxis() - transformable.h; - ponderable.velY = 0.0; - ponderable.grounded = true; + return (leftTrans.y < rightTrans.y); + }); + + for (id_type collider : colliders) + { + auto& colliderTrans = game_.getEntityManager(). + getComponent(collider); + + // Check if the entity would still move into the potential collider. + if (colliderTrans.y >= result.newY + transformable.h) + { + break; + } + + auto& colliderPonder = game_.getEntityManager(). + getComponent(collider); + + processCollision( + entity, + collider, + Direction::down, + colliderPonder.colliderType, + colliderTrans.y, + colliderTrans.x, + colliderTrans.x + colliderTrans.w, + result); + + if (result.stopProcessing) + { + break; + } + } + + // If movement hasn't been stopped by an intermediary object, and + // collision checking hasn't been stopped, process the environmental + // boundaries closest to the entity. + if (!result.stopProcessing && !result.touchedWall && boundaryCollision) + { + double boundaryAxis = it->first; - break; + for (; + (it != std::end(mappable.downBoundaries)) && + (it->first == boundaryAxis); + it++) + { + if ((result.newX + transformable.w > it->second.lower) && + (result.newX < it->second.upper)) + { + processCollision( + entity, + mapEntity, + Direction::down, + it->second.type, + it->first, + it->second.lower, + it->second.upper, + result); + + if (result.stopProcessing) + { + break; + } } } } } // Move - transformable.x = newX; - transformable.y = newY; + transformable.x = result.newX; + transformable.y = result.newY; // Perform cleanup for orientable entites if (game_.getEntityManager().hasComponent(entity)) @@ -381,12 +642,12 @@ void PonderingSystem::tick(double dt) } // Move to an adjacent map, if necessary - if (adjacentlyWarping) + if (result.adjacentlyWarping) { - double warpX = newX; - double warpY = newY; + double warpX = result.newX; + double warpY = result.newY; - switch (adjWarpDir) + switch (result.adjWarpDir) { case Direction::left: { @@ -420,7 +681,7 @@ void PonderingSystem::tick(double dt) game_.getSystemManager().getSystem(). changeMap( entity, - adjWarpMapId, + result.adjWarpMapId, warpX, warpY); } @@ -453,3 +714,196 @@ void PonderingSystem::initPrototype(id_type prototype) ponderable.frozen = false; ponderable.collidable = true; } + +void PonderingSystem::processCollision( + id_type entity, + id_type collider, + Direction dir, + PonderableComponent::Collision type, + double axis, + double lower, + double upper, + CollisionResult& result) +{ + auto& ponderable = game_.getEntityManager(). + getComponent(entity); + + auto& transformable = game_.getEntityManager(). + getComponent(entity); + + switch (type) + { + case PonderableComponent::Collision::wall: + { + result.touchedWall = true; + + break; + } + + case PonderableComponent::Collision::platform: + { + if (game_.getEntityManager(). + hasComponent(entity)) + { + auto& orientable = game_.getEntityManager(). + getComponent(entity); + + if (orientable.getDropState() != + OrientableComponent::DropState::none) + { + orientable.setDropState(OrientableComponent::DropState::active); + } else { + result.touchedWall = true; + } + } else { + result.touchedWall = true; + } + + break; + } + + case PonderableComponent::Collision::adjacency: + { + auto& mappable = game_.getEntityManager(). + getComponent(collider); + + auto& adj = [&] () -> const MappableComponent::Adjacent& { + switch (dir) + { + case Direction::left: return mappable.leftAdjacent; + case Direction::right: return mappable.rightAdjacent; + case Direction::up: return mappable.upAdjacent; + case Direction::down: return mappable.downAdjacent; + } + }(); + + switch (adj.type) + { + case MappableComponent::Adjacent::Type::wall: + { + result.touchedWall = true; + + break; + } + + case MappableComponent::Adjacent::Type::wrap: + { + switch (dir) + { + case Direction::left: + { + result.newX = GAME_WIDTH + WALL_GAP - transformable.w; + + break; + } + + case Direction::right: + { + result.newX = -WALL_GAP; + + break; + } + + case Direction::up: + { + result.newY = + MAP_HEIGHT * TILE_HEIGHT + WALL_GAP - transformable.h; + + break; + } + + case Direction::down: + { + result.newY = -WALL_GAP; + + break; + } + } + } + + case MappableComponent::Adjacent::Type::warp: + { + if (game_.getEntityManager(). + hasComponent(entity)) + { + result.adjacentlyWarping = true; + result.adjWarpDir = dir; + result.adjWarpMapId = adj.mapId; + } + + break; + } + + case MappableComponent::Adjacent::Type::reverse: + { + // TODO: not yet implemented. + + break; + } + } + + break; + } + + case PonderableComponent::Collision::danger: + { + if (game_.getEntityManager(). + hasComponent(entity)) + { + game_.getSystemManager().getSystem().die(entity); + + result.adjacentlyWarping = false; + } + + result.stopProcessing = true; + + break; + } + + default: + { + // Not yet implemented. + + break; + } + } + + if (!result.stopProcessing && result.touchedWall) + { + switch (dir) + { + case Direction::left: + { + result.newX = axis; + ponderable.velX = 0.0; + + break; + } + + case Direction::right: + { + result.newX = axis - transformable.w; + ponderable.velX = 0.0; + + break; + } + + case Direction::up: + { + result.newY = axis; + ponderable.velY = 0.0; + + break; + } + + case Direction::down: + { + result.newY = axis - transformable.h; + ponderable.velY = 0.0; + ponderable.grounded = true; + + break; + } + } + } +} diff --git a/src/systems/pondering.h b/src/systems/pondering.h index 58e6496..aa430db 100644 --- a/src/systems/pondering.h +++ b/src/systems/pondering.h @@ -18,6 +18,29 @@ public: void initPrototype(id_type prototype); +private: + + struct CollisionResult + { + double newX; + double newY; + bool stopProcessing = false; + bool touchedWall = false; + bool adjacentlyWarping = false; + Direction adjWarpDir; + size_t adjWarpMapId; + }; + + void processCollision( + id_type entity, + id_type collider, + Direction dir, + PonderableComponent::Collision type, + double axis, + double lower, + double upper, + CollisionResult& result); + }; #endif /* end of include guard: PONDERING_H_F2530E0E */ diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index c86dd5e..3656acb 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp @@ -207,6 +207,10 @@ EntityManager::id_type RealizingSystem::initSingleton( animatable.origAnimation = "static"; + // Create a physics body. + game_.getSystemManager().getSystem(). + initializeBody(mapObject, PonderableComponent::Type::vacuumed); + mappable.objects.push_back(mapObject); } else if (!xmlStrcmp( mapNode->name, -- cgit 1.4.1 From 046ee24a341468e9b3ea2983a731dbce18b52ac6 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sun, 13 May 2018 11:00:02 -0400 Subject: Integrated RealizableComponent into RealizingSystem --- scripts/checkpoint.lua | 2 +- src/components/realizable.h | 74 ------------------------------- src/game.cpp | 3 +- src/systems/mapping.cpp | 8 +--- src/systems/playing.cpp | 26 ++++------- src/systems/pondering.cpp | 8 +--- src/systems/realizing.cpp | 105 ++++++++++++++++++-------------------------- src/systems/realizing.h | 66 ++++++++++++++++++++++------ src/systems/scripting.cpp | 45 +++++++------------ src/systems/scripting.h | 2 +- 10 files changed, 127 insertions(+), 212 deletions(-) delete mode 100644 src/components/realizable.h (limited to 'src/systems/mapping.cpp') diff --git a/scripts/checkpoint.lua b/scripts/checkpoint.lua index 452f81d..a5c8c54 100644 --- a/scripts/checkpoint.lua +++ b/scripts/checkpoint.lua @@ -1,7 +1,7 @@ checkpoint = {} function checkpoint.OnTouch(id, player) - curMap = entity.new(realizing():singleton():realizable().activeMap) + curMap = entity.new(realizing().activeMap) if not player:playable().checkpointMapObject or not curMap:mappable().mapId == player:playable().checkpointMapId or diff --git a/src/components/realizable.h b/src/components/realizable.h deleted file mode 100644 index b749aeb..0000000 --- a/src/components/realizable.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef REALIZABLE_H_36D8D71E -#define REALIZABLE_H_36D8D71E - -#include "component.h" -#include -#include -#include "entity_manager.h" -#include "vector.h" - -class RealizableComponent : public Component { -public: - - using id_type = EntityManager::id_type; - - /** - * Path to the XML file containing the world definition. - * - * @managed_by RealizingSystem - */ - std::string worldFile; - - /** - * Path to the XML file containing the map object prototype definitions. - * - * @managed_by RealizingSystem - */ - std::string prototypeFile; - - /** - * Starting map and player location for a new game. - * - * @managed_by RealizingSystem - */ - int startingMapId; - vec2i startingPos; - - /** - * The set of map entities loaded by this entity. It is only intended for - * there to be one realizable entity, so this should contain all loaded maps. - * The realizable entity has ownership of the loaded maps. - * - * @managed_by RealizingSystem - */ - std::set maps; - - /** - * A lookup table that translates a map ID to the entity representing that - * loaded map. - * - * @managed_by RealizingSystem - */ - std::map entityByMapId; - - /** - * The entity ID of the currently active map. - * - * @managed_by RealizingSystem - */ - id_type activeMap; - - /** - * Whether or not a map has been activated yet. - * - * @managed_by RealizingSystem - */ - bool hasActiveMap = false; - - /** - * The entity ID of the currently active player. - */ - id_type activePlayer; -}; - -#endif /* end of include guard: REALIZABLE_H_36D8D71E */ diff --git a/src/game.cpp b/src/game.cpp index f2992e1..c76349f 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -32,7 +32,6 @@ void key_callback(GLFWwindow* window, int key, int, int action, int) Game::Game(std::mt19937& rng) : rng_(rng) { - systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); @@ -42,7 +41,7 @@ Game::Game(std::mt19937& rng) : rng_(rng) systemManager_.emplaceSystem(*this); systemManager_.emplaceSystem(*this); - systemManager_.getSystem().initSingleton( + systemManager_.emplaceSystem(*this, "res/maps.xml", "res/entities.xml"); diff --git a/src/systems/mapping.cpp b/src/systems/mapping.cpp index d78c8fe..1275e11 100644 --- a/src/systems/mapping.cpp +++ b/src/systems/mapping.cpp @@ -1,6 +1,5 @@ #include "mapping.h" #include "components/mappable.h" -#include "components/realizable.h" #include "systems/realizing.h" #include "game.h" #include "consts.h" @@ -20,11 +19,8 @@ inline void addBoundary( void MappingSystem::render(Texture& texture) { - auto& realizable = game_.getEntityManager(). - getComponent( - game_.getSystemManager().getSystem().getSingleton()); - - id_type map = realizable.activeMap; + id_type map = + game_.getSystemManager().getSystem().getActiveMap(); auto& mappable = game_.getEntityManager(). getComponent(map); diff --git a/src/systems/playing.cpp b/src/systems/playing.cpp index dabc9a5..6652099 100644 --- a/src/systems/playing.cpp +++ b/src/systems/playing.cpp @@ -5,7 +5,6 @@ #include "components/playable.h" #include "components/controllable.h" #include "components/orientable.h" -#include "components/realizable.h" #include "systems/mapping.h" #include "systems/pondering.h" #include "systems/orienting.h" @@ -36,13 +35,10 @@ void PlayingSystem::initPlayer() auto& realizing = game_.getSystemManager().getSystem(); - auto& realizable = game_.getEntityManager(). - getComponent(realizing.getSingleton()); - auto& transformable = game_.getEntityManager(). emplaceComponent(player); - transformable.pos = realizable.startingPos; + transformable.pos = realizing.getStartingPos(); transformable.size.w() = 10; transformable.size.h() = 12; @@ -56,13 +52,13 @@ void PlayingSystem::initPlayer() auto& playable = game_.getEntityManager(). emplaceComponent(player); - playable.mapId = realizable.activeMap; - playable.checkpointMapId = realizable.startingMapId; - playable.checkpointPos = realizable.startingPos; + playable.mapId = realizing.getActiveMap(); + playable.checkpointMapId = realizing.getStartingMapId(); + playable.checkpointPos = realizing.getStartingPos(); realizing.enterActiveMap(player); - realizable.activePlayer = player; + realizing.setActivePlayer(player); } void PlayingSystem::changeMap( @@ -77,20 +73,16 @@ void PlayingSystem::changeMap( getComponent(player); auto& pondering = game_.getSystemManager().getSystem(); - auto& realizing = game_.getSystemManager().getSystem(); - auto& realizable = game_.getEntityManager(). - getComponent(realizing.getSingleton()); - - id_type newMapEntity = realizable.entityByMapId[mapId]; + id_type newMapEntity = realizing.getEntityByMapId(mapId); if (playable.mapId != newMapEntity) { - if (playable.mapId == realizable.activeMap) + if (playable.mapId == realizing.getActiveMap()) { realizing.leaveActiveMap(player); - } else if (newMapEntity == realizable.activeMap) + } else if (newMapEntity == realizing.getActiveMap()) { realizing.enterActiveMap(player); } @@ -102,7 +94,7 @@ void PlayingSystem::changeMap( transformable.pos = warpPos; - if (realizable.activePlayer == player) + if (realizing.getActivePlayer() == player) { realizing.loadMap(newMapEntity); } diff --git a/src/systems/pondering.cpp b/src/systems/pondering.cpp index a3eb36d..d841679 100644 --- a/src/systems/pondering.cpp +++ b/src/systems/pondering.cpp @@ -6,7 +6,6 @@ #include "components/transformable.h" #include "components/orientable.h" #include "components/mappable.h" -#include "components/realizable.h" #include "components/playable.h" #include "systems/orienting.h" #include "systems/playing.h" @@ -485,11 +484,8 @@ void PonderingSystem::detectCollisionsInDirection( CollisionResult& result) { // Get map data. - auto& realizable = game_.getEntityManager(). - getComponent( - game_.getSystemManager().getSystem().getSingleton()); - - id_type mapEntity = realizable.activeMap; + id_type mapEntity = + game_.getSystemManager().getSystem().getActiveMap(); auto& mappable = game_.getEntityManager(). getComponent(mapEntity); diff --git a/src/systems/realizing.cpp b/src/systems/realizing.cpp index 2ee5897..7f5aefb 100644 --- a/src/systems/realizing.cpp +++ b/src/systems/realizing.cpp @@ -6,7 +6,6 @@ #include "game.h" #include "consts.h" #include "animation.h" -#include "components/realizable.h" #include "components/mappable.h" #include "components/animatable.h" #include "components/playable.h" @@ -30,25 +29,21 @@ inline xmlChar* getProp(xmlNodePtr node, const char* attr) } // TODO: neither the XML doc nor any of the emplaced entities are properly -// destroyed if this method throws an exception. -EntityManager::id_type RealizingSystem::initSingleton( +// destroyed if this constructor throws an exception. +RealizingSystem::RealizingSystem( + Game& game, std::string worldFile, - std::string prototypeFile) + std::string prototypeFile) : + System(game), + worldFile_(std::move(worldFile)), + prototypeFile_(std::move(prototypeFile)) { - id_type world = game_.getEntityManager().emplaceEntity(); - - auto& realizable = game_.getEntityManager(). - emplaceComponent(world); - - realizable.worldFile = worldFile; - realizable.prototypeFile = prototypeFile; - auto& mapping = game_.getSystemManager().getSystem(); xmlChar* key = nullptr; // Create a mapping between prototype names and the XML trees defining them. - xmlDocPtr protoXml = xmlParseFile(prototypeFile.c_str()); + xmlDocPtr protoXml = xmlParseFile(prototypeFile_.c_str()); if (protoXml == nullptr) { throw std::invalid_argument("Cannot find prototypes file"); @@ -82,7 +77,7 @@ EntityManager::id_type RealizingSystem::initSingleton( } // Create entities from the world definition. - xmlDocPtr doc = xmlParseFile(worldFile.c_str()); + xmlDocPtr doc = xmlParseFile(worldFile_.c_str()); if (doc == nullptr) { throw std::invalid_argument("Cannot find world file"); @@ -100,15 +95,15 @@ EntityManager::id_type RealizingSystem::initSingleton( } key = getProp(top, "startx"); - realizable.startingPos.x() = atoi(reinterpret_cast(key)); + startingPos_.x() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(top, "starty"); - realizable.startingPos.y() = atoi(reinterpret_cast(key)); + startingPos_.y() = atoi(reinterpret_cast(key)); xmlFree(key); key = getProp(top, "startmap"); - realizable.startingMapId = atoi(reinterpret_cast(key)); + startingMapId_ = atoi(reinterpret_cast(key)); xmlFree(key); for (xmlNodePtr node = top->xmlChildrenNode; @@ -291,78 +286,62 @@ EntityManager::id_type RealizingSystem::initSingleton( mapping.generateBoundaries(map); - realizable.maps.insert(map); - realizable.entityByMapId[mappable.mapId] = map; + entityByMapId_[mappable.mapId] = map; } } xmlFreeDoc(doc); xmlFreeDoc(protoXml); - loadMap(realizable.entityByMapId[realizable.startingMapId]); - - return world; + activateMap(entityByMapId_[startingMapId_]); } -EntityManager::id_type RealizingSystem::getSingleton() const +void RealizingSystem::loadMap(id_type mapEntity) { - std::set result = - game_.getEntityManager().getEntitiesWithComponents< - RealizableComponent>(); - - if (result.empty()) - { - throw std::logic_error("No realizable entity found"); - } else if (result.size() > 1) - { - throw std::logic_error("Multiple realizable entities found"); - } - - return *std::begin(result); + deactivateMap(); + activateMap(mapEntity); } -void RealizingSystem::loadMap(id_type mapEntity) +void RealizingSystem::deactivateMap() { - id_type world = getSingleton(); + id_type oldMap = activeMap_; - auto& realizable = game_.getEntityManager(). - getComponent(world); + auto& oldMappable = game_.getEntityManager(). + getComponent(oldMap); - auto& animating = game_.getSystemManager().getSystem(); - auto& pondering = game_.getSystemManager().getSystem(); + // Deactivate any map objects from the old map. + for (id_type prototype : oldMappable.objects) + { + leaveActiveMap(prototype); + } + // Deactivate players that were on the old map. std::set players = game_.getEntityManager().getEntitiesWithComponents< PlayableComponent>(); - if (realizable.hasActiveMap) + for (id_type player : players) { - id_type oldMap = realizable.activeMap; - - auto& oldMappable = game_.getEntityManager(). - getComponent(oldMap); + auto& playable = game_.getEntityManager(). + getComponent(player); - // Deactivate any map objects from the old map. - for (id_type prototype : oldMappable.objects) + if (playable.mapId == oldMap) { - leaveActiveMap(prototype); + leaveActiveMap(player); } + } +} - // Deactivate players that were on the old map. - for (id_type player : players) - { - auto& playable = game_.getEntityManager(). - getComponent(player); +void RealizingSystem::activateMap(id_type mapEntity) +{ + auto& animating = game_.getSystemManager().getSystem(); + auto& pondering = game_.getSystemManager().getSystem(); - if (playable.mapId == oldMap) - { - leaveActiveMap(player); - } - } - } + std::set players = + game_.getEntityManager().getEntitiesWithComponents< + PlayableComponent>(); - realizable.hasActiveMap = true; - realizable.activeMap = mapEntity; + activeMap_ = mapEntity; auto& mappable = game_.getEntityManager(). getComponent(mapEntity); diff --git a/src/systems/realizing.h b/src/systems/realizing.h index 595c58f..ab5a150 100644 --- a/src/systems/realizing.h +++ b/src/systems/realizing.h @@ -2,29 +2,56 @@ #define REALIZING_H_6853748C #include +#include #include "system.h" +#include "vector.h" class RealizingSystem : public System { public: - RealizingSystem(Game& game) : System(game) - { - } - /** - * Creates the singleton realizable entity and initializes it with the - * provided world definition and map object prototype definition. + * Constructs the realizing system. + * + * Note that this must be constructed after the following system: + * - Mapping + * - Animating + * - Pondering + * - Scripting */ - id_type initSingleton( + RealizingSystem( + Game& game, std::string worldFile, std::string prototypeFile); - /** - * Helper method that returns the entity ID of the (assumed) singleton entity - * with a RealizableComponent. Throws an exception if the number of realizable - * entities is not exactly one. - */ - id_type getSingleton() const; + id_type getActiveMap() const + { + return activeMap_; + } + + int getStartingMapId() const + { + return startingMapId_; + } + + vec2i getStartingPos() const + { + return startingPos_; + } + + id_type getEntityByMapId(size_t mapId) const + { + return entityByMapId_.at(mapId); + } + + id_type getActivePlayer() const + { + return activePlayer_; + } + + void setActivePlayer(id_type entity) + { + activePlayer_ = entity; + } /** * Loads the given map. @@ -41,6 +68,19 @@ public: */ void leaveActiveMap(id_type entity); +private: + + void deactivateMap(); + + void activateMap(id_type mapEntity); + + std::string worldFile_; + std::string prototypeFile_; + int startingMapId_; + vec2i startingPos_; + std::map entityByMapId_; + id_type activeMap_; + id_type activePlayer_; }; #endif /* end of include guard: REALIZING_H_6853748C */ diff --git a/src/systems/scripting.cpp b/src/systems/scripting.cpp index dc1fff5..57c3fd5 100644 --- a/src/systems/scripting.cpp +++ b/src/systems/scripting.cpp @@ -2,7 +2,6 @@ #include "game.h" #include "components/runnable.h" #include "components/ponderable.h" -#include "components/realizable.h" #include "components/transformable.h" #include "components/playable.h" #include "components/mappable.h" @@ -24,9 +23,9 @@ ScriptingSystem::ScriptingSystem(Game& game) : System(game) { id_type entity = game_.getEntityManager().emplaceEntity(); - engine.open_libraries(sol::lib::base, sol::lib::coroutine); + engine_.open_libraries(sol::lib::base, sol::lib::coroutine); - engine.new_usertype( + engine_.new_usertype( "vec2d", sol::constructors(), "x", sol::property( @@ -36,13 +35,13 @@ ScriptingSystem::ScriptingSystem(Game& game) : System(game) [] (vec2d& v) -> double { return v.y(); }, [] (vec2d& v, double y) { v.y() = y; })); - engine.new_usertype( + engine_.new_usertype( "vec2i", sol::constructors(), "x", [] (vec2i& v) -> int& { return v.x(); }, "y", [] (vec2i& v) -> int& { return v.y(); }); - engine.new_usertype( + engine_.new_usertype( "entity", sol::constructors(), "id", &script_entity::id, @@ -66,62 +65,50 @@ ScriptingSystem::ScriptingSystem(Game& game) : System(game) return game_.getEntityManager(). getComponent(entity.id); }, - "realizable", - [&] (script_entity& entity) -> RealizableComponent& { - return game_.getEntityManager(). - getComponent(entity.id); - }, "prototypable", [&] (script_entity& entity) -> PrototypableComponent& { return game_.getEntityManager(). getComponent(entity.id); }); - engine.new_usertype( + engine_.new_usertype( "transformable", "pos", &TransformableComponent::pos); - engine.new_usertype( + engine_.new_usertype( "ponderable", "vel", &PonderableComponent::vel, "accel", &PonderableComponent::accel); - engine.new_usertype( + engine_.new_usertype( "mappable", "mapId", &MappableComponent::mapId); - engine.new_usertype( + engine_.new_usertype( "playable", "checkpointPos", &PlayableComponent::checkpointPos, "checkpointMapId", &PlayableComponent::checkpointMapId, "checkpointMapObject", &PlayableComponent::checkpointMapObject, "checkpointMapObjectIndex", &PlayableComponent::checkpointMapObjectIndex); - engine.new_usertype( - "realizable", - "activeMap", &RealizableComponent::activeMap); - - engine.new_usertype( + engine_.new_usertype( "prototypable", "mapObjectIndex", &PrototypableComponent::mapObjectIndex, "prototypeId", &PrototypableComponent::prototypeId); - engine.new_usertype( + engine_.new_usertype( "realizing", - "singleton", - [&] (RealizingSystem& realizing) -> script_entity { - return realizing.getSingleton(); - }); + "activeMap", sol::property(&RealizingSystem::getActiveMap)); - engine.set_function( + engine_.set_function( "realizing", [&] () { return game_.getSystemManager().getSystem(); }); - engine.script_file("scripts/common.lua"); - engine.script_file("scripts/movplat.lua"); - engine.script_file("scripts/checkpoint.lua"); + engine_.script_file("scripts/common.lua"); + engine_.script_file("scripts/movplat.lua"); + engine_.script_file("scripts/checkpoint.lua"); } void ScriptingSystem::tick(double dt) @@ -177,7 +164,7 @@ EntityManager::id_type ScriptingSystem::runScript( std::unique_ptr( new sol::thread( sol::thread::create( - engine.lua_state()))); + engine_.lua_state()))); runnable.callable = std::unique_ptr( diff --git a/src/systems/scripting.h b/src/systems/scripting.h index d5380f1..e330316 100644 --- a/src/systems/scripting.h +++ b/src/systems/scripting.h @@ -22,7 +22,7 @@ private: template id_type runScript(std::string event, id_type entity, Args&&... args); - sol::state engine; + sol::state engine_; }; #endif /* end of include guard: AUTOMATING_H_E6E5D76E */ -- cgit 1.4.1