added backgrounds to empty space, updated event polling to be more responsive

This commit is contained in:
Ethan 2025-03-24 18:59:16 -04:00
parent 294d75ff59
commit 268e6d6ebf
21 changed files with 269 additions and 108 deletions

Binary file not shown.

Binary file not shown.

View file

@ -47,6 +47,7 @@ add_executable (YuppleMayham
"src/gameplay/weapons/bulletmanager.cpp"
"src/gameplay/scene.cpp"
"src/graphics/texture.cpp"
"src/graphics/background.cpp"
"src/utility/resourcemanager.cpp"
"src/utility/xmlloader.cpp"

View file

@ -27,7 +27,8 @@ public:
bool loadDebugScene();
void handleInput(SDL_Event& e);
void captureInput(SDL_Event& e);
void executeInputs();
void update(double deltaTime);
void render();
@ -38,6 +39,7 @@ public:
const unsigned getWindowHeight() const;
void quit() { game_state = GAME_QUITTING; }
~Game();
private:
unsigned game_state = GAME_QUITTING;
@ -52,4 +54,4 @@ private:
std::shared_ptr<EventManager> globalEventManager;
};
#endif
#endif

View file

@ -2,6 +2,7 @@
#define _H_INPUT_H
#include "utility/logger.h"
#include <SDL_mouse.h>
#include <unordered_map>
#include <SDL_keycode.h>
#include <SDL_events.h>
@ -46,15 +47,12 @@ protected:
mouseButtons[MOUSE_BUTTON_LEFT] = true;
if (e.type == SDL_MOUSEBUTTONUP)
mouseButtons[MOUSE_BUTTON_LEFT] = false;
if (e.type == SDL_MOUSEMOTION)
{
mouse_state.x = static_cast<float>(e.motion.x);
mouse_state.y = static_cast<float>(e.motion.y);
// Quick fix regarding linux holding storing too many mouse events
SDL_FlushEvent(SDL_MOUSEMOTION);
}
if (e.type == SDL_MOUSEWHEEL)
mouse_state.scroll = e.wheel.preciseY;
if (e.type == SDL_MOUSEMOTION) {
mouse_state.x = static_cast<float>(e.motion.x);
mouse_state.y = static_cast<float>(e.motion.y);
}
}
MouseState mouse_state;
std::unordered_map<Uint8, bool> mouseButtons;
@ -95,11 +93,14 @@ public:
mouseScrollCommand = command;
}
void handleInput();
void executeCommands();
private:
GameActor* actor = nullptr;
std::unordered_map<SDL_Keycode, CommandWithDelay> keyCommands;
std::unordered_map<Uint8, MouseCommandWithDelay> mouseCommands;
std::vector<Command*> commandQueue;
std::vector<MouseCommand*> mouseQueue;
MouseCommand* mouseMotionCommand = nullptr;
MouseCommand* mouseScrollCommand = nullptr;
};

View file

@ -7,6 +7,7 @@
#include <gameplay/gameactor.h>
class Background;
class Entity;
class Camera;
class Map;
@ -61,6 +62,9 @@ private:
void hookSceneEvents();
GameActor* getGameActorByID(const unsigned int ID);
// Non-owning pointer DO NOT DELETE, resource manager will handle that!
Background *background;
SceneType type;
std::shared_ptr<Map> map;
//std::shared_ptr<TileSet> tileSet;
@ -77,4 +81,4 @@ private:
std::shared_ptr<SceneData> sceneData;
};
#endif //_H_SCENE_H
#endif //_H_SCENE_H

View file

@ -0,0 +1,24 @@
#ifndef _H_BACKGROUND_H
#define _H_BACKGROUND_H
#include "graphics/quad.h"
#include "graphics/drawable.h"
#include "graphics/texture.h"
#include "utility/logger.h"
class Background : public Drawable
{
public:
Background(const std::string& fileName);
void draw() override;
~Background();
private:
ScreenQuad *quad;
Texture *texture;
};
#endif // _H_BACKGROUND_H

View file

@ -0,0 +1,50 @@
#ifndef _H_DRAWABLE_H
#define _H_DRAWABLE_H
#include <vector>
#include <variant>
#include <glm/glm.hpp>
#include <string>
using UniformValue = std::variant<
int,
bool,
float,
double,
glm::mat4,
glm::vec2
>;
struct Uniform {
std::string name;
UniformValue value;
};
class Drawable
{
public:
virtual void draw() = 0;
const unsigned getShaderID() const { return shaderID; }
const std::vector<Uniform>& getUniforms() { return uniforms; }
const std::vector<Uniform>& getOneShotUniforms() { return oneShotUniforms; }
void clearOneShot() { oneShotUniforms.clear(); }
void clearUniforms() { uniforms.clear(); }
protected:
unsigned shaderID;
template <typename T>
void editUniform(const std::string& name, T value)
{
uniforms.emplace_back(Uniform {name, value});
}
template <typename T>
void editUniformOnce(const std::string& name, T value)
{
oneShotUniforms.emplace_back(Uniform {name, value});
}
private:
std::vector<Uniform> uniforms;
std::vector<Uniform> oneShotUniforms;
};
#endif // _H_DRAWABLE_H

