Preload scripts on boot and grab by ID for less file io operations + general cleanup

This commit is contained in:
ethan 2025-04-22 13:37:50 -04:00
parent 3241eafea0
commit 43bbc9e21d
12 changed files with 1195 additions and 1002 deletions

View file

@ -0,0 +1,3 @@
<monster id="monster/shooty/bighead" anim="character/tmp" weapon="gun/pistol" aggressive="true" behaviour="script/behaviour/grunt" hp="10.0"/>
<monster id="monster/shooty/clone" anim="character/player" weapon="gun/shotgun" aggressive="true" behaviour="script/behaviour/grunt" hp="15.0"/>

View file

@ -3,20 +3,17 @@
<scene type="shooter" id="000" bg="backgrounds/blue_sky.png"> <scene type="shooter" id="000" bg="backgrounds/blue_sky.png">
<map name="newmap"/> <map name="newmap"/>
<entities> <entities>
<player x="7" y="5" weapon="gun/machine"> <player x="7" y="5" weapon="gun/shotgun">
<animation id="character/player"/> <animation id="character/player"/>
</player> </player>
<entity x="10" y="3" weapon="gun/pistol"> <entity x="10" y="3" weapon="gun/pistol">
<animation id="character/player"/> <animation id="character/player"/>
<script file="scripts/ai/grunt_behaviour.lua"/> <script id="script/behaviour/grunt"/>
</entity> </entity>
<entity x="6" y="3" weapon="gun/pistol"> <entity x="6" y="3" weapon="gun/pistol">
<animation id="character/tmp"/> <animation id="character/tmp"/>
<script file="scripts/ai/grunt_behaviour.lua"/> <script id="script/behaviour/grunt"/>
</entity>
<entity x="5" y="3" weapon="gun/pistol">
<animation id="character/tmp"/>
<script file="scripts/ai/grunt_behaviour.lua"/>
</entity> </entity>
<entity x="5" y="3" weapon="gun/pistol" monster_id="monster/shooty/bighead"/>
</entities> </entities>
</scene> </scene>

View file

@ -0,0 +1,4 @@
<!-- Shooter Scripts -->
<script id="script/behaviour/grunt" file="scripts/ai/grunt_behaviour.lua"/>
<script id="script/behaviour/scared" file="scripts/ai/scared_behaviour.lua"/>

View file

@ -0,0 +1,3 @@
<!-- Ranged weapon scripts -->
<script id="script/weapon/shotgun" file="scripts/weapons/shotgun_script.lua"/>

View file

@ -12,7 +12,7 @@
</weapon> </weapon>
<weapon id="gun/shotgun" fireSpeed="1750.0" maxAmmo="64" clipSize="4"> <weapon id="gun/shotgun" fireSpeed="1750.0" maxAmmo="64" clipSize="4">
<script file="scripts/weapons/shotgun_script.lua"/> <script id="script/weapon/shotgun"/>
<animation> <animation>
<size x="55.0" y="55.0"/> <size x="55.0" y="55.0"/>
<offset x="-30.0" y="0.0"/> <offset x="-30.0" y="0.0"/>

View file

