From 0620990611871d179fef78c417a18c112c6b27d5 Mon Sep 17 00:00:00 2001 From: Ethan Adams Date: Thu, 22 Aug 2024 20:13:38 -0400 Subject: [PATCH] Added Tiled mapping support --- Resources/maps/debugmap.tmx | 70 ++++ Resources/maps/tilesets/shooterWorldOne.tsx | 366 ++++++++++++++++++ .../tilesets}/shooterWorldOneAtlas64.png | Bin Resources/maps/yupple.tiled-project | 14 + Resources/maps/yupple.tiled-session | 54 +++ Resources/scenes/debugScene.xml | 33 +- Resources/shaders/GL_tile.frag | 9 +- Resources/shaders/GL_tile.vert | 28 +- YuppleMayham/CMakeLists.txt | 6 +- YuppleMayham/include/gameplay/map.h | 16 +- .../include/gameplay/weapons/weapon.h | 2 +- YuppleMayham/include/graphics/instancedraw.h | 3 +- YuppleMayham/include/graphics/tile.h | 37 -- YuppleMayham/include/graphics/tiletype.h | 78 ---- .../include/utility/resourcemanager.h | 10 +- YuppleMayham/include/utility/xmlloader.h | 80 +++- YuppleMayham/src/gameplay/map.cpp | 82 ++-- YuppleMayham/src/gameplay/scene.cpp | 15 +- YuppleMayham/src/gameplay/weapons/weapon.cpp | 2 +- YuppleMayham/src/graphics/instancedraw.cpp | 4 +- YuppleMayham/src/graphics/texture.cpp | 2 +- YuppleMayham/src/graphics/tile.cpp | 80 ---- YuppleMayham/src/utility/resourcemanager.cpp | 14 +- YuppleMayham/src/utility/xmlloader.cpp | 362 +++++++++++++---- build0.0.0.1/debugScene.xml | 53 +++ 25 files changed, 1023 insertions(+), 397 deletions(-) create mode 100644 Resources/maps/debugmap.tmx create mode 100644 Resources/maps/tilesets/shooterWorldOne.tsx rename Resources/{sprites => maps/tilesets}/shooterWorldOneAtlas64.png (100%) create mode 100644 Resources/maps/yupple.tiled-project create mode 100644 Resources/maps/yupple.tiled-session delete mode 100644 YuppleMayham/include/graphics/tile.h delete mode 100644 YuppleMayham/include/graphics/tiletype.h delete mode 100644 YuppleMayham/src/graphics/tile.cpp create mode 100644 build0.0.0.1/debugScene.xml diff --git a/Resources/maps/debugmap.tmx b/Resources/maps/debugmap.tmx new file mode 100644 index 0000000..ea7b84c --- /dev/null +++ b/Resources/maps/debugmap.tmx @@ -0,0 +1,70 @@ + + + + + +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,28,28,29,25,28,28,29,27,31,28,28,31,28,32,26,29,30,27,25,27,32,28,31,26,29,25,29,32,0, +0,32,27,22,16,16,23,31,26,30,31,31,29,28,30,25,28,26,26,25,29,26,25,32,28,28,31,32,25,0, +0,30,24,11,4,8,14,26,28,31,26,24,16,16,16,16,23,27,25,25,30,26,29,29,30,29,25,29,27,0, +0,25,15,6,1,2,14,28,32,30,24,11,8,7,3,8,12,23,28,27,25,32,30,32,30,28,27,27,26,0, +0,28,20,13,13,13,19,32,28,29,15,5,2,5,3,5,2,14,25,32,31,32,24,16,16,16,23,29,27,0, +0,31,29,31,27,26,27,30,29,27,15,8,7,5,2,3,6,14,27,32,32,29,15,8,4,2,14,26,27,0, +0,30,30,30,25,32,29,31,30,29,20,10,1,3,5,3,9,19,27,25,29,31,20,13,13,13,19,31,26,0, +0,28,30,30,28,24,23,27,31,28,29,20,13,13,13,13,19,27,31,28,27,26,29,26,32,25,31,25,32,0, +0,26,26,31,29,20,19,30,25,29,25,25,25,28,30,27,28,25,31,30,27,26,26,28,25,29,25,32,31,0, +0,30,32,29,29,32,31,27,25,31,31,31,26,30,31,27,29,25,29,32,0,0,0,0,30,26,31,0,0,0, +0,30,27,31,31,28,25,25,28,26,30,27,26,28,30,29,30,25,30,32,0,0,0,0,0,0,0,0,0,0, +0,27,29,22,21,30,29,29,32,0,0,0,0,32,30,30,31,32,26,28,0,0,0,0,0,0,0,0,0,0, +0,27,25,18,17,30,28,30,0,0,0,0,0,26,31,26,25,30,27,29,26,0,0,0,0,0,0,0,0,0, +0,32,30,31,30,30,28,0,0,0,0,0,0,32,27,31,30,28,26,31,26,0,0,0,0,0,0,0,0,0, +0,32,31,27,31,31,25,28,25,27,27,32,27,30,32,26,31,28,25,30,31,31,0,0,0,0,0,0,0,0, +0,32,26,26,26,31,29,27,26,27,30,26,29,29,28,25,32,28,29,28,32,25,0,0,0,0,0,0,0,0, +0,25,26,28,30,32,28,25,27,29,30,30,28,28,28,26,25,30,26,27,29,27,0,0,0,0,0,0,0,0, +0,29,26,31,29,30,26,25,32,29,31,28,26,32,32,27,28,26,27,28,29,30,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + + + + + + + + + + + + + + + + + + + + +35,36,34,35,33,38,38,34,35,34,34,39,38,39,35,39,38,37,39,39,33,33,38,34,34,34,33,37,34,40, +37,41,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,42,34, +38,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,33, +36,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,39, +37,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,36, +35,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,34, +37,46,0,28,28,30,25,27,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,39, +34,46,0,30,30,29,25,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,34, +40,46,0,29,26,31,29,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,35, +33,46,0,30,31,32,27,27,0,0,0,0,0,0,0,0,0,0,0,56,48,48,48,48,55,0,56,48,43,33, +35,46,0,25,30,32,31,28,0,0,0,0,0,0,0,0,0,0,0,47,34,36,34,39,44,48,43,34,36,40, +40,46,0,0,0,0,0,0,56,48,48,48,48,55,0,0,0,0,0,47,36,36,34,39,38,36,36,38,36,37, +35,46,0,0,0,0,0,56,43,36,38,38,39,46,0,0,0,0,0,47,40,39,38,36,38,40,40,34,38,37, +38,46,0,0,0,0,56,43,37,39,36,36,33,46,0,0,0,0,0,0,42,39,35,36,33,34,33,38,40,39, +34,46,0,0,0,0,47,35,34,40,39,38,36,46,0,0,0,0,0,0,47,38,36,34,34,33,36,40,35,40, +38,46,0,0,0,0,52,45,45,45,45,45,45,51,0,0,0,0,0,0,52,42,37,34,40,37,33,38,37,37, +37,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,35,33,33,39,35,40,34,37, +39,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,47,34,37,36,34,34,39,40,33, +38,44,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,48,43,39,35,34,35,38,38,37,39, +38,38,40,37,38,35,36,37,36,39,36,39,36,36,34,39,40,36,40,37,37,33,40,37,38,38,33,38,39,38 + + + diff --git a/Resources/maps/tilesets/shooterWorldOne.tsx b/Resources/maps/tilesets/shooterWorldOne.tsx new file mode 100644 index 0000000..6b26e74 --- /dev/null +++ b/Resources/maps/tilesets/shooterWorldOne.tsx @@ -0,0 +1,366 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Resources/sprites/shooterWorldOneAtlas64.png b/Resources/maps/tilesets/shooterWorldOneAtlas64.png similarity index 100% rename from Resources/sprites/shooterWorldOneAtlas64.png rename to Resources/maps/tilesets/shooterWorldOneAtlas64.png diff --git a/Resources/maps/yupple.tiled-project b/Resources/maps/yupple.tiled-project new file mode 100644 index 0000000..911167b --- /dev/null +++ b/Resources/maps/yupple.tiled-project @@ -0,0 +1,14 @@ +{ + "automappingRulesFile": "", + "commands": [ + ], + "compatibilityVersion": 1100, + "extensionsPath": "extensions", + "folders": [ + "../" + ], + "properties": [ + ], + "propertyTypes": [ + ] +} diff --git a/Resources/maps/yupple.tiled-session b/Resources/maps/yupple.tiled-session new file mode 100644 index 0000000..c555e9b --- /dev/null +++ b/Resources/maps/yupple.tiled-session @@ -0,0 +1,54 @@ +{ + "activeFile": "", + "expandedProjectPaths": [ + ], + "fileStates": { + "": { + "scaleInDock": 1 + }, + ":/automap-tiles.tsx": { + "scaleInDock": 1 + }, + "C:/Users/Ethan/Documents/yupplemayham/debugmap.tmx": { + "scale": 0.5472395833333333, + "selectedLayer": 0, + "viewCenter": { + "x": 959.3604263824117, + "y": 639.5736175882744 + } + }, + "C:/Users/Ethan/Documents/yupplemayham/shooterWorldOneAtlas.tsx": { + "scaleInDock": 0.5, + "scaleInEditor": 1 + }, + "debugmap.tmx": { + "scale": 0.45125, + "selectedLayer": 0, + "viewCenter": { + "x": 959.5567867036011, + "y": 955.1246537396123 + } + }, + "shooterWorldOneAtlas.tsx": { + "scaleInDock": 0.5, + "scaleInEditor": 1 + }, + "tilesets/shooterWorldOneAtlas.tsx": { + "scaleInDock": 0.5, + "scaleInEditor": 1 + } + }, + "last.externalTilesetPath": "C:/Users/Ethan/source/repos/YuppleMayham/Resources/maps/tilesets", + "last.imagePath": "C:/Users/Ethan/source/repos/YuppleMayham/Resources/maps/tilesets", + "openFiles": [ + ], + "project": "yupple.tiled-project", + "property.type": "bool", + "recentFiles": [ + "tilesets/shooterWorldOneAtlas.tsx", + "shooterWorldOneAtlas.tsx", + "C:/Users/Ethan/Documents/yupplemayham/shooterWorldOneAtlas.tsx", + "C:/Users/Ethan/Documents/yupplemayham/debugmap.tmx" + ], + "tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)" +} diff --git a/Resources/scenes/debugScene.xml b/Resources/scenes/debugScene.xml index 11d39e8..ec2d95f 100644 --- a/Resources/scenes/debugScene.xml +++ b/Resources/scenes/debugScene.xml @@ -1,38 +1,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Resources/shaders/GL_tile.frag b/Resources/shaders/GL_tile.frag index 2c9de80..f53e7bd 100644 --- a/Resources/shaders/GL_tile.frag +++ b/Resources/shaders/GL_tile.frag @@ -6,6 +6,13 @@ uniform sampler2D tileTexture; void main() { - FragColor = texture(tileTexture, texCoord); + if (texCoord.x == 0.0 && texCoord.y == 0.0) + { + FragColor = vec4(0.0, 0.0, 0.0, 0.0); + } + else + { + FragColor = texture(tileTexture, texCoord); + } //FragColor = vec4(mod(tileindex / float(tilesperrow), 1.0), mod(tileindex / float(tilesperrow), 1.0), 0.0, 1.0); } \ No newline at end of file diff --git a/Resources/shaders/GL_tile.vert b/Resources/shaders/GL_tile.vert index 1160865..7e1ddbd 100644 --- a/Resources/shaders/GL_tile.vert +++ b/Resources/shaders/GL_tile.vert @@ -1,7 +1,7 @@ #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; -layout (location = 2) in int tileIndex; +layout (location = 2) in int aTileIndex; layout (location = 3) in mat4 aModel; uniform int tilesPerRow; @@ -9,19 +9,29 @@ uniform mat4 proj; uniform mat4 view; out vec2 texCoord; +out int tileIndex; void main() { - float tileSize = 1.0 / float(tilesPerRow); - + tileIndex = aTileIndex; gl_Position = proj * view * aModel * vec4(aPos, 1.0); + if (tileIndex != 0) + { + int index = tileIndex - 1; + float tileSize = 1.0 / float(tilesPerRow); - int row = tileIndex / tilesPerRow; - int col = tileIndex % tilesPerRow; + int row = index / tilesPerRow; + int col = index % tilesPerRow; - float offsetX = float(col) * tileSize; - float offsetY = float(row) * tileSize; + float offsetX = float(col) * tileSize; + float offsetY = float(row) * tileSize; - texCoord.x = (aTexCoord.x + col) * tileSize; - texCoord.y = (aTexCoord.y + row) * tileSize; + texCoord.x = (aTexCoord.x + col) * tileSize; + texCoord.y = (aTexCoord.y + row) * tileSize; + } + else + { + texCoord.x = 0.0; + texCoord.y = 0.0; + } } \ No newline at end of file diff --git a/YuppleMayham/CMakeLists.txt b/YuppleMayham/CMakeLists.txt index 77a6d5e..cf36d30 100644 --- a/YuppleMayham/CMakeLists.txt +++ b/YuppleMayham/CMakeLists.txt @@ -35,7 +35,7 @@ add_executable (YuppleMayham "src/gameplay/weapons/bulletmanager.cpp" "src/gameplay/scene.cpp" "src/graphics/texture.cpp" - "src/graphics/tile.cpp" + "src/utility/resourcemanager.cpp" "src/utility/xmlloader.cpp" "src/gameplay/game.cpp" @@ -52,9 +52,9 @@ add_executable (YuppleMayham "include/utility/mousestate.h" "include/gameplay/weapons/bulletmanager.h" "include/gameplay/scene.h" - "include/graphics/tile.h" + "include/graphics/texture.h" - "include/graphics/tiletype.h" + "include/utility/resourcemanager.h" "include/utility/xmlloader.h" "include/gameplay/game.h" diff --git a/YuppleMayham/include/gameplay/map.h b/YuppleMayham/include/gameplay/map.h index 25e28f9..f9f498e 100644 --- a/YuppleMayham/include/gameplay/map.h +++ b/YuppleMayham/include/gameplay/map.h @@ -6,17 +6,16 @@ #include "graphics/instancedraw.h" -//class TileTextureInstance; class Shader; class Camera; +class ResourceManager; struct MapData; -//struct InstanceData; -struct Tile; +struct TileSetData; class Map { public: - Map(const std::shared_ptr& mapData, const std::shared_ptr& shader); + Map(std::shared_ptr mapData, const std::shared_ptr& shader, std::shared_ptr resourceManager); const std::vector> getCollisionMap() const { return collisionMap; } @@ -26,13 +25,14 @@ private: void loadMap(); void createCollisionMap(); - const std::shared_ptr mapData; + std::shared_ptr mapData; + std::shared_ptr tileSetData; - std::shared_ptr instanceHandle; + std::vector> instanceHandles; std::shared_ptr shader; - std::vector>> tileIds; + std::vector>> tileIds; std::vector> collisionMap; - std::vector tileData; + std::vector> tileData; }; #endif \ No newline at end of file diff --git a/YuppleMayham/include/gameplay/weapons/weapon.h b/YuppleMayham/include/gameplay/weapons/weapon.h index fb7fd64..9ac11e6 100644 --- a/YuppleMayham/include/gameplay/weapons/weapon.h +++ b/YuppleMayham/include/gameplay/weapons/weapon.h @@ -27,7 +27,7 @@ struct WeaponData; class Weapon : public Entity, public std::enable_shared_from_this { public: - Weapon(const WeaponData* data, const std::shared_ptr& weaponShader, const std::shared_ptr& bulletShader, ResourceManager* resourceManager); + Weapon(std::shared_ptr data, const std::shared_ptr& weaponShader, const std::shared_ptr& bulletShader, ResourceManager* resourceManager); void setWielder(const std::shared_ptr& wielder) { this->wielder = wielder; } void toggleInfiniteAmmo() { infiniteAmmo = !infiniteAmmo; } diff --git a/YuppleMayham/include/graphics/instancedraw.h b/YuppleMayham/include/graphics/instancedraw.h index 6c4ba99..8002572 100644 --- a/YuppleMayham/include/graphics/instancedraw.h +++ b/YuppleMayham/include/graphics/instancedraw.h @@ -10,7 +10,7 @@ class Texture; struct InstanceData { glm::mat4 modelMatrix; - int tileIndex; + int tileIndex = 0; }; class BaseInstanceDraw @@ -26,6 +26,7 @@ protected: Texture* texture = nullptr; InstanceData instanceData[MAX_INSTANCES]; size_t numOfInstances = 0; + size_t numOfLayers = 0; unsigned indices[6] = { 0, 1, 2, 3, 2, 0 diff --git a/YuppleMayham/include/graphics/tile.h b/YuppleMayham/include/graphics/tile.h deleted file mode 100644 index 078506a..0000000 --- a/YuppleMayham/include/graphics/tile.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef _H_TILE_H -#define _H_TILE_H - -#include -#include -#include -#include - -enum class TileType; -class Texture; - -// TODO: Decide how this struct will be used. -// This holds only the texture data of the tile, this texture data will be held in -// a further struct called Tile -struct TileTexture { - Texture* texture = nullptr; - unsigned VAO, VBO, EBO; -}; - -// TODO: Finished replacing the Sprite version of tile set with this one -// This class is NOT for game logic, only for the storage of the tile pointers that point to the TileTexture -class TileSet -{ -public: - TileSet(const char* tileSetImage, float frameSize); - void setupTiles(float frameSize); - ~TileSet(); - - const std::shared_ptr& getTileTexture(TileType tileType) const; - -private: - Texture* texture; - - std::unordered_map> tiles; -}; - -#endif \ No newline at end of file diff --git a/YuppleMayham/include/graphics/tiletype.h b/YuppleMayham/include/graphics/tiletype.h deleted file mode 100644 index 14620f6..0000000 --- a/YuppleMayham/include/graphics/tiletype.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef _H_TILETYPE_H -#define _H_TILETYPE_H - -struct Tile { - enum class SpriteID { - TILE_SOLID_GRASS_1, - TILE_SOLID_GRASS_2, - TILE_SOLID_GRASS_3, - TILE_SOLID_GRASS_4, - TILE_SOLID_GRASS_5, - TILE_SOLID_GRASS_6, - TILE_SOLID_GRASS_7, - TILE_SOLID_GRASS_8, - TILE_LSHAPE_GRASS_TL, - TILE_LSHAPE_GRASS_TR, - TILE_LSHAPE_GRASS_BR, - TILE_LSHAPE_GRASS_BL, - TILE_LINE_GRASS_T, - TILE_LINE_GRASS_L, - TILE_LINE_GRASS_R, - TILE_LINE_GRASS_B, - TILE_VSEG_GRASS_TL, - TILE_VSEG_GRASS_TR, - TILE_HSEG_GRASS_TL, - TILE_HSEG_GRASS_TR, - TILE_VSEG_GRASS_BL, - TILE_VSEG_GRASS_BR, - TILE_HSEG_GRASS_BL, - TILE_HSEG_GRASS_BR, - TILE_SOLID_DIRT_1, - TILE_SOLID_DIRT_2, - TILE_SOLID_DIRT_3, - TILE_SOLID_DIRT_4, - TILE_SOLID_DIRT_5, - TILE_SOLID_DIRT_6, - TILE_SOLID_DIRT_7, - TILE_SOLID_DIRT_8, - TILE_SOLID_WATER_1, - TILE_SOLID_WATER_2, - TILE_SOLID_WATER_3, - TILE_SOLID_WATER_4, - TILE_SOLID_WATER_5, - TILE_SOLID_WATER_6, - TILE_SOLID_WATER_7, - TILE_SOLID_WATER_8, - TILE_LSHAPE_WATER_TL, - TILE_LSHAPE_WATER_TR, - TILE_LSHAPE_WATER_BR, - TILE_LSHAPE_WATER_BL, - TILE_LINE_WATER_T, - TILE_LINE_WATER_L, - TILE_LINE_WATER_R, - TILE_LINE_WATER_B, - TILE_VSEG_WATER_TL, - TILE_VSEG_WATER_TR, - TILE_HSEG_WATER_TL, - TILE_HSEG_WATER_TR, - TILE_VSEG_WATER_BL, - TILE_VSEG_WATER_BR, - TILE_HSEG_WATER_BL, - TILE_HSEG_WATER_BR, - TILE_MISC_FLOWERS_1, - TILE_MISC_FLOWERS_2, - TILE_MISC_DUCK, - TILE_MISC_PEBBLE_1, - TILE_MISC_ROCK, - TILE_MISC_SHRUB, - TILE_MISC_PEBBLE_2, - TILE_MISC_PEBBLE_3 - }spriteID; - enum class TileData { - TILE_WALKABLE = 1, - TILE_WATER = 2, - TILE_UNWALKABLE = 64 - }tileData; -}; - -#endif // _H_TILETYPE_H \ No newline at end of file diff --git a/YuppleMayham/include/utility/resourcemanager.h b/YuppleMayham/include/utility/resourcemanager.h index 9d86652..ce20586 100644 --- a/YuppleMayham/include/utility/resourcemanager.h +++ b/YuppleMayham/include/utility/resourcemanager.h @@ -16,7 +16,7 @@ class Script; class AnimationSet; class AIScript; class WeaponScript; -class TileSet; +class TileSetData; class SpriteComponent; class ResourceManager @@ -26,20 +26,22 @@ public: xmlLoader(std::make_shared()) { xmlLoader->loadWeapons("weapons"); - xmlLoader->loadScenes("scenes"); xmlLoader->loadAnimations("animations"); + xmlLoader->loadTileSets("maps/tilesets"); + xmlLoader->loadMaps("maps"); + xmlLoader->loadScenes("scenes"); }; std::shared_ptr loadSpriteAtlas (const std::string& path, float frameSize, bool isDirectional = false); std::shared_ptr loadSpriteStatic (const std::string& path); std::shared_ptr loadAIScript (const std::string& path); std::shared_ptr loadWeaponScript (const std::string& path); - std::shared_ptr loadTileSet (const std::string& path, float frameSize); std::shared_ptr loadShader (const std::string& name, const std::string& vertexPath, const std::string& fragPath); std::shared_ptr loadWeapon (const std::string& name, std::shared_ptr weaponShader, std::shared_ptr bulletShader); std::shared_ptr loadScene (const std::string& id); std::shared_ptr loadAnimationSet(const std::string& name, int entityid = 0); + std::shared_ptr loadTileSet (const std::string& name); void clearResources(); @@ -48,7 +50,7 @@ private: std::unordered_map> shaders; std::unordered_map> weapons; std::unordered_map> scripts; - std::unordered_map> tileSets; + std::unordered_map>tileSets; //std::unordered_map> entityData; //std::unordered_map> scenes; //std::unordered_map> maps; diff --git a/YuppleMayham/include/utility/xmlloader.h b/YuppleMayham/include/utility/xmlloader.h index 7a6fbbc..4a7ff62 100644 --- a/YuppleMayham/include/utility/xmlloader.h +++ b/YuppleMayham/include/utility/xmlloader.h @@ -5,6 +5,12 @@ #include #include #include +#include + +#include +#include +#include +#include struct Tile; @@ -19,17 +25,44 @@ struct EntityData { struct MapData { std::string name; - std::string file; + std::string tileSet; int width = 0, height = 0; float tileSize = 32.f; - std::vector>> groundTiles; + // 3D array, 0: layer, 1: y, 2: x + // Holding tile ids + 1, 0 is an empty tile + std::vector>> 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> objects; + }; + std::vector> tiles; }; struct SceneData { std::string id; std::string type; - MapData map; + std::shared_ptr map; std::vector entities; }; @@ -74,6 +107,8 @@ public: 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); const std::shared_ptr getSceneData(const std::string& id) const { try { @@ -84,6 +119,24 @@ public: } } + const std::shared_ptr getMapData(const std::string& name) const { + try { + return maps.at(name); + } + catch (std::exception&) { + return nullptr; + } + } + + const std::shared_ptr getWeaponData(const std::string& name) const { + try { + return weapons.at(name); + } + catch (std::exception&) { + return nullptr; + } + } + const std::shared_ptr getAnimationData(const std::string& name) const { try { return animations.at(name); @@ -93,6 +146,15 @@ public: } } + const std::shared_ptr getTileSetData(const std::string& name) const { + try { + return tileSets.at(name); + } + 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. @@ -106,18 +168,18 @@ public: return animSet; } - const WeaponData* getWeaponDataByName(const char* name) const; - - void clearData() { scenes.clear(); weaponData.clear(); animations.clear(); } + void clearData() { scenes.clear(); weapons.clear(); animations.clear(); maps.clear(); tileSets.clear(); } protected: bool loadXmlScene(const char* xmlFile, SceneData* out); - bool loadMap(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> scenes; + std::unordered_map> weapons; std::unordered_map> animations; - - std::vector weaponData; + std::unordered_map> maps; + std::unordered_map> tileSets; }; #endif // _H_XMLLOADER_H \ No newline at end of file diff --git a/YuppleMayham/src/gameplay/map.cpp b/YuppleMayham/src/gameplay/map.cpp index 461b7dc..6dec0a3 100644 --- a/YuppleMayham/src/gameplay/map.cpp +++ b/YuppleMayham/src/gameplay/map.cpp @@ -1,52 +1,75 @@ #include "gameplay/map.h" -#include "utility/xmlloader.h" -#include "graphics/tiletype.h" -#include "graphics/shader.h" #include "gameplay/camera.h" +#include "graphics/shader.h" +#include "utility/xmlloader.h" +#include "utility/resourcemanager.h" #include -Map::Map(const std::shared_ptr& mapData, const std::shared_ptr& shader) : - shader(shader), - mapData(mapData) +Map::Map(std::shared_ptr mapData, const std::shared_ptr& shader, std::shared_ptr resourceManager) : + shader(shader), + mapData(mapData), + tileIds(mapData->tiles) { - instanceHandle = std::make_shared(this->mapData->file.c_str()); - tileIds = mapData->groundTiles; - loadMap(); - createCollisionMap(); + tileSetData = resourceManager->loadTileSet(mapData->tileSet); + if (tileSetData) + { + for (int layer = 0; layer < tileIds.size(); layer++) + instanceHandles.push_back(std::make_shared(tileSetData->file.c_str())); + loadMap(); + createCollisionMap(); + } + } void Map::loadMap() { - for (int y = 0; y < tileIds.size(); y++) + tileData.resize(tileIds.size()); + for (int layer = 0; layer < tileIds.size(); layer++) { - for (int x = 0; x < tileIds[y].size(); x++) + for (int y = 0; y < tileIds[layer].size(); y++) { - glm::mat4 modelMatrix = - glm::translate(glm::mat4(1.f), glm::vec3(x * mapData->tileSize, y * mapData->tileSize, 0.0f)) * - glm::scale(glm::mat4(1.f), glm::vec3(mapData->tileSize, mapData->tileSize, 1.0f)); - int tileIndex = static_cast(tileIds[y][x]->spriteID); - tileData.push_back({ modelMatrix, tileIndex }); + for (int x = 0; x < tileIds[layer][y].size(); x++) + { + glm::mat4 modelMatrix = + glm::translate(glm::mat4(1.f), glm::vec3(x * mapData->tileSize, y * mapData->tileSize, 0.0f)) * + glm::scale(glm::mat4(1.f), glm::vec3(mapData->tileSize, mapData->tileSize, 1.0f)); + int tileIndex = static_cast(tileIds[layer][y][x]); + tileData[layer].push_back({modelMatrix, tileIndex}); + } } } shader->use(); // TODO: Figure someway to put these in with my xml data - shader->setInt("tilesPerRow", 8); + shader->setInt("tilesPerRow", tileSetData->columns); } void Map::createCollisionMap() { - for (int y = 0; y < mapData->groundTiles.size(); y++) + // Match collisionMap to map size + collisionMap.resize(tileIds[0].size()); + for (int y = 0; y < tileIds[0].size(); ++y) { - std::vector row; - for (int x = 0; x < mapData->groundTiles[y].size(); x++) + collisionMap[y].resize(tileIds[0][y].size(), 0); + } + + for (int layer = 0; layer < tileIds.size(); layer++) + { + for (int y = 0; y < tileIds[layer].size(); y++) { - if ((int)mapData->groundTiles[y][x]->tileData & (int)Tile::TileData::TILE_UNWALKABLE) - row.push_back(1); - else - row.push_back(0); + for (int x = 0; x < tileIds[layer][y].size(); x++) + { + int id = tileIds[layer][y][x]; + if (id == 0) + collisionMap[y][x] = 0; + else + { + auto& tile = tileSetData->tiles[id - 1]; + if (!tile->walkable) + collisionMap[y][x] = 1; + } + } } - collisionMap.push_back(row); } } @@ -55,6 +78,9 @@ void Map::render(const std::shared_ptr& camera) shader->use(); shader->setMatrix4f("proj", glm::value_ptr(camera->getProjectionMatrix())); shader->setMatrix4f("view", glm::value_ptr(camera->getViewMatrix())); - instanceHandle->updateInstanceData(tileData); - instanceHandle->draw(); + for (int layer = 0; layer < instanceHandles.size(); layer++) + { + instanceHandles[layer]->updateInstanceData(tileData[layer]); + instanceHandles[layer]->draw(); + } } \ No newline at end of file diff --git a/YuppleMayham/src/gameplay/scene.cpp b/YuppleMayham/src/gameplay/scene.cpp index 4eb674e..244bbed 100644 --- a/YuppleMayham/src/gameplay/scene.cpp +++ b/YuppleMayham/src/gameplay/scene.cpp @@ -6,7 +6,6 @@ #include "gameplay/physics.h" #include "gameplay/ai.h" -#include "graphics/tile.h" #include "graphics/sprite.h" #include "graphics/animation.h" @@ -46,14 +45,14 @@ void Scene::loadDebugShooterScene() if (!sceneData) return; EntityData playerData = sceneData->entities[0]; - MapData mapData = sceneData->map; + auto mapData = sceneData->map; auto playerShader = resourceManager->loadShader("GL_player", "shaders/GL_player.vert", "shaders/GL_player.frag"); auto bubbleShader = resourceManager->loadShader("GL_bubble", "shaders/GL_bubble.vert", "shaders/GL_bubble.frag"); auto weaponShader = resourceManager->loadShader("GL_pistol", "shaders/GL_pistol.vert", "shaders/GL_pistol.frag"); // creating map from scene auto tileShader = resourceManager->loadShader("GL_tile", "shaders/GL_tile.vert", "shaders/GL_tile.frag"); - map = std::make_shared(std::make_shared(mapData), tileShader); + map = std::make_shared(mapData, tileShader, resourceManager); for (EntityData entityData : sceneData->entities) { @@ -80,15 +79,15 @@ void Scene::loadDebugShooterScene() entity->pickupWeapon(defaultWeapon); entity->pickupWeapon(entityWeapon); entity->hookEventManager(eventManager); - entity->setPosition(glm::vec3(entityData.x * mapData.tileSize, entityData.y * mapData.tileSize, 0.f)); - entity->setScale(glm::vec3(mapData.tileSize, mapData.tileSize, 1.f)); + entity->setPosition(glm::vec3(entityData.x * mapData->tileSize, entityData.y * mapData->tileSize, 0.f)); + entity->setScale(glm::vec3(mapData->tileSize, mapData->tileSize, 1.f)); entity->addPhysicsComponent( physicsEngine->createObject(entity->getEntityID(), entity->getPosition(), 49.0, PhysicsComponent::Collider::Shape::Circle, - glm::vec3(mapData.tileSize / 2), + glm::vec3(mapData->tileSize / 2), glm::abs(entity->getCenter() - entity->getPosition())) ); @@ -104,7 +103,7 @@ void Scene::loadDebugShooterScene() if (!entityData.script.empty()) { auto behaviourScript = resourceManager->loadAIScript(entityData.script); - auto rayCaster = std::make_shared(40.f, 300.f, map->getCollisionMap(), mapData.tileSize); + auto rayCaster = std::make_shared(40.f, 300.f, map->getCollisionMap(), mapData->tileSize); auto ai = std::make_shared(entity, rayCaster); ai->setTarget(player); ai->attachBehaviourScript(behaviourScript); @@ -114,7 +113,7 @@ void Scene::loadDebugShooterScene() entities.emplace(entity->getEntityID(), entity); } - physicsEngine->loadCollisionMap(map->getCollisionMap(), sceneData->map.tileSize); + physicsEngine->loadCollisionMap(map->getCollisionMap(), mapData->tileSize); // Setup map and other entities... } diff --git a/YuppleMayham/src/gameplay/weapons/weapon.cpp b/YuppleMayham/src/gameplay/weapons/weapon.cpp index 2028e21..d518fd1 100644 --- a/YuppleMayham/src/gameplay/weapons/weapon.cpp +++ b/YuppleMayham/src/gameplay/weapons/weapon.cpp @@ -13,7 +13,7 @@ // TODO: Regular clean up, make this mess readable! -Weapon::Weapon(const WeaponData* data, const std::shared_ptr& weaponShader, const std::shared_ptr& bulletShader, ResourceManager* resourceManager) +Weapon::Weapon(std::shared_ptr data, const std::shared_ptr& weaponShader, const std::shared_ptr& bulletShader, ResourceManager* resourceManager) : Entity (weaponShader), bulletShader (bulletShader), diff --git a/YuppleMayham/src/graphics/instancedraw.cpp b/YuppleMayham/src/graphics/instancedraw.cpp index 4be4138..3a33544 100644 --- a/YuppleMayham/src/graphics/instancedraw.cpp +++ b/YuppleMayham/src/graphics/instancedraw.cpp @@ -58,8 +58,8 @@ void TileTextureInstance::updateInstanceData(const std::vector& in numOfInstances = instanceData.size(); glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); - InstanceData* instances = (InstanceData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, - sizeof(InstanceData) * instanceData.size(), + InstanceData* instances = (InstanceData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, + sizeof(InstanceData) * instanceData.size(), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); if (!instances) return; diff --git a/YuppleMayham/src/graphics/texture.cpp b/YuppleMayham/src/graphics/texture.cpp index 9204e4c..2bc8377 100644 --- a/YuppleMayham/src/graphics/texture.cpp +++ b/YuppleMayham/src/graphics/texture.cpp @@ -24,12 +24,12 @@ bool Texture::loadTexture(const char* imagePath) glBindTexture(GL_TEXTURE_2D, ID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, buffer->w, buffer->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer->pixels); - 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_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glGenerateMipmap(ID); textureWidth = buffer->w; textureHeight = buffer->h; diff --git a/YuppleMayham/src/graphics/tile.cpp b/YuppleMayham/src/graphics/tile.cpp deleted file mode 100644 index 0c6d4fe..0000000 --- a/YuppleMayham/src/graphics/tile.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "graphics/tile.h" -#include "graphics/texture.h" -#include "graphics/tiletype.h" - -TileSet::TileSet(const char* tileSetImage, float frameSize) -{ - texture->loadTexture(tileSetImage); - setupTiles(frameSize); -} - -void TileSet::setupTiles(float frameSize) -{ - int width = texture->getWidth(); - int height = texture->getHeight(); - float frameRows = height / frameSize; - float frameCols = width / frameSize; - int frame = 0; - - float indices[] = { - 0, 1, 2, - 3, 2, 0 - }; - - for (int row = 0; row < frameRows; row++) - { - for (int column = 0; column < frameCols; column++) - { - float left = (column) * (frameSize / width); - float right = (column + 1) * (frameSize / width); - float bottom = (row) * (frameSize / height); - float top = (row + 1) * (frameSize / height); - float vertices[] = { - 0.0f, 0.0f, 0.0f, left, bottom, // bottom left - 1.0f, 0.0f, 0.0f, right, bottom, // bottom right - 1.0f, 1.0f, 0.0f, right, top, // top right - 0.0f, 1.0f, 0.0f, left, top // top left - }; - unsigned VAO, EBO, VBO; - glGenVertexArrays(1, &VAO); - glGenBuffers(1, &EBO); - glGenBuffers(1, &VBO); - - glBindVertexArray(VAO); - glBindBuffer(GL_VERTEX_ARRAY, VBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); - - glBufferData(GL_VERTEX_ARRAY, sizeof(vertices), vertices, GL_STATIC_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); - - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - - glBindBuffer(GL_VERTEX_ARRAY, 0); - glBindVertexArray(0); - - tiles[(TileType)frame++] = std::make_shared(TileTexture({ this->texture, VAO, EBO, VBO })); - } - } -} - -const std::shared_ptr& TileSet::getTileTexture(TileType tileType) const -{ - auto tile = tiles.find(tileType); - if (tile != tiles.end()) - return tile->second; - return nullptr; -} - -TileSet::~TileSet() -{ - delete texture; - for (auto& [type, tile] : tiles) - { - glDeleteBuffers(1, &tile->EBO); - glDeleteBuffers(1, &tile->VBO); - glDeleteVertexArrays(1, &tile->VAO); - } -} diff --git a/YuppleMayham/src/utility/resourcemanager.cpp b/YuppleMayham/src/utility/resourcemanager.cpp index a97f06b..779cbf0 100644 --- a/YuppleMayham/src/utility/resourcemanager.cpp +++ b/YuppleMayham/src/utility/resourcemanager.cpp @@ -1,9 +1,7 @@ #include "utility/resourcemanager.h" #include "utility/component.h" #include "graphics/sprite.h" -#include "graphics/tile.h" #include "utility/script.h" -#include "graphics/tiletype.h" #include "graphics/shader.h" #include "graphics/animation.h" #include "gameplay/weapons/weapons.h" @@ -38,14 +36,9 @@ std::shared_ptr ResourceManager::loadWeaponScript(const std::strin return std::make_shared(path.c_str()); } -std::shared_ptr ResourceManager::loadTileSet(const std::string& path, float frameSize) +std::shared_ptr ResourceManager::loadTileSet(const std::string& name) { - auto iterator = tileSets.find(path); - if (iterator != tileSets.end()) - return iterator->second; - auto tileset = std::make_shared(path.c_str(), frameSize); - tileSets[path] = tileset; - return tileset; + return xmlLoader->getTileSetData(name); } std::shared_ptr ResourceManager::loadShader(const std::string& name, const std::string& vertexPath, const std::string& fragPath) @@ -62,7 +55,7 @@ std::shared_ptr ResourceManager::loadShader(const std::string& name, con // incomplete reference to our script. std::shared_ptr ResourceManager::loadWeapon(const std::string& name, std::shared_ptr weaponShader, std::shared_ptr shader) { - const WeaponData* data = xmlLoader->getWeaponDataByName(name.c_str()); + std::shared_ptr data = xmlLoader->getWeaponData(name); auto weapon = std::make_shared(data, weaponShader, shader, this); if (!data->script.empty()) weapon->attachScript(loadWeaponScript(data->script)); @@ -99,4 +92,5 @@ void ResourceManager::clearResources() sprites.clear(); shaders.clear(); weapons.clear(); + tileSets.clear(); } diff --git a/YuppleMayham/src/utility/xmlloader.cpp b/YuppleMayham/src/utility/xmlloader.cpp index e0f4298..6005c0c 100644 --- a/YuppleMayham/src/utility/xmlloader.cpp +++ b/YuppleMayham/src/utility/xmlloader.cpp @@ -1,21 +1,9 @@ #include "utility/xmlloader.h" -#include "graphics/tiletype.h" - -#include -#include -#include -#include - -const WeaponData* XMLLoader::getWeaponDataByName(const char* name) const -{ - for (int i = 0; i < weaponData.size(); i++) - { - if (weaponData[i].name == name) - return &weaponData[i]; - } - return nullptr; -} +/* + Loading every scene in the scene folder and storing in hashmap scenes + hashkey is the id of the scene +*/ bool XMLLoader::loadScenes(const char* sceneFolder) { std::filesystem::path folder(sceneFolder); @@ -31,6 +19,9 @@ bool XMLLoader::loadScenes(const char* sceneFolder) return true; } +/* + Loading scene data +*/ bool XMLLoader::loadXmlScene(const char* xmlFile, SceneData* out) { tinyxml2::XMLDocument doc; @@ -38,83 +29,29 @@ bool XMLLoader::loadXmlScene(const char* xmlFile, SceneData* out) return false; tinyxml2::XMLElement* scene = doc.FirstChildElement("scene"); - const char* type, * id; + const char* type, * id, * mapName; if (scene->QueryStringAttribute("type", &type) != tinyxml2::XML_SUCCESS || scene->QueryStringAttribute("id", &id) != tinyxml2::XML_SUCCESS) return false; out->type = type; out->id = id; - if (!loadMap(xmlFile, out)) + + tinyxml2::XMLElement* map = scene->FirstChildElement("map"); + if (map == NULL) + return false; + if (map->QueryStringAttribute("name", &mapName) != tinyxml2::XML_SUCCESS) + return false; + + if (!(out->map = getMapData(mapName))) return false; if (!loadEntityData(xmlFile, out)) return false; return true; } -bool XMLLoader::loadMap(const char* xmlFile, SceneData* out) -{ - tinyxml2::XMLDocument doc; - if (doc.LoadFile(xmlFile) != tinyxml2::XML_SUCCESS) - return false; - - tinyxml2::XMLElement* map = doc.FirstChildElement("scene")->FirstChildElement("map"); - const char* name, * file; - if (map->QueryStringAttribute("name", &name) != tinyxml2::XML_SUCCESS || - map->QueryIntAttribute("width", &out->map.width) != tinyxml2::XML_SUCCESS || - map->QueryIntAttribute("height", &out->map.height) != tinyxml2::XML_SUCCESS || - map->QueryStringAttribute("file", &file) != tinyxml2::XML_SUCCESS) - return false; - map->QueryFloatAttribute("tilesize", &out->map.tileSize); - - out->map.name = name; - out->map.file = file; - - auto spriteIDs = map->FirstChildElement("spriteids"); - auto tileData = map->FirstChildElement("tiledata"); - if (spriteIDs == NULL || tileData == NULL) - return false; - - auto data = tileData->FirstChildElement("row"); - - for (tinyxml2::XMLElement* e = spriteIDs->FirstChildElement("row"); e != NULL; e = e->NextSiblingElement("row")) - { - std::vector> buffer; - std::string row = e->Attribute("data", NULL); - std::string dataRow = (data == NULL) ? "0" : data->Attribute("data", NULL); - - std::string tileString; - std::stringstream ss(row); - std::stringstream ssData(dataRow); - - Tile t; - - // TODO: Read TileData and move this loop to make sure we're reading SpriteID refer to xml - while (!ss.eof()) - { - std::getline(ss, tileString, ','); - std::stringstream idss(tileString); - int id; - idss >> id; - if (!ssData.eof()) - { - std::getline(ssData, tileString, ','); - std::stringstream datass(tileString); - int dataID; - datass >> dataID; - t.tileData = (Tile::TileData)dataID; - } - else - t.tileData = (Tile::TileData)1; - t.spriteID = (Tile::SpriteID)id; - buffer.push_back(std::make_shared(t)); - } - if (data != NULL) - data = data->NextSiblingElement("row"); - out->map.groundTiles.push_back(buffer); - } - return true; -} - +/* + Load entity data held in the scene file, store inside of the SceneData out parameter +*/ bool XMLLoader::loadEntityData(const char* xmlFile, SceneData* out) { tinyxml2::XMLDocument doc; @@ -208,6 +145,10 @@ float getFloatIfExists(tinyxml2::XMLElement* e) return buf; } +/* + Load every weapon file, weapon nodes can be fit together in one file or placed in seperate files. + hash key is the weapon name +*/ bool XMLLoader::loadWeapons(const char* weaponFolder) { // We are gonna check every xml file within the weaponFolder, then check every weapon node within each file. @@ -320,12 +261,15 @@ bool XMLLoader::loadWeapons(const char* weaponFolder) modifier->QueryFloatAttribute("max", &data.modMax); } - weaponData.push_back(data); + weapons.try_emplace(data.name, std::make_shared(data)); } } - return (!weaponData.empty()); + return (!weapons.empty()); } +/* + Load every animation file and store inside of hashmap -> animations, filename is the hash key +*/ bool XMLLoader::loadAnimations(const char* animationFolder) { std::filesystem::path folder(animationFolder); @@ -368,4 +312,254 @@ bool XMLLoader::loadAnimations(const char* animationFolder) } } return true; +} + +/* + Start of (LoadTileSets) + + Load every tileset and store them in hashmap tileSets + hashkey is the /, this is due to the expected + file structure, being + Resources + | + - maps + | + - tilesets + Thats is, the tilesets folder is within the maps folder. + This makes it easier to import maps from the Tiled program. + Refer to the Tiled project file for the file structure +*/ +bool XMLLoader::loadTileSets(const char* tileSetFolder) +{ + std::filesystem::path folder(tileSetFolder); + if (!std::filesystem::exists(folder) || !std::filesystem::is_directory(folder)) + return false; + + for (auto& file : std::filesystem::directory_iterator(folder)) + { + if (!file.path().has_extension() || !file.path().has_filename() || !file.exists() || file.is_directory()) + continue; + tinyxml2::XMLDocument doc; + if (doc.LoadFile(file.path().generic_string().c_str()) != tinyxml2::XML_SUCCESS) + continue; + + tinyxml2::XMLElement* tileSet = doc.FirstChildElement("tileset"); + if (tileSet == NULL) + continue; + TileSetData tileSetData; + const char* setName, * setType, * setFile; + + // Read attributes of tileset element + if (tileSet->QueryStringAttribute("name", &setName) != tinyxml2::XML_SUCCESS || + tileSet->QueryStringAttribute("class", &setType) != tinyxml2::XML_SUCCESS || + tileSet->QueryFloatAttribute("tilewidth", &tileSetData.tileSize) != tinyxml2::XML_SUCCESS || + tileSet->QueryIntAttribute("tilecount", &tileSetData.tileCount) != tinyxml2::XML_SUCCESS || + tileSet->QueryIntAttribute("columns", &tileSetData.columns) != tinyxml2::XML_SUCCESS) + continue; + + tinyxml2::XMLElement* image = tileSet->FirstChildElement("image"); + if (image == NULL) + continue; + + // Reading image element attribs + if (image->QueryStringAttribute("source", &setFile) != tinyxml2::XML_SUCCESS || + image->QueryIntAttribute("width", &tileSetData.width) != tinyxml2::XML_SUCCESS || + image->QueryIntAttribute("height", &tileSetData.height) != tinyxml2::XML_SUCCESS) + continue; + + for (tinyxml2::XMLElement* tileElement = tileSet->FirstChildElement("tile"); tileElement != NULL; tileElement = tileElement->NextSiblingElement("tile")) + { + TileSetData::TileData tileData; + if (!loadTile(tileElement, &tileData)) + continue; + tileSetData.tiles.push_back(std::make_shared(tileData)); + } + tileSetData.name = setName; + tileSetData.type = setType; + tileSetData.file = file.path().parent_path().string() + "/" + std::string(setFile); + std::string key = folder.filename().string() + "/" + file.path().filename().string(); + + tileSets.try_emplace(key, std::make_shared(tileSetData)); + } + return true; +} + +bool XMLLoader::loadTile(tinyxml2::XMLElement* tileElement, TileSetData::TileData* out) +{ + TileSetData::TileData tileData; + const char* tileType; + + if (tileElement == NULL) + return false; + if (tileElement->QueryIntAttribute("id", &tileData.id) != tinyxml2::XML_SUCCESS || + tileElement->QueryStringAttribute("type", &tileType) != tinyxml2::XML_SUCCESS) + return false; + + if (std::string(tileType).compare("object") == 0) + { + /* + Refer to .tsx file of tile set for specific details regarding the layout of the tile element. + You will notice each tile has the potential to be a container for objects, so that is how we are + handling it here. If the tileType was specified as an object, but it contains no objects, then we don't + load the tile. Objects are defined in the Tiled program using the collision editor. + */ + tinyxml2::XMLElement* objectGroup = tileElement->FirstChildElement("objectgroup"); + if (objectGroup == NULL) + return false; + for (tinyxml2::XMLElement* obj = objectGroup->FirstChildElement("object"); obj != NULL; obj = obj->NextSiblingElement("object")) + { + TileSetData::TileData::ObjectData objData; + if (!loadObject(obj, &objData)) + continue; + tileData.objects.push_back(std::make_shared(objData)); + } + if (tileData.objects.empty()) + return false; + } + else + { + /* + May support multiple properties in the future with a future property struct to hold any value type + But this may not be needed, so we'll just capture the walkable property for now. + Notice we just return true if there is no property, we can just safely default to walkable = true + */ + tinyxml2::XMLElement* properties = tileElement->FirstChildElement("properties"); + if (properties == NULL) + return true; + tinyxml2::XMLElement* propWalk = properties->FirstChildElement("property"); + if (propWalk == NULL || !propWalk->Attribute("name", "walkable")) + return true; + propWalk->QueryBoolAttribute("value", &tileData.walkable); + } + + tileData.type = tileType; + + *out = tileData; + return true; +} + +bool XMLLoader::loadObject(tinyxml2::XMLElement* objElement, TileSetData::TileData::ObjectData* out) +{ + TileSetData::TileData::ObjectData objData; + const char* objName; + + // avoid null pointer exception + if (objElement == NULL) + return false; + // load id and name + if (objElement->QueryIntAttribute("id", &objData.id) != tinyxml2::XML_SUCCESS || + objElement->QueryStringAttribute("name", &objName) != tinyxml2::XML_SUCCESS) + return false; + // load position into vec2 + if (objElement->QueryFloatAttribute("x", &objData.pos.x) != tinyxml2::XML_SUCCESS || + objElement->QueryFloatAttribute("y", &objData.pos.y) != tinyxml2::XML_SUCCESS) + return false; + // load size into seperate vec2 + if (objElement->QueryFloatAttribute("width", &objData.size.x) != tinyxml2::XML_SUCCESS || + objElement->QueryFloatAttribute("height", &objData.size.y) != tinyxml2::XML_SUCCESS) + return false; + + // refer to comment in XMLLoader::loadTile regarding the properties portion as to why we return true here + tinyxml2::XMLElement* properties = objElement->FirstChildElement("properties"); + if (properties != NULL) + { + tinyxml2::XMLElement* propCollide = properties->FirstChildElement("property"); + if (propCollide != NULL && propCollide->Attribute("name", "collidable")) + propCollide->QueryBoolAttribute("value", &objData.collidable); + } + + objData.name = objName; + + *out = objData; + return true; +} +/* End of (LoadTileSets) */ + +bool XMLLoader::loadMaps(const char* mapFolder) +{ + std::filesystem::path folder(mapFolder); + if (!std::filesystem::exists(folder) || !std::filesystem::is_directory(folder)) + return false; + + for (auto& file : std::filesystem::directory_iterator(folder)) + { + if (!file.path().has_extension() || !file.path().has_filename() || !file.exists() || file.is_directory()) + continue; + tinyxml2::XMLDocument doc; + if (doc.LoadFile(file.path().generic_string().c_str()) != tinyxml2::XML_SUCCESS) + continue; + + tinyxml2::XMLElement* map = doc.FirstChildElement("map"); + if (map == NULL) + continue; + MapData mapData; + const char* tileSetPath; + + if (map->QueryIntAttribute("width", &mapData.width) != tinyxml2::XML_SUCCESS || + map->QueryIntAttribute("height", &mapData.height) != tinyxml2::XML_SUCCESS || + map->QueryFloatAttribute("tilewidth", &mapData.tileSize) != tinyxml2::XML_SUCCESS) + continue; + + tinyxml2::XMLElement* tileSet = map->FirstChildElement("tileset"); + if (tileSet == NULL) + continue; + if (tileSet->QueryStringAttribute("source", &tileSetPath) != tinyxml2::XML_SUCCESS) + continue; + + mapData.tiles.reserve(10); + mapData.tiles.resize(10); + + for (int layer = 0; layer < 10; ++layer) + { + mapData.tiles[layer].reserve(mapData.height); + mapData.tiles[layer].resize(mapData.height); + for (int y = 0; y < mapData.height; ++y) + { + mapData.tiles[layer][y].reserve(mapData.width); + mapData.tiles[layer][y].resize(mapData.width); + } + } + + int layerNumber = 0; + for (tinyxml2::XMLElement* layer = map->FirstChildElement("layer"); layer != NULL; layer = layer->NextSiblingElement("layer")) + { + tinyxml2::XMLElement* data = layer->FirstChildElement("data"); + if (data == NULL) + continue; + if (layerNumber >= 10) + continue; + + std::string idSet = data->GetText(); + std::string tileRow; + std::string tileString; + + std::stringstream ssidSet(idSet); + int x = 0, y = -1; + + while (std::getline(ssidSet, tileRow) && y < mapData.height) + { + std::stringstream ssid(tileRow); + x = 0; + while (std::getline(ssid, tileString, ',') && x < mapData.width) + { + int id = std::stoi(tileString); + mapData.tiles[layerNumber][y][x] = id; + x++; + } + y++; + } + + layerNumber += 1; + } + + mapData.tiles.resize(layerNumber); + mapData.tiles.shrink_to_fit(); + /* TODO: Add object layer */ + + mapData.name = file.path().stem().string(); + mapData.tileSet = tileSetPath; + + maps.try_emplace(mapData.name, std::make_shared(mapData)); + } + return true; } \ No newline at end of file diff --git a/build0.0.0.1/debugScene.xml b/build0.0.0.1/debugScene.xml new file mode 100644 index 0000000..11d39e8 --- /dev/null +++ b/build0.0.0.1/debugScene.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +