improved support for different resolutions + general documentation and cleanup

This commit is contained in:
ethan 2025-04-14 16:04:43 -04:00
parent 6ad1272d7d
commit 701ac3e151
14 changed files with 614 additions and 653 deletions

View file

@ -1,42 +1,30 @@
-- global vars -- global vars
patrolDestination = { x=500.0, y=500.0, z=0.0 } MoveLeft = true
moveLeft = true
-- helper functions -- helper functions
function watchPosition(actor, pos) local function watchPosition(actor, pos)
local y = pos.y - actor.position.y local y = pos.y - actor.position.y
local x = pos.x - actor.position.x local x = pos.x - actor.position.x
local rotation = math.atan2(y, x) local rotation = math.atan2(y, x)
actor.rotation = math.deg(rotation) actor.rotation = math.deg(rotation)
end end
function distance(a, b) local function distance(a, b)
local dx = a.x - b.x local dx = a.x - b.x
local dy = a.y - b.y local dy = a.y - b.y
return math.sqrt(dx * dx + dy * dy) return math.sqrt(dx * dx + dy * dy)
end end
function moveTo(actor, pos)
local a = actor
local p = pos
watchPosition(a, p)
if distance(a.position, p) < 50 then
return true
end
a:moveForward()
return false
end
-- Behaviour Functions called on AI -- Behaviour Functions called on AI
-- These functions are ai behaviour functions called in the game -- These functions are ai behaviour functions called in the game
-- The AI will be spawned in idle mode, so if you want to put the bot into patrol mode -- The AI will be spawned in idle mode, so if you want to put the bot into patrol mode
-- It's on you to do that in this function. -- It's on you to do that in this function.
function idle(actor, target) function idle(actor, target)
local a = actor local a = actor
local t = target local t = target
if t ~= nil then if t ~= nil then
-- print("target is at " .. target.position.x) -- print("target is at " .. target.position.x)
-- watchPosition(actor, target.position) -- watchPosition(actor, target.position)
ai.state = AIState.Patrol ai.state = AIState.Patrol
a.rotation = 180 a.rotation = 180
end end
@ -48,24 +36,12 @@ end
function patrol(actor, target) function patrol(actor, target)
local a = actor local a = actor
local t = target local t = target
if t ~= nil then
-- print("target is at " .. target.position.x)
end
--if moveTo(actor, patrolDestination) == true then
-- patrolDestination = { x=math.random(400.0, 750.0), y=math.random(400.0, 750.0), z=0.0 }
--end
-- performRaycast returns if true if the raycast hits the target position it also sets the getter function
-- distFromWall, at bot creation distFromWall is and infinite float value.
-- This performRaycast function is highly discourage due to slow down
--if raycaster:performRaycast(actor.position, actor.rotation, target.position) == true then
--ai.state = AIState.Alert
--end
if raycaster:bresenhamRaycast(a.position, a.rotation, t.position) == true then if raycaster:bresenhamRaycast(a.position, a.rotation, t.position) == true then
--target hit! --target hit!
ai.state = AIState.Alert ai.state = AIState.Alert
end end
if raycaster:distFromWall() < 3 then if raycaster:distFromWall() < 3 then
upOrDown = math.random(2) local upOrDown = math.random(2)
if moveLeft == true then if moveLeft == true then
if upOrDown == 1 then if upOrDown == 1 then
a.rotation = 180 a.rotation = 180
@ -73,7 +49,7 @@ function patrol(actor, target)
a.rotation = 270 a.rotation = 270
end end
moveLeft = false moveLeft = false
else else
if upOrDown == 1 then if upOrDown == 1 then
a.rotation = 0 a.rotation = 0
else else
@ -90,10 +66,8 @@ function alert(actor, target)
local a = actor local a = actor
local t = target local t = target
if target ~= nil then if target ~= nil then
-- print("target is at " .. target.position.x)
watchPosition(a, t.position) watchPosition(a, t.position)
end end
--print("actor is alert at " .. actor.position.x)
if distance(a.position, t.position) > 300 then if distance(a.position, t.position) > 300 then
a:moveForward() a:moveForward()
end end

View file

@ -1,18 +1,18 @@
-- helper functions -- helper functions
function lookAway(actor, pos) local function lookAway(actor, pos)
y = actor.position.y - pos.y local y = actor.position.y - pos.y
x = actor.position.x - pos.x local x = actor.position.x - pos.x
rotation = math.atan(y, x) local rotation = math.atan(y, x)
actor.rotation = math.deg(rotation) actor.rotation = math.deg(rotation)
end end
function distance(a, b) local function distance(a, b)
return math.sqrt((math.abs(a.x - b.x)^2) + (math.abs(a.y - b.y)^2)) return math.sqrt((math.abs(a.x - b.x) ^ 2) + (math.abs(a.y - b.y) ^ 2))
end end
function idle(actor, target) function idle(actor, target)
-- do nothing here for now -- do nothing here for now
end end
function patrol(actor, target) function patrol(actor, target)

View file

@ -3,7 +3,7 @@
# #
find_package(SDL2 2.30.2 REQUIRED) find_package(SDL2 2.30.2 REQUIRED)
find_package(SDL2_IMAGE 2.8.2 REQUIRED) find_package(SDL2_image 2.8.2 REQUIRED)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET GLOBAL luajit) pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET GLOBAL luajit)
find_package(sol2 REQUIRED) find_package(sol2 REQUIRED)
@ -42,6 +42,7 @@ add_executable (YuppleMayham
"src/utility/ftfont.cpp" "src/utility/ftfont.cpp"
"src/graphics/sprite.cpp" "src/graphics/sprite.cpp"
"src/graphics/mesh.cpp" "src/graphics/mesh.cpp"
"src/graphics/glwindow.cpp"
"src/gameplay/entity.cpp" "src/gameplay/entity.cpp"
"src/gameplay/gameactor.cpp" "src/gameplay/gameactor.cpp"
"src/graphics/shader.cpp" "src/graphics/shader.cpp"
@ -104,6 +105,6 @@ endif()
target_include_directories(YuppleMayham PRIVATE "${PROJECT_SOURCE_DIR}/YuppleMayham/include" ${LuaJIT_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build}) target_include_directories(YuppleMayham PRIVATE "${PROJECT_SOURCE_DIR}/YuppleMayham/include" ${LuaJIT_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIR_ft2build})
target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image openal glm::glm-header-only tinyxml2 freetype ${LuaJIT_LINK_LIBRARIES}) target_link_libraries(YuppleMayham SDL2::SDL2main SDL2::SDL2 SDL2_image::SDL2_image openal glm::glm tinyxml2 freetype ${LuaJIT_LINK_LIBRARIES})
# TODO: Add tests and install targets if needed. # TODO: Add tests and install targets if needed.

View file