@ -1,77 +1,90 @@
#ifndef _H_RESOURCEMANAGER_H #ifndef _H_RESOURCEMANAGER_H
#define _H_RESOURCEMANAGER_H #define _H_RESOURCEMANAGER_H
#include <unordered_map>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map>
#include "sound/soundeffect.h" #include "graphics/background.h"
#include "utility/xmlloader.h"
#include "graphics/shader.h" #include "graphics/shader.h"
#include "graphics/sprite.h" #include "graphics/sprite.h"
#include "graphics/background.h" #include "sound/soundeffect.h"
#include "utility/script.h"
#include "utility/xmlloader.h"
#include <cassert> #include <cassert>
class Weapon; class Weapon;
class Script;
class AnimationSet; class AnimationSet;
class AIScript;
class WeaponScript;
class SpriteComponent; class SpriteComponent;
class ResourceManager class ResourceManager {
{
public: public:
ResourceManager() : ResourceManager() : xmlLoader(std::make_shared<XMLLoader>()) {
xmlLoader(std::make_shared<XMLLoader>()) xmlLoader->loadWeapons("weapons");
{ xmlLoader->loadAnimations("animations");
xmlLoader->loadWeapons("weapons"); xmlLoader->loadMonsters("monsters");
xmlLoader->loadAnimations("animations"); xmlLoader->loadTileSets("maps/tilesets");
xmlLoader->loadTileSets("maps/tilesets"); xmlLoader->loadMaps("maps");
xmlLoader->loadMaps("maps"); xmlLoader->loadScripts("scripts", loadLuaString);
xmlLoader->loadScenes("scenes"); xmlLoader->loadScenes("scenes");
xmlLoader->loadSoundEffects("sounds"); xmlLoader->loadSoundEffects("sounds");
shaders["__fallback__"] = std::make_unique<GenericShader>(); shaders["__fallback__"] = std::make_unique<GenericShader>();
}; };
// Returns a NON-OWNING pointer to a sprite atlas // Returns a NON-OWNING pointer to a sprite atlas
SpriteAtlas* loadSpriteAtlas (const std::string& path, float frameSize, bool isDirectional = false); SpriteAtlas *loadSpriteAtlas(const std::string &path, float frameSize,
// Returns a NON-OWNING pointer to a static sprite bool isDirectional = false);
Sprite* loadSpriteStatic (const std::string& path); // Returns a NON-OWNING pointer to a static sprite
// Returns a NON-OWNING pointer to a background Sprite *loadSpriteStatic(const std::string &path);
Background* loadBackground (const std::string& path); // Returns a NON-OWNING pointer to a background
const unsigned loadSoundEffect (const std::string& id); Background *loadBackground(const std::string &path);
std::unique_ptr<AIScript> loadAIScript (const std::string& path); const unsigned loadSoundEffect(const std::string &id);
std::unique_ptr<WeaponScript> loadWeaponScript (const std::string& path); template <typename T = Script>
std::unique_ptr<Weapon> loadWeapon (const std::string& name, const unsigned weaponShaderID, const unsigned bulletShaderID); std::unique_ptr<T> loadScript(const std::string &id);
std::shared_ptr<AnimationSet> loadAnimationSet (const std::string& name, int entityid = 0); std::unique_ptr<Weapon> loadWeapon(const std::string &name,
const unsigned weaponShaderID,
const unsigned bulletShaderID);
std::shared_ptr<AnimationSet> loadAnimationSet(const std::string &name,
int entityid = 0);
const unsigned loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath); const unsigned loadShader(const std::string &name,
const SceneData* loadScene (const std::string& id); const std::string &vertexPath,
const TileSetData* loadTileSet (const std::string& name); const std::string &fragPath);
const SceneData *loadScene(const std::string &id);
const TileSetData *loadTileSet(const std::string &name);
// Returns a NON-OWNING pointer to a shader by ID // Returns a NON-OWNING pointer to a shader by ID
Shader* getShaderByID(unsigned int ID); Shader *getShaderByID(unsigned int ID);
void clearResources(); void clearResources();
~ResourceManager(); ~ResourceManager();
private: private:
std::unordered_map<std::string, std::unique_ptr<Shader>> shaders; std::unordered_map<std::string, std::unique_ptr<Shader>> shaders;
std::unordered_map<unsigned, Shader*> shaderIDs; std::unordered_map<unsigned, Shader *> shaderIDs;
std::unordered_map<std::string, std::unique_ptr<SoundEffect>> sounds; std::unordered_map<std::string, std::unique_ptr<SoundEffect>> sounds;
std::unordered_map<std::string, std::unique_ptr<Sprite>> sprites; std::unordered_map<std::string, std::unique_ptr<Sprite>> sprites;
//std::unordered_map<std::string, std::unique_ptr<Weapon>> weapons; std::unordered_map<std::string, std::unique_ptr<Background>> backgrounds;
//std::unordered_map<std::string, std::string> scripts; std::unordered_map<std::string, std::shared_ptr<TileSetData>> tileSets;
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;
//std::unordered_map<std::string, std::shared_ptr<TileType>> tiles;
std::shared_ptr<XMLLoader> xmlLoader; std::shared_ptr<XMLLoader> xmlLoader;
static bool loadLuaString(ScriptData *script);
}; };
template <typename T>
std::unique_ptr<T> ResourceManager::loadScript(const std::string &id) {
const ScriptData *data = xmlLoader->getScriptData(id);
if (data == nullptr)
return nullptr;
try {
std::unique_ptr<T> script = std::make_unique<T>(data->script);
return std::move(script);
} catch (std::exception &e) {
LOG(ERROR, "Failed to load script '{}'", data->fileName);
return nullptr;
}
}
#endif // _H_RESOURCEMANAGER_H #endif // _H_RESOURCEMANAGER_H

View file

@ -3,45 +3,45 @@
#define SOL_ALL_SAFETIES_ON 1 #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp>
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <sol/sol.hpp>
class Script { class Script {
public: public:
sol::state lua; sol::state lua;
Script(const std::string& path); Script(const std::string &str);
~Script(); ~Script();
private: private:
bool loadScript(const std::string& path) { bool loadScript(const std::string &str) {
auto result = lua.script_file(path); auto result = lua.script(str);
if (!result.valid()) if (!result.valid()) {
{ sol::error err = result;
sol::error err = result; std::cerr << "Failed to load script. Error: " << err.what() << std::endl;
std::cerr << "Failed to load script ( " << path << " ) Error: " << err.what() << std::endl; }
} return result.valid();
return result.valid(); }
} void registerGlobalUserTypes();
void registerGlobalUserTypes();
}; };
class AIScript : public Script { class AIScript : public Script {
public: public:
AIScript(const std::string& path) : Script(path) { registerUserTypes(); } AIScript(const std::string &str) : Script(str) { registerUserTypes(); }
~AIScript(); ~AIScript();
private: private:
void registerUserTypes();
void registerUserTypes() ;
}; };
class WeaponScript : public Script { class WeaponScript : public Script {
public: public:
WeaponScript(const std::string& path) : Script(path) { registerUserTypes(); } WeaponScript(const std::string &str) : Script(str) { registerUserTypes(); }
~WeaponScript(); ~WeaponScript();
private: private:
void registerUserTypes() ; void registerUserTypes();
}; };
#endif // _H_SCRIPT_H #endif // _H_SCRIPT_H

