From 40a6c2b04868d9580fb415cefe0b5645cb15acc4 Mon Sep 17 00:00:00 2001 From: sebastian Date: Sat, 14 Feb 2026 14:42:29 +0100 Subject: [PATCH] ADD: Basic WorldSpriteSystem --- CMakeLists.txt | 7 ++ assets/shaders/worldSpriteFragmentShader.glsl | 12 +++ assets/shaders/worldSpriteVertexShader.glsl | 14 +++ assets/worldIcons/warning.png | Bin 0 -> 10731 bytes src/engine/core/ECS/RenderSystem.cpp | 5 + src/engine/core/ECS/WorldSpriteComponent.cpp | 5 + src/engine/core/ECS/WorldSpriteComponent.h | 26 +++++ src/engine/renderer/MasterRenderer.cpp | 14 +++ src/engine/renderer/MasterRenderer.h | 7 +- src/engine/renderer/WorldSpriteRenderer.cpp | 100 ++++++++++++++++++ src/engine/renderer/WorldSpriteRenderer.h | 34 ++++++ .../components/WorldSpriteRenderingData.h | 20 ++++ src/engine/renderer/loader/Loader.cpp | 8 ++ src/engine/renderer/loader/Loader.h | 4 + .../renderer/shaders/WorldSpriteShader.cpp | 28 +++++ .../renderer/shaders/WorldSpriteShader.h | 36 +++++++ src/game/GameLayer.cpp | 10 ++ src/game/hexWorld/MapGenerator.cpp | 7 +- 18 files changed, 333 insertions(+), 4 deletions(-) create mode 100644 assets/shaders/worldSpriteFragmentShader.glsl create mode 100644 assets/shaders/worldSpriteVertexShader.glsl create mode 100644 assets/worldIcons/warning.png create mode 100644 src/engine/core/ECS/WorldSpriteComponent.cpp create mode 100644 src/engine/core/ECS/WorldSpriteComponent.h create mode 100644 src/engine/renderer/WorldSpriteRenderer.cpp create mode 100644 src/engine/renderer/WorldSpriteRenderer.h create mode 100644 src/engine/renderer/components/WorldSpriteRenderingData.h create mode 100644 src/engine/renderer/shaders/WorldSpriteShader.cpp create mode 100644 src/engine/renderer/shaders/WorldSpriteShader.h 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 0000000000000000000000000000000000000000..b22e78c08f57c6b9752bb285087c08e1f1316815 GIT binary patch literal 10731 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 literal 0 HcmV?d00001 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);