@ -1,10 +1,12 @@
#ifndef _H_GAME_H #ifndef _H_GAME_H
#define _H_GAME_H #define _H_GAME_H
#include <memory>
#include <SDL_events.h> #include <SDL_events.h>
#include <memory>
#include <utility/events.h> #include <utility/events.h>
#include "gameplay/camera.h"
class InputHandler; class InputHandler;
class Scene; class Scene;
class Text; class Text;
@ -13,47 +15,42 @@ class Renderer;
class AudioEngine; class AudioEngine;
class GLWindow; class GLWindow;
enum { enum { GAME_QUITTING = 0, GAME_RUNNING = 1, GAME_MENU = 2, GAME_PLAYING = 4 };
GAME_QUITTING = 0,
GAME_RUNNING = 1,
GAME_MENU = 2,
GAME_PLAYING = 4
};
class Game class Game {
{
public: public:
Game() {} Game() {}
bool init(); bool init();
bool loadDebugScene(); bool loadDebugScene();
void captureInput(SDL_Event& e);
void executeInputs();
void update(double deltaTime); void captureInput(SDL_Event &e);
void render(); void executeInputs();
const unsigned getGameState() const { return game_state; } void update(double deltaTime);
void render();
const unsigned getWindowWidth() const; const unsigned getGameState() const { return game_state; }
const unsigned getWindowHeight() const;
void quit(); const unsigned getWindowWidth() const;
~Game(); const unsigned getWindowHeight() const;
void quit();
~Game();
private: private:
unsigned game_state = GAME_QUITTING; unsigned game_state = GAME_QUITTING;
std::shared_ptr<GLWindow> window; std::shared_ptr<GLWindow> window;
std::shared_ptr<Scene> currentScene; std::unique_ptr<Camera> camera;
std::shared_ptr<InputHandler> inputHandler; std::shared_ptr<Scene> currentScene;
std::shared_ptr<ResourceManager> resourceManager; std::shared_ptr<InputHandler> inputHandler;
std::shared_ptr<Renderer> renderer; std::shared_ptr<ResourceManager> resourceManager;
std::shared_ptr<Text> textHandler; std::shared_ptr<Renderer> renderer;
std::shared_ptr<AudioEngine> audioEngine; std::shared_ptr<Text> textHandler;
std::shared_ptr<EventManager> globalEventManager; std::shared_ptr<AudioEngine> audioEngine;
std::shared_ptr<EventManager> globalEventManager;
}; };
#endif #endif

View file

@ -2,52 +2,33 @@
#define _H_GLWINDOW_H #define _H_GLWINDOW_H
#include <SDL_video.h> #include <SDL_video.h>
//#include <SDL_opengl.h>
#include <thirdparty/glad/glad.h> #include <thirdparty/glad/glad.h>
class GLWindow #include "utility/logger.h"
{
class GLWindow {
public: public:
GLWindow(const char* windowName, int width, int height) : GLWindow(const char *windowName, int width, int height)
w(width), : w(width), h(height), name(windowName) {};
h(height), ~GLWindow();
name(windowName) {};
~GLWindow();
bool Init(); bool Init();
void swap() { SDL_GL_SwapWindow(window); } void swap() { SDL_GL_SwapWindow(window); }
void makeCurrent() { SDL_GL_MakeCurrent(window, glContext); } void makeCurrent() { SDL_GL_MakeCurrent(window, glContext); }
unsigned width() const { return w; } unsigned Width() const { return w; }
unsigned height() const { return h; } unsigned Height() const { return h; }
SDL_Window *getWindow() const { return window; }
const SDL_GLContext &getContext() const { return glContext; }
SDL_Window* getWindow() const { return window; }
const SDL_GLContext& getContext() const { return glContext; }
private: private:
SDL_Window* window = nullptr; SDL_Window *window = nullptr;
SDL_GLContext glContext = NULL; SDL_GLContext glContext = NULL;
unsigned w, h; unsigned w, h;
const char* name; const char *name;
}; };
bool GLWindow::Init()
{
window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_OPENGL);
if (!window)
return false;
glContext = SDL_GL_CreateContext(window);
if (!glContext)
return false;
return true;
}
GLWindow::~GLWindow()
{
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
}
#endif // _H_GLWINDOW_H #endif // _H_GLWINDOW_H

View file

@ -1,74 +1,65 @@
#ifndef _H_RENDERER_H #ifndef _H_RENDERER_H
#define _H_RENDERER_H #define _H_RENDERER_H
#include <unordered_map>
#include <memory>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <memory>
#include <unordered_map>
#include "graphics/quad.h"
#include "graphics/drawable.h" #include "graphics/drawable.h"
#include "graphics/glwindow.h"
#include "graphics/postprocess.h" #include "graphics/postprocess.h"
#include "graphics/quad.h"
class ResourceManager; class ResourceManager;
class Shader; class Shader;
enum class RenderLayer { enum class RenderLayer { Background, Map, GameObjects, Effects, HUD, Menu };
Background,
Map,
GameObjects,
Effects,
HUD,
Menu
};
class Renderer class Renderer {
{
public: public:
Renderer(const std::shared_ptr<ResourceManager>&); Renderer(const std::shared_ptr<ResourceManager> &,
const std::shared_ptr<GLWindow> &);
void clear(); void clear();
void hookEventManager(const std::weak_ptr<EventManager> eventManager); void hookEventManager(const std::weak_ptr<EventManager> eventManager);
void setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view); void setProjAndViewMatrix(const glm::mat4 &proj, const glm::mat4 &view);
void addDrawable(RenderLayer renderLayer, Drawable *drawable); void addDrawable(RenderLayer renderLayer, Drawable *drawable);
void removeDrawable(RenderLayer renderLayer, Drawable *drawable); void removeDrawable(RenderLayer renderLayer, Drawable *drawable);
void render(); void render();
private: private:
std::unordered_map<RenderLayer, std::vector<Drawable*>> worldLayerPool; std::unordered_map<RenderLayer, std::vector<Drawable *>> worldLayerPool;
std::unordered_map<RenderLayer, std::vector<Drawable*>> HUDLayerPool; std::unordered_map<RenderLayer, std::vector<Drawable *>> HUDLayerPool;
std::vector<RenderLayer> renderingOrder = { std::vector<RenderLayer> renderingOrder = {
RenderLayer::Background, RenderLayer::Background, RenderLayer::Map, RenderLayer::GameObjects,
RenderLayer::Map, RenderLayer::Effects, RenderLayer::HUD, RenderLayer::Menu};
RenderLayer::GameObjects,
RenderLayer::Effects,
RenderLayer::HUD,
RenderLayer::Menu
};
void uploadUniforms(const unsigned shaderID, const std::vector<Uniform>& uniforms); void uploadUniforms(const unsigned shaderID,
const std::vector<Uniform> &uniforms);
unsigned uboMatrices; unsigned uboMatrices;
union FrameBuffer { union FrameBuffer {
unsigned int frame; unsigned int frame;
unsigned int texture; unsigned int texture;
}worldBuffer, hudBuffer; } worldBuffer, hudBuffer;
std::unique_ptr<ScreenQuad> screenQuad; std::unique_ptr<ScreenQuad> screenQuad;
std::shared_ptr<ResourceManager> resourceManager; std::shared_ptr<ResourceManager> resourceManager;
std::shared_ptr<GLWindow> glWindow;
void initFrameBuffers(); void initFrameBuffers();
void initUniformBuffers(); void initUniformBuffers();
std::unique_ptr<Postprocessor> postProcessor; std::unique_ptr<Postprocessor> postProcessor;
void sortLayerPool(auto& layerPool); void sortLayerPool(auto &layerPool);
void renderPool(auto& layerPool); void renderPool(auto &layerPool);
}; };
#endif #endif

View file

