ADD: Basic WorldSpriteSystem

This commit is contained in:
sebastian 2026-02-14 14:42:29 +01:00
parent 9f1cc007b1
commit 40a6c2b048
18 changed files with 333 additions and 4 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -14,6 +14,7 @@ void RenderSystem::render(EntityManager &entityManager, MasterRenderer &renderer
auto model = entityManager.getComponent<ModelComponent>(id);
auto tileRenderingComponent = entityManager.getComponent<TileRenderComponent>(id);
auto modelStateComponent = entityManager.getComponent<ModelStateComponent>(id);
auto worldSpriteComponent = entityManager.getComponent<WorldSpriteComponent>(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 {

View File

@ -0,0 +1,5 @@
//
// Created by sebastian on 14.02.26.
//
#include "WorldSpriteComponent.h"

View File

@ -0,0 +1,26 @@
//
// Created by sebastian on 14.02.26.
//
#ifndef DICEWARS_SIEDLER_WORLDSPRITECOMPONENT_H
#define DICEWARS_SIEDLER_WORLDSPRITECOMPONENT_H
#include <memory>
#include "Component.h"
#include "../../renderer/model/TexturedModel.h"
#include "glm/vec2.hpp"
class WorldSpriteComponent : public Component {
public:
std::shared_ptr<ModelTexture> 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

View File

@ -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> entity) {
@ -36,6 +41,15 @@ void MasterRenderer::submitTerrainTile(std::shared_ptr<TransformComponent> trans
terrainTiles[terrain.modelComponent->getActiveModel().get()].push_back(std::make_unique<TerrainRenderingData>(std::move(terrain)));
}
void MasterRenderer::submitWorldSprite(
const std::shared_ptr<TransformComponent>& transform,
const std::shared_ptr<WorldSpriteComponent>& sprite,
const std::shared_ptr<ModelTexture>& 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<float>(Application::getInstance().getWindow().GetWidth()) / static_cast<float>(Application::getInstance().getWindow().GetHeight());

View File

@ -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<TexturedModel*, std::vector<std::unique_ptr<Entity>>> entities;
std::unordered_map<TexturedModel*, std::vector<std::unique_ptr<TerrainRenderingData>>> terrainTiles;
std::unordered_map<ModelTexture*, std::vector<WorldSpriteRenderingData>> worldSprites;
glm::mat4 projectionMatrix;
std::unique_ptr<Renderer> entityRenderer;
std::unique_ptr<TerrainRenderer> terrainRenderer;
std::unique_ptr<WorldSpriteRenderer> 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<Renderer>(projectionMatrix)),
terrainRenderer(std::make_unique<TerrainRenderer>(projectionMatrix))
terrainRenderer(std::make_unique<TerrainRenderer>(projectionMatrix)), worldSpriteRenderer(std::make_unique<WorldSpriteRenderer>(projectionMatrix))
{
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
@ -40,6 +43,8 @@ public:
void submitEntity(std::unique_ptr<Entity> entity);
void submitTerrainTile(std::shared_ptr<TransformComponent> transform, std::shared_ptr<ModelComponent> model, std::shared_ptr<TileRenderComponent>);
void submitWorldSprite(const std::shared_ptr<TransformComponent> &transform, const std::shared_ptr<WorldSpriteComponent> &sprite, const std::
shared_ptr<ModelTexture> &texture);
[[nodiscard]] glm::mat4 getProjectionMatrix() const {return projectionMatrix;}
};

View File

@ -0,0 +1,100 @@
//
// Created by sebastian on 14.02.26.
//
#include "WorldSpriteRenderer.h"
#include <vector>
#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<float> 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<float> uvs = {
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 0.0f,
1.0f, 1.0f
};
std::vector<int> indices = {
0, 1, 2,
2, 1, 3
};
quadModel = std::make_unique<RawModel>(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<ModelTexture*, std::vector<WorldSpriteRenderingData>> 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<glm::mat4>();
//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;
}

View File

@ -0,0 +1,34 @@
//
// Created by sebastian on 14.02.26.
//
#ifndef DICEWARS_SIEDLER_WORLDSPRITERENDERER_H
#define DICEWARS_SIEDLER_WORLDSPRITERENDERER_H
#include <memory>
#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<ModelTexture *, std::vector<WorldSpriteRenderingData>> worldSprites, const Camera &camera);
void finalize();
private:
std::unique_ptr<RawModel> quadModel;
WorldSpriteShader worldSpriteShader;
glm::mat4 projectionMatrix;
static glm::mat4 buildWorldSpriteMatrix(const TransformComponent& objectTransform, const WorldSpriteComponent& worldSpriteComponent, const Camera& camera);
};
#endif //DICEWARS_SIEDLER_WORLDSPRITERENDERER_H

View File

@ -0,0 +1,20 @@
//
// Created by sebastian on 14.02.26.
//
#ifndef DICEWARS_SIEDLER_WORLDSPRITERENDERINGDATA_H
#define DICEWARS_SIEDLER_WORLDSPRITERENDERINGDATA_H
#include <memory>
#include "../../core/ECS/TransformComponent.h"
#include "../../core/ECS/WorldSpriteComponent.h"
#include "../model/RawModel.h"
#include "../textures/ModelTexture.h"
struct WorldSpriteRenderingData {
std::shared_ptr<TransformComponent> worldSpriteTransform;
std::shared_ptr<WorldSpriteComponent> worldSpriteComponent;
std::shared_ptr<ModelTexture> texture;
};
#endif //DICEWARS_SIEDLER_WORLDSPRITERENDERINGDATA_H

View File

@ -29,6 +29,14 @@ RawModel Loader::loadToVAO(const std::vector<float> &vertices) {
return {vaoID, static_cast<int>(vertices.size() / 2)};
}
RawModel Loader::loadToVAO(const std::vector<float>& vertices, const std::vector<float>& textureCoords, const std::vector<int> &indices) {
GLuint vaoID = createVAO();
storeDataInAttributeList(0, 3, vertices);
storeDataInAttributeList(1, 2, textureCoords);
bindIndicesBuffer(indices);
return {vaoID, static_cast<int>(indices.size())};
}
Loader::~Loader() {
cleanUp();
}

View File

@ -20,6 +20,10 @@ public:
RawModel loadToVAO(const std::vector<float> &vertices, const std::vector<float> &normals, const std::vector<float> &textureCoords, const
std::vector<int> &indices);
RawModel loadToVAO(const std::vector<float>& vertices);
RawModel loadToVAO(const std::vector<float> &vertices, const std::vector<float> &textureCoords,
const std::vector<int> &indices);
~Loader();
void cleanUp();

View File

@ -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");
}

View File

@ -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

View File

@ -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>(modelTexture);
entityManager->addComponent(testEntity, std::make_shared<WorldSpriteComponent>(worldspriteComponent));
events.subscribe<TurnChangedEvent>([this](const TurnChangedEvent& event) {
ProducingSystem::onTurnEnded(*entityManager);

View File

@ -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<TexturedModel> 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;
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<TransformComponent>(worldPos, glm::vec3(0), 1.0f);
const auto modelComponent = std::make_shared<ModelComponent>(hexModel, modelName);
const auto tileHighlightComponent = std::make_shared<TileRenderComponent>(false);