From f8592886899fe936be29b1d0268ec86ea46e5ad6cae0dc4d1d4ba31299662c27 Mon Sep 17 00:00:00 2001 From: Mexpert_PRO Date: Sun, 1 Mar 2026 20:51:39 +0100 Subject: [PATCH] added further fixes, and upgrades --- .../src/BehaviourScripts/OrdererSpriteSet.cpp | 237 +++++++++++++++++ .../src/BehaviourScripts/OrdererSpriteSet.hpp | 74 ++++++ TSE_Core/src/BehaviourScripts/TileMap.cpp | 25 +- TSE_Core/src/BehaviourScripts/TileMap.hpp | 18 +- TSE_Core/src/enums/SortingOrder.hpp | 12 + TSE_Editor/src/EditorSubsystem.cpp | 2 +- TSE_Editor/src/UI/ElementDrawer.cpp | 54 +++- TSE_Editor/src/UI/windows/HirearchieView.hpp | 6 +- .../shader/basicOrderedSpriteSetShader.cpp | 238 ++++++++++++++++++ .../shader/basicOrderedSpriteSetShader.hpp | 44 ++++ .../basicOrderedSpriteSetShaderGLSL.hpp | 95 +++++++ .../src/shader/defaultShaderHandler.cpp | 5 + TSE_Math/src/MathF.hpp | 2 +- TSE_Math/src/Random.cpp | 107 ++++++++ TSE_Math/src/Random.hpp | 20 ++ TSE_Math/src/Vector3.hpp | 22 ++ 16 files changed, 937 insertions(+), 24 deletions(-) create mode 100644 TSE_Core/src/BehaviourScripts/OrdererSpriteSet.cpp create mode 100644 TSE_Core/src/BehaviourScripts/OrdererSpriteSet.hpp create mode 100644 TSE_Core/src/enums/SortingOrder.hpp create mode 100644 TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp create mode 100644 TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.hpp create mode 100644 TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShaderGLSL.hpp create mode 100644 TSE_Math/src/Random.cpp create mode 100644 TSE_Math/src/Random.hpp diff --git a/TSE_Core/src/BehaviourScripts/OrdererSpriteSet.cpp b/TSE_Core/src/BehaviourScripts/OrdererSpriteSet.cpp new file mode 100644 index 0000000..f588688 --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/OrdererSpriteSet.cpp @@ -0,0 +1,237 @@ +#include "OrdererSpriteSet.hpp" +#include +#include + +TSE::OrdererSpriteSetChunk::OrdererSpriteSetChunk(int _chunksize, const Vector2 &_pos, SortingOrder _order) +{ + chunksize = _chunksize; + pos = _pos; + order = _order; +} + +void TSE::OrdererSpriteSetChunk::SetSprite(const Vector2 &p, const Vector2 &Spriteindex, const Vector2 &Normalindex, TileSet *set, float height, Vector2& scale) +{ + int normalid = -1; + if(Normalindex != Vector2(-1,-1)) + normalid = set->GetSpriteIdAt(Normalindex.x, Normalindex.y); + sprites[Vector3(p.x, p.y, height)] = {set->GetSpriteIdAt(Spriteindex.x, Spriteindex.y), normalid}; + spriteScales[Vector3(p.x, p.y, height)] = scale; + dirtyPositions = true; + dirtySpriteIds = true; + dirtyScales = true; +} + +void TSE::OrdererSpriteSetChunk::RemoveSprite(Vector2 p, float height) +{ + sprites.erase(Vector3(p.x, p.y, height)); +} + +void TSE::OrdererSpriteSetChunk::SetOrdering(SortingOrder _order) +{ + order = _order; + dirtyPositions = true; + dirtySpriteIds = true; +} + +const std::vector *TSE::OrdererSpriteSetChunk::GetOrderedPositions() +{ + if(dirtyPositions) + { + orderedPositions.clear(); + for(auto pos : sprites) + { + orderedPositions.push_back(pos.first); + } + switch (order) + { + case TopLeft: + std::sort(orderedPositions.begin(), orderedPositions.end(), [](const Vector3& a, const Vector3& b) + { + return std::tie(a.y, a.x) > std::tie(b.y, b.x); + }); + break; + case TopRight: + std::sort(orderedPositions.begin(), orderedPositions.end(), [](const Vector3& a, const Vector3& b) + { + if (a.y != b.y) + return a.y > b.y; + return a.x > b.x; + }); + break; + case BottomLeft: + std::sort(orderedPositions.begin(), orderedPositions.end(), [](const Vector3& a, const Vector3& b) + { + return std::tie(a.y, a.x) < std::tie(b.y, b.x); + }); + break; + case BottomRight: + std::sort(orderedPositions.begin(), orderedPositions.end(), [](const Vector3& a, const Vector3& b) + { + if (a.y != b.y) + return a.y < b.y; + return a.x > b.x; + }); + break; + } + dirtyPositions = false; + } + return &orderedPositions; +} + +const std::vector *TSE::OrdererSpriteSetChunk::GetOrderedSpriteIds() +{ + if(dirtySpriteIds) + { + orderedSpriteIDs.clear(); + + auto tmp = GetOrderedPositions(); + + for(auto& pos : *tmp) + { + auto v = sprites.find(pos); + if(v != sprites.end()) + orderedSpriteIDs.push_back(v->second); + else + orderedSpriteIDs.push_back({0,0}); + } + + dirtySpriteIds = false; + } + return &orderedSpriteIDs; +} + +const std::vector *TSE::OrdererSpriteSetChunk::GetOrderedScales() +{ + if(dirtyScales) + { + orderedScales.clear(); + + auto tmp = GetOrderedPositions(); + + for(auto& pos : *tmp) + { + auto v = spriteScales.find(pos); + if(v != spriteScales.end()) + orderedScales.push_back(v->second); + else + orderedScales.push_back({1,1}); + } + + dirtyScales = false; + } + return &orderedScales; +} + +int TSE::OrdererSpriteSetChunk::GetChunksize() +{ + return chunksize; +} + +int TSE::OrdererSpriteSetChunk::GetSpriteCount() +{ + return sprites.size(); +} + +void TSE::OrdererSpriteSet::RemoveSprite(Vector2 p, float height) +{ + Vector2 chunkInnerPos = LocalToChunkPos(p); + Vector2 chunkIndex = p - chunkInnerPos; + if(chunks.contains(chunkIndex)) + chunks[chunkIndex].RemoveSprite(chunkInnerPos, height); +} + +void TSE::OrdererSpriteSet::SetSprite(Vector2 p, Vector2 Spriteindex, float height, Vector2 scale, Vector2 Normalindex) +{ + Vector2 chunkInnerPos = LocalToChunkPos(p); + Vector2 chunkIndex = p - chunkInnerPos; + if(!chunks.contains(chunkIndex)) + { + dirty = true; + chunks[chunkIndex] = OrdererSpriteSetChunk(chunkSize, chunkIndex, order); + } + chunks[chunkIndex].SetSprite(chunkInnerPos, Spriteindex, Normalindex, set, height, scale); +} + +TSE::OrdererSpriteSetChunk *TSE::OrdererSpriteSet::GetChunk(const Vector2 &pos) +{ + auto chunk = chunks.find(pos); + if(chunk == chunks.end()) + return nullptr; + return &chunks[pos]; +} + +const std::vector *TSE::OrdererSpriteSet::GetChunkPositionsInOrder() +{ + if(dirty) + { + orderedChunks.clear(); + + for(auto pos : chunks) + { + orderedChunks.push_back(pos.first); + } + switch (order) + { + case TopLeft: + std::sort(orderedChunks.begin(), orderedChunks.end(), [](const Vector2& a, const Vector2& b) + { + return std::tie(a.y, a.x) > std::tie(b.y, b.x); + }); + break; + case TopRight: + std::sort(orderedChunks.begin(), orderedChunks.end(), [](const Vector2& a, const Vector2& b) + { + if (a.y != b.y) + return a.y > b.y; + return a.x > b.x; + }); + break; + case BottomLeft: + std::sort(orderedChunks.begin(), orderedChunks.end(), [](const Vector2& a, const Vector2& b) + { + return std::tie(a.y, a.x) < std::tie(b.y, b.x); + }); + break; + case BottomRight: + std::sort(orderedChunks.begin(), orderedChunks.end(), [](const Vector2& a, const Vector2& b) + { + if (a.y != b.y) + return a.y < b.y; + return a.x > b.x; + }); + break; + } + + dirty = false; + } + return &orderedChunks; +} + +int TSE::OrdererSpriteSet::GetChunkCount() +{ + return chunks.size(); +} + +TSE::TileSet *TSE::OrdererSpriteSet::GetTileSet() +{ + return set; +} + +void TSE::OrdererSpriteSet::DirtyAll() +{ + dirty = true; + for(auto& chunk : chunks) + { + chunk.second.dirtyPositions = true; + chunk.second.dirtySpriteIds = true; + chunk.second.dirtyScales = true; + } +} + +TSE::Vector2 TSE::OrdererSpriteSet::LocalToChunkPos(const Vector2 &v) +{ + Vector2 p = Vector2((int)v.x % chunkSize, (int)v.y % chunkSize); + if(p.x < 0) p.x += chunkSize; + if(p.y < 0) p.y += chunkSize; + return p; +} diff --git a/TSE_Core/src/BehaviourScripts/OrdererSpriteSet.hpp b/TSE_Core/src/BehaviourScripts/OrdererSpriteSet.hpp new file mode 100644 index 0000000..5f59f83 --- /dev/null +++ b/TSE_Core/src/BehaviourScripts/OrdererSpriteSet.hpp @@ -0,0 +1,74 @@ +#pragma once + +#define ORDERERSPRITESET typeid(OrdererSpriteSet).name() + +#include "elements/BehaviourScript.hpp" +#include "elements/Transformable.hpp" +#include "Types.hpp" +#include "enums/SortingOrder.hpp" +#include "Vector2.hpp" +#include "Vector2i.hpp" +#include +#include "elements/Sprite.hpp" +#include "elements/TileSet.hpp" + +namespace TSE +{ + struct OrdererSpriteSetChunk + { + private: + std::vector orderedPositions; + std::vector orderedSpriteIDs; + std::vector orderedScales; + SortingOrder order; + int chunksize; + std::unordered_map sprites; + std::unordered_map spriteScales; + public: + bool dirtyPositions = true; + bool dirtySpriteIds = true; + bool dirtyScales = true; + Vector2 pos; + + OrdererSpriteSetChunk(int _chunksize = 16, const Vector2& _pos = {0,0}, SortingOrder _order = TopRight); + void SetSprite(const Vector2& p, const Vector2& Spriteindex, const Vector2& Normalindex, TileSet* set, float height, Vector2& scale); + void RemoveSprite(Vector2 p, float height); + void SetOrdering(SortingOrder _order); + const std::vector* GetOrderedPositions(); + const std::vector* GetOrderedSpriteIds(); + const std::vector* GetOrderedScales(); + int GetChunksize(); + int GetSpriteCount(); + }; + + + class OrdererSpriteSet : public TSE::BehaviourScript + { + private: + bool dirty = true; + std::vector orderedChunks; + Rect bounds = Rect(0,0,0,0); + + public: + int chunkSize = 16; + SortingOrder order = TopRight; + TileSet* set; + std::unordered_map chunks; + + void RemoveSprite(Vector2 p, float height); + void SetSprite(Vector2 p, Vector2 Spriteindex, float height, Vector2 scale, Vector2 Normalindex = {-1,-1}); + OrdererSpriteSetChunk* GetChunk(const Vector2& pos); + const std::vector* GetChunkPositionsInOrder(); + int GetChunkCount(); + TileSet* GetTileSet(); + void DirtyAll(); + + inline const char* GetName() override + { + return "Orderer Sprite Set"; + } + + private: + Vector2 LocalToChunkPos(const Vector2& v); + }; +} // namespace TSE \ No newline at end of file diff --git a/TSE_Core/src/BehaviourScripts/TileMap.cpp b/TSE_Core/src/BehaviourScripts/TileMap.cpp index 6d0e2f1..c763f5d 100644 --- a/TSE_Core/src/BehaviourScripts/TileMap.cpp +++ b/TSE_Core/src/BehaviourScripts/TileMap.cpp @@ -25,6 +25,8 @@ void TSE::TileMapChunk::RemoveTile(Vector2 p) void TSE::TileMapChunk::SetOrdering(SortingOrder _order) { order = _order; + dirtyPositions = true; + dirtySpriteIds = true; } const std::vector* TSE::TileMapChunk::GetOrderedPositions() @@ -43,7 +45,7 @@ const std::vector* TSE::TileMapChunk::GetOrderedPositions() Vector2 p(x,y); auto v = sprites.find(p); if(v != sprites.end()) - orderedPositions.push_back(v->first - offset); + orderedPositions.push_back(v->first * Vector2(nextLine.x, nextLine.y * 0.5f) + Vector2(-nextLine.x * y, nextLine.y * 0.5f * x)); } } break; @@ -56,7 +58,7 @@ const std::vector* TSE::TileMapChunk::GetOrderedPositions() Vector2 p(x,y); auto v = sprites.find(p); if(v != sprites.end()) - orderedPositions.push_back(v->first - offset); + orderedPositions.push_back(v->first * Vector2(nextLine.x, nextLine.y * 0.5f) + Vector2(-nextLine.x * y, nextLine.y * 0.5f * x)); } } break; @@ -69,7 +71,7 @@ const std::vector* TSE::TileMapChunk::GetOrderedPositions() Vector2 p(x,y); auto v = sprites.find(p); if(v != sprites.end()) - orderedPositions.push_back(v->first - offset); + orderedPositions.push_back(v->first * Vector2(nextLine.x, nextLine.y * 0.5f) + Vector2(-nextLine.x * y, nextLine.y * 0.5f * x)); } } break; @@ -82,7 +84,7 @@ const std::vector* TSE::TileMapChunk::GetOrderedPositions() Vector2 p(x,y); auto v = sprites.find(p); if(v != sprites.end()) - orderedPositions.push_back(v->first - offset); + orderedPositions.push_back(v->first * Vector2(nextLine.x, nextLine.y * 0.5f) + Vector2(-nextLine.x * y, nextLine.y * 0.5f * x)); } } break; @@ -271,6 +273,7 @@ void TSE::TileMap::SetNextLineOffset(const Vector2 &offset) { chunk.nextLine = offset; } + DirtyAll(); } TSE::Vector2 TSE::TileMap::GetNextLineOffset() @@ -278,6 +281,16 @@ TSE::Vector2 TSE::TileMap::GetNextLineOffset() return nextLine; } +void TSE::TileMap::DirtyAll() +{ + dirty = true; + for(auto& chunk : chunks) + { + chunk.second.dirtyPositions = true; + chunk.second.dirtySpriteIds = true; + } +} + void TSE::TileMap::CheckBounds(Vector2 pos) { if(pos.x > bounds.p2.x) @@ -305,10 +318,10 @@ TSE::Vector2 TSE::TileMap::ChunkToLocalPos(const Vector2 &v, const TileMapChunk TSE::Vector2 TSE::TileMap::RealPosToTileMapPos(const Vector2 &v) { - return v + nextLine * v.y; + return v * Vector2(nextLine.x, nextLine.y * 0.5f) + Vector2(-nextLine.x * v.y, nextLine.y * 0.5f * v.x); } TSE::Vector2 TSE::TileMap::TileMapToRealPos(const Vector2 &v) { - return v - nextLine * v.y; + return v * Vector2(nextLine.x, nextLine.y * 0.5f) + Vector2(-nextLine.x * v.y, nextLine.y * 0.5f * v.x); } diff --git a/TSE_Core/src/BehaviourScripts/TileMap.hpp b/TSE_Core/src/BehaviourScripts/TileMap.hpp index 1ca0ae9..7ce2e59 100644 --- a/TSE_Core/src/BehaviourScripts/TileMap.hpp +++ b/TSE_Core/src/BehaviourScripts/TileMap.hpp @@ -8,28 +8,21 @@ #include "Vector2i.hpp" #include "elements/Sprite.hpp" #include "elements/TileSet.hpp" +#include "enums/SortingOrder.hpp" namespace TSE { - enum SortingOrder - { - TopRight, - TopLeft, - BottomRight, - BottomLeft, - }; - struct TileMapChunk { private: - bool dirtyPositions = true; - bool dirtySpriteIds = true; std::vector orderedPositions; std::vector orderedSpriteIDs; SortingOrder order; int chunksize; std::unordered_map sprites; public: + bool dirtyPositions = true; + bool dirtySpriteIds = true; Vector2 nextLine; Vector2 pos; TileMapChunk(int _chunksize = 16, const Vector2& _pos = {0,0}, SortingOrder _order = TopRight); @@ -50,10 +43,10 @@ namespace TSE bool dirty = true; std::vector orderedChunks; Rect bounds = Rect(0,0,0,0); - Vector2 nextLine = Vector2(-0.5f, 1.25f); + Vector2 nextLine = Vector2(0.5f, 0.5f); public: int chunkSize = 16; - SortingOrder order = TopRight; + SortingOrder order = BottomRight; Vector2 SpriteScale = Vector2(1,1); TileSet* set; std::unordered_map chunks; @@ -69,6 +62,7 @@ namespace TSE Vector2 GetNextLineOffset(); Vector2 RealPosToTileMapPos(const Vector2& v); Vector2 TileMapToRealPos(const Vector2& v); + void DirtyAll(); inline const char* GetName() override { diff --git a/TSE_Core/src/enums/SortingOrder.hpp b/TSE_Core/src/enums/SortingOrder.hpp new file mode 100644 index 0000000..982712f --- /dev/null +++ b/TSE_Core/src/enums/SortingOrder.hpp @@ -0,0 +1,12 @@ +#pragma once + +namespace TSE +{ + enum SortingOrder + { + TopRight, + TopLeft, + BottomRight, + BottomLeft, + }; +} \ No newline at end of file diff --git a/TSE_Editor/src/EditorSubsystem.cpp b/TSE_Editor/src/EditorSubsystem.cpp index 22c6144..6c2a887 100644 --- a/TSE_Editor/src/EditorSubsystem.cpp +++ b/TSE_Editor/src/EditorSubsystem.cpp @@ -7,7 +7,7 @@ TSE::EDITOR::EditorSubsystem::EditorSubsystem() : sv(nullptr), editorLayer("") { - rt = IRenderTexture::factory->CreateTextureHeap({100,100}, 2); + rt = IRenderTexture::factory->CreateTextureHeap({100,100}, 5); sv = SceneView(rt); controller.AddGuiElement("Scene", &sv); diff --git a/TSE_Editor/src/UI/ElementDrawer.cpp b/TSE_Editor/src/UI/ElementDrawer.cpp index d8b0b05..2d3402d 100644 --- a/TSE_Editor/src/UI/ElementDrawer.cpp +++ b/TSE_Editor/src/UI/ElementDrawer.cpp @@ -2,6 +2,7 @@ #include "BehaviourScriptRegistry.hpp" #include "elements/ShaderRegistry.hpp" #include "BehaviourScripts/Camera.hpp" +#include "windows/HirearchieView.hpp" #include namespace TSE::EDITOR @@ -581,7 +582,7 @@ namespace TSE::EDITOR ImGui::Text("Editor Camera can't be main camera"); } - ImGui::Separator(); + ImGui::Separator(); // Render Scale float renderScale = element->GetRenderScale(); if (ImGui::DragFloat("Render Scale", &renderScale, 0.1f)) @@ -612,6 +613,56 @@ namespace TSE::EDITOR if (ImGui::DragFloat("Field of View (deg)", &fov, 0.1f)) element->SetFov(fov); if (!isPerspective) ImGui::EndDisabled(); + + Scene* curScene = HirearchieView::currentScene; + ImGui::SeparatorText("Layers To Render"); + if (curScene == nullptr) + { + ImGui::TextDisabled("No active scene available."); + return; + } + + if (ImGui::BeginChild("LayersToRender", ImVec2(0, 170), ImGuiChildFlags_Borders)) + { + const int layerCount = curScene->GetLayerCount(); + if (layerCount <= 0) + { + ImGui::TextDisabled("Scene has no layers."); + } + else + { + for (int i = 0; i < layerCount; i++) + { + Layer* layer = curScene->GetLayerAt(i); + if (layer == nullptr) continue; + if (!layer->IsVisual()) continue; + + const uuids::uuid& layerID = layer->GetID(); + auto it = std::find(element->layersNotToRender.begin(), element->layersNotToRender.end(), layerID); + bool shouldRenderLayer = (it == element->layersNotToRender.end()); + + const std::string checkBoxLabel = layer->GetName() + "##layer_to_render_" + std::to_string(i); + if (ImGui::Checkbox(checkBoxLabel.c_str(), &shouldRenderLayer)) + { + if (shouldRenderLayer) + { + auto removeIt = std::find(element->layersNotToRender.begin(), element->layersNotToRender.end(), layerID); + if (removeIt != element->layersNotToRender.end()) + { + element->layersNotToRender.erase(removeIt); + } + } + else if (it == element->layersNotToRender.end()) + { + element->layersNotToRender.push_back(layerID); + } + } + } + } + } + ImGui::EndChild(); + + } void ElementDrawer::Draw(ParticleSystem *element, const bool &debug) { @@ -836,6 +887,7 @@ namespace TSE::EDITOR { chunk.SetOrdering(element->order); } + element->DirtyAll(); } ImGui::BeginDisabled(); diff --git a/TSE_Editor/src/UI/windows/HirearchieView.hpp b/TSE_Editor/src/UI/windows/HirearchieView.hpp index 7add1a7..daf66a2 100644 --- a/TSE_Editor/src/UI/windows/HirearchieView.hpp +++ b/TSE_Editor/src/UI/windows/HirearchieView.hpp @@ -17,9 +17,8 @@ namespace TSE::EDITOR RenamingTransformable = 3, RenamingScene = 4 }; - Scene* currentScene; uuids::uuid selected = uuids::uuid(); - + bool openPopUpNamingLayer = false; bool activatePopUpNamingLayer = false; char inputBuffer[128] = ""; @@ -27,8 +26,9 @@ namespace TSE::EDITOR NamingPopUpPorPuse inputPurpose = Nothing; Transformable* tmpHolder1 = nullptr; Layer* tmpHolder2 = nullptr; - + public: + inline static Scene* currentScene = nullptr; HirearchieView(Scene* s); void SetScene(Scene* s); void Define() override; diff --git a/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp b/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp new file mode 100644 index 0000000..03040c4 --- /dev/null +++ b/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.cpp @@ -0,0 +1,238 @@ +#include "basicOrderedSpriteSetShader.hpp" +#include "BehaviourScripts/Renderable.hpp" +#include "BehaviourScripts/OrdererSpriteSet.hpp" +#include "Color.hpp" +#include "basicOrderedSpriteSetShaderGLSL.hpp" + +using namespace TSE; +using namespace TSE::GLFW; + +#define SHADER_MESH_INDEX 0 +#define SHADER_POS_INDEX 1 +#define SHADER_LAYER_HEIGHT_INDEX 2 +#define SHADER_SPRITE_INDEX 3 +#define SHADER_NORMAL_INDEX 4 +#define SHADER_SCALE_INDEX 5 + +#define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1 + 1 + 1 + 2) + + +TSE::GLFW::BasicOrderedSpriteSetShader* BasicOrderedSpriteSetShader::instance = nullptr; + +TSE::GLFW::BasicOrderedSpriteSetShader *TSE::GLFW::BasicOrderedSpriteSetShader::Instance() +{ + return instance; +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::Destroy() +{ + if(instance != nullptr) + delete instance; + instance = nullptr; +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::Init(float width, float height) +{ + std::vector> parts; + parts.push_back(ShaderPart::LoadFromString(vertOrderedSet, GL_VERTEX_SHADER)); + parts.push_back(ShaderPart::LoadFromString(fragOrderedSet, GL_FRAGMENT_SHADER)); + instance = new BasicOrderedSpriteSetShader(std::move(parts)); + + instance->Enable(); + int texIDs[] = { 0 }; + instance->SetUniform("atlas", 0); + instance->Disable(); +} + +TSE::GLFW::BasicOrderedSpriteSetShader::BasicOrderedSpriteSetShader(std::vector> &&parts) : Shader(parts) +{ + PackageSize = SHADER_PACKAGE_SIZE; +} + +TSE::GLFW::BasicOrderedSpriteSetShader::~BasicOrderedSpriteSetShader() +{ + if (meshVBO) glDeleteBuffers(1, &meshVBO); + if (meshIBO) glDeleteBuffers(1, &meshIBO); +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::SetMesh(const void *verts, int vertCount, int stride, int floatCountPerVertex, int posOffsetBytes, GLenum primitive, const void *indices, int indexCount, GLenum indexType) +{ + GLint prevVAO = 0, prevArrayBuffer = 0, prevElementBuffer = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO); + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prevArrayBuffer); + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &prevElementBuffer); + + if (!meshVBO) glGenBuffers(1, &meshVBO); + glBindBuffer(GL_ARRAY_BUFFER, meshVBO); + glBufferData(GL_ARRAY_BUFFER, vertCount * stride, verts, GL_STATIC_DRAW); + + if (indices && indexCount > 0) + { + if (!meshIBO) glGenBuffers(1, &meshIBO); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshIBO); + GLsizeiptr idxSize = + (indexType == GL_UNSIGNED_INT ? 4 : + indexType == GL_UNSIGNED_SHORT? 2 : 1) * indexCount; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, idxSize, indices, GL_STATIC_DRAW); + meshIndexCount = indexCount; + meshIndexType = indexType; + } + else + { + // Kein Index-Buffer + if (meshIBO) { glDeleteBuffers(1, &meshIBO); meshIBO = 0; } + meshIndexCount = 0; + } + + meshVertexCount = vertCount; + meshStride = stride; + meshPosOffset = posOffsetBytes; + meshPosSize = floatCountPerVertex; + meshPrimitive = primitive; + meshReady = true; + + glBindBuffer(GL_ARRAY_BUFFER, prevArrayBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prevElementBuffer); + glBindVertexArray(prevVAO); +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::OnEnable() const +{ + if (!meshReady) + { + // Fallback: unit-Quad als TRIANGLE_FAN (4 Vertices, 2D Positionen) + const float quad[8] = { -0.5f,0, 0.5f,0, 0.5f,1, -0.5f,1 }; + const_cast(this)->SetMesh( + quad, 4, sizeof(float)*2, 2, 0, GL_TRIANGLE_FAN + ); + } + + GLint prevArrayBuffer = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prevArrayBuffer); + + glBindBuffer(GL_ARRAY_BUFFER, meshVBO); + glEnableVertexAttribArray(SHADER_MESH_INDEX); // LOC_QUAD/pos + glVertexAttribPointer(SHADER_MESH_INDEX, meshPosSize, GL_FLOAT, GL_FALSE, meshStride, (void*)meshPosOffset); + glVertexAttribDivisor(SHADER_MESH_INDEX, 0); // per-vertex (Mesh) + + glBindBuffer(GL_ARRAY_BUFFER, prevArrayBuffer); + + glEnableVertexAttribArray(SHADER_POS_INDEX); + glVertexAttribPointer(SHADER_POS_INDEX, 3, GL_FLOAT, GL_FALSE, PackageSize, (void*)0); + glVertexAttribDivisor(SHADER_POS_INDEX, 1); + + glEnableVertexAttribArray(SHADER_LAYER_HEIGHT_INDEX); + glVertexAttribPointer(SHADER_LAYER_HEIGHT_INDEX, 1, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*3)); + glVertexAttribDivisor(SHADER_LAYER_HEIGHT_INDEX, 1); + + glEnableVertexAttribArray(SHADER_SPRITE_INDEX); + glVertexAttribPointer(SHADER_SPRITE_INDEX, 1, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*4)); + glVertexAttribDivisor(SHADER_SPRITE_INDEX, 1); + + glEnableVertexAttribArray(SHADER_NORMAL_INDEX); + glVertexAttribPointer(SHADER_NORMAL_INDEX, 1, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*5)); + glVertexAttribDivisor(SHADER_NORMAL_INDEX, 1); + + glEnableVertexAttribArray(SHADER_SCALE_INDEX); + glVertexAttribPointer(SHADER_SCALE_INDEX, 2, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*6)); + glVertexAttribDivisor(SHADER_SCALE_INDEX, 1); +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::OnDisable() const +{ + glDisableVertexAttribArray(SHADER_MESH_INDEX); + glDisableVertexAttribArray(SHADER_POS_INDEX); + glDisableVertexAttribArray(SHADER_LAYER_HEIGHT_INDEX); + glDisableVertexAttribArray(SHADER_SPRITE_INDEX); + glDisableVertexAttribArray(SHADER_NORMAL_INDEX); + glDisableVertexAttribArray(SHADER_SCALE_INDEX); +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::OnFlush() +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, TextureID); + glDisable(GL_BLEND); +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::OnDrawCall(int indexCount) +{ + if (instanceCount <= 0) return; + SetUniform("spriteCount", &SpriteCount); + + GLint prevElementBuffer = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &prevElementBuffer); + + if (meshIBO && meshIndexCount > 0) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshIBO); + glDrawElementsInstanced(meshPrimitive, meshIndexCount, meshIndexType, (void*)0, instanceCount); + } + else + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDrawArraysInstanced(meshPrimitive, 0, meshVertexCount, instanceCount); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)prevElementBuffer); +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::OnPostDraw() +{ + glEnable(GL_BLEND); + instanceCount = 0; +} + +void TSE::GLFW::BasicOrderedSpriteSetShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) +{ + auto* r = dynamic_cast(t.GetBehaviourScript(RENDERABLE)); + if (!r) return; + + auto* tm = dynamic_cast(t.GetBehaviourScript(ORDERERSPRITESET)); + if (!tm) return; + + auto tileSet = tm->GetTileSet(); + TextureID = tileSet->GetTextueID(); + SpriteCount = tileSet->GetCount(); + + const std::vector orderedChunks = *tm->GetChunkPositionsInOrder(); + + Matrix4x4 matr = t.GetLocalMatrix(); + + stack.Push(matr); + + for(auto chunkPos : orderedChunks) + { + auto chunk = tm->GetChunk(chunkPos); + const int spriteCount = chunk->GetSpriteCount(); + const std::vector spritePositions = *chunk->GetOrderedPositions(); + const std::vector spriteIds = *chunk->GetOrderedSpriteIds(); + const std::vector spriteScales = *chunk->GetOrderedScales(); + int chunkSize = chunk->GetChunksize(); + + for (int i = 0; i < spriteCount; i++) + { + Matrix4x4 mat = Matrix4x4::ToTranslationMatrix(chunkPos + spritePositions[i].ToVector2()) * Matrix4x4::ToRotationMatrix(Quaternion()) * Matrix4x4::ToScaleMatrix({1,1,1}); + stack.Push(mat); + Vector3 pos = stack.Top() * Vector3(0,0,0); + + *target++ = pos.x; + *target++ = pos.y; + *target++ = pos.z; + *target++ = spritePositions[i].z; + *target++ = spriteIds[i].x; + *target++ = spriteIds[i].y; + *target++ = spriteScales[i].x; + *target++ = spriteScales[i].y; + + ++instanceCount; + stack.Pop(); + + if(instanceCount >= 16000) + restartDrawcall(rnd); + } + } + + stack.Pop(); + restartDrawcall(rnd); +} diff --git a/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.hpp b/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.hpp new file mode 100644 index 0000000..a8786df --- /dev/null +++ b/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShader.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "GL/gl3w.h" +#include "GL/gl.h" +#include "Shader.hpp" +#include "Types.hpp" + +namespace TSE::GLFW +{ + class BasicOrderedSpriteSetShader : public Shader + { + private: + static BasicOrderedSpriteSetShader* instance; + mutable bool meshReady = false; + GLuint meshVBO = 0; + GLuint meshIBO = 0; + GLsizei meshVertexCount = 0; // für DrawArraysInstanced + GLsizei meshIndexCount = 0; // für DrawElementsInstanced + GLenum meshPrimitive = GL_TRIANGLES; + GLenum meshIndexType = GL_UNSIGNED_SHORT; + int instanceCount = 0; // eigener Instanzzähler + GLint meshPosSize = 2; // 2D (Billboard-Formen), für 3D Meshes: 3 + GLsizei meshStride = sizeof(float) * 2; + size_t meshPosOffset = 0; + GLuint TextureID; + Vector2 SpriteCount; + + public: + static BasicOrderedSpriteSetShader* Instance(); + static void Destroy(); + static void Init(float width, float height); + BasicOrderedSpriteSetShader(std::vector>&& parts); + ~BasicOrderedSpriteSetShader(); + void SetMesh(const void* verts, int vertCount, int stride, int floatCountPerVertex, int posOffsetBytes, GLenum primitive, const void* indices = nullptr, int indexCount = 0, GLenum indexType = GL_UNSIGNED_SHORT); + + protected: + void OnEnable() const override; + void OnDisable() const override; + void OnFlush() override; + void OnDrawCall(int indexCount) override; + void OnPostDraw() override; + void OnSubmit(const Transformable& t, float*& target, TransformationStack& stack, void (*restartDrawcall)(IRenderer&), IRenderer& rnd) override; + }; +} // namespace TSE::GLFW diff --git a/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShaderGLSL.hpp b/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShaderGLSL.hpp new file mode 100644 index 0000000..326e43b --- /dev/null +++ b/TSE_GlfwOpenGlImpl/src/shader/basicOrderedSpriteSetShaderGLSL.hpp @@ -0,0 +1,95 @@ +#pragma once + +inline const char* vertOrderedSet = R"( + #version 330 core + + layout(location = 0) in vec2 aPos; + + layout(location = 1) in vec3 iTilePos; + layout(location = 2) in float height; + layout(location = 3) in float iSpriteId; + layout(location = 4) in float iNormalId; + layout(location = 5) in vec2 spriteScale; + + uniform mat4 prMatrix; + uniform mat4 camMatrix; + + out vec2 vUV; + flat out int vSpriteId; + flat out int vNormalId; + flat out float vTileNdcY; + flat out float layerHeight; + + void main() + { + vec3 local = vec3(aPos.x, aPos.y, 0); + vec2 baseUV = aPos + vec2(0.5, 0); + vec3 tileSize = vec3(spriteScale.x, spriteScale.y, 1); + + vec3 worldPos = (iTilePos * tileSize) + (local * tileSize); + + vec4 clip = prMatrix * camMatrix * vec4(worldPos, 1.0); + gl_Position = clip; + + vUV = baseUV; + vSpriteId = int(iSpriteId + 0.5); + vNormalId = int(iNormalId + 0.5); + layerHeight = height; + + vec3 localbottom = vec3(0.5, 0, 0); + vec3 worldPosBottom = (iTilePos * tileSize) + (localbottom * tileSize); + vec4 clipbottom = prMatrix * camMatrix * vec4(worldPosBottom, 1.0); + float ndcY = clipbottom.y / clipbottom.w; + vTileNdcY = ndcY * 0.5 + 0.5; + } +)"; + +inline const char* fragOrderedSet = R"( + #version 330 core + + in vec2 vUV; + flat in int vSpriteId; + flat in int vNormalId; + flat in float vTileNdcY; + flat in float layerHeight; + + uniform sampler2D atlas; + uniform vec2 spriteCount; + + layout(location = 0) out vec4 FragColor; + layout(location = 1) out vec4 FragHeight; + layout(location = 2) out vec4 FragDepth; + + void main() + { + float t = (vTileNdcY + 1.0) * 0.5 *0.8; + FragDepth = vec4(t, 0, 0, 1.0); + + vec2 tileUVSize = 1.0 / spriteCount; + + int cols = int(spriteCount.x); + int sx = vSpriteId % cols; + int sy = vSpriteId / cols; + + vec2 atlasOffset = vec2(float(sx), float(sy)) * tileUVSize; + vec2 atlasUV = atlasOffset + (vUV * tileUVSize); + vec4 c = texture(atlas, atlasUV); + if (c.a < 0.01) discard; + float colorScaler = 1 - ((layerHeight - 1) * -1) * 0.3; + c = vec4(c.x * colorScaler,c.y * colorScaler,c.z * colorScaler,c.w); + + FragColor = c; + + if(vNormalId != -1) + { + int sx2 = vNormalId % cols; + int sy2 = vNormalId / cols; + vec2 atlasOffsetNormal = vec2(float(sx2), float(sy2)) * tileUVSize; + vec2 atlasUVNormal = atlasOffsetNormal + (vUV * tileUVSize); + vec4 cNormal = texture(atlas, atlasUVNormal); + cNormal.w = layerHeight; + + FragHeight = cNormal; + } + } +)"; diff --git a/TSE_GlfwOpenGlImpl/src/shader/defaultShaderHandler.cpp b/TSE_GlfwOpenGlImpl/src/shader/defaultShaderHandler.cpp index c8a51d8..c2c3f9e 100644 --- a/TSE_GlfwOpenGlImpl/src/shader/defaultShaderHandler.cpp +++ b/TSE_GlfwOpenGlImpl/src/shader/defaultShaderHandler.cpp @@ -4,6 +4,7 @@ #include "ditheringShader.hpp" #include "basicParticleShader.hpp" #include "basicTileMapShader.hpp" +#include "basicOrderedSpriteSetShader.hpp" #include "elements/ShaderRegistry.hpp" void TSE::GLFW::LoadBasicShaders(float width, float height) @@ -13,11 +14,13 @@ void TSE::GLFW::LoadBasicShaders(float width, float height) DitheringShader::Init(width, height); BasicParticleShader::Init(width, height); BasicTileMapShader::Init(width, height); + BasicOrderedSpriteSetShader::Init(width, height); ShaderRegistry::SetShader("Basic Unlit Shader", BasicShader::Instance()); ShaderRegistry::SetShader("Basic Unlit Texture Shader", BasicTextureShader::Instance()); ShaderRegistry::SetShader("Basic Unlit Dithering Shader", DitheringShader::Instance()); ShaderRegistry::SetShader("Basic Unlit Particle Shader", BasicParticleShader::Instance()); ShaderRegistry::SetShader("Basic Unlit TileMap Shader", BasicTileMapShader::Instance()); + ShaderRegistry::SetShader("Basic Ordered Sprite Set Shader", BasicOrderedSpriteSetShader::Instance()); } void TSE::GLFW::UnLoadBasicShaders() @@ -27,9 +30,11 @@ void TSE::GLFW::UnLoadBasicShaders() ShaderRegistry::RemoveShader("Basic Unlit Dithering Shader"); ShaderRegistry::RemoveShader("Basic Unlit Particle Shader"); ShaderRegistry::RemoveShader("Basic Unlit TileMap Shader"); + ShaderRegistry::RemoveShader("Basic Ordered Sprite Set Shader"); BasicShader::Destroy(); BasicTextureShader::Destroy(); DitheringShader::Destroy(); BasicParticleShader::Destroy(); BasicTileMapShader::Destroy(); + BasicOrderedSpriteSetShader::Destroy(); } diff --git a/TSE_Math/src/MathF.hpp b/TSE_Math/src/MathF.hpp index f59f368..76c447f 100644 --- a/TSE_Math/src/MathF.hpp +++ b/TSE_Math/src/MathF.hpp @@ -13,7 +13,7 @@ namespace TSE /// @brief 32-bit golden ratio constant used for hash mixing constexpr uint TSE_HASH_GOLDEN_RATIO_32 = 0x9e3779b9u; - + /// @brief a simple degrees to radiant conversion function /// @param deg the degrees value /// @return the radiant value diff --git a/TSE_Math/src/Random.cpp b/TSE_Math/src/Random.cpp new file mode 100644 index 0000000..fa41153 --- /dev/null +++ b/TSE_Math/src/Random.cpp @@ -0,0 +1,107 @@ +#include "Random.hpp" +#include +#include + +TSE::Random::Random(uint seed) +{ + state = static_cast(static_cast(seed)); +} + +TSE::uint TSE::Random::nextUInt(uint minInc, uint maxInc) +{ + constexpr std::uint32_t multiplier = 1664525u; + constexpr std::uint32_t increment = 1013904223u; + + std::uint32_t nextState = static_cast(state); + nextState = nextState * multiplier + increment; + state = static_cast(nextState); + + if(minInc == 0u && maxInc == 0u) + { + return state; + } + + if(minInc > maxInc) + { + uint temp = minInc; + minInc = maxInc; + maxInc = temp; + } + + const std::uint64_t span = static_cast(maxInc) - static_cast(minInc) + 1ull; + const std::uint64_t offset = static_cast(nextState) % span; + return static_cast(static_cast(minInc) + offset); +} + +int TSE::Random::nextInt(int minInc, int maxInc) +{ + const std::uint32_t bits = static_cast(nextUInt()); + + if(minInc == 0 && maxInc == 0) + { + return static_cast(std::bit_cast(bits)); + } + + if(minInc > maxInc) + { + int temp = minInc; + minInc = maxInc; + maxInc = temp; + } + + const std::int64_t span = static_cast(maxInc) - static_cast(minInc) + 1ll; + const std::uint64_t offset = static_cast(bits) % static_cast(span); + const std::int64_t value = static_cast(minInc) + static_cast(offset); + return static_cast(value); +} + +short TSE::Random::nextShort(short minInc, short maxInc) +{ + const std::uint32_t bits = static_cast(nextUInt()); + const std::uint16_t lowerBits = static_cast(bits); + + if(minInc == 0 && maxInc == 0) + { + return static_cast(std::bit_cast(lowerBits)); + } + + if(minInc > maxInc) + { + short temp = minInc; + minInc = maxInc; + maxInc = temp; + } + + const std::int32_t span = static_cast(maxInc) - static_cast(minInc) + 1; + const std::uint32_t offset = static_cast(lowerBits) % static_cast(span); + const std::int32_t value = static_cast(minInc) + static_cast(offset); + return static_cast(value); +} + +TSE::byte TSE::Random::nextByte(byte minInc, byte maxInc) +{ + const std::uint32_t bits = static_cast(nextUInt()); + const std::uint8_t lowerBits = static_cast(bits); + + if(minInc == 0 && maxInc == 0) + { + return static_cast(lowerBits); + } + + if(minInc > maxInc) + { + byte temp = minInc; + minInc = maxInc; + maxInc = temp; + } + + const std::uint16_t span = static_cast(maxInc) - static_cast(minInc) + 1u; + const std::uint16_t offset = static_cast(lowerBits) % span; + return static_cast(static_cast(minInc) + offset); +} + +float TSE::Random::nextFloat01() +{ + constexpr float invRange = 1.0f / 4294967296.0f; + return static_cast(static_cast(nextUInt())) * invRange; +} diff --git a/TSE_Math/src/Random.hpp b/TSE_Math/src/Random.hpp new file mode 100644 index 0000000..a739eae --- /dev/null +++ b/TSE_Math/src/Random.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "Types.hpp" + +namespace TSE +{ + class Random + { + private: + uint state; + public: + Random(uint seed); + + uint nextUInt(uint minInc = 0, uint maxInc = 0); + int nextInt(int minInc = 0, int maxInc = 0); + short nextShort(short minInc = 0, short maxInc = 0); + byte nextByte(byte minInc = 0, byte maxInc = 0); + float nextFloat01(); + }; + +} \ No newline at end of file diff --git a/TSE_Math/src/Vector3.hpp b/TSE_Math/src/Vector3.hpp index 5d91694..687adfa 100644 --- a/TSE_Math/src/Vector3.hpp +++ b/TSE_Math/src/Vector3.hpp @@ -1,6 +1,9 @@ #pragma once #include "Types.hpp" +#include +#include +#include "MathF.hpp" namespace TSE { @@ -145,3 +148,22 @@ namespace TSE }; } // namespace TSE + + +namespace std +{ + template<> + struct hash + { + size_t operator()(const TSE::Vector3& v) const noexcept + { + size_t h1 = std::hash{}(v.x); + size_t h2 = std::hash{}(v.y); + size_t h3 = std::hash{}(v.z); + size_t hash = h1; + hash ^= h2 + TSE::TSE_HASH_GOLDEN_RATIO_32 + (hash << 6) + (hash >> 2); + hash ^= h3 + TSE::TSE_HASH_GOLDEN_RATIO_32 + (hash << 6) + (hash >> 2); + return hash; + } + }; +} \ No newline at end of file