@ -9,6 +9,9 @@ AI::AI(GameActor* actor, std::unique_ptr<Raycaster> raycaster)
lastGCTime(std::chrono::high_resolution_clock::now()), GCTimeout(3) lastGCTime(std::chrono::high_resolution_clock::now()), GCTimeout(3)
{} {}
/*
* The behavior script works off of three different states, idle, patrol and alert.
* When the script is idle, this is */
void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour) void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour)
{ {
// passing out instance of raycaster and this AI into our scripting api // passing out instance of raycaster and this AI into our scripting api
@ -25,6 +28,8 @@ void AI::attachBehaviourScript(std::unique_ptr<AIScript> behaviour)
void AI::update() void AI::update()
{ {
try { try {
// If our ai doesn't have a reference to the actor it's controlling it will do nothing.
// If our ai script does have an actor but no target, it will default to its idle state.
if (actor && target) { if (actor && target) {
switch (state) switch (state)
{ {
@ -35,7 +40,7 @@ void AI::update()
if (!result.valid()) if (!result.valid())
{ {
sol::error err = result; sol::error err = result;
std::cerr << "lua error: " << err.what() << std::endl; LOG(ERROR, "Lua error: {}", err.what(), NULL);
} }
} }
break; break;
@ -46,7 +51,7 @@ void AI::update()
if (!result.valid()) if (!result.valid())
{ {
sol::error err = result; sol::error err = result;
std::cerr << "lua error: " << err.what() << std::endl; LOG(ERROR, "Lua error: {}", err.what(), NULL);
} }
} }
break; break;
@ -57,12 +62,25 @@ void AI::update()
if (!result.valid()) if (!result.valid())
{ {
sol::error err = result; sol::error err = result;
std::cerr << "lua error: " << err.what() << std::endl; LOG(ERROR, "Lua error: {}", err.what(), NULL);
} }
} }
break; break;
} }
} } else if (actor) {
switch (state)
{
case AIState::Idle:
if (idleFunc.valid()) {
auto result = idleFunc(actor, nullptr);
if (!result.valid()) {
sol::error err = result;
LOG(ERROR, "Lua Error: {}", err.what());
}
}
}
}
// Collect garbage just to be sure.
std::chrono::high_resolution_clock::time_point curTime = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point curTime = std::chrono::high_resolution_clock::now();
if (curTime - lastGCTime >= GCTimeout) if (curTime - lastGCTime >= GCTimeout)
{ {
@ -71,13 +89,14 @@ void AI::update()
} }
} }
catch (const std::exception& e) { catch (const std::exception& e) {
std::cerr << "Error during AI update: " << e.what() << std::endl; LOG(ERROR, "Problem occured during AI update: {}", e.what());
state = AIState::Idle; state = AIState::Idle;
} }
} }
AI::~AI() AI::~AI()
{ {
// Just set all of the references to null, given they are smart pointers, they are freed when no longer used.
behaviour->lua["raycaster"] = sol::nil; behaviour->lua["raycaster"] = sol::nil;
behaviour->lua["ai"] = sol::nil; behaviour->lua["ai"] = sol::nil;
behaviour->lua["idle"] = sol::nil; behaviour->lua["idle"] = sol::nil;

View file

@ -1,5 +1,6 @@
#include "gameplay/camera.h" #include "gameplay/camera.h"
// follow our target set using the setTarget. If there is no target set the camera will not move.
void Camera::update(double deltaTime) void Camera::update(double deltaTime)
{ {
if (target == nullptr) if (target == nullptr)
@ -19,6 +20,7 @@ glm::mat4 Camera::getProjectionMatrix()
return glm::ortho(0.f, viewPortW, viewPortH, 0.f); return glm::ortho(0.f, viewPortW, viewPortH, 0.f);
} }
// The local coordinates are the corrdinates relative to the camera.
const glm::vec3 Camera::worldToLocal(const glm::vec3& worldCoordinates) const glm::vec3 Camera::worldToLocal(const glm::vec3& worldCoordinates)
{ {
//return worldCoordinates - position; //return worldCoordinates - position;

View file

@ -32,6 +32,7 @@ void Entity::flip()
flipped = !flipped; flipped = !flipped;
} }
// Add physics component implies more than one can be attached to an entity...
void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physics) void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physics)
{ {
this->physics = physics; this->physics = physics;
@ -39,6 +40,8 @@ void Entity::addPhysicsComponent(const std::shared_ptr<PhysicsComponent>& physic
void Entity::update(double deltaTime) void Entity::update(double deltaTime)
{ {
// If our entity has a physics component attached and that they're moving,
// we will update the entity position to match the rigidBody position.
if (physics && physics->rigidBody.velocity != glm::vec2(0.0f)) if (physics && physics->rigidBody.velocity != glm::vec2(0.0f))
{ {
position = glm::vec3(physics->rigidBody.position, 0.f); position = glm::vec3(physics->rigidBody.position, 0.f);
@ -47,21 +50,24 @@ void Entity::update(double deltaTime)
} }
else if (!physics) else if (!physics)
{ {
// In the case that the entity does not have a physics component we will handle movement on the entity itself.
position += deltaPosition * 1.f; position += deltaPosition * 1.f;
updateModelMatrix(); updateModelMatrix();
deltaPosition = glm::vec3(0.f); deltaPosition = glm::vec3(0.f);
} }
} }
// The entity will only need to update its model matrix and the flipped flag on its attached shader.
// The shader and sprite component will handle the drawing.
void Entity::draw() void Entity::draw()
{ {
//glm::mat4 mvp = camera->getProjectionMatrix() * camera->getViewMatrix() * modelMatrix;
editUniform("model", modelMatrix); editUniform("model", modelMatrix);
editUniform("flip", flipped); editUniform("flip", flipped);
} }
void Entity::updateModelMatrix() void Entity::updateModelMatrix()
{ {
// Quick sanity check to make sure our Z position is zero. Since we are in 2D
position.z = 0.f; position.z = 0.f;
glm::mat4 rotationMat = (isRotatable) ? glm::rotate(glm::mat4(1.f), glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f)) : glm::mat4(1.0f); glm::mat4 rotationMat = (isRotatable) ? glm::rotate(glm::mat4(1.f), glm::radians(rotation), glm::vec3(0.0f, 0.0f, 1.0f)) : glm::mat4(1.0f);
glm::mat4 translation = glm::translate(glm::mat4(1.f), position); glm::mat4 translation = glm::translate(glm::mat4(1.f), position);

View file

@ -1,15 +1,11 @@
#include "gameplay/game.h" #include "gameplay/game.h"
#include "gameplay/input.h" #include "gameplay/input.h"
#include "gameplay/scene.h" #include "gameplay/scene.h"
/*due for possible removal!*/
#include "gameplay/gameactor.h"
#include "gameplay/weapons/weapon.h"
/*-------------------------*/
#include "utility/command.h" #include "utility/command.h"
#include "utility/resourcemanager.h"
#include "utility/ftfont.h" #include "utility/ftfont.h"
#include "utility/logger.h" #include "utility/logger.h"
#include "utility/resourcemanager.h"
#include "graphics/glwindow.h" #include "graphics/glwindow.h"
@ -17,122 +13,115 @@
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <memory> #include <memory>
#include <thread>
#include <utility/events.h> #include <utility/events.h>
bool Game::init() bool Game::init() {
{ window = std::make_shared<GLWindow>("Yupple Mayham", 1024, 768);
window = std::make_shared<GLWindow>("Yupple Mayham", 800, 600);
if (!window->Init()) if (!window->Init())
ERROR_LOG("Failed to init GLWindow: {}", SDL_GetError()); ERROR_LOG("Failed to init GLWindow: {}", SDL_GetError());
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
ERROR_LOG("Failed to load GLLoader", NULL); ERROR_LOG("Failed to load GLLoader", NULL);
#if _DEBUG #if _DEBUG
LOG_LEVEL(DEBUG); LOG_LEVEL(DEBUG);
#else #else
LOG_LEVEL(INFO); LOG_LEVEL(INFO);
#endif #endif
SDL_GL_SetSwapInterval(1); SDL_GL_SetSwapInterval(1);
glViewport(0, 0, window->width(), window->height()); glViewport(0, 0, window->Width(), window->Height());
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// For now we'll init default bindings, but as we move forward controls will be set by the player and saved in controls.xml // For now we'll init default bindings, but as we move forward controls will
inputHandler = std::make_shared<InputHandler>(); // be set by the player and saved in controls.xml
inputHandler->bindKeyCommand(SDLK_w, 0, std::make_unique<MoveUpCommand>()); inputHandler = std::make_shared<InputHandler>();
inputHandler->bindKeyCommand(SDLK_a, 0, std::make_unique<MoveLeftCommand>()); inputHandler->bindKeyCommand(SDLK_w, 0, std::make_unique<MoveUpCommand>());
inputHandler->bindKeyCommand(SDLK_s, 0, std::make_unique<MoveDownCommand>()); inputHandler->bindKeyCommand(SDLK_a, 0, std::make_unique<MoveLeftCommand>());
inputHandler->bindKeyCommand(SDLK_d, 0, std::make_unique<MoveRightCommand>()); inputHandler->bindKeyCommand(SDLK_s, 0, std::make_unique<MoveDownCommand>());
inputHandler->bindKeyCommand(SDLK_d, 0, std::make_unique<MoveRightCommand>());
inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100, std::make_unique<ShootCommand>()); inputHandler->bindMouseCommand(MOUSE_BUTTON_LEFT, 100,
inputHandler->bindMouseMotion(std::make_unique<FollowMouseCommand>()); std::make_unique<ShootCommand>());
inputHandler->bindMouseScroll(std::make_unique<CycleCommand>()); inputHandler->bindMouseMotion(std::make_unique<FollowMouseCommand>());
inputHandler->bindMouseScroll(std::make_unique<CycleCommand>());
game_state |= GAME_RUNNING; game_state |= GAME_RUNNING;
globalEventManager = std::make_shared<EventManager>(); globalEventManager = std::make_shared<EventManager>();
resourceManager = std::make_shared<ResourceManager>(); resourceManager = std::make_shared<ResourceManager>();
renderer = std::make_shared<Renderer>(resourceManager); renderer = std::make_shared<Renderer>(resourceManager, window);
audioEngine = std::make_shared<AudioEngine>(resourceManager); audioEngine = std::make_shared<AudioEngine>(resourceManager);
audioEngine->hookEventManager(globalEventManager); camera = std::make_unique<Camera>(static_cast<float>(window->Width()),
/* Testing */ static_cast<float>(window->Height()));
audioEngine->pushMusic("music/short_song.ogg"); audioEngine->hookEventManager(globalEventManager);
audioEngine->pushMusic("music/bright.ogg"); /* Testing */
audioEngine->pushMusic("music/main_song.ogg"); audioEngine->pushMusic("music/short_song.ogg");
audioEngine->playMusic(); audioEngine->pushMusic("music/bright.ogg");
/* */ audioEngine->pushMusic("music/main_song.ogg");
renderer->hookEventManager(globalEventManager); audioEngine->playMusic();
textHandler = std::make_shared<Text>(); /* */
if (!textHandler->loadFonts("fonts")) renderer->hookEventManager(globalEventManager);
return false; textHandler = std::make_shared<Text>();
return true; if (!textHandler->loadFonts("fonts"))
return false;
return true;
} }
const unsigned Game::getWindowWidth() const { return window->width(); } const unsigned Game::getWindowWidth() const { return window->Width(); }
const unsigned Game::getWindowHeight() const { return window->height(); } const unsigned Game::getWindowHeight() const { return window->Height(); }
bool Game::loadDebugScene() bool Game::loadDebugScene() {
{ currentScene = std::make_shared<Scene>(SCENE_SHOOTER, resourceManager,
currentScene = std::make_shared<Scene>(SCENE_SHOOTER, resourceManager, globalEventManager); globalEventManager);
currentScene->init(); currentScene->init();
audioEngine->hookSceneManager(currentScene->getEventManager()); audioEngine->hookSceneManager(currentScene->getEventManager());
if (currentScene->getPlayer() == nullptr) if (currentScene->getPlayer() == nullptr)
return false; return false;
inputHandler->setActor(currentScene->getPlayer().get()); inputHandler->setActor(currentScene->getPlayer().get());
return true; camera->setTarget(currentScene->getPlayer().get());
return true;
} }
void Game::captureInput(SDL_Event& e) void Game::captureInput(SDL_Event &e) { inputHandler->captureInput(e); }
{
inputHandler->captureInput(e); void Game::executeInputs() {
inputHandler->handleInput();
inputHandler->executeCommands();
} }
void Game::executeInputs() void Game::update(double deltaTime) {
{ if (currentScene) {
inputHandler->handleInput(); currentScene->update(deltaTime);
inputHandler->executeCommands(); if (auto player = currentScene->getPlayer()) {
audioEngine->updateListener(player->getPosition());
player->setLocalPosition(camera->worldToLocal(player->getPosition()));
}
}
camera->update(deltaTime);
audioEngine->poll();
} }
void Game::update(double deltaTime) void Game::render() {
{ glClearColor(0.05f, 0.25f, 0.05f, 1.0f);
if (currentScene) { glClear(GL_COLOR_BUFFER_BIT);
currentScene->update(deltaTime);
if (auto player = currentScene->getPlayer()) renderer->setProjAndViewMatrix(camera->getProjectionMatrix(),
audioEngine->updateListener(player->getPosition()); camera->getViewMatrix());
}
audioEngine->poll(); if (currentScene)
currentScene->render(renderer);
window->swap();
} }
void Game::render() void Game::quit() { game_state = GAME_QUITTING; }
{
glClearColor(0.05f, 0.25f, 0.05f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (currentScene) Game::~Game() {
{ if (audioEngine) {
currentScene->render(renderer); audioEngine->killMusic();
/*Debug ammo indicator*/ }
textHandler->DrawText("comic.ttf", std::to_string(currentScene->getPlayer()->getHeldWeapon()->getMagazine()), glm::vec2(10, 10), 0.5f); resourceManager->clearResources();
textHandler->DrawText("comic.ttf", "/", glm::vec2(50, 10), 0.5f);
textHandler->DrawText("comic.ttf", std::to_string(currentScene->getPlayer()->getHeldWeapon()->getAmmo()), glm::vec2(90, 10), 0.5f);
}
window->swap();
}
void Game::quit()
{
game_state = GAME_QUITTING;
}
Game::~Game()
{
if (audioEngine) {
audioEngine->killMusic();
}
resourceManager->clearResources();
} }

View file

@ -1,249 +1,238 @@
#include "gameplay/scene.h" #include "gameplay/scene.h"
#include "gameplay/camera.h" #include "gameplay/ai.h"
#include "gameplay/gameactor.h" #include "gameplay/gameactor.h"
#include "gameplay/weapons/weapons.h"
#include "gameplay/weapons/bulletmanager.h"
#include "gameplay/map.h" #include "gameplay/map.h"
#include "gameplay/physics.h" #include "gameplay/physics.h"
#include "gameplay/ai.h" #include "gameplay/weapons/bulletmanager.h"
#include "gameplay/weapons/weapons.h"
#include "graphics/sprite.h"
#include "graphics/animation.h" #include "graphics/animation.h"
#include "graphics/background.h" #include "graphics/background.h"
#include "graphics/sprite.h"
#include "utility/script.h"
#include "utility/component.h" #include "utility/component.h"
#include "utility/ftfont.h"
#include "utility/xmlloader.h"
#include "utility/resourcemanager.h"
#include "utility/events.h"
#include "utility/raycaster.h"
#include "utility/debugdraw.h" #include "utility/debugdraw.h"
#include "utility/events.h"
#include "utility/ftfont.h"
#include "utility/raycaster.h"
#include "utility/resourcemanager.h"
#include "utility/script.h"
#include "utility/xmlloader.h"
#include <memory> #include <memory>
#include <execution> #include <execution>
// Scene xml files, should contain a node called <player> that holds the sprite location // Scene xml files, should contain a node called <player> that holds the sprite
// location
/* /*
like this: like this:
<player sprite="sprites/player2Atlas.png" frameSize=64.0> <player sprite="sprites/player2Atlas.png" frameSize=64.0>
<x=5/> <x=5/>
<y=6/> <y=6/>
</player> </player>
*/ */
Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources, std::weak_ptr<EventManager> globalEvents) Scene::Scene(SceneType sceneType, std::shared_ptr<ResourceManager> resources,
: type(sceneType), resourceManager(resources), globalEventManager(globalEvents) std::weak_ptr<EventManager> globalEvents)
{ : type(sceneType), resourceManager(resources),
camera = std::make_shared<Camera>(800.f, 600.f); globalEventManager(globalEvents) {
physicsEngine = std::make_shared<PhysicsEngine>(); physicsEngine = std::make_shared<PhysicsEngine>();
eventManager = std::make_shared<EventManager>(); eventManager = std::make_shared<EventManager>();
} }
void Scene::init() void Scene::init() {
{ physicsEngine->hookEventManager(eventManager);
physicsEngine->hookEventManager(eventManager); // if (sceneType == SCENE_SHOOTER)
//if (sceneType == SCENE_SHOOTER) loadDebugShooterScene();
loadDebugShooterScene();
} }
// This function is full of hardcoded values and test sprites, NOT for use with final product // This function is full of hardcoded values and test sprites, NOT for use with
void Scene::loadDebugShooterScene() // final product
{ void Scene::loadDebugShooterScene() {
hookSceneEvents(); hookSceneEvents();
sceneData = resourceManager->loadScene("000"); sceneData = resourceManager->loadScene("000");
if (!sceneData) if (!sceneData)
return; return;
EntityData playerData = sceneData->entities[0]; EntityData playerData = sceneData->entities[0];
auto mapData = sceneData->map; auto mapData = sceneData->map;
auto playerShader = resourceManager->loadShader("GL_player", "shaders/GL_player.vert", "shaders/GL_player.frag"); auto playerShader = resourceManager->loadShader(
auto bubbleShader = resourceManager->loadShader("GL_bubble", "shaders/GL_bubble.vert", "shaders/GL_bubble.frag"); "GL_player", "shaders/GL_player.vert", "shaders/GL_player.frag");
auto weaponShader = resourceManager->loadShader("GL_pistol", "shaders/GL_pistol.vert", "shaders/GL_pistol.frag"); auto bubbleShader = resourceManager->loadShader(
"GL_bubble", "shaders/GL_bubble.vert", "shaders/GL_bubble.frag");
auto weaponShader = resourceManager->loadShader(
"GL_pistol", "shaders/GL_pistol.vert", "shaders/GL_pistol.frag");
if (!sceneData->bgFile.empty()) { if (!sceneData->bgFile.empty()) {
LOG(INFO, "Found background loading '{}'", sceneData->bgFile); LOG(INFO, "Found background loading '{}'", sceneData->bgFile);
background = resourceManager->loadBackground(sceneData->bgFile); background = resourceManager->loadBackground(sceneData->bgFile);
} }
// creating map from scene // creating map from scene
auto tileShader = resourceManager->loadShader("GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag"); auto tileShader = resourceManager->loadShader(
map = std::make_shared<Map>(mapData, tileShader, resourceManager); "GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag");
map = std::make_shared<Map>(mapData, tileShader, resourceManager);
for (EntityData entityData : sceneData->entities) for (EntityData entityData : sceneData->entities) {
{ auto entity = std::make_shared<GameActor>(this, playerShader);
auto entity = std::make_shared<GameActor>(this, playerShader); // Directional is the kind of sprite sheet we are using, in this case for
// Directional is the kind of sprite sheet we are using, in this case for directional, I have the sprite sheet handle the rotations // directional, I have the sprite sheet handle the rotations instead of just
// instead of just rotating the object, this makes it look quite a bit better from the end user perspective. // rotating the object, this makes it look quite a bit better from the end
if (entityData.animated) // user perspective.
{ if (entityData.animated) {
auto entityAnimation = resourceManager->loadAnimationSet(entityData.graphic, entity->getEntityID()); auto entityAnimation = resourceManager->loadAnimationSet(
// because we don't want to have the engine rotate the object based on the entities rotation, entityData.graphic, entity->getEntityID());
// we set the this value to false so we no longer rotate the object. // because we don't want to have the engine rotate the object based on the
if (entityAnimation->getDirectional()) // entities rotation, we set the this value to false so we no longer
entity->setRotatable(false); // rotate the object.
entity->addComponent(std::make_unique<AnimationComponent>(entityAnimation, eventManager)); if (entityAnimation->getDirectional())
} entity->setRotatable(false);
else entity->addComponent(
{ std::make_unique<AnimationComponent>(entityAnimation, eventManager));
auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic); } else {
entity->addComponent(std::make_unique<SpriteComponent>(entitySprite)); auto entitySprite = resourceManager->loadSpriteStatic(entityData.graphic);
} entity->addComponent(std::make_unique<SpriteComponent>(entitySprite));
auto defaultWeapon = resourceManager->loadWeapon("gun/pistol", weaponShader, bubbleShader); }
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon, weaponShader, bubbleShader); auto defaultWeapon =
resourceManager->loadWeapon("gun/pistol", weaponShader, bubbleShader);
auto entityWeapon = resourceManager->loadWeapon(entityData.weapon,
weaponShader, bubbleShader);
entity->pickupWeapon(std::move(defaultWeapon)); entity->pickupWeapon(std::move(defaultWeapon));
entity->pickupWeapon(std::move(entityWeapon)); entity->pickupWeapon(std::move(entityWeapon));
entity->setPosition(glm::vec3(entityData.x * mapData->tileSize, entityData.y * mapData->tileSize, 0.f)); entity->setPosition(glm::vec3(entityData.x * mapData->tileSize,
entity->setScale(glm::vec3(mapData->tileSize, mapData->tileSize, 1.f)); entityData.y * mapData->tileSize, 0.f));
entity->setScale(glm::vec3(mapData->tileSize, mapData->tileSize, 1.f));
entity->addPhysicsComponent( entity->addPhysicsComponent(physicsEngine->createObject(
physicsEngine->createObject(entity->getEntityID(), entity->getEntityID(), entity->getPosition(), 49.0,
entity->getPosition(), PhysicsComponent::Collider::Shape::Circle,
49.0, glm::vec3(mapData->tileSize / 2)));
PhysicsComponent::Collider::Shape::Circle,
glm::vec3(mapData->tileSize / 2))
);
if (entityData.isPlayer) if (entityData.isPlayer) {
{ player = entity;
player = entity; } else {
camera->setTarget(entity.get()); // attach ai
entity->setLocalPosition(camera->worldToLocal(entity->getPosition())); if (!entityData.script.empty()) {
} auto behaviour = resourceManager->loadAIScript(entityData.script);
else auto rayCaster = std::make_unique<Raycaster>(
{ 40.f, 300.f, map->getCollisionMap(), mapData->tileSize);
// attach ai auto ai = std::make_shared<AI>(entity.get(), std::move(rayCaster));
if (!entityData.script.empty()) ai->setTarget(player.get());
{ ai->attachBehaviourScript(std::move(behaviour));
auto behaviour = resourceManager->loadAIScript(entityData.script); entity->addComponent(std::make_unique<AIComponent>(ai));
auto rayCaster = std::make_unique<Raycaster>(40.f, 300.f, map->getCollisionMap(), mapData->tileSize); }
auto ai = std::make_shared<AI>(entity.get(), std::move(rayCaster)); }
ai->setTarget(player.get()); entities.emplace(entity->getEntityID(), entity);
ai->attachBehaviourScript(std::move(behaviour)); SDL_Delay(1); // This is to make sure each entity gets a unique ID
entity->addComponent(std::make_unique<AIComponent>(ai)); }
}
}
entities.emplace(entity->getEntityID(), entity);
SDL_Delay(1); // This is to make sure each entity gets a unique ID
}
physicsEngine->loadCollisionMap(map->getCollisionMap(), mapData->tileSize); physicsEngine->loadCollisionMap(map->getCollisionMap(), mapData->tileSize);
// Setup map and other entities... // Setup map and other entities...
} }
std::shared_ptr<GameActor> Scene::getPlayer() const std::shared_ptr<GameActor> Scene::getPlayer() const {
{ return (!player) ? nullptr : player;
return (!player) ? nullptr : player;
} }
void Scene::update(double deltaTime) void Scene::update(double deltaTime) {
{
for (const auto& [id, e] : entities) for (const auto &[id, e] : entities) {
{ e->update(deltaTime);
e->update(deltaTime); }
if (camera->getTarget() == e.get()) physicsEngine->update(deltaTime);
e->setLocalPosition(camera->worldToLocal(e->getPosition()));
}
physicsEngine->update(deltaTime);
camera->update(deltaTime);
} }
void Scene::render(std::shared_ptr<Renderer> renderer) void Scene::render(std::shared_ptr<Renderer> renderer) {
{ renderer->clear();
renderer->clear();
renderer->setProjAndViewMatrix(camera->getProjectionMatrix(), camera->getViewMatrix()); renderer->addDrawable(RenderLayer::Map, map.get());
renderer->addDrawable(RenderLayer::Map, map.get()); if (background) {
if (background) { renderer->addDrawable(RenderLayer::Background,
renderer->addDrawable(RenderLayer::Background, static_cast<Drawable*>(background)); static_cast<Drawable *>(background));
} }
//map->draw(); // map->draw();
for (auto& [id, e] : entities) for (auto &[id, e] : entities) {
{ // e->draw();
//e->draw(); renderer->addDrawable(RenderLayer::GameObjects, e.get());
renderer->addDrawable(RenderLayer::GameObjects, e.get()); if (e->getHeldWeapon()) {
if (e->getHeldWeapon()) { renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon());
renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon()); const auto &weapons = e->getAllWeapons();
const auto& weapons = e->getAllWeapons(); for (auto &w : weapons) {
for (auto& w : weapons) { for (auto b : w->getBulletManager()->getBullets()) {
for (auto b : w->getBulletManager()->getBullets()) { renderer->addDrawable(RenderLayer::GameObjects, b.get());
renderer->addDrawable(RenderLayer::GameObjects, b.get()); }
} }
} }
} }
}
renderer->render(); renderer->render();
/* /*
for (const auto& bullet : player->getHeldWeapon()->getBulletManager()->getBullets()) { for (const auto& bullet :
DebugDrawer::getInstance().addLine(player->getCenter(), bullet->getCenter(), glm::vec4(1.f, 0.f, 0.f, 0.f)); player->getHeldWeapon()->getBulletManager()->getBullets()) {
DEBUG_TEXT( DebugDrawer::getInstance().addLine(player->getCenter(),
glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x, bullet->getCenter(), glm::vec4(1.f, 0.f, 0.f, 0.f)); DEBUG_TEXT(
camera.get()->worldToLocal(bullet->getCenter()).y, 0.f), glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x,
glm::vec4(1.f, 0.f, 0.f, 1.f), camera.get()->worldToLocal(bullet->getCenter()).y,
0.2f, 0.f), glm::vec4(1.f, 0.f, 0.f, 1.f), 0.2f,
"( {}, {}, {} )", "( {}, {}, {} )",
bullet->getCenter().x, bullet->getCenter().x,
bullet->getCenter().y, bullet->getCenter().y,
bullet->getCenter().z bullet->getCenter().z
); );
DEBUG_TEXT( DEBUG_TEXT(
glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x, glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x,
camera.get()->worldToLocal(bullet->getCenter()).y + 10.f, 0.f), camera.get()->worldToLocal(bullet->getCenter()).y
glm::vec4(1.f, 1.f, 0.f, 1.f), + 10.f, 0.f), glm::vec4(1.f, 1.f, 0.f, 1.f), 0.2f,
0.2f, "( {}, {} )",
"( {}, {} )", bullet->getPhysicsComponent()->rigidBody.position.x,
bullet->getPhysicsComponent()->rigidBody.position.x, bullet->getPhysicsComponent()->rigidBody.position.y
bullet->getPhysicsComponent()->rigidBody.position.y );
); DEBUG_TEXT(
DEBUG_TEXT( glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x,
glm::vec3(camera.get()->worldToLocal(bullet->getCenter()).x, camera.get()->worldToLocal(bullet->getCenter()).y
camera.get()->worldToLocal(bullet->getCenter()).y + 20.f, 0.f), + 20.f, 0.f), glm::vec4(0.f, 1.f, 1.f, 1.f), 0.2f,
glm::vec4(0.f, 1.f, 1.f, 1.f), "( {}, {} )",
0.2f, bullet->getPhysicsComponent()->rigidBody.velocity.x,
"( {}, {} )", bullet->getPhysicsComponent()->rigidBody.velocity.y);
bullet->getPhysicsComponent()->rigidBody.velocity.x, }
bullet->getPhysicsComponent()->rigidBody.velocity.y); */
}
*/ DEBUG_TEXT(glm::vec3(10.f, 10.f, 0.f), glm::vec4(0.f, 0.f, 0.f, 1.f), 0.5f,
DebugDrawer::getInstance().draw(camera->getProjectionMatrix() * camera->getViewMatrix()); "{} / {}", getPlayer()->getHeldWeapon()->getMagazine(),
getPlayer()->getHeldWeapon()->getAmmo());
} }
void Scene::unloadScene() void Scene::unloadScene() {
{ // xmlLoader.reset();
//xmlLoader.reset();
} }
void Scene::hookSceneEvents() void Scene::hookSceneEvents() {
{ std::weak_ptr<Scene> weakSelf = shared_from_this();
std::weak_ptr<Scene> weakSelf = shared_from_this(); eventManager->subscribe<BulletCollideEvent>(
eventManager->subscribe<BulletCollideEvent>([weakSelf](const BulletCollideEvent& e) { [weakSelf](const BulletCollideEvent &e) {
if (auto self = weakSelf.lock()) { if (auto self = weakSelf.lock()) {
GameActor* shooter = self->getGameActorByID(e.ownerID); GameActor *shooter = self->getGameActorByID(e.ownerID);
GameActor* target = self->getGameActorByID(e.victimID); GameActor *target = self->getGameActorByID(e.victimID);
if (shooter && target) if (shooter && target)
if (auto weapon = shooter->getHeldWeapon()) if (auto weapon = shooter->getHeldWeapon())
weapon->onHitCallback(target, e.bullet.get(), e.normal); weapon->onHitCallback(target, e.bullet.get(), e.normal);
} }
}); });
} }
GameActor* Scene::getGameActorByID(const unsigned int ID) GameActor *Scene::getGameActorByID(const unsigned int ID) {
{ auto iterator = entities.find(ID);
auto iterator = entities.find(ID); if (iterator == entities.end())
if (iterator == entities.end()) return nullptr;
return nullptr; return iterator->second.get();
return iterator->second.get();
} }
std::span<std::weak_ptr<GameActor>> Scene::getAllEntities() const std::span<std::weak_ptr<GameActor>> Scene::getAllEntities() const {
{ entityCache.clear();
entityCache.clear(); for (const auto &[_, entity] : entities) {
for (const auto& [_, entity] : entities) { entityCache.push_back(entity);
entityCache.push_back(entity); }
} return entityCache;
return entityCache;
} }

