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">
<map name="newmap"/>
<entities>
<player x="7" y="5" weapon="gun/machine">
<player x="7" y="5" weapon="gun/shotgun">
<animation id="character/player"/>
</player>
<entity x="10" y="3" weapon="gun/pistol">
<animation id="character/player"/>
<script file="scripts/ai/grunt_behaviour.lua"/>
<script id="script/behaviour/grunt"/>
</entity>
<entity x="6" y="3" weapon="gun/pistol">
<animation id="character/tmp"/>
<script file="scripts/ai/grunt_behaviour.lua"/>
</entity>
<entity x="5" y="3" weapon="gun/pistol">
<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" monster_id="monster/shooty/bighead"/>
</entities>
</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 id="gun/shotgun" fireSpeed="1750.0" maxAmmo="64" clipSize="4">
<script file="scripts/weapons/shotgun_script.lua"/>
<script id="script/weapon/shotgun"/>
<animation>
<size x="55.0" y="55.0"/>
<offset x="-30.0" y="0.0"/>

View file

@ -1,77 +1,90 @@
#ifndef _H_RESOURCEMANAGER_H
#define _H_RESOURCEMANAGER_H
#include <unordered_map>
#include <memory>
#include <string>
#include <unordered_map>
#include "sound/soundeffect.h"
#include "utility/xmlloader.h"
#include "graphics/background.h"
#include "graphics/shader.h"
#include "graphics/sprite.h"
#include "graphics/background.h"
#include "sound/soundeffect.h"
#include "utility/script.h"
#include "utility/xmlloader.h"
#include <cassert>
class Weapon;
class Script;
class AnimationSet;
class AIScript;
class WeaponScript;
class SpriteComponent;
class ResourceManager
{
class ResourceManager {
public:
ResourceManager() :
xmlLoader(std::make_shared<XMLLoader>())
{
xmlLoader->loadWeapons("weapons");
xmlLoader->loadAnimations("animations");
xmlLoader->loadTileSets("maps/tilesets");
xmlLoader->loadMaps("maps");
xmlLoader->loadScenes("scenes");
xmlLoader->loadSoundEffects("sounds");
shaders["__fallback__"] = std::make_unique<GenericShader>();
};
ResourceManager() : xmlLoader(std::make_shared<XMLLoader>()) {
xmlLoader->loadWeapons("weapons");
xmlLoader->loadAnimations("animations");
xmlLoader->loadMonsters("monsters");
xmlLoader->loadTileSets("maps/tilesets");
xmlLoader->loadMaps("maps");
xmlLoader->loadScripts("scripts", loadLuaString);
xmlLoader->loadScenes("scenes");
xmlLoader->loadSoundEffects("sounds");
shaders["__fallback__"] = std::make_unique<GenericShader>();
};
// 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);
const unsigned loadSoundEffect (const std::string& id);
std::unique_ptr<AIScript> loadAIScript (const std::string& path);
std::unique_ptr<WeaponScript> loadWeaponScript (const std::string& path);
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);
// 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);
const unsigned loadSoundEffect(const std::string &id);
template <typename T = Script>
std::unique_ptr<T> loadScript(const std::string &id);
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 SceneData* loadScene (const std::string& id);
const TileSetData* loadTileSet (const std::string& name);
const unsigned loadShader(const std::string &name,
const std::string &vertexPath,
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
Shader* getShaderByID(unsigned int ID);
// Returns a NON-OWNING pointer to a shader by ID
Shader *getShaderByID(unsigned int ID);
void clearResources();
void clearResources();
~ResourceManager();
~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<SoundEffect>> sounds;
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::string> 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;
//std::unordered_map<std::string, std::shared_ptr<TileType>> tiles;
std::unordered_map<std::string, std::unique_ptr<Shader>> shaders;
std::unordered_map<unsigned, Shader *> shaderIDs;
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<Background>> backgrounds;
std::unordered_map<std::string, std::shared_ptr<TileSetData>> tileSets;
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

View file

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

View file

@ -1,91 +1,103 @@
#ifndef _H_XMLLOADER_H
#define _H_XMLLOADER_H
#include <vector>
#include <string>
#include <memory>
#include <unordered_map>
#include <functional>
#include <glm/glm.hpp>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <sstream>
#include <fstream>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <tinyxml2.h>
struct Tile;
// Except for the player, these definitions are mostly overrides of the monster
// data
struct EntityData {
bool isPlayer;
bool animated;
int x = 0, y = 0;
std::string graphic;
std::string weapon = "pistol";
std::string script;
bool isPlayer;
bool animated;
int x = 0, y = 0;
float hp;
std::string monsterDef;
std::string graphic;
std::string weapon = "gun/pistol";
std::string script;
};
struct MapData {
std::string name;
struct TileSet {
int startID = 1;
std::string path;
};
std::vector<TileSet> tileSets;
int width = 0, height = 0;
float tileSize = 32.f;
// 3D array, 0: layer, 1: y, 2: x
// Holding tile startID + ids, 0 is an empty tile
std::vector<std::vector<std::vector<int>>> tiles;
std::string name;
struct TileSet {
int startID = 1;
std::string path;
};
std::vector<TileSet> tileSets;
int width = 0, height = 0;
float tileSize = 32.f;
// 3D array, 0: layer, 1: y, 2: x
// Holding tile startID + ids, 0 is an empty tile
std::vector<std::vector<std::vector<int>>> tiles;
};
struct TileSetData {
std::string name;
std::string type;
std::string file;
int width = 0, height = 0;
int columns = 0;
int tileCount = 0;
float tileSize = 64.f;
struct TileData {
int id = 0;
std::string type;
bool walkable = true;
struct ObjectData {
int id = 0;
std::string name;
glm::vec2 pos;
glm::vec2 size;
bool collidable = false;
bool pickup = false;
};
std::vector<std::shared_ptr<ObjectData>> objects;
};
std::vector<std::shared_ptr<TileData>> tiles;
std::string name;
std::string type;
std::string file;
int width = 0, height = 0;
int columns = 0;
int tileCount = 0;
float tileSize = 64.f;
struct TileData {
int id = 0;
std::string type;
bool walkable = true;
struct ObjectData {
int id = 0;
std::string name;
glm::vec2 pos;
glm::vec2 size;
bool collidable = false;
bool pickup = false;
};
std::vector<std::shared_ptr<ObjectData>> objects;
};
std::vector<std::shared_ptr<TileData>> tiles;
};
struct SceneData {
std::string id;
std::string type;
std::string bgFile;
std::string id;
std::string type;
std::string bgFile;
const MapData* map;
std::vector<EntityData> entities;
const MapData *map;
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 {
std::string id;
float fireSpeed = 250.0f;
int clipSize = 21;
int maxAmmo = 512;
std::string script = "";
std::string graphic;
bool animated = false;
float sizeX = 50.f, sizeY = 50.f;
float offsetX = 0.f, offsetY = 0.f;
float bulletSizeX = 50.f, bulletSizeY = 50.f;
std::string bulletGraphic;
bool bulletAnimated = false;
float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f;
float modMin = 0.5f, modMax = 1.0f;
std::string id;
float fireSpeed = 250.0f;
int clipSize = 21;
int maxAmmo = 512;
std::string script = "";
std::string graphic;
bool animated = false;
float sizeX = 50.f, sizeY = 50.f;
float offsetX = 0.f, offsetY = 0.f;
float bulletSizeX = 50.f, bulletSizeY = 50.f;
std::string bulletGraphic;
bool bulletAnimated = false;
float bulletSpread = 1.0f, bulletSpeed = 3.0f, bulletDrop = 500.f;
float modMin = 0.5f, modMax = 1.0f;
};
// 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
// the animation map
struct AnimationData {
std::string id;
std::string spriteAtlas;
bool directional = false;
bool oneShot;
float FPS = 1.f;
float frameSize;
std::string id;
std::string spriteAtlas;
bool directional = false;
bool oneShot;
float FPS = 1.f;
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 {
std::string id; // <type>/<object>/<state>
std::string path;
bool spatial;
std::string id; // <type>/<object>/<state>
std::string path;
bool spatial;
};
class XMLLoader
{
class XMLLoader {
public:
XMLLoader() {}
bool loadScenes(const char* sceneFolder);
bool loadWeapons(const char* weaponFolder);
bool loadAnimations(const char* animationFolder);
bool loadTileSets(const char* tileSetFolder);
bool loadMaps(const char* mapFolder);
bool loadSoundEffects(const char* soundFolder);
XMLLoader() {}
bool loadScenes(const char *sceneFolder);
bool loadWeapons(const char *weaponFolder);
bool loadAnimations(const char *animationFolder);
bool loadTileSets(const char *tileSetFolder);
bool loadMaps(const char *mapFolder);
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 {
try {
return scenes.at(id).get();
}
catch (std::exception&) {
return nullptr;
}
}
const SceneData *getSceneData(const std::string &id) const {
try {
return scenes.at(id).get();
} catch (std::exception &) {
return nullptr;
}
}
const MapData* getMapData(const std::string& name) const {
try {
return maps.at(name).get();
}
catch (std::exception&) {
return nullptr;
}
}
const ScriptData *getScriptData(const std::string &id) const {
try {
return scripts.at(id).get();
} catch (std::exception &) {
return nullptr;
}
}
const WeaponData* getWeaponData(const std::string& id) const {
try {
return weapons.at(id).get();
}
catch (std::exception&) {
return nullptr;
}
}
const MapData *getMapData(const std::string &name) const {
try {
return maps.at(name).get();
} catch (std::exception &) {
return nullptr;
}
}
const AnimationData* getAnimationData(const std::string& id) const {
try {
return animations.at(id).get();
}
catch (std::exception&) {
return nullptr;
}
}
const WeaponData *getWeaponData(const std::string &id) const {
try {
return weapons.at(id).get();
} catch (std::exception &) {
return nullptr;
}
}
const TileSetData* getTileSetData(const std::string& name) const {
try {
return tileSets.at(name).get();
}
catch (std::exception&) {
return nullptr;
}
}
const AnimationData *getAnimationData(const std::string &id) const {
try {
return animations.at(id).get();
} catch (std::exception &) {
return nullptr;
}
}
const SoundData* getSoundData(const std::string& id) const {
try {
return sounds.at(id).get();
}
catch (std::exception&) {
return nullptr;
}
}
const TileSetData *getTileSetData(const std::string &name) const {
try {
return tileSets.at(name).get();
} catch (std::exception &) {
return nullptr;
}
}
// return a full set of animations, may need further optimization.
// 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,
// 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;
}
const SoundData *getSoundData(const std::string &id) const {
try {
return sounds.at(id).get();
} catch (std::exception &) {
return nullptr;
}
}
// return a full set of animations, may need further optimization.
// 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, 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:
bool loadXmlScene(const char* xmlFile, SceneData* out);
bool loadEntityData(const char* xmlFile, SceneData* out);
bool loadTile(tinyxml2::XMLElement* tileElement, TileSetData::TileData* out);
bool loadObject(tinyxml2::XMLElement* objectElement, TileSetData::TileData::ObjectData* out);
bool loadXmlScene(const char *xmlFile, SceneData *out);
bool loadEntityData(const char *xmlFile, SceneData *out);
bool loadTile(tinyxml2::XMLElement *tileElement, TileSetData::TileData *out);
bool loadObject(tinyxml2::XMLElement *objectElement,
TileSetData::TileData::ObjectData *out);
private:
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<AnimationData>> animations;
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;
std::unordered_map<std::string, std::unique_ptr<SceneData>> scenes;
std::unordered_map<std::string, std::unique_ptr<ScriptData>> scripts;
std::unordered_map<std::string, std::unique_ptr<WeaponData>> weapons;
std::unordered_map<std::string, std::unique_ptr<AnimationData>> animations;
std::unordered_map<std::string, std::unique_ptr<MonsterData>> monsters;
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

View file

@ -101,12 +101,14 @@ void Scene::loadDebugShooterScene() {
} else {
// attach ai
if (!entityData.script.empty()) {
auto behaviour = resourceManager->loadAIScript(entityData.script);
auto behaviour =
resourceManager->loadScript<AIScript>(entityData.script);
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());
ai->attachBehaviourScript(std::move(behaviour));
if (behaviour != nullptr)
ai->attachBehaviourScript(std::move(behaviour));
entity->addComponent(std::make_unique<AIComponent>(ai));
}
}

View file

@ -1,155 +1,129 @@
#include "graphics/texture.h"
#include "utility/logger.h"
#include "util.h"
#include "utility/logger.h"
bool Texture::loadTexture(const char* imagePath)
{
SDL_Surface* buffer = IMG_Load(imagePath);
if (!buffer)
ERROR_LOG("Failed to load image file: {}", imagePath);
//UTIL::flip_surface(buffer);
bool Texture::loadTexture(const char *imagePath) {
SDL_Surface *buffer = IMG_Load(imagePath);
if (!buffer)
ERROR_LOG("Failed to load image file: {}", imagePath);
// UTIL::flip_surface(buffer);
glGenTextures(1, &ID);
/*
GLenum error = glGetError();
if(error != GL_NO_ERROR) {
std::cout << "OpenGL error: " << error << std::endl;
}
*/
glGenTextures(1, &ID);
/*
GLenum error = glGetError();
if(error != GL_NO_ERROR) {
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_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenerateMipmap(ID);
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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glGenerateMipmap(ID);
textureWidth = buffer->w;
textureHeight = buffer->h;
textureWidth = buffer->w;
textureHeight = buffer->h;
SDL_FreeSurface(buffer);
SDL_FreeSurface(buffer);
return true;
return true;
}
void Texture::bind()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, ID);
void Texture::bind() {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, ID);
}
Texture::~Texture() { glDeleteTextures(1, &ID); }
Texture::~Texture()
{
glDeleteTextures(1, &ID);
bool TextureArray::loadTextures(std::vector<const char *> imagePaths) {
std::vector<SDL_Surface *> surfaces;
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)
{
std::vector<SDL_Surface*> surfaces;
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() {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
}
void TextureArray::bind()
{
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, ID);
bool TextureArray::adjustCanvasSizes(std::vector<SDL_Surface *> &surfaces) {
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;
}
bool TextureArray::adjustCanvasSizes(std::vector<SDL_Surface*>& surfaces)
{
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);
}
TextureArray::~TextureArray() { glDeleteTextures(1, &ID); }

View file

@ -1,149 +1,145 @@
#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 "util.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)
{
auto iterator = sprites.find(path);
if (iterator != sprites.end())
return static_cast<SpriteAtlas*>(iterator->second.get());
auto sprite = std::make_unique<SpriteAtlas>(path.c_str(), frameSize, isDirectional);
sprites[path] = std::move(sprite);
SpriteAtlas& l = static_cast<SpriteAtlas&>(*sprites[path].get());
return static_cast<SpriteAtlas*>(sprites[path].get());
SpriteAtlas *ResourceManager::loadSpriteAtlas(const std::string &path,
float frameSize,
bool isDirectional) {
auto iterator = sprites.find(path);
if (iterator != sprites.end())
return static_cast<SpriteAtlas *>(iterator->second.get());
auto sprite =
std::make_unique<SpriteAtlas>(path.c_str(), frameSize, isDirectional);
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)
{
auto iterator = sprites.find(path);
if (iterator != sprites.end())
return iterator->second.get();
auto sprite = std::make_unique<SpriteStatic>(path.c_str());
if (!sprite->loaded())
return nullptr;
sprites[path] = std::move(sprite);
return sprites[path].get();
Sprite *ResourceManager::loadSpriteStatic(const std::string &path) {
auto iterator = sprites.find(path);
if (iterator != sprites.end())
return iterator->second.get();
auto sprite = std::make_unique<SpriteStatic>(path.c_str());
if (!sprite->loaded())
return nullptr;
sprites[path] = std::move(sprite);
return sprites[path].get();
}
std::unique_ptr<AIScript> ResourceManager::loadAIScript(const std::string& path)
{
return std::make_unique<AIScript>(path.c_str());
const TileSetData *ResourceManager::loadTileSet(const std::string &name) {
return xmlLoader->getTileSetData(name);
}
std::unique_ptr<WeaponScript> ResourceManager::loadWeaponScript(const std::string& path)
{
return std::make_unique<WeaponScript>(path.c_str());
const unsigned ResourceManager::loadShader(const std::string &name,
const std::string &vertexPath,
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)
{
return xmlLoader->getTileSetData(name);
Shader *ResourceManager::getShaderByID(unsigned int ID) {
auto iterator = shaderIDs.find(ID);
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)
{
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;
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();
}
Shader* ResourceManager::getShaderByID(unsigned int ID)
{
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
// 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.
std::unique_ptr<Weapon> ResourceManager::loadWeapon(const std::string& id, const unsigned weaponShaderID, const unsigned bulletShaderID)
{
const WeaponData* data = xmlLoader->getWeaponData(id);
if (!data) {
LOG(ERROR, "Could not load weapon id '{}', falling back to pistol", id);
data = xmlLoader->getWeaponData("gun/pistol");// using this as a fallback for now
}
auto weapon = std::make_unique<Weapon>(data, weaponShaderID, bulletShaderID, this);
if (!data->script.empty())
weapon->attachScript(loadWeaponScript(data->script));
return std::move(weapon);
std::unique_ptr<Weapon>
ResourceManager::loadWeapon(const std::string &id,
const unsigned weaponShaderID,
const unsigned bulletShaderID) {
const WeaponData *data = xmlLoader->getWeaponData(id);
if (!data) {
LOG(ERROR, "Could not load weapon id '{}', falling back to pistol", id);
data = xmlLoader->getWeaponData(
"gun/pistol"); // using this as a fallback for now
}
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)
{
auto iterator = sounds.find(id);
if (iterator != sounds.end())
return iterator->second->getBuffer();
auto soundData = xmlLoader->getSoundData(id) ;
if (!soundData) {
soundData = xmlLoader->getSoundData(UTIL::get_generic(id));
LOG(WARN, "Could not load sound id: '{}' trying generic", id);
if (!soundData)
return 0;
}
auto sound = std::make_unique<SoundEffect>(soundData->path);
sounds[id] = std::move(sound);
return sounds[id]->getBuffer();
const unsigned ResourceManager::loadSoundEffect(const std::string &id) {
auto iterator = sounds.find(id);
if (iterator != sounds.end())
return iterator->second->getBuffer();
auto soundData = xmlLoader->getSoundData(id);
if (!soundData) {
soundData = xmlLoader->getSoundData(UTIL::get_generic(id));
LOG(WARN, "Could not load sound id: '{}' trying generic", id);
if (!soundData)
return 0;
}
auto sound = std::make_unique<SoundEffect>(soundData->path);
sounds[id] = std::move(sound);
return sounds[id]->getBuffer();
}
const SceneData* ResourceManager::loadScene(const std::string& id)
{
return xmlLoader->getSceneData(id);
const SceneData *ResourceManager::loadScene(const std::string &id) {
return xmlLoader->getSceneData(id);
}
std::shared_ptr<AnimationSet> ResourceManager::loadAnimationSet(const std::string& prefix, int entityid)
{
auto animSetData = xmlLoader->getAnimationSet(prefix);
std::shared_ptr<AnimationSet>
ResourceManager::loadAnimationSet(const std::string &prefix, int entityid) {
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
// TODO: Allow different weapon types!
/*
template <typename T>
std::shared_ptr<T> ResourceManager::loadWeapon(const std::string& name, std::shared_ptr<Shader> shader, std::shared_ptr<SpriteComponent> spriteComponent)
{
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();
void ResourceManager::clearResources() {
sprites.clear();
shaders.clear();
shaderIDs.clear();
tileSets.clear();
}
ResourceManager::~ResourceManager()
{
clearResources();
xmlLoader.reset();
ResourceManager::~ResourceManager() {
clearResources();
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