View file

@ -8,7 +8,7 @@ class Quad
{
public:
void draw();
~Quad();
virtual ~Quad();
protected:
Quad(const float* vertexData);
private:

View file

@ -3,12 +3,11 @@
#include <unordered_map>
#include <memory>
#include <variant>
#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "graphics/quad.h"
#include "graphics/drawable.h"
#include "graphics/postprocess.h"
class ResourceManager;
@ -23,47 +22,6 @@ enum class RenderLayer {
Menu
};
using UniformValue = std::variant<
int,
bool,
float,
double,
glm::mat4,
glm::vec2
>;
struct Uniform {
std::string name;
UniformValue value;
};
class Drawable
{
public:
virtual void draw() = 0;
const unsigned getShaderID() const { return shaderID; }
const std::vector<Uniform>& getUniforms() { return uniforms; }
const std::vector<Uniform>& getOneShotUniforms() { return oneShotUniforms; }
void clearOneShot() { oneShotUniforms.clear(); }
void clearUniforms() { uniforms.clear(); }
protected:
unsigned shaderID;
template <typename T>
void editUniform(const std::string& name, T value)
{
uniforms.emplace_back(Uniform {name, value});
}
template <typename T>
void editUniformOnce(const std::string& name, T value)
{
oneShotUniforms.emplace_back(Uniform {name, value});
}
private:
std::vector<Uniform> uniforms;
std::vector<Uniform> oneShotUniforms;
};
class Renderer
{
public:
@ -74,14 +32,14 @@ public:
void hookEventManager(const std::weak_ptr<EventManager> eventManager);
void setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view);
void addDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable);
void removeDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable);
void addDrawable(RenderLayer renderLayer, Drawable *drawable);
void removeDrawable(RenderLayer renderLayer, Drawable *drawable);
void render();
private:
std::unordered_map<RenderLayer, std::vector<std::shared_ptr<Drawable>>> worldLayerPool;
std::unordered_map<RenderLayer, std::vector<std::shared_ptr<Drawable>>> HUDLayerPool;
std::unordered_map<RenderLayer, std::vector<Drawable*>> worldLayerPool;
std::unordered_map<RenderLayer, std::vector<Drawable*>> HUDLayerPool;
std::vector<RenderLayer> renderingOrder = {
RenderLayer::Background,
RenderLayer::Map,

View file

@ -7,10 +7,15 @@
class Shader
{
private:
protected:
bool compileAndLink(const char *vSource, const char *fSource);
std::string vertexPath;
std::string fragmentPath;
bool valid;
Shader();
public:
unsigned int ID;
Shader(const char* vertexPath, const char* fragmentPath);
Shader(const char* vertPath, const char* fragPath);
void use() { glUseProgram(ID); }
void setFloat(const std::string& name, float value);
@ -21,7 +26,15 @@ public:
void setVec2(const std::string& name, const float* value);
void setMatrix4f(const std::string& name, const float* value);
~Shader();
bool isValid() { return valid; }
virtual ~Shader();
};
class GenericShader : public Shader
{
public:
GenericShader() : Shader() {};
};
#endif // _H_SHADER_H

View file

@ -8,6 +8,7 @@
#include "utility/xmlloader.h"
#include "graphics/shader.h"
#include "graphics/sprite.h"
#include "graphics/background.h"
#include <cassert>
class Weapon;
@ -28,19 +29,25 @@ public:
xmlLoader->loadTileSets("maps/tilesets");
xmlLoader->loadMaps("maps");
xmlLoader->loadScenes("scenes");
shaders["__fallback__"] = std::make_unique<GenericShader>();
};
SpriteAtlas* loadSpriteAtlas (const std::string& path, float frameSize, bool isDirectional = false);
Sprite* loadSpriteStatic (const std::string& path);
std::shared_ptr<AIScript> loadAIScript (const std::string& path);
// Returns a NON-OWNING pointer to a sprite atlas
SpriteAtlas* loadSpriteAtlas (const std::string& path, float frameSize, bool isDirectional = false);
// Returns a NON-OWNING pointer to a static sprite
Sprite* loadSpriteStatic (const std::string& path);
// Returns a NON-OWNING pointer to a background
Background* loadBackground (const std::string& path);
std::shared_ptr<AIScript> loadAIScript (const std::string& path);
std::shared_ptr<WeaponScript> loadWeaponScript (const std::string& path);
const unsigned loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath);
std::shared_ptr<Weapon> loadWeapon (const std::string& name, const unsigned weaponShaderID, const unsigned bulletShaderID);
std::shared_ptr<SceneData> loadScene (const std::string& id);
std::shared_ptr<AnimationSet> loadAnimationSet(const std::string& name, int entityid = 0);
std::shared_ptr<TileSetData> loadTileSet (const std::string& name);
const unsigned loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath);
std::shared_ptr<Weapon> loadWeapon (const std::string& name, const unsigned weaponShaderID, const unsigned bulletShaderID);
std::shared_ptr<SceneData> loadScene (const std::string& id);
std::shared_ptr<AnimationSet> loadAnimationSet (const std::string& name, int entityid = 0);
std::shared_ptr<TileSetData> loadTileSet (const std::string& name);
// Returns a NON-OWNING pointer to a shader by ID
Shader* getShaderByID(unsigned int ID);
void clearResources();
@ -48,12 +55,13 @@ public:
~ResourceManager();
private:
std::unordered_map<std::string, std::unique_ptr<Shader>> shaders;
std::unordered_map<unsigned, Shader*> shaderIDs;
std::unordered_map<std::string, std::unique_ptr<Sprite>> sprites;
std::unordered_map<std::string, std::shared_ptr<Weapon>> weapons;
std::unordered_map<std::string, std::shared_ptr<Script>> scripts;
std::unordered_map<std::string, std::shared_ptr<TileSetData>>tileSets;
std::unordered_map<std::string, std::unique_ptr<Shader>> shaders;
std::unordered_map<unsigned, Shader*> shaderIDs;
std::unordered_map<std::string, std::unique_ptr<Sprite>> sprites;
std::unordered_map<std::string, std::shared_ptr<Weapon>> weapons;
std::unordered_map<std::string, std::shared_ptr<Script>> scripts;
std::unordered_map<std::string, std::unique_ptr<Background>> backgrounds;
std::unordered_map<std::string, std::shared_ptr<TileSetData>> tileSets;
//std::unordered_map<std::string, std::shared_ptr<EntityData>> entityData;
//std::unordered_map<std::string, std::shared_ptr<SceneData>> scenes;
//std::unordered_map<std::string, std::shared_ptr<MapData>> maps;
@ -62,4 +70,4 @@ private:
std::shared_ptr<XMLLoader> xmlLoader;
};
#endif // _H_RESOURCEMANAGER_H
#endif // _H_RESOURCEMANAGER_H