View file

@ -0,0 +1,22 @@
#include "graphics/glwindow.h"
#include <SDL_error.h>
bool GLWindow::Init() {
window = SDL_CreateWindow(name, SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, w, h, SDL_WINDOW_OPENGL);
if (!window) {
LOG(ERROR, "Failed to create window! {}", SDL_GetError());
return false;
}
glContext = SDL_GL_CreateContext(window);
if (!glContext) {
LOG(ERROR, "Failed to create opengl context! {}", SDL_GetError());
return false;
}
return true;
}
GLWindow::~GLWindow() {
SDL_GL_DeleteContext(glContext);
SDL_DestroyWindow(window);
}

View file

@ -1,221 +1,217 @@
#include "graphics/renderer.h" #include "graphics/renderer.h"
#include "graphics/shader.h" #include "graphics/shader.h"
#include "utility/resourcemanager.h"
#include "utility/logger.h"
#include "utility/events.h" #include "utility/events.h"
#include "utility/logger.h"
#include "utility/resourcemanager.h"
#include <algorithm> #include <algorithm>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
#include <graphics/postprocess.h> #include <graphics/postprocess.h>
#include <memory> #include <memory>
Renderer::Renderer(const std::shared_ptr<ResourceManager>& r) Renderer::Renderer(const std::shared_ptr<ResourceManager> &r,
: resourceManager(r) const std::shared_ptr<GLWindow> &w)
{ : resourceManager(r), glWindow(w) {
initFrameBuffers(); initFrameBuffers();
initUniformBuffers(); initUniformBuffers();
screenQuad = std::make_unique<ScreenQuad>(); screenQuad = std::make_unique<ScreenQuad>();
postProcessor = std::make_unique<Postprocessor>(r); postProcessor = std::make_unique<Postprocessor>(r);
} }
void Renderer::hookEventManager(std::weak_ptr<EventManager> eventManager) void Renderer::hookEventManager(std::weak_ptr<EventManager> eventManager) {
{ if (auto e = eventManager.lock()) {
if (auto e = eventManager.lock()) { e->subscribe<ScreenShakeEvent>([this](const ScreenShakeEvent &event) {
e->subscribe<ScreenShakeEvent>([this](const ScreenShakeEvent& event) { postProcessor->ApplyEffect(Postprocessor::SHAKE, event.intensity,
postProcessor->ApplyEffect(Postprocessor::SHAKE, event.duration);
event.intensity, });
event.duration e->subscribe<ScreenBlurEvent>([this](const ScreenBlurEvent &event) {
); postProcessor->ApplyEffect(Postprocessor::BLUR, event.intensity,
}); event.duration);
e->subscribe<ScreenBlurEvent>([this](const ScreenBlurEvent& event) { });
postProcessor->ApplyEffect(Postprocessor::BLUR, }
event.intensity,
event.duration
);
});
}
} }
// Initialize the framebuffers used by the renderer to allow for post-processing effects // Initialize the framebuffers used by the renderer to allow for post-processing
void Renderer::initFrameBuffers() // effects
{ void Renderer::initFrameBuffers() {
glGenFramebuffers(1, &worldBuffer.frame); glGenFramebuffers(1, &worldBuffer.frame);
glGenFramebuffers(1, &hudBuffer.frame); glGenFramebuffers(1, &hudBuffer.frame);
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame); glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
// World buffer creation // World buffer creation
glGenTextures(1, &worldBuffer.texture); glGenTextures(1, &worldBuffer.texture);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture); glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
// !!! NEED TO CREATE STATIC WINDOW SIZING !!!
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
//Attaching empty texture as color buffer for framebuffer glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(),
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, worldBuffer.texture, 0); 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
LOG(ERROR, "Failed to complete world framebuffer: {}", glCheckFramebufferStatus(GL_FRAMEBUFFER)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
assert(1 == 0); // force crash glBindTexture(GL_TEXTURE_2D, 0);
}
glEnable(GL_BLEND); // Attaching empty texture as color buffer for framebuffer
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
worldBuffer.texture, 0);
// same thing is done for the hud texture if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame); LOG(ERROR, "Failed to complete world framebuffer: {}",
glCheckFramebufferStatus(GL_FRAMEBUFFER));
assert(1 == 0); // force crash
}
glGenTextures(1, &hudBuffer.texture); glEnable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 800, 600, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, hudBuffer.texture, 0); // same thing is done for the hud texture
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { glGenTextures(1, &hudBuffer.texture);
LOG(ERROR, "Failed to complete hud framebuffer: {}", glCheckFramebufferStatus(GL_FRAMEBUFFER)); glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
assert(1 == 0); // force crash glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glWindow->Width(), glWindow->Height(),
} 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_BLEND); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); hudBuffer.texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG(ERROR, "Failed to complete hud framebuffer: {}",
glCheckFramebufferStatus(GL_FRAMEBUFFER));
assert(1 == 0); // force crash
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
void Renderer::initUniformBuffers() void Renderer::initUniformBuffers() {
{ glGenBuffers(1, &uboMatrices);
glGenBuffers(1, &uboMatrices);
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboMatrices); glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
//glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 * sizeof(glm::mat4)); glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, uboMatrices);
// glBindBufferRange(GL_UNIFORM_BUFFER, 0, uboMatrices, 0, 2 *
// sizeof(glm::mat4));
} }
void Renderer::setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view) void Renderer::setProjAndViewMatrix(const glm::mat4 &proj,
{ const glm::mat4 &view) {
glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices); glBindBuffer(GL_UNIFORM_BUFFER, uboMatrices);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), glm::value_ptr(proj)); glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4),
glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), glm::value_ptr(view)); glm::value_ptr(proj));
glBindBuffer(GL_UNIFORM_BUFFER, 0); glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4),
glm::value_ptr(view));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
} }
void Renderer::addDrawable(RenderLayer renderLayer, Drawable *drawable) void Renderer::addDrawable(RenderLayer renderLayer, Drawable *drawable) {
{ if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu)
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu) HUDLayerPool[renderLayer].push_back(drawable);
HUDLayerPool[renderLayer].push_back(drawable); else
else worldLayerPool[renderLayer].push_back(drawable);
worldLayerPool[renderLayer].push_back(drawable);
} }
void Renderer::removeDrawable(RenderLayer renderLayer, Drawable *drawable) void Renderer::removeDrawable(RenderLayer renderLayer, Drawable *drawable) {
{ auto erase = [&](auto &pool) {
auto erase = [&](auto& pool) { pool.erase(std::remove(pool.begin(), pool.end(), drawable), pool.end());
pool.erase(std::remove(pool.begin(), pool.end(), drawable), pool.end()); };
}; if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu) {
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu) { erase(HUDLayerPool[renderLayer]);
erase(HUDLayerPool[renderLayer]); } else
erase(worldLayerPool[renderLayer]);
}
void Renderer::render() {
// Bind the world frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
// clear color and depth buffer
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(worldLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(HUDLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
postProcessor->applyPostProcess(0);
screenQuad->draw();
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
// postProcessor->applyPostProcess(1);
screenQuad->draw();
}
void Renderer::renderPool(auto &layerPool) {
sortLayerPool(layerPool);
Shader *curShader = nullptr;
for (const auto &layer : renderingOrder) {
unsigned curShaderID = static_cast<unsigned>(-1);
for (const auto &item : layerPool[layer]) {
if (item->getShaderID() != curShaderID) {
curShaderID = item->getShaderID();
curShader = resourceManager->getShaderByID(curShaderID);
curShader->use();
}
if (!item->getOneShotUniforms().empty()) {
uploadUniforms(curShaderID, item->getOneShotUniforms());
item->clearOneShot();
}
uploadUniforms(curShaderID, item->getUniforms());
item->clearUniforms();
item->draw();
} }
else }
erase(worldLayerPool[renderLayer]);
} }
void Renderer::render() void Renderer::clear() {
{ worldLayerPool.clear();
// Bind the world frame buffer HUDLayerPool.clear();
glBindFramebuffer(GL_FRAMEBUFFER, worldBuffer.frame);
// clear color and depth buffer
glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(worldLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, hudBuffer.frame);
glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
renderPool(HUDLayerPool);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, worldBuffer.texture);
postProcessor->applyPostProcess(0);
screenQuad->draw();
glBindTexture(GL_TEXTURE_2D, hudBuffer.texture);
postProcessor->applyPostProcess(1);
screenQuad->draw();
} }
void Renderer::renderPool(auto& layerPool) void Renderer::uploadUniforms(const unsigned shaderID,
{ const std::vector<Uniform> &uniforms) {
sortLayerPool(layerPool); Shader *shader = resourceManager->getShaderByID(shaderID);
Shader* curShader = nullptr; if (shader == nullptr) {
for (const auto& layer : renderingOrder) { LOG(ERROR, "No shader found with id {} !", shaderID);
unsigned curShaderID = static_cast<unsigned>(-1); return;
for (const auto& item : layerPool[layer]) { }
if (item->getShaderID() != curShaderID) { for (const auto &uniform : uniforms) {
curShaderID = item->getShaderID(); std::visit(
curShader = resourceManager->getShaderByID(curShaderID); [&](auto &&arg) {
curShader->use(); using T = std::decay_t<decltype(arg)>;
} if constexpr (std::is_same_v<T, bool>) {
if (!item->getOneShotUniforms().empty()) { shader->setBool(uniform.name, arg);
uploadUniforms(curShaderID, item->getOneShotUniforms()); } else if constexpr (std::is_same_v<T, int>) {
item->clearOneShot(); shader->setInt(uniform.name, arg);
} } else if constexpr (std::is_same_v<T, float>) {
uploadUniforms(curShaderID, item->getUniforms()); shader->setFloat(uniform.name, arg);
item->clearUniforms(); } else if constexpr (std::is_same_v<T, glm::vec2>) {
shader->setVec2(uniform.name, glm::value_ptr(arg));
item->draw(); } else if constexpr (std::is_same_v<T, glm::mat4>) {
} shader->setMatrix4f(uniform.name, glm::value_ptr(arg));
} }
},
uniform.value);
}
} }
void Renderer::sortLayerPool(auto &layerPool) {
void Renderer::clear() // Sort by shader id, this works to batch shaders together to avoid shader
{ // switching too much
worldLayerPool.clear(); for (auto &[_, pool] : layerPool) {
HUDLayerPool.clear(); std::sort(pool.begin(), pool.end(),
} [](const Drawable *a, const Drawable *b) {
return a->getShaderID() < b->getShaderID();
void Renderer::uploadUniforms(const unsigned shaderID, const std::vector<Uniform>& uniforms) });
{ }
Shader *shader = resourceManager->getShaderByID(shaderID);
if (shader == nullptr) {
LOG(ERROR, "No shader found with id {} !", shaderID);
return;
}
for (const auto& uniform : uniforms) {
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, bool>) {
shader->setBool(uniform.name, arg);
}
else if constexpr (std::is_same_v<T, int>) {
shader->setInt(uniform.name, arg);
}
else if constexpr (std::is_same_v<T, float>) {
shader->setFloat(uniform.name, arg);
}
else if constexpr (std::is_same_v<T, glm::vec2>) {
shader->setVec2(uniform.name, glm::value_ptr(arg));
}
else if constexpr (std::is_same_v<T, glm::mat4>) {
shader->setMatrix4f(uniform.name, glm::value_ptr(arg));
}
}, uniform.value);
}
}
void Renderer::sortLayerPool(auto& layerPool)
{
// Sort by shader id, this works to batch shaders together to avoid shader switching too much
for (auto& [_,pool] : layerPool) {
std::sort(pool.begin(), pool.end(),
[](const Drawable* a, const Drawable* b) {
return a->getShaderID() < b->getShaderID();
});
}
} }