View file

@ -1,91 +1,103 @@
#ifndef _H_XMLLOADER_H #ifndef _H_XMLLOADER_H
#define _H_XMLLOADER_H #define _H_XMLLOADER_H
#include <vector> #include <functional>
#include <string>
#include <memory>
#include <unordered_map>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <sstream>
#include <fstream>
#include <filesystem> #include <filesystem>
#include <fstream>
#include <sstream>
#include <tinyxml2.h> #include <tinyxml2.h>
struct Tile; struct Tile;
// Except for the player, these definitions are mostly overrides of the monster
// data
struct EntityData { struct EntityData {
bool isPlayer; bool isPlayer;
bool animated; bool animated;
int x = 0, y = 0; int x = 0, y = 0;
std::string graphic; float hp;
std::string weapon = "pistol"; std::string monsterDef;
std::string script; std::string graphic;
std::string weapon = "gun/pistol";
std::string script;
}; };
struct MapData { struct MapData {
std::string name; std::string name;
struct TileSet { struct TileSet {
int startID = 1; int startID = 1;
std::string path; std::string path;
}; };
std::vector<TileSet> tileSets; std::vector<TileSet> tileSets;
int width = 0, height = 0; int width = 0, height = 0;
float tileSize = 32.f; float tileSize = 32.f;
// 3D array, 0: layer, 1: y, 2: x // 3D array, 0: layer, 1: y, 2: x
// Holding tile startID + ids, 0 is an empty tile // Holding tile startID + ids, 0 is an empty tile
std::vector<std::vector<std::vector<int>>> tiles; std::vector<std::vector<std::vector<int>>> tiles;
}; };
struct TileSetData { struct TileSetData {
std::string name; std::string name;
std::string type; std::string type;
std::string file; std::string file;
int width = 0, height = 0; int width = 0, height = 0;
int columns = 0; int columns = 0;
int tileCount = 0; int tileCount = 0;
float tileSize = 64.f; float tileSize = 64.f;
struct TileData { struct TileData {
int id = 0; int id = 0;
std::string type; std::string type;
bool walkable = true; bool walkable = true;
struct ObjectData { struct ObjectData {
int id = 0; int id = 0;
std::string name; std::string name;
glm::vec2 pos; glm::vec2 pos;
glm::vec2 size; glm::vec2 size;
bool collidable = false; bool collidable = false;
bool pickup = false; bool pickup = false;
}; };
std::vector<std::shared_ptr<ObjectData>> objects; std::vector<std::shared_ptr<ObjectData>> objects;
}; };
std::vector<std::shared_ptr<TileData>> tiles; std::vector<std::shared_ptr<TileData>> tiles;
}; };
struct SceneData { struct SceneData {
std::string id; std::string id;
std::string type; std::string type;
std::string bgFile; std::string bgFile;
const MapData* map; const MapData *map;
std::vector<EntityData> entities; std::vector<EntityData> entities;
};
// Store our script as a string so we only need to read the file at boot
struct ScriptData {
std::string id;
std::string fileName;
std::string script;
}; };
struct WeaponData { struct WeaponData {
std::string id; std::string id;
float fireSpeed = 250.0f; float fireSpeed = 250.0f;
int clipSize = 21; int clipSize = 21;
int maxAmmo = 512; int maxAmmo = 512;
std::string script = ""; std::string script = "";
std::string graphic; std::string graphic;
bool animated = false; bool animated = false;
float sizeX = 50.f, sizeY = 50.f; float sizeX = 50.f, sizeY = 50.f;
float offsetX = 0.f, offsetY = 0.f; float offsetX = 0.f, offsetY = 0.f;
float bulletSizeX = 50.f, bulletSizeY = 50.f; float bulletSizeX = 50.f, bulletSizeY = 50.f;
std::string bulletGraphic; std::string bulletGraphic;
bool bulletAnimated = false; bool bulletAnimated = false;
float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f; float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f;
float modMin = 0.5f, modMax = 1.0f; float modMin = 0.5f, modMax = 1.0f;
}; };
// ID is the new tactic I've decided on. // ID is the new tactic I've decided on.
@ -94,111 +106,141 @@ struct WeaponData {
// This prefix will be used to look up the animation on // This prefix will be used to look up the animation on
// the animation map // the animation map
struct AnimationData { struct AnimationData {
std::string id; std::string id;
std::string spriteAtlas; std::string spriteAtlas;
bool directional = false; bool directional = false;
bool oneShot; bool oneShot;
float FPS = 1.f; float FPS = 1.f;
float frameSize; float frameSize;
};
// This holds a lot of defaults, but the scene can
// override a large portion of the definitions here
struct MonsterData {
std::string id;
std::string anim;
std::string weapon;
bool aggressive = false;
std::string behaviour;
float hp;
}; };
struct SoundData { struct SoundData {
std::string id; // <type>/<object>/<state> std::string id; // <type>/<object>/<state>
std::string path; std::string path;
bool spatial; bool spatial;
}; };
class XMLLoader class XMLLoader {
{
public: public:
XMLLoader() {} XMLLoader() {}
bool loadScenes(const char* sceneFolder); bool loadScenes(const char *sceneFolder);
bool loadWeapons(const char* weaponFolder); bool loadWeapons(const char *weaponFolder);
bool loadAnimations(const char* animationFolder); bool loadAnimations(const char *animationFolder);
bool loadTileSets(const char* tileSetFolder); bool loadTileSets(const char *tileSetFolder);
bool loadMaps(const char* mapFolder); bool loadMaps(const char *mapFolder);
bool loadSoundEffects(const char* soundFolder); bool loadMonsters(const char *monsterFolder);
// luaLoader is the function we use to load the lua file.
// This will be defined in our resource manager
bool loadScripts(const char *scriptFolder,
std::function<bool(ScriptData *)> luaLoader);
bool loadSoundEffects(const char *soundFolder);
const SceneData* getSceneData(const std::string& id) const { const SceneData *getSceneData(const std::string &id) const {
try { try {
return scenes.at(id).get(); return scenes.at(id).get();
} } catch (std::exception &) {
catch (std::exception&) { return nullptr;
return nullptr; }
} }
}
const MapData* getMapData(const std::string& name) const { const ScriptData *getScriptData(const std::string &id) const {
try { try {
return maps.at(name).get(); return scripts.at(id).get();
} } catch (std::exception &) {
catch (std::exception&) { return nullptr;
return nullptr; }
} }
}
const WeaponData* getWeaponData(const std::string& id) const { const MapData *getMapData(const std::string &name) const {
try { try {
return weapons.at(id).get(); return maps.at(name).get();
} } catch (std::exception &) {
catch (std::exception&) { return nullptr;
return nullptr; }
} }
}
const AnimationData* getAnimationData(const std::string& id) const { const WeaponData *getWeaponData(const std::string &id) const {
try { try {
return animations.at(id).get(); return weapons.at(id).get();
} } catch (std::exception &) {
catch (std::exception&) { return nullptr;
return nullptr; }
} }
}
const TileSetData* getTileSetData(const std::string& name) const { const AnimationData *getAnimationData(const std::string &id) const {
try { try {
return tileSets.at(name).get(); return animations.at(id).get();
} } catch (std::exception &) {
catch (std::exception&) { return nullptr;
return nullptr; }
} }
}
const SoundData* getSoundData(const std::string& id) const { const TileSetData *getTileSetData(const std::string &name) const {
try { try {
return sounds.at(id).get(); return tileSets.at(name).get();
} } catch (std::exception &) {
catch (std::exception&) { return nullptr;
return nullptr; }
} }
}
// return a full set of animations, may need further optimization. const SoundData *getSoundData(const std::string &id) const {
// one idea is when loading animations we create a seperate map that holds each set by their reference, so we can just do a simple, try {
// hash table lookup. return sounds.at(id).get();
std::vector<AnimationData*> getAnimationSet(const std::string& prefix) const { } catch (std::exception &) {
std::vector<AnimationData*> animSet; return nullptr;
animSet.reserve(animations.size()); }
for (const auto& [id, anim] : animations) { }
if (id.starts_with(prefix)) animSet.push_back(anim.get());
} // return a full set of animations, may need further optimization.
animSet.shrink_to_fit(); // one idea is when loading animations we create a seperate map that holds
return animSet; // each set by their reference, so we can just do a simple, hash table lookup.
} std::vector<AnimationData *>
getAnimationSet(const std::string &prefix) const {
std::vector<AnimationData *> animSet;
animSet.reserve(animations.size());
for (const auto &[id, anim] : animations) {
if (id.starts_with(prefix))
animSet.push_back(anim.get());
}
animSet.shrink_to_fit();
return animSet;
}
void clearData() {
scenes.clear();
weapons.clear();
animations.clear();
maps.clear();
tileSets.clear();
}
void clearData() { scenes.clear(); weapons.clear(); animations.clear(); maps.clear(); tileSets.clear(); }
protected: protected:
bool loadXmlScene(const char* xmlFile, SceneData* out); bool loadXmlScene(const char *xmlFile, SceneData *out);
bool loadEntityData(const char* xmlFile, SceneData* out); bool loadEntityData(const char *xmlFile, SceneData *out);
bool loadTile(tinyxml2::XMLElement* tileElement, TileSetData::TileData* out); bool loadTile(tinyxml2::XMLElement *tileElement, TileSetData::TileData *out);
bool loadObject(tinyxml2::XMLElement* objectElement, TileSetData::TileData::ObjectData* out); bool loadObject(tinyxml2::XMLElement *objectElement,
TileSetData::TileData::ObjectData *out);
private: private:
std::unordered_map<std::string, std::unique_ptr<SceneData>> scenes; std::unordered_map<std::string, std::unique_ptr<SceneData>> scenes;
std::unordered_map<std::string, std::unique_ptr<WeaponData>> weapons; std::unordered_map<std::string, std::unique_ptr<ScriptData>> scripts;
std::unordered_map<std::string, std::unique_ptr<AnimationData>> animations; std::unordered_map<std::string, std::unique_ptr<WeaponData>> weapons;
std::unordered_map<std::string, std::unique_ptr<MapData>> maps; std::unordered_map<std::string, std::unique_ptr<AnimationData>> animations;
std::unordered_map<std::string, std::unique_ptr<TileSetData>> tileSets; std::unordered_map<std::string, std::unique_ptr<MonsterData>> monsters;
std::unordered_map<std::string, std::unique_ptr<SoundData>> sounds; std::unordered_map<std::string, std::unique_ptr<MapData>> maps;
std::unordered_map<std::string, std::unique_ptr<TileSetData>> tileSets;
std::unordered_map<std::string, std::unique_ptr<SoundData>> sounds;
}; };
#endif // _H_XMLLOADER_H #endif // _H_XMLLOADER_H

