added further fixes, and upgrades

This commit is contained in:
2026-03-01 20:51:39 +01:00
parent 769bbd4261
commit f859288689
16 changed files with 937 additions and 24 deletions

View File

@@ -0,0 +1,237 @@
#include "OrdererSpriteSet.hpp"
#include <algorithm>
#include <tuple>
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::Vector3> *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::Vector2i> *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::Vector2> *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::Vector2> *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;
}

View File

@@ -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 <unordered_map>
#include "elements/Sprite.hpp"
#include "elements/TileSet.hpp"
namespace TSE
{
struct OrdererSpriteSetChunk
{
private:
std::vector<Vector3> orderedPositions;
std::vector<Vector2i> orderedSpriteIDs;
std::vector<Vector2> orderedScales;
SortingOrder order;
int chunksize;
std::unordered_map<Vector3, Vector2i> sprites;
std::unordered_map<Vector3, Vector2> 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<Vector3>* GetOrderedPositions();
const std::vector<Vector2i>* GetOrderedSpriteIds();
const std::vector<Vector2>* GetOrderedScales();
int GetChunksize();
int GetSpriteCount();
};
class OrdererSpriteSet : public TSE::BehaviourScript
{
private:
bool dirty = true;
std::vector<Vector2> orderedChunks;
Rect bounds = Rect(0,0,0,0);
public:
int chunkSize = 16;
SortingOrder order = TopRight;
TileSet* set;
std::unordered_map<Vector2, OrdererSpriteSetChunk> 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<Vector2>* GetChunkPositionsInOrder();
int GetChunkCount();
TileSet* GetTileSet();
void DirtyAll();
inline const char* GetName() override
{
return "Orderer Sprite Set";
}
private:
Vector2 LocalToChunkPos(const Vector2& v);
};
} // namespace TSE

View File

@@ -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::Vector2>* TSE::TileMapChunk::GetOrderedPositions()
@@ -43,7 +45,7 @@ const std::vector<TSE::Vector2>* 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::Vector2>* 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::Vector2>* 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::Vector2>* 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);
}

View File

@@ -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<Vector2> orderedPositions;
std::vector<Vector2i> orderedSpriteIDs;
SortingOrder order;
int chunksize;
std::unordered_map<Vector2, Vector2i> 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<Vector2> 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<Vector2, TileMapChunk> 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
{

View File

@@ -0,0 +1,12 @@
#pragma once
namespace TSE
{
enum SortingOrder
{
TopRight,
TopLeft,
BottomRight,
BottomLeft,
};
}

View File

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

View File

@@ -2,6 +2,7 @@
#include "BehaviourScriptRegistry.hpp"
#include "elements/ShaderRegistry.hpp"
#include "BehaviourScripts/Camera.hpp"
#include "windows/HirearchieView.hpp"
#include <algorithm>
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();

View File

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

View File

@@ -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<std::unique_ptr<ShaderPart>> 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<std::unique_ptr<ShaderPart>> &&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<BasicOrderedSpriteSetShader*>(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<Renderable*>(t.GetBehaviourScript(RENDERABLE));
if (!r) return;
auto* tm = dynamic_cast<OrdererSpriteSet*>(t.GetBehaviourScript(ORDERERSPRITESET));
if (!tm) return;
auto tileSet = tm->GetTileSet();
TextureID = tileSet->GetTextueID();
SpriteCount = tileSet->GetCount();
const std::vector<Vector2> 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<Vector3> spritePositions = *chunk->GetOrderedPositions();
const std::vector<Vector2i> spriteIds = *chunk->GetOrderedSpriteIds();
const std::vector<Vector2> 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);
}

View File

@@ -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<std::unique_ptr<ShaderPart>>&& 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

View File

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

View File

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

View File

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

107
TSE_Math/src/Random.cpp Normal file
View File

@@ -0,0 +1,107 @@
#include "Random.hpp"
#include <bit>
#include <cstdint>
TSE::Random::Random(uint seed)
{
state = static_cast<uint>(static_cast<std::uint32_t>(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<std::uint32_t>(state);
nextState = nextState * multiplier + increment;
state = static_cast<uint>(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<std::uint64_t>(maxInc) - static_cast<std::uint64_t>(minInc) + 1ull;
const std::uint64_t offset = static_cast<std::uint64_t>(nextState) % span;
return static_cast<uint>(static_cast<std::uint64_t>(minInc) + offset);
}
int TSE::Random::nextInt(int minInc, int maxInc)
{
const std::uint32_t bits = static_cast<std::uint32_t>(nextUInt());
if(minInc == 0 && maxInc == 0)
{
return static_cast<int>(std::bit_cast<std::int32_t>(bits));
}
if(minInc > maxInc)
{
int temp = minInc;
minInc = maxInc;
maxInc = temp;
}
const std::int64_t span = static_cast<std::int64_t>(maxInc) - static_cast<std::int64_t>(minInc) + 1ll;
const std::uint64_t offset = static_cast<std::uint64_t>(bits) % static_cast<std::uint64_t>(span);
const std::int64_t value = static_cast<std::int64_t>(minInc) + static_cast<std::int64_t>(offset);
return static_cast<int>(value);
}
short TSE::Random::nextShort(short minInc, short maxInc)
{
const std::uint32_t bits = static_cast<std::uint32_t>(nextUInt());
const std::uint16_t lowerBits = static_cast<std::uint16_t>(bits);
if(minInc == 0 && maxInc == 0)
{
return static_cast<short>(std::bit_cast<std::int16_t>(lowerBits));
}
if(minInc > maxInc)
{
short temp = minInc;
minInc = maxInc;
maxInc = temp;
}
const std::int32_t span = static_cast<std::int32_t>(maxInc) - static_cast<std::int32_t>(minInc) + 1;
const std::uint32_t offset = static_cast<std::uint32_t>(lowerBits) % static_cast<std::uint32_t>(span);
const std::int32_t value = static_cast<std::int32_t>(minInc) + static_cast<std::int32_t>(offset);
return static_cast<short>(value);
}
TSE::byte TSE::Random::nextByte(byte minInc, byte maxInc)
{
const std::uint32_t bits = static_cast<std::uint32_t>(nextUInt());
const std::uint8_t lowerBits = static_cast<std::uint8_t>(bits);
if(minInc == 0 && maxInc == 0)
{
return static_cast<byte>(lowerBits);
}
if(minInc > maxInc)
{
byte temp = minInc;
minInc = maxInc;
maxInc = temp;
}
const std::uint16_t span = static_cast<std::uint16_t>(maxInc) - static_cast<std::uint16_t>(minInc) + 1u;
const std::uint16_t offset = static_cast<std::uint16_t>(lowerBits) % span;
return static_cast<byte>(static_cast<std::uint16_t>(minInc) + offset);
}
float TSE::Random::nextFloat01()
{
constexpr float invRange = 1.0f / 4294967296.0f;
return static_cast<float>(static_cast<std::uint32_t>(nextUInt())) * invRange;
}

20
TSE_Math/src/Random.hpp Normal file
View File

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

View File

@@ -1,6 +1,9 @@
#pragma once
#include "Types.hpp"
#include <functional>
#include <cstddef>
#include "MathF.hpp"
namespace TSE
{
@@ -145,3 +148,22 @@ namespace TSE
};
} // namespace TSE
namespace std
{
template<>
struct hash<TSE::Vector3>
{
size_t operator()(const TSE::Vector3& v) const noexcept
{
size_t h1 = std::hash<float>{}(v.x);
size_t h2 = std::hash<float>{}(v.y);
size_t h3 = std::hash<float>{}(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;
}
};
}