View file

@ -1,28 +1,22 @@
#undef GLM_FORCE_FAST_MATH
#define GLM_FORCE_PURE
#define GLM_FORCE_CTOR_INIT
#define GLM_FORCE_COLUMN_MAJOR
#include <SDL.h> #include <SDL.h>
#include <SDL_events.h> #include <SDL_events.h>
#include <SDL_image.h> #include <SDL_image.h>
#include <iostream> #include <iostream>
// TODO: Fix circular dependency issues, mostly with input.h needing gameactor.h and command.h
#include "gameplay/game.h" #include "gameplay/game.h"
#include "utility/logger.h"
const float vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
int main(int argc, char* args[]) int main(int argc, char* args[])
{ {
if (SDL_Init(SDL_INIT_VIDEO) < 0) if (SDL_Init(SDL_INIT_VIDEO) < 0) {
LOG(ERROR, "Failed to initialize SDL!", NULL);
return -1; return -1;
if (IMG_Init(IMG_INIT_PNG) < 0) }
if (IMG_Init(IMG_INIT_PNG) < 0) {
LOG(ERROR, "Could not initialize png library", NULL);
return -1; return -1;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
@ -31,13 +25,13 @@ int main(int argc, char* args[])
Game* game = new Game(); Game* game = new Game();
if (!game->init()) if (!game->init())
{ {
std::cout << "Failed to init game!" << std::endl; LOG(ERROR, "Failed to init game!", NULL);
return -1; return -1;
} }
if (!game->loadDebugScene()) if (!game->loadDebugScene())
{ {
std::cout << "Failed to load debug scene!" << std::endl; LOG(ERROR, "Failed to load debug scene!", NULL);
return -1; return -1;
} }