View file

@ -101,12 +101,14 @@ void Scene::loadDebugShooterScene() {
} else { } else {
// attach ai // attach ai
if (!entityData.script.empty()) { if (!entityData.script.empty()) {
auto behaviour = resourceManager->loadAIScript(entityData.script); auto behaviour =
resourceManager->loadScript<AIScript>(entityData.script);
auto rayCaster = std::make_unique<Raycaster>( auto rayCaster = std::make_unique<Raycaster>(
40.f, 300.f, map->getCollisionMap(), mapData->tileSize); 40.f, 300.f, map->getCollisionMap(), mapData->tileSize);
auto ai = std::make_shared<AI>(entity.get(), std::move(rayCaster)); auto ai = std::make_shared<AI>(entity.get(), std::move(rayCaster));
ai->setTarget(player.get()); ai->setTarget(player.get());
ai->attachBehaviourScript(std::move(behaviour)); if (behaviour != nullptr)
ai->attachBehaviourScript(std::move(behaviour));
entity->addComponent(std::make_unique<AIComponent>(ai)); entity->addComponent(std::make_unique<AIComponent>(ai));
} }
} }

View file

@ -1,155 +1,129 @@
#include "graphics/texture.h" #include "graphics/texture.h"
#include "utility/logger.h"
#include "util.h" #include "util.h"
#include "utility/logger.h"
bool Texture::loadTexture(const char* imagePath) bool Texture::loadTexture(const char *imagePath) {
{ SDL_Surface *buffer = IMG_Load(imagePath);
SDL_Surface* buffer = IMG_Load(imagePath); if (!buffer)
if (!buffer) ERROR_LOG("Failed to load image file: {}", imagePath);
ERROR_LOG("Failed to load image file: {}", imagePath); // UTIL::flip_surface(buffer);
//UTIL::flip_surface(buffer);
glGenTextures(1, &ID); glGenTextures(1, &ID);
/* /*
GLenum error = glGetError(); GLenum error = glGetError();
if(error != GL_NO_ERROR) { if(error != GL_NO_ERROR) {
std::cout << "OpenGL error: " << error << std::endl; std::cout << "OpenGL error: " << error << std::endl;
} }
*/ */
glBindTexture(GL_TEXTURE_2D, ID); glBindTexture(GL_TEXTURE_2D, ID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, buffer->w, buffer->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer->pixels); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, buffer->w, buffer->h, 0, GL_RGBA,
GL_UNSIGNED_BYTE, buffer->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glGenerateMipmap(ID); glGenerateMipmap(ID);
textureWidth = buffer->w; textureWidth = buffer->w;
textureHeight = buffer->h; textureHeight = buffer->h;
SDL_FreeSurface(buffer); SDL_FreeSurface(buffer);
return true; return true;
} }
void Texture::bind() void Texture::bind() {
{ glActiveTexture(GL_TEXTURE0);
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, ID);
glBindTexture(GL_TEXTURE_2D, ID);
} }
Texture::~Texture() { glDeleteTextures(1, &ID); }
Texture::~Texture() bool TextureArray::loadTextures(std::vector<const char *> imagePaths) {
{ std::vector<SDL_Surface *> surfaces;
glDeleteTextures(1, &ID); surfaces.resize(imagePaths.size());
// Fill surfaces vector
for (int i = 0; i < imagePaths.size(); ++i) {
surfaces[i] = IMG_Load(imagePaths[i]);
if (!surfaces[i])
ERROR_LOG("Failed to load image file: {}", imagePaths[i]);
}
if (!adjustCanvasSizes(surfaces))
ERROR_LOG(
"Failed to adjust canvas size of images! \n Make sure to check that "
"every tileset has square dimensions! (512x512, 756x756 ... etc)",
NULL);
if (surfaces.empty())
ERROR_LOG("No surfaces created!", NULL);
numOfLayers = imagePaths.size();
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
// Creating the texture array all of our textures will live in.
// adjustCanvasSizes makes every image the same size, so we can just use the
// first surface in the list of surfaces
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, surfaces[0]->w, surfaces[0]->h,
(GLsizei)numOfLayers, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
for (int layer = 0; layer < numOfLayers; ++layer) {
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer, surfaces[layer]->w,
surfaces[layer]->h, 1, GL_RGBA, GL_UNSIGNED_BYTE,
surfaces[layer]->pixels);
}
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
for (auto &surface : surfaces)
SDL_FreeSurface(surface);
surfaces.clear();
return true;
} }
bool TextureArray::loadTextures(std::vector<const char*> imagePaths) void TextureArray::bind() {
{ glActiveTexture(GL_TEXTURE0);
std::vector<SDL_Surface*> surfaces; glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
surfaces.resize(imagePaths.size());
// Fill surfaces vector
for (int i = 0; i < imagePaths.size(); ++i)
{
surfaces[i] = IMG_Load(imagePaths[i]);
if (!surfaces[i])
ERROR_LOG("Failed to load image file: {}", imagePaths[i]);
}
if (!adjustCanvasSizes(surfaces))
ERROR_LOG("Failed to adjust canvas size of images! \n Make sure to check that every tileset has square dimensions! (512x512, 756x756 ... etc)", NULL);
if (surfaces.empty())
ERROR_LOG("No surfaces created!", NULL);
numOfLayers = imagePaths.size();
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
// Creating the texture array all of our textures will live in.
// adjustCanvasSizes makes every image the same size, so we can just use the first
// surface in the list of surfaces
glTexImage3D(GL_TEXTURE_2D_ARRAY,
0,
GL_RGBA,
surfaces[0]->w,
surfaces[0]->h,
(GLsizei)numOfLayers,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
nullptr);
for (int layer = 0; layer < numOfLayers; ++layer)
{
glTexSubImage3D(GL_TEXTURE_2D_ARRAY,
0,
0, 0, layer,
surfaces[layer]->w,
surfaces[layer]->h,
1,
GL_RGBA,
GL_UNSIGNED_BYTE,
surfaces[layer]->pixels);
}
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
for (auto& surface : surfaces)
SDL_FreeSurface(surface);
surfaces.clear();
return true;
} }
void TextureArray::bind() bool TextureArray::adjustCanvasSizes(std::vector<SDL_Surface *> &surfaces) {
{ int maxWidth = 0;
glActiveTexture(GL_TEXTURE0); int maxHeight = 0;
glBindTexture(GL_TEXTURE_2D_ARRAY, ID); for (auto &surface : surfaces) {
if (surface->w != surface->h)
ERROR_LOG("Image must be a square!", NULL);
if (surface->w > maxWidth)
maxWidth = surface->w;
if (surface->h > maxHeight)
maxHeight = surface->h;
textures.push_back(TextureData({surface->w, surface->h}));
}
for (int i = 0; i < surfaces.size(); ++i) {
SDL_Surface *canvas = SDL_CreateRGBSurface(
0, maxWidth, maxHeight, surfaces[i]->format->BitsPerPixel,
surfaces[i]->format->Rmask, surfaces[i]->format->Gmask,
surfaces[i]->format->Bmask, surfaces[i]->format->Amask);
SDL_FillRect(canvas, NULL, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
SDL_BlitSurface(surfaces[i], NULL, canvas, NULL);
SDL_FreeSurface(surfaces[i]);
surfaces[i] = canvas;
}
canvasWidth = maxWidth;
canvasHeight = maxHeight;
return true;
} }
bool TextureArray::adjustCanvasSizes(std::vector<SDL_Surface*>& surfaces) TextureArray::~TextureArray() { glDeleteTextures(1, &ID); }
{
int maxWidth = 0;
int maxHeight = 0;
for (auto& surface : surfaces)
{
if (surface->w != surface->h)
ERROR_LOG("Image must be a square!", NULL);
if (surface->w > maxWidth) maxWidth = surface->w;
if (surface->h > maxHeight) maxHeight = surface->h;
textures.push_back(TextureData({ surface->w, surface->h }));
}
for (int i = 0; i < surfaces.size(); ++i)
{
SDL_Surface* canvas = SDL_CreateRGBSurface(0, maxWidth, maxHeight,
surfaces[i]->format->BitsPerPixel,
surfaces[i]->format->Rmask,
surfaces[i]->format->Gmask,
surfaces[i]->format->Bmask,
surfaces[i]->format->Amask);
SDL_FillRect(canvas, NULL, SDL_MapRGBA(canvas->format, 0, 0, 0, 0));
SDL_BlitSurface(surfaces[i], NULL, canvas, NULL);
SDL_FreeSurface(surfaces[i]);
surfaces[i] = canvas;
}
canvasWidth = maxWidth;
canvasHeight = maxHeight;
return true;
}
TextureArray::~TextureArray()
{
glDeleteTextures(1, &ID);
}

View file

@ -1,149 +1,145 @@
#include "utility/resourcemanager.h" #include "utility/resourcemanager.h"
#include "gameplay/weapons/weapons.h"
#include "graphics/animation.h"
#include "graphics/background.h"
#include "graphics/shader.h"
#include "graphics/sprite.h" #include "graphics/sprite.h"
#include "util.h" #include "util.h"
#include "utility/script.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) SpriteAtlas *ResourceManager::loadSpriteAtlas(const std::string &path,
{ float frameSize,
auto iterator = sprites.find(path); bool isDirectional) {
if (iterator != sprites.end()) auto iterator = sprites.find(path);
return static_cast<SpriteAtlas*>(iterator->second.get()); if (iterator != sprites.end())
auto sprite = std::make_unique<SpriteAtlas>(path.c_str(), frameSize, isDirectional); return static_cast<SpriteAtlas *>(iterator->second.get());
sprites[path] = std::move(sprite); auto sprite =
SpriteAtlas& l = static_cast<SpriteAtlas&>(*sprites[path].get()); std::make_unique<SpriteAtlas>(path.c_str(), frameSize, isDirectional);
return static_cast<SpriteAtlas*>(sprites[path].get()); sprites[path] = std::move(sprite);
SpriteAtlas &l = static_cast<SpriteAtlas &>(*sprites[path].get());
return static_cast<SpriteAtlas *>(sprites[path].get());
} }
Sprite* ResourceManager::loadSpriteStatic(const std::string& path) Sprite *ResourceManager::loadSpriteStatic(const std::string &path) {
{ auto iterator = sprites.find(path);
auto iterator = sprites.find(path); if (iterator != sprites.end())
if (iterator != sprites.end()) return iterator->second.get();
return iterator->second.get(); auto sprite = std::make_unique<SpriteStatic>(path.c_str());
auto sprite = std::make_unique<SpriteStatic>(path.c_str()); if (!sprite->loaded())
if (!sprite->loaded()) return nullptr;
return nullptr; sprites[path] = std::move(sprite);
sprites[path] = std::move(sprite); return sprites[path].get();
return sprites[path].get();
} }
std::unique_ptr<AIScript> ResourceManager::loadAIScript(const std::string& path) const TileSetData *ResourceManager::loadTileSet(const std::string &name) {
{ return xmlLoader->getTileSetData(name);
return std::make_unique<AIScript>(path.c_str());
} }
std::unique_ptr<WeaponScript> ResourceManager::loadWeaponScript(const std::string& path) const unsigned ResourceManager::loadShader(const std::string &name,
{ const std::string &vertexPath,
return std::make_unique<WeaponScript>(path.c_str()); const std::string &fragPath) {
auto iterator = shaders.find(name);
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);
return id;
} }
const TileSetData* ResourceManager::loadTileSet(const std::string& name) Shader *ResourceManager::getShaderByID(unsigned int ID) {
{ auto iterator = shaderIDs.find(ID);
return xmlLoader->getTileSetData(name); return (iterator != shaderIDs.end() ? iterator->second
: shaders["__fallback__"].get());
} }
const unsigned ResourceManager::loadShader(const std::string& name, const std::string& vertexPath, const std::string& fragPath) Background *ResourceManager::loadBackground(const std::string &path) {
{ auto iterator = backgrounds.find(path);
auto iterator = shaders.find(name); if (iterator != backgrounds.end())
if (iterator != shaders.end()) return iterator->second.get();
return iterator->second->ID; auto background = std::make_unique<Background>(path);
auto shader = std::make_unique<Shader>(vertexPath.c_str(), fragPath.c_str()); backgrounds[path] = std::move(background);
if (!shader->isValid()) { return backgrounds[path].get();
return shaders["__fallback__"]->ID;
}
unsigned id = shader->ID;
shaderIDs[shader->ID] = shader.get();
shaders[name] = std::move(shader);
return id;
} }
Shader* ResourceManager::getShaderByID(unsigned int ID) // 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
auto iterator = shaderIDs.find(ID);
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
// incomplete reference to our script. // incomplete reference to our script.
std::unique_ptr<Weapon> ResourceManager::loadWeapon(const std::string& id, const unsigned weaponShaderID, const unsigned bulletShaderID) std::unique_ptr<Weapon>
{ ResourceManager::loadWeapon(const std::string &id,
const WeaponData* data = xmlLoader->getWeaponData(id); const unsigned weaponShaderID,
if (!data) { const unsigned bulletShaderID) {
LOG(ERROR, "Could not load weapon id '{}', falling back to pistol", id); const WeaponData *data = xmlLoader->getWeaponData(id);
data = xmlLoader->getWeaponData("gun/pistol");// using this as a fallback for now if (!data) {
} LOG(ERROR, "Could not load weapon id '{}', falling back to pistol", id);
auto weapon = std::make_unique<Weapon>(data, weaponShaderID, bulletShaderID, this); data = xmlLoader->getWeaponData(
if (!data->script.empty()) "gun/pistol"); // using this as a fallback for now
weapon->attachScript(loadWeaponScript(data->script)); }
return std::move(weapon); auto weapon =
std::make_unique<Weapon>(data, weaponShaderID, bulletShaderID, this);
if (!data->script.empty())
weapon->attachScript(loadScript<WeaponScript>(data->script));
return std::move(weapon);
} }
const unsigned ResourceManager::loadSoundEffect(const std::string& id) const unsigned ResourceManager::loadSoundEffect(const std::string &id) {
{ auto iterator = sounds.find(id);
auto iterator = sounds.find(id); if (iterator != sounds.end())
if (iterator != sounds.end()) return iterator->second->getBuffer();
return iterator->second->getBuffer(); auto soundData = xmlLoader->getSoundData(id);
auto soundData = xmlLoader->getSoundData(id) ; if (!soundData) {
if (!soundData) { soundData = xmlLoader->getSoundData(UTIL::get_generic(id));
soundData = xmlLoader->getSoundData(UTIL::get_generic(id)); LOG(WARN, "Could not load sound id: '{}' trying generic", id);
LOG(WARN, "Could not load sound id: '{}' trying generic", id); if (!soundData)
if (!soundData) return 0;
return 0; }
} auto sound = std::make_unique<SoundEffect>(soundData->path);
auto sound = std::make_unique<SoundEffect>(soundData->path); sounds[id] = std::move(sound);
sounds[id] = std::move(sound); return sounds[id]->getBuffer();
return sounds[id]->getBuffer();
} }
const SceneData* ResourceManager::loadScene(const std::string& id) const SceneData *ResourceManager::loadScene(const std::string &id) {
{ return xmlLoader->getSceneData(id);
return xmlLoader->getSceneData(id);
} }
std::shared_ptr<AnimationSet> ResourceManager::loadAnimationSet(const std::string& prefix, int entityid) std::shared_ptr<AnimationSet>
{ ResourceManager::loadAnimationSet(const std::string &prefix, int entityid) {
auto animSetData = xmlLoader->getAnimationSet(prefix); auto animSetData = xmlLoader->getAnimationSet(prefix);
return std::make_shared<AnimationSet>(entityid, this, animSetData); return std::make_shared<AnimationSet>(entityid, this, animSetData);
} }
// this will need some work, but for now where there is only one weapon type we can work with it void ResourceManager::clearResources() {
// TODO: Allow different weapon types! sprites.clear();
/* shaders.clear();
template <typename T> shaderIDs.clear();
std::shared_ptr<T> ResourceManager::loadWeapon(const std::string& name, std::shared_ptr<Shader> shader, std::shared_ptr<SpriteComponent> spriteComponent) tileSets.clear();
{
auto iterator = weapons.find(name);
if (iterator != weapons.end())
return iterator->second;
auto weapon = std::make_shared<T>(T(shader, spriteComponent));
weapons[name] = weapon;
return weapon;
}
*/
void ResourceManager::clearResources()
{
sprites.clear();
shaders.clear();
shaderIDs.clear();
tileSets.clear();
} }
ResourceManager::~ResourceManager() ResourceManager::~ResourceManager() {
{ clearResources();
clearResources(); xmlLoader.reset();
xmlLoader.reset(); }
bool ResourceManager::loadLuaString(ScriptData *scriptData) {
std::filesystem::path file(scriptData->fileName);
if (file.empty() || !file.has_extension())
return false;
try {
std::ifstream fileStream(file);
std::ostringstream sstr;
sstr << fileStream.rdbuf();
scriptData->script = sstr.str().c_str();
fileStream.close();
} catch (std::exception &e) {
ERROR_LOG("Failed to read script file: '{}' - '{}'", scriptData->fileName,
e.what());
}
return true;
} }

File diff suppressed because it is too large Load diff