View file

@ -74,10 +74,15 @@ bool Game::loadDebugScene()
return true;
}
void Game::handleInput(SDL_Event& e)
void Game::captureInput(SDL_Event& e)
{
inputHandler->captureInput(e);
}
void Game::executeInputs()
{
inputHandler->handleInput();
inputHandler->executeCommands();
}
void Game::update(double deltaTime)
@ -102,3 +107,8 @@ void Game::render()
window->swap();
}
Game::~Game()
{
resourceManager->clearResources();
}

View file

@ -1,6 +1,5 @@
#include "gameplay/input.h"
#include "utility/command.h"
#include "gameplay/gameactor.h"
#include <SDL_timer.h>
@ -17,7 +16,7 @@ void InputHandler::handleInput()
{
if (currentTime - command.lastExecution >= command.delay)
{
command.cmd->execute(*actor);
commandQueue.push_back(command.cmd);
command.lastExecution = currentTime;
}
}
@ -29,7 +28,7 @@ void InputHandler::handleInput()
{
if (currentTime - command.lastExecution >= command.delay)
{
command.cmd->execute(*actor, mouse_state);
mouseQueue.push_back(command.cmd);
command.lastExecution = currentTime;
}
}
@ -40,3 +39,16 @@ void InputHandler::handleInput()
mouseScrollCommand->execute(*actor, mouse_state);
mouse_state.scroll = 0.0f; // clear mouse scroll since we have handled the event.
}
// Executes every captured command during the frame
void InputHandler::executeCommands()
{
for (auto& command : commandQueue) {
command->execute(*actor);
}
for (auto& mouse : mouseQueue) {
mouse->execute(*actor, mouse_state);
}
commandQueue.clear();
mouseQueue.clear();
}

View file

