diff --git a/CMakeLists.txt b/CMakeLists.txt index 211d909..17cee1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,6 +185,13 @@ add_executable(Dicewars_Siedler src/main.cpp src/game/hexWorld/ecs/components/ProducingComponent.h src/game/hexWorld/ecs/systems/ProducingSystem.cpp src/game/hexWorld/ecs/systems/ProducingSystem.h + src/engine/core/ECS/WorldSpriteComponent.cpp + src/engine/core/ECS/WorldSpriteComponent.h + src/engine/renderer/WorldSpriteRenderer.cpp + src/engine/renderer/WorldSpriteRenderer.h + src/engine/renderer/components/WorldSpriteRenderingData.h + src/engine/renderer/shaders/WorldSpriteShader.cpp + src/engine/renderer/shaders/WorldSpriteShader.h ) target_compile_options(Dicewars_Siedler PRIVATE diff --git a/assets/shaders/worldSpriteFragmentShader.glsl b/assets/shaders/worldSpriteFragmentShader.glsl new file mode 100644 index 0000000..3a04020 --- /dev/null +++ b/assets/shaders/worldSpriteFragmentShader.glsl @@ -0,0 +1,12 @@ +#version 400 core + +in vec2 passTexCoords; +out vec4 outColor; + +uniform sampler2D spriteTexture; +uniform float alpha; + +void main() { + vec4 tex = texture(spriteTexture, passTexCoords); + outColor = vec4(tex.rgb, tex.a * alpha); +} diff --git a/assets/shaders/worldSpriteVertexShader.glsl b/assets/shaders/worldSpriteVertexShader.glsl new file mode 100644 index 0000000..9725fad --- /dev/null +++ b/assets/shaders/worldSpriteVertexShader.glsl @@ -0,0 +1,14 @@ +#version 400 core + +layout (location = 0) in vec3 position; +layout (location = 1) in vec2 texCoords; + +out vec2 passTexCoords; + +uniform mat4 projectionViewMatrix; +uniform mat4 modelMatrix; + +void main() { + gl_Position = projectionViewMatrix * modelMatrix * vec4(position, 1.0); + passTexCoords = texCoords; +} diff --git a/assets/worldIcons/warning.png b/assets/worldIcons/warning.png new file mode 100644 index 0000000..b22e78c Binary files /dev/null and b/assets/worldIcons/warning.png differ diff --git a/src/engine/core/ECS/RenderSystem.cpp b/src/engine/core/ECS/RenderSystem.cpp index 7f1b424..aa5e866 100644 --- a/src/engine/core/ECS/RenderSystem.cpp +++ b/src/engine/core/ECS/RenderSystem.cpp @@ -14,6 +14,7 @@ void RenderSystem::render(EntityManager &entityManager, MasterRenderer &renderer auto model = entityManager.getComponent(id); auto tileRenderingComponent = entityManager.getComponent(id); auto modelStateComponent = entityManager.getComponent(id); + auto worldSpriteComponent = entityManager.getComponent(id); if (modelStateComponent) { updateModelStage(model.get(), modelStateComponent.get()); @@ -21,6 +22,10 @@ void RenderSystem::render(EntityManager &entityManager, MasterRenderer &renderer if (!transform || !model) continue; + if (worldSpriteComponent) { + renderer.submitWorldSprite(transform, worldSpriteComponent, worldSpriteComponent->texture); + } + if (tileRenderingComponent) { renderer.submitTerrainTile(transform, model, tileRenderingComponent); } else { diff --git a/src/engine/core/ECS/WorldSpriteComponent.cpp b/src/engine/core/ECS/WorldSpriteComponent.cpp new file mode 100644 index 0000000..30ebddc --- /dev/null +++ b/src/engine/core/ECS/WorldSpriteComponent.cpp @@ -0,0 +1,5 @@ +// +// Created by sebastian on 14.02.26. +// + +#include "WorldSpriteComponent.h" \ No newline at end of file diff --git a/src/engine/core/ECS/WorldSpriteComponent.h b/src/engine/core/ECS/WorldSpriteComponent.h new file mode 100644 index 0000000..5602369 --- /dev/null +++ b/src/engine/core/ECS/WorldSpriteComponent.h @@ -0,0 +1,26 @@ +// +// Created by sebastian on 14.02.26. +// + +#ifndef DICEWARS_SIEDLER_WORLDSPRITECOMPONENT_H +#define DICEWARS_SIEDLER_WORLDSPRITECOMPONENT_H +#include + +#include "Component.h" +#include "../../renderer/model/TexturedModel.h" +#include "glm/vec2.hpp" + + +class WorldSpriteComponent : public Component { +public: + std::shared_ptr texture; + + glm::vec3 offset = {0.f, 6.0f, 0.f}; + glm::vec2 scale = glm::vec2(2.f); + bool billboard = true; + + float alpha = 1.0f; +}; + + +#endif //DICEWARS_SIEDLER_WORLDSPRITECOMPONENT_H \ No newline at end of file diff --git a/src/engine/renderer/MasterRenderer.cpp b/src/engine/renderer/MasterRenderer.cpp index 8bbbe3a..e83bea0 100644 --- a/src/engine/renderer/MasterRenderer.cpp +++ b/src/engine/renderer/MasterRenderer.cpp @@ -24,6 +24,11 @@ void MasterRenderer::render(const Light &light, const Camera &camera) { terrainRenderer->finalizeFrame(); terrainTiles.clear(); + worldSpriteRenderer->prepare(camera); + worldSpriteRenderer->render(worldSprites, camera); + worldSpriteRenderer->finalize(); + worldSprites.clear(); + } void MasterRenderer::submitEntity(std::unique_ptr entity) { @@ -36,6 +41,15 @@ void MasterRenderer::submitTerrainTile(std::shared_ptr trans terrainTiles[terrain.modelComponent->getActiveModel().get()].push_back(std::make_unique(std::move(terrain))); } +void MasterRenderer::submitWorldSprite( + const std::shared_ptr& transform, + const std::shared_ptr& sprite, + const std::shared_ptr& texture +) { + WorldSpriteRenderingData world_sprite_rendering_data(transform, sprite, texture); + worldSprites[texture.get()].push_back(world_sprite_rendering_data); +} + glm::mat4 MasterRenderer::createProjectionMatrix() { float aspectRatio = static_cast(Application::getInstance().getWindow().GetWidth()) / static_cast(Application::getInstance().getWindow().GetHeight()); diff --git a/src/engine/renderer/MasterRenderer.h b/src/engine/renderer/MasterRenderer.h index ee1890f..3f3c656 100644 --- a/src/engine/renderer/MasterRenderer.h +++ b/src/engine/renderer/MasterRenderer.h @@ -9,6 +9,7 @@ #include "Renderer.h" #include "TerrainRenderer.h" +#include "WorldSpriteRenderer.h" #include "../layer/entities/Entity.h" #include "model/TexturedModel.h" @@ -20,9 +21,11 @@ class MasterRenderer { private: std::unordered_map>> entities; std::unordered_map>> terrainTiles; + std::unordered_map> worldSprites; glm::mat4 projectionMatrix; std::unique_ptr entityRenderer; std::unique_ptr terrainRenderer; + std::unique_ptr worldSpriteRenderer; constexpr static float FOV = 70.0f; constexpr static float NEAR_PLANE = 0.1f; constexpr static float FAR_PLANE = 1000.0f; @@ -31,7 +34,7 @@ private: public: MasterRenderer() : projectionMatrix(createProjectionMatrix()), entityRenderer(std::make_unique(projectionMatrix)), - terrainRenderer(std::make_unique(projectionMatrix)) + terrainRenderer(std::make_unique(projectionMatrix)), worldSpriteRenderer(std::make_unique(projectionMatrix)) { glEnable(GL_CULL_FACE); glCullFace(GL_BACK); @@ -40,6 +43,8 @@ public: void submitEntity(std::unique_ptr entity); void submitTerrainTile(std::shared_ptr transform, std::shared_ptr model, std::shared_ptr); + void submitWorldSprite(const std::shared_ptr &transform, const std::shared_ptr &sprite, const std:: + shared_ptr &texture); [[nodiscard]] glm::mat4 getProjectionMatrix() const {return projectionMatrix;} }; diff --git a/src/engine/renderer/WorldSpriteRenderer.cpp b/src/engine/renderer/WorldSpriteRenderer.cpp new file mode 100644 index 0000000..e02a590 --- /dev/null +++ b/src/engine/renderer/WorldSpriteRenderer.cpp @@ -0,0 +1,100 @@ +// +// Created by sebastian on 14.02.26. +// + +#include "WorldSpriteRenderer.h" + +#include + +#include "../layer/entities/Camera.h" +#include "../toolbox/MathUtils.h" +#include "glm/ext/matrix_transform.hpp" +#include "loader/Loader.h" + +WorldSpriteRenderer::WorldSpriteRenderer(const glm::mat4& projectionMatrix) : projectionMatrix(projectionMatrix) { + std::vector positions = { + -0.5f, 0.5f, 0.0f, // oben links + -0.5f, -0.5f, 0.0f, // unten links + 0.5f, 0.5f, 0.0f, // oben rechts + 0.5f, -0.5f, 0.0f // unten rechts + }; + + std::vector uvs = { + 0.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 0.0f, + 1.0f, 1.0f + }; + + std::vector indices = { + 0, 1, 2, + 2, 1, 3 + }; + + quadModel = std::make_unique(Loader::instance().loadToVAO(positions, uvs, indices)); +} + +void WorldSpriteRenderer::prepare(const Camera &camera) { + glm::mat4 viewMatrix = MathUtils::createViewMatrix(camera); + glm::mat4 projectionViewMatrix = projectionMatrix * viewMatrix; + worldSpriteShader.start(); + worldSpriteShader.loadProjectionViewMatrix(projectionViewMatrix); +} + +void WorldSpriteRenderer::render(std::unordered_map> worldSprites, const Camera &camera) { + glDepthMask(false); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + for (auto& batch : worldSprites) { + glBindTexture(GL_TEXTURE_2D, batch.first->getTextureID()); + + for (auto& sprite : batch.second) { + glm::mat4 model = buildWorldSpriteMatrix(*sprite.worldSpriteTransform, *sprite.worldSpriteComponent, camera); + worldSpriteShader.loadModelMatrix(model); + worldSpriteShader.loadAlpha(sprite.worldSpriteComponent->alpha); + + glBindVertexArray(quadModel->vaoID); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glDrawElements(GL_TRIANGLES, quadModel->vertexCount, GL_UNSIGNED_INT, 0); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glBindVertexArray(0); + } + } + glDepthMask(true); + glDisable(GL_BLEND); +} + +void WorldSpriteRenderer::finalize() { + worldSpriteShader.stop(); +} + +glm::mat4 WorldSpriteRenderer::buildWorldSpriteMatrix(const TransformComponent &objectTransform, + const WorldSpriteComponent &worldSpriteComponent, + const Camera &camera +) { + glm::vec3 worldPos = objectTransform.position + worldSpriteComponent.offset; + + auto model = glm::identity(); + + //Transform + model = glm::translate(model, worldPos); + + //Billboard (nur y Achse) + if (worldSpriteComponent.billboard) { + glm::vec3 camPos = camera.getPosition(); + glm::vec3 direction = camPos - worldPos; + direction.y = 0.0f; + direction = glm::normalize(direction); + + float angle = std::atan2(direction.x, direction.z); + model = glm::rotate(model, angle, glm::vec3(0, 1, 0)); + } + + //Scale + model = glm::scale(model, glm::vec3(worldSpriteComponent.scale.x, worldSpriteComponent.scale.y, 1.0f)); + + return model; + +} diff --git a/src/engine/renderer/WorldSpriteRenderer.h b/src/engine/renderer/WorldSpriteRenderer.h new file mode 100644 index 0000000..5c821f9 --- /dev/null +++ b/src/engine/renderer/WorldSpriteRenderer.h @@ -0,0 +1,34 @@ +// +// Created by sebastian on 14.02.26. +// + +#ifndef DICEWARS_SIEDLER_WORLDSPRITERENDERER_H +#define DICEWARS_SIEDLER_WORLDSPRITERENDERER_H +#include + +#include "../core/ECS/TransformComponent.h" +#include "../core/ECS/WorldSpriteComponent.h" +#include "components/WorldSpriteRenderingData.h" +#include "glm/mat4x4.hpp" +#include "model/RawModel.h" +#include "shaders/WorldSpriteShader.h" + + +class Camera; + +class WorldSpriteRenderer { +public: + WorldSpriteRenderer(const glm::mat4& projectionMatrix); + void prepare(const Camera &camera); + void render(std::unordered_map> worldSprites, const Camera &camera); + void finalize(); +private: + std::unique_ptr quadModel; + WorldSpriteShader worldSpriteShader; + glm::mat4 projectionMatrix; + + static glm::mat4 buildWorldSpriteMatrix(const TransformComponent& objectTransform, const WorldSpriteComponent& worldSpriteComponent, const Camera& camera); +}; + + +#endif //DICEWARS_SIEDLER_WORLDSPRITERENDERER_H \ No newline at end of file diff --git a/src/engine/renderer/components/WorldSpriteRenderingData.h b/src/engine/renderer/components/WorldSpriteRenderingData.h new file mode 100644 index 0000000..c5c1c8e --- /dev/null +++ b/src/engine/renderer/components/WorldSpriteRenderingData.h @@ -0,0 +1,20 @@ +// +// Created by sebastian on 14.02.26. +// + +#ifndef DICEWARS_SIEDLER_WORLDSPRITERENDERINGDATA_H +#define DICEWARS_SIEDLER_WORLDSPRITERENDERINGDATA_H +#include + +#include "../../core/ECS/TransformComponent.h" +#include "../../core/ECS/WorldSpriteComponent.h" +#include "../model/RawModel.h" +#include "../textures/ModelTexture.h" + +struct WorldSpriteRenderingData { + std::shared_ptr worldSpriteTransform; + std::shared_ptr worldSpriteComponent; + std::shared_ptr texture; +}; + +#endif //DICEWARS_SIEDLER_WORLDSPRITERENDERINGDATA_H \ No newline at end of file diff --git a/src/engine/renderer/loader/Loader.cpp b/src/engine/renderer/loader/Loader.cpp index 9d44df6..c22dc41 100644 --- a/src/engine/renderer/loader/Loader.cpp +++ b/src/engine/renderer/loader/Loader.cpp @@ -29,6 +29,14 @@ RawModel Loader::loadToVAO(const std::vector &vertices) { return {vaoID, static_cast(vertices.size() / 2)}; } +RawModel Loader::loadToVAO(const std::vector& vertices, const std::vector& textureCoords, const std::vector &indices) { + GLuint vaoID = createVAO(); + storeDataInAttributeList(0, 3, vertices); + storeDataInAttributeList(1, 2, textureCoords); + bindIndicesBuffer(indices); + return {vaoID, static_cast(indices.size())}; +} + Loader::~Loader() { cleanUp(); } diff --git a/src/engine/renderer/loader/Loader.h b/src/engine/renderer/loader/Loader.h index dc6652d..9d59c2c 100644 --- a/src/engine/renderer/loader/Loader.h +++ b/src/engine/renderer/loader/Loader.h @@ -20,6 +20,10 @@ public: RawModel loadToVAO(const std::vector &vertices, const std::vector &normals, const std::vector &textureCoords, const std::vector &indices); RawModel loadToVAO(const std::vector& vertices); + + RawModel loadToVAO(const std::vector &vertices, const std::vector &textureCoords, + const std::vector &indices); + ~Loader(); void cleanUp(); diff --git a/src/engine/renderer/shaders/WorldSpriteShader.cpp b/src/engine/renderer/shaders/WorldSpriteShader.cpp new file mode 100644 index 0000000..7c87b71 --- /dev/null +++ b/src/engine/renderer/shaders/WorldSpriteShader.cpp @@ -0,0 +1,28 @@ +// +// Created by sebastian on 14.02.26. +// + +#include "WorldSpriteShader.h" + +void WorldSpriteShader::loadProjectionViewMatrix(const glm::mat4 &matrix) { + loadMatrix(location_projectionViewMatrix, matrix); +} + +void WorldSpriteShader::loadModelMatrix(const glm::mat4 &matrix) { + loadMatrix(location_modelMatrix, matrix); +} + +void WorldSpriteShader::loadAlpha(float alpha) { + loadFloat(location_alpha, alpha); +} + +void WorldSpriteShader::bindAttributes() const { + bindAttribute(0, "position"); + bindAttribute(1, "texCoords"); +} + +void WorldSpriteShader::getAllUniformLocations() { + location_projectionViewMatrix = getUniformLocation("projectionViewMatrix"); + location_modelMatrix = getUniformLocation("modelMatrix"); + location_alpha = getUniformLocation("alpha"); +} diff --git a/src/engine/renderer/shaders/WorldSpriteShader.h b/src/engine/renderer/shaders/WorldSpriteShader.h new file mode 100644 index 0000000..2fd59fb --- /dev/null +++ b/src/engine/renderer/shaders/WorldSpriteShader.h @@ -0,0 +1,36 @@ +// +// Created by sebastian on 14.02.26. +// + +#ifndef DICEWARS_SIEDLER_WORLDSPRITESHADER_H +#define DICEWARS_SIEDLER_WORLDSPRITESHADER_H +#include "ShaderProgram.h" + + +class WorldSpriteShader : public ShaderProgram { +public: + WorldSpriteShader() : ShaderProgram(VERTEX_FILE, FRAGMENT_FILE) { + WorldSpriteShader::bindAttributes(); + WorldSpriteShader::getAllUniformLocations(); + } + + void loadProjectionViewMatrix(const glm::mat4 &matrix); + void loadModelMatrix(const glm::mat4 &matrix); + void loadAlpha(float alpha); + +private: + inline static const std::string VERTEX_FILE = "assets/shaders/worldSpriteVertexShader.glsl"; + inline static const std::string FRAGMENT_FILE = "assets/shaders/worldSpriteFragmentShader.glsl"; + + int location_projectionViewMatrix; + int location_modelMatrix; + int location_alpha; + +protected: + void bindAttributes() const override; + void getAllUniformLocations() override; + +}; + + +#endif //DICEWARS_SIEDLER_WORLDSPRITESHADER_H \ No newline at end of file diff --git a/src/game/GameLayer.cpp b/src/game/GameLayer.cpp index 6e6af60..1e0060d 100644 --- a/src/game/GameLayer.cpp +++ b/src/game/GameLayer.cpp @@ -5,6 +5,7 @@ #include "GameLayer.h" #include "../engine/core/ECS/ModelStateComponent.h" +#include "../engine/core/ECS/WorldSpriteComponent.h" #include "../engine/platform/glfw/InputManager.h" #include "../engine/renderer/Renderer.h" #include "../engine/renderer/loader/OBJLoader.h" @@ -15,6 +16,8 @@ #include "../engine/renderer/loader/AssetManager.h" #include "hexWorld/HexModelFactory.h" #include "hexWorld/MapGenerator.h" +#include "hexWorld/building/BuildingFactory.h" +#include "hexWorld/building/TemporaryBuildingDefinitionFactory.h" #include "hexWorld/ecs/components/ProducingComponent.h" #include "hexWorld/ecs/systems/ProducingSystem.h" #include "hexWorld/events/TurnChangedEvent.h" @@ -52,6 +55,13 @@ void GameLayer::onAttach() AssetManager::loadAsset("assets/buildings/stone_mason/stone_mason.json", loader); AssetManager::loadAsset("assets/buildings/forest_hut/cabin.json", loader); + ModelTexture modelTexture = loader.loadTextureFromFile("assets/worldIcons/warning.png"); + + TransformComponent transformComponent(glm::vec3(0,0,0), glm::vec3(0), 1.0f); + testEntity = BuildingFactory::create(*entityManager, TemporaryBuildingDefinitionFactory::createForestHutDefinition(), transformComponent, 0, 0); + auto worldspriteComponent = WorldSpriteComponent(); + worldspriteComponent.texture = std::make_shared(modelTexture); + entityManager->addComponent(testEntity, std::make_shared(worldspriteComponent)); events.subscribe([this](const TurnChangedEvent& event) { ProducingSystem::onTurnEnded(*entityManager); diff --git a/src/game/hexWorld/MapGenerator.cpp b/src/game/hexWorld/MapGenerator.cpp index d5c4bb1..542bd08 100644 --- a/src/game/hexWorld/MapGenerator.cpp +++ b/src/game/hexWorld/MapGenerator.cpp @@ -32,7 +32,7 @@ void MapGenerator::generateHexMap(Map &map, int width, int height, EntityManager float z = zOffset * r; glm::vec3 worldPos = glm::vec3(x, 0.0f, z); float radius = hexRadius; - + EntityID entityID = entityManager.createEntity(); float randomValue = random.randomFloat(0.0f, 1.0f); RessourceType resourceType = RessourceType::NONE; std::shared_ptr hexModel = AssetManager::getModel("hexModelNone"); @@ -41,7 +41,9 @@ void MapGenerator::generateHexMap(Map &map, int width, int height, EntityManager if (randomValue < 0.5f) { resourceType = RessourceType::WOOD; ForestTileGenerator forestTileGenerator; - tileEntities = forestTileGenerator.generateHexTile(entityManager, random, worldPos); + if (entityID != 0) { //Todo: Only temporary for tests! + tileEntities = forestTileGenerator.generateHexTile(entityManager, random, worldPos); + } hexModel = AssetManager::getModel("hexModelWood"); modelName = "hexModelWood"; } else if (randomValue < 0.75f) { @@ -50,7 +52,6 @@ void MapGenerator::generateHexMap(Map &map, int width, int height, EntityManager modelName = "hexModelStone"; } - EntityID entityID = entityManager.createEntity(); const auto transformComponent = std::make_shared(worldPos, glm::vec3(0), 1.0f); const auto modelComponent = std::make_shared(hexModel, modelName); const auto tileHighlightComponent = std::make_shared(false);