@ -9,6 +9,7 @@
#include "graphics/sprite.h"
#include "graphics/animation.h"
#include "graphics/background.h"
#include "utility/component.h"
#include "utility/ftfont.h"
@ -59,6 +60,11 @@ void Scene::loadDebugShooterScene()
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()) {
LOG(INFO, "Found background loading '{}'", sceneData->bgFile);
background = resourceManager->loadBackground(sceneData->bgFile);
}
// creating map from scene
auto tileShader = resourceManager->loadShader("GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag");
map = std::make_shared<Map>(mapData, tileShader, resourceManager);
@ -149,16 +155,19 @@ void Scene::render(std::shared_ptr<Renderer> renderer)
renderer->clear();
renderer->setProjAndViewMatrix(camera->getProjectionMatrix(), camera->getViewMatrix());
renderer->addDrawable(RenderLayer::Map, map);
renderer->addDrawable(RenderLayer::Map, map.get());
if (background) {
renderer->addDrawable(RenderLayer::Background, static_cast<Drawable*>(background));
}
//map->draw();
for (auto& [id, e] : entities)
{
//e->draw();
renderer->addDrawable(RenderLayer::GameObjects, e);
renderer->addDrawable(RenderLayer::GameObjects, e.get());
if (e->getHeldWeapon()) {
renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon());
renderer->addDrawable(RenderLayer::GameObjects, e->getHeldWeapon().get());
for (const auto& bullet : e->getHeldWeapon()->getBulletManager()->getBullets()) {
renderer->addDrawable(RenderLayer::GameObjects, bullet);
renderer->addDrawable(RenderLayer::GameObjects, bullet.get());
}
}
}

View file

@ -0,0 +1,23 @@
#include "graphics/background.h"
Background::Background(const std::string& fileName)
{
// This will fallback to the generic shader.
shaderID = 0;
texture = new Texture();
quad = new ScreenQuad();
if (!texture->loadTexture(fileName.c_str()))
LOG(ERROR, "Failed to load background texture: '{}'", fileName);
}
void Background::draw()
{
texture->bind();
quad->draw();
}
Background::~Background()
{
delete texture;
delete quad;
}

View file

@ -1,7 +1,5 @@
#include "graphics/quad.h"
#include <memory>
Quad::Quad(const float* vertexData)
{
std::memcpy(vertices, vertexData, sizeof(vertices));
@ -43,4 +41,4 @@ Quad::~Quad()
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteVertexArrays(1, &VAO);
}
}

View file

@ -108,7 +108,7 @@ void Renderer::setProjAndViewMatrix(const glm::mat4& proj, const glm::mat4& view
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
void Renderer::addDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable)
void Renderer::addDrawable(RenderLayer renderLayer, Drawable *drawable)
{
if (renderLayer == RenderLayer::HUD || renderLayer == RenderLayer::Menu)
HUDLayerPool[renderLayer].push_back(drawable);
@ -116,7 +116,7 @@ void Renderer::addDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> dr
worldLayerPool[renderLayer].push_back(drawable);
}
void Renderer::removeDrawable(RenderLayer renderLayer, std::shared_ptr<Drawable> drawable)
void Renderer::removeDrawable(RenderLayer renderLayer, Drawable *drawable)
{
auto erase = [&](auto& pool) {
pool.erase(std::remove(pool.begin(), pool.end(), drawable), pool.end());
@ -163,17 +163,7 @@ void Renderer::renderPool(auto& layerPool)
if (item->getShaderID() != curShaderID) {
curShaderID = item->getShaderID();
curShader = resourceManager->getShaderByID(curShaderID);
if (curShader) {
curShader->use();
/*
curShader->setMatrix4f("projection", glm::value_ptr(projMat));
curShader->setMatrix4f("view", glm::value_ptr(viewMat));
*/
} else {
LOG(ERROR, "Shader with ID {} not found!", curShaderID);
continue;
}
curShader->use();
}
if (!item->getOneShotUniforms().empty()) {
uploadUniforms(curShaderID, item->getOneShotUniforms());
@ -226,7 +216,7 @@ 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 std::shared_ptr<Drawable>& a, const std::shared_ptr<Drawable>& b) {
[](const Drawable* a, const Drawable* b) {
return a->getShaderID() < b->getShaderID();
});
}

View file

@ -3,7 +3,37 @@
#include "utility/logger.h"
#include <cassert>
Shader::Shader(const char* vertexPath, const char* fragmentPath)
// Load a generic fallback shader
Shader::Shader()
{
std::string vertexSource = {
// generic vertex shader program
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"out vec2 texCoord;\n"
"void main()\n"
"{\n"
" texCoord = aTexCoord;\n"
" gl_Position = vec4(aPos, 1.0);\n"
"}\0"
};
std::string fragmentSource = {
// generic fragment shader program
"#version 330 core\n"
"in vec2 texCoord;\n"
"out vec4 FragColor;\n"
"uniform sampler2D sprite;\n"
"void main()\n"
"{\n"
" FragColor = texture(sprite, texCoord);\n"
"}\0"
};
compileAndLink(vertexSource.c_str(), fragmentSource.c_str());
valid = true;
}
Shader::Shader(const char* vertPath, const char* fragPath) : vertexPath(vertPath), fragmentPath(fragPath)
{
std::string vertexSource;
std::string fragmentSource;
@ -35,7 +65,15 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
}
const char* vSource = vertexSource.c_str();
const char* fSource = fragmentSource.c_str();
if (!compileAndLink(vertexSource.c_str(), fragmentSource.c_str())) {
LOG(ERROR, "Failed to compile and/or link shader! Check error details for more information.\nUsing fallback shader!", NULL);
valid = false;
}
valid = true;
}
bool Shader::compileAndLink(const char *vSource, const char *fSource)
{
unsigned int vertexid, fragmentid;
char infoLog[512];
int success;
@ -50,6 +88,7 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
glGetShaderInfoLog(vertexid, 512, NULL, infoLog);
LOG(ERROR, "VERTEX SHADER '{}' COMPILE ERROR\n{}", vertexPath, infoLog);
return false;
}
//compile fragment shader
@ -62,6 +101,7 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
glGetShaderInfoLog(fragmentid, 512, NULL, infoLog);
LOG(ERROR, "FRAGMENT SHADER '{}' COMPILE ERROR\n{}", fragmentPath, infoLog);
return false;
}
//create and link program with compiled shaders
@ -75,12 +115,14 @@ Shader::Shader(const char* vertexPath, const char* fragmentPath)
{
glGetProgramInfoLog(ID, 512, NULL, infoLog);
LOG(ERROR, "PROGRAM LINKER ERROR\n{}", infoLog);
return false;
}
glDeleteShader(vertexid);
glDeleteShader(fragmentid);
glUniformBlockBinding(ID, glGetUniformBlockIndex(ID, "Matrices"), 0);
return true;
}
void Shader::setFloat(const std::string& name, float value)

View file

@ -49,11 +49,13 @@ int main(int argc, char* args[])
Uint64 curCounter = SDL_GetPerformanceCounter();
double deltaTime = ((curCounter - lastCounter) / freq);
deltaTime = (deltaTime < 1.0) ? deltaTime : 1.0;
SDL_PollEvent(&e);
if (e.type == SDL_QUIT)
game->quit();
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT)
game->quit();
game->handleInput(e);
game->captureInput(e);
}
game->executeInputs();
game->render();
game->update(deltaTime);
@ -61,6 +63,7 @@ int main(int argc, char* args[])
}
delete game;
IMG_Quit();
SDL_Quit();

View file

@ -1,9 +1,9 @@
#include "utility/resourcemanager.h"
#include "utility/component.h"
#include "graphics/sprite.h"
#include "utility/script.h"
#include "graphics/shader.h"
#include "graphics/animation.h"
#include "graphics/background.h"
#include "gameplay/weapons/weapons.h"
SpriteAtlas* ResourceManager::loadSpriteAtlas(const std::string& path, float frameSize, bool isDirectional)
@ -50,6 +50,9 @@ const unsigned ResourceManager::loadShader(const std::string& name, const std::s
if (iterator != shaders.end())
return iterator->second->ID;
auto shader = std::make_unique<Shader>(vertexPath.c_str(), fragPath.c_str());
if (!shader->isValid()) {
return shaders["__fallback__"]->ID;
}
unsigned id = shader->ID;
shaderIDs[shader->ID] = shader.get();
shaders[name] = std::move(shader);
@ -59,7 +62,17 @@ const unsigned ResourceManager::loadShader(const std::string& name, const std::s
Shader* ResourceManager::getShaderByID(unsigned int ID)
{
auto iterator = shaderIDs.find(ID);
return (iterator != shaderIDs.end() ? iterator->second : nullptr);
return (iterator != shaderIDs.end() ? iterator->second : shaders["__fallback__"].get());
}
Background* ResourceManager::loadBackground(const std::string& path)
{
auto iterator = backgrounds.find(path);
if (iterator != backgrounds.end())
return iterator->second.get();
auto background = std::make_unique<Background>(path);
backgrounds[path] = std::move(background);
return backgrounds[path].get();
}
// We attach our script after we create our weapon because we are passing a reference to the weapon into the script and we don't want to pass an